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; })() || 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 = sharedStore.state || (sharedStore.state = 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 metadata.facade = it;
274 wmset.call(store$1, it, metadata);
277 get = function (it) {
278 return wmget.call(store$1, it) || {};
280 has$1 = function (it) {
281 return wmhas.call(store$1, it);
284 var STATE = sharedKey('state');
285 hiddenKeys[STATE] = true;
286 set = function (it, metadata) {
287 metadata.facade = it;
288 createNonEnumerableProperty(it, STATE, metadata);
291 get = function (it) {
292 return has(it, STATE) ? it[STATE] : {};
294 has$1 = function (it) {
295 return has(it, STATE);
299 var internalState = {
307 var redefine = createCommonjsModule(function (module) {
308 var getInternalState = internalState.get;
309 var enforceInternalState = internalState.enforce;
310 var TEMPLATE = String(String).split('String');
312 (module.exports = function (O, key, value, options) {
313 var unsafe = options ? !!options.unsafe : false;
314 var simple = options ? !!options.enumerable : false;
315 var noTargetGet = options ? !!options.noTargetGet : false;
317 if (typeof value == 'function') {
318 if (typeof key == 'string' && !has(value, 'name')) {
319 createNonEnumerableProperty(value, 'name', key);
321 state = enforceInternalState(value);
323 state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
326 if (O === global_1) {
327 if (simple) O[key] = value;
328 else setGlobal(key, value);
330 } else if (!unsafe) {
332 } else if (!noTargetGet && O[key]) {
335 if (simple) O[key] = value;
336 else createNonEnumerableProperty(O, key, value);
337 // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
338 })(Function.prototype, 'toString', function toString() {
339 return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
345 var aFunction = function (variable) {
346 return typeof variable == 'function' ? variable : undefined;
349 var getBuiltIn = function (namespace, method) {
350 return arguments.length < 2 ? aFunction(path[namespace]) || aFunction(global_1[namespace])
351 : path[namespace] && path[namespace][method] || global_1[namespace] && global_1[namespace][method];
354 var ceil = Math.ceil;
355 var floor = Math.floor;
357 // `ToInteger` abstract operation
358 // https://tc39.github.io/ecma262/#sec-tointeger
359 var toInteger = function (argument) {
360 return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
365 // `ToLength` abstract operation
366 // https://tc39.github.io/ecma262/#sec-tolength
367 var toLength = function (argument) {
368 return argument > 0 ? min(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
372 var min$1 = Math.min;
374 // Helper for a popular repeating case of the spec:
375 // Let integer be ? ToInteger(index).
376 // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
377 var toAbsoluteIndex = function (index, length) {
378 var integer = toInteger(index);
379 return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
382 // `Array.prototype.{ indexOf, includes }` methods implementation
383 var createMethod = function (IS_INCLUDES) {
384 return function ($this, el, fromIndex) {
385 var O = toIndexedObject($this);
386 var length = toLength(O.length);
387 var index = toAbsoluteIndex(fromIndex, length);
389 // Array#includes uses SameValueZero equality algorithm
390 // eslint-disable-next-line no-self-compare
391 if (IS_INCLUDES && el != el) while (length > index) {
393 // eslint-disable-next-line no-self-compare
394 if (value != value) return true;
395 // Array#indexOf ignores holes, Array#includes - not
396 } else for (;length > index; index++) {
397 if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
398 } return !IS_INCLUDES && -1;
402 var arrayIncludes = {
403 // `Array.prototype.includes` method
404 // https://tc39.github.io/ecma262/#sec-array.prototype.includes
405 includes: createMethod(true),
406 // `Array.prototype.indexOf` method
407 // https://tc39.github.io/ecma262/#sec-array.prototype.indexof
408 indexOf: createMethod(false)
411 var indexOf = arrayIncludes.indexOf;
414 var objectKeysInternal = function (object, names) {
415 var O = toIndexedObject(object);
419 for (key in O) !has(hiddenKeys, key) && has(O, key) && result.push(key);
420 // Don't enum bug & hidden keys
421 while (names.length > i) if (has(O, key = names[i++])) {
422 ~indexOf(result, key) || result.push(key);
427 // IE8- don't enum bug keys
432 'propertyIsEnumerable',
438 var hiddenKeys$1 = enumBugKeys.concat('length', 'prototype');
440 // `Object.getOwnPropertyNames` method
441 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
442 var f$3 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
443 return objectKeysInternal(O, hiddenKeys$1);
446 var objectGetOwnPropertyNames = {
450 var f$4 = Object.getOwnPropertySymbols;
452 var objectGetOwnPropertySymbols = {
456 // all object keys, includes non-enumerable and symbols
457 var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
458 var keys = objectGetOwnPropertyNames.f(anObject(it));
459 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
460 return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
463 var copyConstructorProperties = function (target, source) {
464 var keys = ownKeys(source);
465 var defineProperty = objectDefineProperty.f;
466 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
467 for (var i = 0; i < keys.length; i++) {
469 if (!has(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
473 var replacement = /#|\.prototype\./;
475 var isForced = function (feature, detection) {
476 var value = data[normalize(feature)];
477 return value == POLYFILL ? true
478 : value == NATIVE ? false
479 : typeof detection == 'function' ? fails(detection)
483 var normalize = isForced.normalize = function (string) {
484 return String(string).replace(replacement, '.').toLowerCase();
487 var data = isForced.data = {};
488 var NATIVE = isForced.NATIVE = 'N';
489 var POLYFILL = isForced.POLYFILL = 'P';
491 var isForced_1 = isForced;
493 var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
501 options.target - name of the target object
502 options.global - target is the global object
503 options.stat - export as static methods of target
504 options.proto - export as prototype methods of target
505 options.real - real prototype method for the `pure` version
506 options.forced - export even if the native feature is available
507 options.bind - bind methods to the target, required for the `pure` version
508 options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
509 options.unsafe - use the simple assignment of property instead of delete + defineProperty
510 options.sham - add a flag to not completely full polyfills
511 options.enumerable - export as enumerable property
512 options.noTargetGet - prevent calling a getter on target
514 var _export = function (options, source) {
515 var TARGET = options.target;
516 var GLOBAL = options.global;
517 var STATIC = options.stat;
518 var FORCED, target, key, targetProperty, sourceProperty, descriptor;
522 target = global_1[TARGET] || setGlobal(TARGET, {});
524 target = (global_1[TARGET] || {}).prototype;
526 if (target) for (key in source) {
527 sourceProperty = source[key];
528 if (options.noTargetGet) {
529 descriptor = getOwnPropertyDescriptor$1(target, key);
530 targetProperty = descriptor && descriptor.value;
531 } else targetProperty = target[key];
532 FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
533 // contained in target
534 if (!FORCED && targetProperty !== undefined) {
535 if (typeof sourceProperty === typeof targetProperty) continue;
536 copyConstructorProperties(sourceProperty, targetProperty);
538 // add a flag to not completely full polyfills
539 if (options.sham || (targetProperty && targetProperty.sham)) {
540 createNonEnumerableProperty(sourceProperty, 'sham', true);
543 redefine(target, key, sourceProperty, options);
548 // https://tc39.github.io/ecma262/#sec-date.now
549 _export({ target: 'Date', stat: true }, {
550 now: function now() {
551 return new Date().getTime();
555 var DatePrototype = Date.prototype;
556 var INVALID_DATE = 'Invalid Date';
557 var TO_STRING = 'toString';
558 var nativeDateToString = DatePrototype[TO_STRING];
559 var getTime = DatePrototype.getTime;
561 // `Date.prototype.toString` method
562 // https://tc39.github.io/ecma262/#sec-date.prototype.tostring
563 if (new Date(NaN) + '' != INVALID_DATE) {
564 redefine(DatePrototype, TO_STRING, function toString() {
565 var value = getTime.call(this);
566 // eslint-disable-next-line no-self-compare
567 return value === value ? nativeDateToString.call(this) : INVALID_DATE;
571 var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
572 // Chrome 38 Symbol has incorrect toString conversion
573 // eslint-disable-next-line no-undef
574 return !String(Symbol());
577 var useSymbolAsUid = nativeSymbol
578 // eslint-disable-next-line no-undef
580 // eslint-disable-next-line no-undef
581 && typeof Symbol.iterator == 'symbol';
583 // `IsArray` abstract operation
584 // https://tc39.github.io/ecma262/#sec-isarray
585 var isArray = Array.isArray || function isArray(arg) {
586 return classofRaw(arg) == 'Array';
589 // `ToObject` abstract operation
590 // https://tc39.github.io/ecma262/#sec-toobject
591 var toObject = function (argument) {
592 return Object(requireObjectCoercible(argument));
595 // `Object.keys` method
596 // https://tc39.github.io/ecma262/#sec-object.keys
597 var objectKeys = Object.keys || function keys(O) {
598 return objectKeysInternal(O, enumBugKeys);
601 // `Object.defineProperties` method
602 // https://tc39.github.io/ecma262/#sec-object.defineproperties
603 var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
605 var keys = objectKeys(Properties);
606 var length = keys.length;
609 while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
613 var html = getBuiltIn('document', 'documentElement');
617 var PROTOTYPE = 'prototype';
618 var SCRIPT = 'script';
619 var IE_PROTO = sharedKey('IE_PROTO');
621 var EmptyConstructor = function () { /* empty */ };
623 var scriptTag = function (content) {
624 return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
627 // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
628 var NullProtoObjectViaActiveX = function (activeXDocument) {
629 activeXDocument.write(scriptTag(''));
630 activeXDocument.close();
631 var temp = activeXDocument.parentWindow.Object;
632 activeXDocument = null; // avoid memory leak
636 // Create object with fake `null` prototype: use iframe Object with cleared prototype
637 var NullProtoObjectViaIFrame = function () {
638 // Thrash, waste and sodomy: IE GC bug
639 var iframe = documentCreateElement('iframe');
640 var JS = 'java' + SCRIPT + ':';
642 iframe.style.display = 'none';
643 html.appendChild(iframe);
644 // https://github.com/zloirock/core-js/issues/475
645 iframe.src = String(JS);
646 iframeDocument = iframe.contentWindow.document;
647 iframeDocument.open();
648 iframeDocument.write(scriptTag('document.F=Object'));
649 iframeDocument.close();
650 return iframeDocument.F;
653 // Check for document.domain and active x support
654 // No need to use active x approach when document.domain is not set
655 // see https://github.com/es-shims/es5-shim/issues/150
656 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
659 var NullProtoObject = function () {
661 /* global ActiveXObject */
662 activeXDocument = document.domain && new ActiveXObject('htmlfile');
663 } catch (error) { /* ignore */ }
664 NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
665 var length = enumBugKeys.length;
666 while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]];
667 return NullProtoObject();
670 hiddenKeys[IE_PROTO] = true;
672 // `Object.create` method
673 // https://tc39.github.io/ecma262/#sec-object.create
674 var objectCreate = Object.create || function create(O, Properties) {
677 EmptyConstructor[PROTOTYPE] = anObject(O);
678 result = new EmptyConstructor();
679 EmptyConstructor[PROTOTYPE] = null;
680 // add "__proto__" for Object.getPrototypeOf polyfill
681 result[IE_PROTO] = O;
682 } else result = NullProtoObject();
683 return Properties === undefined ? result : objectDefineProperties(result, Properties);
686 var nativeGetOwnPropertyNames = objectGetOwnPropertyNames.f;
688 var toString$1 = {}.toString;
690 var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
691 ? Object.getOwnPropertyNames(window) : [];
693 var getWindowNames = function (it) {
695 return nativeGetOwnPropertyNames(it);
697 return windowNames.slice();
701 // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
702 var f$5 = function getOwnPropertyNames(it) {
703 return windowNames && toString$1.call(it) == '[object Window]'
705 : nativeGetOwnPropertyNames(toIndexedObject(it));
708 var objectGetOwnPropertyNamesExternal = {
712 var WellKnownSymbolsStore = shared('wks');
713 var Symbol$1 = global_1.Symbol;
714 var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
716 var wellKnownSymbol = function (name) {
717 if (!has(WellKnownSymbolsStore, name)) {
718 if (nativeSymbol && has(Symbol$1, name)) WellKnownSymbolsStore[name] = Symbol$1[name];
719 else WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name);
720 } return WellKnownSymbolsStore[name];
723 var f$6 = wellKnownSymbol;
725 var wellKnownSymbolWrapped = {
729 var defineProperty = objectDefineProperty.f;
731 var defineWellKnownSymbol = function (NAME) {
732 var Symbol = path.Symbol || (path.Symbol = {});
733 if (!has(Symbol, NAME)) defineProperty(Symbol, NAME, {
734 value: wellKnownSymbolWrapped.f(NAME)
738 var defineProperty$1 = objectDefineProperty.f;
742 var TO_STRING_TAG = wellKnownSymbol('toStringTag');
744 var setToStringTag = function (it, TAG, STATIC) {
745 if (it && !has(it = STATIC ? it : it.prototype, TO_STRING_TAG)) {
746 defineProperty$1(it, TO_STRING_TAG, { configurable: true, value: TAG });
750 var aFunction$1 = function (it) {
751 if (typeof it != 'function') {
752 throw TypeError(String(it) + ' is not a function');
756 // optional / simple context binding
757 var functionBindContext = function (fn, that, length) {
759 if (that === undefined) return fn;
761 case 0: return function () {
762 return fn.call(that);
764 case 1: return function (a) {
765 return fn.call(that, a);
767 case 2: return function (a, b) {
768 return fn.call(that, a, b);
770 case 3: return function (a, b, c) {
771 return fn.call(that, a, b, c);
774 return function (/* ...args */) {
775 return fn.apply(that, arguments);
779 var SPECIES = wellKnownSymbol('species');
781 // `ArraySpeciesCreate` abstract operation
782 // https://tc39.github.io/ecma262/#sec-arrayspeciescreate
783 var arraySpeciesCreate = function (originalArray, length) {
785 if (isArray(originalArray)) {
786 C = originalArray.constructor;
787 // cross-realm fallback
788 if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
789 else if (isObject(C)) {
791 if (C === null) C = undefined;
793 } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
798 // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex }` methods implementation
799 var createMethod$1 = function (TYPE) {
800 var IS_MAP = TYPE == 1;
801 var IS_FILTER = TYPE == 2;
802 var IS_SOME = TYPE == 3;
803 var IS_EVERY = TYPE == 4;
804 var IS_FIND_INDEX = TYPE == 6;
805 var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
806 return function ($this, callbackfn, that, specificCreate) {
807 var O = toObject($this);
808 var self = indexedObject(O);
809 var boundFunction = functionBindContext(callbackfn, that, 3);
810 var length = toLength(self.length);
812 var create = specificCreate || arraySpeciesCreate;
813 var target = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;
815 for (;length > index; index++) if (NO_HOLES || index in self) {
817 result = boundFunction(value, index, O);
819 if (IS_MAP) target[index] = result; // map
820 else if (result) switch (TYPE) {
821 case 3: return true; // some
822 case 5: return value; // find
823 case 6: return index; // findIndex
824 case 2: push.call(target, value); // filter
825 } else if (IS_EVERY) return false; // every
828 return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
832 var arrayIteration = {
833 // `Array.prototype.forEach` method
834 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
835 forEach: createMethod$1(0),
836 // `Array.prototype.map` method
837 // https://tc39.github.io/ecma262/#sec-array.prototype.map
838 map: createMethod$1(1),
839 // `Array.prototype.filter` method
840 // https://tc39.github.io/ecma262/#sec-array.prototype.filter
841 filter: createMethod$1(2),
842 // `Array.prototype.some` method
843 // https://tc39.github.io/ecma262/#sec-array.prototype.some
844 some: createMethod$1(3),
845 // `Array.prototype.every` method
846 // https://tc39.github.io/ecma262/#sec-array.prototype.every
847 every: createMethod$1(4),
848 // `Array.prototype.find` method
849 // https://tc39.github.io/ecma262/#sec-array.prototype.find
850 find: createMethod$1(5),
851 // `Array.prototype.findIndex` method
852 // https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
853 findIndex: createMethod$1(6)
856 var $forEach = arrayIteration.forEach;
858 var HIDDEN = sharedKey('hidden');
859 var SYMBOL = 'Symbol';
860 var PROTOTYPE$1 = 'prototype';
861 var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
862 var setInternalState = internalState.set;
863 var getInternalState = internalState.getterFor(SYMBOL);
864 var ObjectPrototype = Object[PROTOTYPE$1];
865 var $Symbol = global_1.Symbol;
866 var $stringify = getBuiltIn('JSON', 'stringify');
867 var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
868 var nativeDefineProperty$1 = objectDefineProperty.f;
869 var nativeGetOwnPropertyNames$1 = objectGetOwnPropertyNamesExternal.f;
870 var nativePropertyIsEnumerable$1 = objectPropertyIsEnumerable.f;
871 var AllSymbols = shared('symbols');
872 var ObjectPrototypeSymbols = shared('op-symbols');
873 var StringToSymbolRegistry = shared('string-to-symbol-registry');
874 var SymbolToStringRegistry = shared('symbol-to-string-registry');
875 var WellKnownSymbolsStore$1 = shared('wks');
876 var QObject = global_1.QObject;
877 // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
878 var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
880 // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
881 var setSymbolDescriptor = descriptors && fails(function () {
882 return objectCreate(nativeDefineProperty$1({}, 'a', {
883 get: function () { return nativeDefineProperty$1(this, 'a', { value: 7 }).a; }
885 }) ? function (O, P, Attributes) {
886 var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype, P);
887 if (ObjectPrototypeDescriptor) delete ObjectPrototype[P];
888 nativeDefineProperty$1(O, P, Attributes);
889 if (ObjectPrototypeDescriptor && O !== ObjectPrototype) {
890 nativeDefineProperty$1(ObjectPrototype, P, ObjectPrototypeDescriptor);
892 } : nativeDefineProperty$1;
894 var wrap = function (tag, description) {
895 var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
896 setInternalState(symbol, {
899 description: description
901 if (!descriptors) symbol.description = description;
905 var isSymbol = useSymbolAsUid ? function (it) {
906 return typeof it == 'symbol';
908 return Object(it) instanceof $Symbol;
911 var $defineProperty = function defineProperty(O, P, Attributes) {
912 if (O === ObjectPrototype) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
914 var key = toPrimitive(P, true);
915 anObject(Attributes);
916 if (has(AllSymbols, key)) {
917 if (!Attributes.enumerable) {
918 if (!has(O, HIDDEN)) nativeDefineProperty$1(O, HIDDEN, createPropertyDescriptor(1, {}));
919 O[HIDDEN][key] = true;
921 if (has(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
922 Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
923 } return setSymbolDescriptor(O, key, Attributes);
924 } return nativeDefineProperty$1(O, key, Attributes);
927 var $defineProperties = function defineProperties(O, Properties) {
929 var properties = toIndexedObject(Properties);
930 var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
931 $forEach(keys, function (key) {
932 if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
937 var $create = function create(O, Properties) {
938 return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
941 var $propertyIsEnumerable = function propertyIsEnumerable(V) {
942 var P = toPrimitive(V, true);
943 var enumerable = nativePropertyIsEnumerable$1.call(this, P);
944 if (this === ObjectPrototype && has(AllSymbols, P) && !has(ObjectPrototypeSymbols, P)) return false;
945 return enumerable || !has(this, P) || !has(AllSymbols, P) || has(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
948 var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
949 var it = toIndexedObject(O);
950 var key = toPrimitive(P, true);
951 if (it === ObjectPrototype && has(AllSymbols, key) && !has(ObjectPrototypeSymbols, key)) return;
952 var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
953 if (descriptor && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) {
954 descriptor.enumerable = true;
959 var $getOwnPropertyNames = function getOwnPropertyNames(O) {
960 var names = nativeGetOwnPropertyNames$1(toIndexedObject(O));
962 $forEach(names, function (key) {
963 if (!has(AllSymbols, key) && !has(hiddenKeys, key)) result.push(key);
968 var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
969 var IS_OBJECT_PROTOTYPE = O === ObjectPrototype;
970 var names = nativeGetOwnPropertyNames$1(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
972 $forEach(names, function (key) {
973 if (has(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has(ObjectPrototype, key))) {
974 result.push(AllSymbols[key]);
980 // `Symbol` constructor
981 // https://tc39.github.io/ecma262/#sec-symbol-constructor
983 $Symbol = function Symbol() {
984 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
985 var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
986 var tag = uid(description);
987 var setter = function (value) {
988 if (this === ObjectPrototype) setter.call(ObjectPrototypeSymbols, value);
989 if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
990 setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
992 if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype, tag, { configurable: true, set: setter });
993 return wrap(tag, description);
996 redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
997 return getInternalState(this).tag;
1000 redefine($Symbol, 'withoutSetter', function (description) {
1001 return wrap(uid(description), description);
1004 objectPropertyIsEnumerable.f = $propertyIsEnumerable;
1005 objectDefineProperty.f = $defineProperty;
1006 objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
1007 objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1008 objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1010 wellKnownSymbolWrapped.f = function (name) {
1011 return wrap(wellKnownSymbol(name), name);
1015 // https://github.com/tc39/proposal-Symbol-description
1016 nativeDefineProperty$1($Symbol[PROTOTYPE$1], 'description', {
1018 get: function description() {
1019 return getInternalState(this).description;
1023 redefine(ObjectPrototype, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1028 _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1032 $forEach(objectKeys(WellKnownSymbolsStore$1), function (name) {
1033 defineWellKnownSymbol(name);
1036 _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1037 // `Symbol.for` method
1038 // https://tc39.github.io/ecma262/#sec-symbol.for
1039 'for': function (key) {
1040 var string = String(key);
1041 if (has(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1042 var symbol = $Symbol(string);
1043 StringToSymbolRegistry[string] = symbol;
1044 SymbolToStringRegistry[symbol] = string;
1047 // `Symbol.keyFor` method
1048 // https://tc39.github.io/ecma262/#sec-symbol.keyfor
1049 keyFor: function keyFor(sym) {
1050 if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol');
1051 if (has(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1053 useSetter: function () { USE_SETTER = true; },
1054 useSimple: function () { USE_SETTER = false; }
1057 _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1058 // `Object.create` method
1059 // https://tc39.github.io/ecma262/#sec-object.create
1061 // `Object.defineProperty` method
1062 // https://tc39.github.io/ecma262/#sec-object.defineproperty
1063 defineProperty: $defineProperty,
1064 // `Object.defineProperties` method
1065 // https://tc39.github.io/ecma262/#sec-object.defineproperties
1066 defineProperties: $defineProperties,
1067 // `Object.getOwnPropertyDescriptor` method
1068 // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptors
1069 getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1072 _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1073 // `Object.getOwnPropertyNames` method
1074 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
1075 getOwnPropertyNames: $getOwnPropertyNames,
1076 // `Object.getOwnPropertySymbols` method
1077 // https://tc39.github.io/ecma262/#sec-object.getownpropertysymbols
1078 getOwnPropertySymbols: $getOwnPropertySymbols
1081 // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1082 // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1083 _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1084 getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1085 return objectGetOwnPropertySymbols.f(toObject(it));
1089 // `JSON.stringify` method behavior with symbols
1090 // https://tc39.github.io/ecma262/#sec-json.stringify
1092 var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1093 var symbol = $Symbol();
1094 // MS Edge converts symbol values to JSON as {}
1095 return $stringify([symbol]) != '[null]'
1096 // WebKit converts symbol values to JSON as null
1097 || $stringify({ a: symbol }) != '{}'
1098 // V8 throws on boxed symbols
1099 || $stringify(Object(symbol)) != '{}';
1102 _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1103 // eslint-disable-next-line no-unused-vars
1104 stringify: function stringify(it, replacer, space) {
1108 while (arguments.length > index) args.push(arguments[index++]);
1109 $replacer = replacer;
1110 if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
1111 if (!isArray(replacer)) replacer = function (key, value) {
1112 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1113 if (!isSymbol(value)) return value;
1116 return $stringify.apply(null, args);
1121 // `Symbol.prototype[@@toPrimitive]` method
1122 // https://tc39.github.io/ecma262/#sec-symbol.prototype-@@toprimitive
1123 if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1124 createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1126 // `Symbol.prototype[@@toStringTag]` property
1127 // https://tc39.github.io/ecma262/#sec-symbol.prototype-@@tostringtag
1128 setToStringTag($Symbol, SYMBOL);
1130 hiddenKeys[HIDDEN] = true;
1132 var defineProperty$2 = objectDefineProperty.f;
1135 var NativeSymbol = global_1.Symbol;
1137 if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1139 NativeSymbol().description !== undefined
1141 var EmptyStringDescriptionStore = {};
1142 // wrap Symbol constructor for correct work with undefined description
1143 var SymbolWrapper = function Symbol() {
1144 var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1145 var result = this instanceof SymbolWrapper
1146 ? new NativeSymbol(description)
1147 // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1148 : description === undefined ? NativeSymbol() : NativeSymbol(description);
1149 if (description === '') EmptyStringDescriptionStore[result] = true;
1152 copyConstructorProperties(SymbolWrapper, NativeSymbol);
1153 var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1154 symbolPrototype.constructor = SymbolWrapper;
1156 var symbolToString = symbolPrototype.toString;
1157 var native = String(NativeSymbol('test')) == 'Symbol(test)';
1158 var regexp = /^Symbol\((.*)\)[^)]+$/;
1159 defineProperty$2(symbolPrototype, 'description', {
1161 get: function description() {
1162 var symbol = isObject(this) ? this.valueOf() : this;
1163 var string = symbolToString.call(symbol);
1164 if (has(EmptyStringDescriptionStore, symbol)) return '';
1165 var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1166 return desc === '' ? undefined : desc;
1170 _export({ global: true, forced: true }, {
1171 Symbol: SymbolWrapper
1175 // `Symbol.iterator` well-known symbol
1176 // https://tc39.github.io/ecma262/#sec-symbol.iterator
1177 defineWellKnownSymbol('iterator');
1179 var arrayMethodIsStrict = function (METHOD_NAME, argument) {
1180 var method = [][METHOD_NAME];
1181 return !!method && fails(function () {
1182 // eslint-disable-next-line no-useless-call,no-throw-literal
1183 method.call(null, argument || function () { throw 1; }, 1);
1187 var defineProperty$3 = Object.defineProperty;
1190 var thrower = function (it) { throw it; };
1192 var arrayMethodUsesToLength = function (METHOD_NAME, options) {
1193 if (has(cache, METHOD_NAME)) return cache[METHOD_NAME];
1194 if (!options) options = {};
1195 var method = [][METHOD_NAME];
1196 var ACCESSORS = has(options, 'ACCESSORS') ? options.ACCESSORS : false;
1197 var argument0 = has(options, 0) ? options[0] : thrower;
1198 var argument1 = has(options, 1) ? options[1] : undefined;
1200 return cache[METHOD_NAME] = !!method && !fails(function () {
1201 if (ACCESSORS && !descriptors) return true;
1202 var O = { length: -1 };
1204 if (ACCESSORS) defineProperty$3(O, 1, { enumerable: true, get: thrower });
1207 method.call(O, argument0, argument1);
1211 var $forEach$1 = arrayIteration.forEach;
1215 var STRICT_METHOD = arrayMethodIsStrict('forEach');
1216 var USES_TO_LENGTH = arrayMethodUsesToLength('forEach');
1218 // `Array.prototype.forEach` method implementation
1219 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
1220 var arrayForEach = (!STRICT_METHOD || !USES_TO_LENGTH) ? function forEach(callbackfn /* , thisArg */) {
1221 return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
1224 // `Array.prototype.forEach` method
1225 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
1226 _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
1227 forEach: arrayForEach
1230 var $indexOf = arrayIncludes.indexOf;
1234 var nativeIndexOf = [].indexOf;
1236 var NEGATIVE_ZERO = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
1237 var STRICT_METHOD$1 = arrayMethodIsStrict('indexOf');
1238 var USES_TO_LENGTH$1 = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
1240 // `Array.prototype.indexOf` method
1241 // https://tc39.github.io/ecma262/#sec-array.prototype.indexof
1242 _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO || !STRICT_METHOD$1 || !USES_TO_LENGTH$1 }, {
1243 indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
1244 return NEGATIVE_ZERO
1246 ? nativeIndexOf.apply(this, arguments) || 0
1247 : $indexOf(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
1251 // `Array.isArray` method
1252 // https://tc39.github.io/ecma262/#sec-array.isarray
1253 _export({ target: 'Array', stat: true }, {
1257 var UNSCOPABLES = wellKnownSymbol('unscopables');
1258 var ArrayPrototype = Array.prototype;
1260 // Array.prototype[@@unscopables]
1261 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
1262 if (ArrayPrototype[UNSCOPABLES] == undefined) {
1263 objectDefineProperty.f(ArrayPrototype, UNSCOPABLES, {
1265 value: objectCreate(null)
1269 // add a key to Array.prototype[@@unscopables]
1270 var addToUnscopables = function (key) {
1271 ArrayPrototype[UNSCOPABLES][key] = true;
1276 var correctPrototypeGetter = !fails(function () {
1277 function F() { /* empty */ }
1278 F.prototype.constructor = null;
1279 return Object.getPrototypeOf(new F()) !== F.prototype;
1282 var IE_PROTO$1 = sharedKey('IE_PROTO');
1283 var ObjectPrototype$1 = Object.prototype;
1285 // `Object.getPrototypeOf` method
1286 // https://tc39.github.io/ecma262/#sec-object.getprototypeof
1287 var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
1289 if (has(O, IE_PROTO$1)) return O[IE_PROTO$1];
1290 if (typeof O.constructor == 'function' && O instanceof O.constructor) {
1291 return O.constructor.prototype;
1292 } return O instanceof Object ? ObjectPrototype$1 : null;
1295 var ITERATOR = wellKnownSymbol('iterator');
1296 var BUGGY_SAFARI_ITERATORS = false;
1298 var returnThis = function () { return this; };
1300 // `%IteratorPrototype%` object
1301 // https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object
1302 var IteratorPrototype, PrototypeOfArrayIteratorPrototype, arrayIterator;
1305 arrayIterator = [].keys();
1306 // Safari 8 has buggy iterators w/o `next`
1307 if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS = true;
1309 PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
1310 if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype = PrototypeOfArrayIteratorPrototype;
1314 if (IteratorPrototype == undefined) IteratorPrototype = {};
1316 // 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
1317 if ( !has(IteratorPrototype, ITERATOR)) {
1318 createNonEnumerableProperty(IteratorPrototype, ITERATOR, returnThis);
1321 var iteratorsCore = {
1322 IteratorPrototype: IteratorPrototype,
1323 BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS
1326 var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1332 var returnThis$1 = function () { return this; };
1334 var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1335 var TO_STRING_TAG = NAME + ' Iterator';
1336 IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1337 setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1338 iterators[TO_STRING_TAG] = returnThis$1;
1339 return IteratorConstructor;
1342 var aPossiblePrototype = function (it) {
1343 if (!isObject(it) && it !== null) {
1344 throw TypeError("Can't set " + String(it) + ' as a prototype');
1348 // `Object.setPrototypeOf` method
1349 // https://tc39.github.io/ecma262/#sec-object.setprototypeof
1350 // Works with __proto__ only. Old v8 can't work with null proto objects.
1351 /* eslint-disable no-proto */
1352 var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1353 var CORRECT_SETTER = false;
1357 setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1358 setter.call(test, []);
1359 CORRECT_SETTER = test instanceof Array;
1360 } catch (error) { /* empty */ }
1361 return function setPrototypeOf(O, proto) {
1363 aPossiblePrototype(proto);
1364 if (CORRECT_SETTER) setter.call(O, proto);
1365 else O.__proto__ = proto;
1370 var IteratorPrototype$2 = iteratorsCore.IteratorPrototype;
1371 var BUGGY_SAFARI_ITERATORS$1 = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1372 var ITERATOR$1 = wellKnownSymbol('iterator');
1374 var VALUES = 'values';
1375 var ENTRIES = 'entries';
1377 var returnThis$2 = function () { return this; };
1379 var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1380 createIteratorConstructor(IteratorConstructor, NAME, next);
1382 var getIterationMethod = function (KIND) {
1383 if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1384 if (!BUGGY_SAFARI_ITERATORS$1 && KIND in IterablePrototype) return IterablePrototype[KIND];
1386 case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1387 case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1388 case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1389 } return function () { return new IteratorConstructor(this); };
1392 var TO_STRING_TAG = NAME + ' Iterator';
1393 var INCORRECT_VALUES_NAME = false;
1394 var IterablePrototype = Iterable.prototype;
1395 var nativeIterator = IterablePrototype[ITERATOR$1]
1396 || IterablePrototype['@@iterator']
1397 || DEFAULT && IterablePrototype[DEFAULT];
1398 var defaultIterator = !BUGGY_SAFARI_ITERATORS$1 && nativeIterator || getIterationMethod(DEFAULT);
1399 var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1400 var CurrentIteratorPrototype, methods, KEY;
1403 if (anyNativeIterator) {
1404 CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1405 if (IteratorPrototype$2 !== Object.prototype && CurrentIteratorPrototype.next) {
1406 if ( objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype$2) {
1407 if (objectSetPrototypeOf) {
1408 objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype$2);
1409 } else if (typeof CurrentIteratorPrototype[ITERATOR$1] != 'function') {
1410 createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$1, returnThis$2);
1413 // Set @@toStringTag to native iterators
1414 setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1418 // fix Array#{values, @@iterator}.name in V8 / FF
1419 if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1420 INCORRECT_VALUES_NAME = true;
1421 defaultIterator = function values() { return nativeIterator.call(this); };
1425 if ( IterablePrototype[ITERATOR$1] !== defaultIterator) {
1426 createNonEnumerableProperty(IterablePrototype, ITERATOR$1, defaultIterator);
1428 iterators[NAME] = defaultIterator;
1430 // export additional methods
1433 values: getIterationMethod(VALUES),
1434 keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1435 entries: getIterationMethod(ENTRIES)
1437 if (FORCED) for (KEY in methods) {
1438 if (BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1439 redefine(IterablePrototype, KEY, methods[KEY]);
1441 } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME }, methods);
1447 var ARRAY_ITERATOR = 'Array Iterator';
1448 var setInternalState$1 = internalState.set;
1449 var getInternalState$1 = internalState.getterFor(ARRAY_ITERATOR);
1451 // `Array.prototype.entries` method
1452 // https://tc39.github.io/ecma262/#sec-array.prototype.entries
1453 // `Array.prototype.keys` method
1454 // https://tc39.github.io/ecma262/#sec-array.prototype.keys
1455 // `Array.prototype.values` method
1456 // https://tc39.github.io/ecma262/#sec-array.prototype.values
1457 // `Array.prototype[@@iterator]` method
1458 // https://tc39.github.io/ecma262/#sec-array.prototype-@@iterator
1459 // `CreateArrayIterator` internal method
1460 // https://tc39.github.io/ecma262/#sec-createarrayiterator
1461 var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1462 setInternalState$1(this, {
1463 type: ARRAY_ITERATOR,
1464 target: toIndexedObject(iterated), // target
1465 index: 0, // next index
1468 // `%ArrayIteratorPrototype%.next` method
1469 // https://tc39.github.io/ecma262/#sec-%arrayiteratorprototype%.next
1471 var state = getInternalState$1(this);
1472 var target = state.target;
1473 var kind = state.kind;
1474 var index = state.index++;
1475 if (!target || index >= target.length) {
1476 state.target = undefined;
1477 return { value: undefined, done: true };
1479 if (kind == 'keys') return { value: index, done: false };
1480 if (kind == 'values') return { value: target[index], done: false };
1481 return { value: [index, target[index]], done: false };
1484 // argumentsList[@@iterator] is %ArrayProto_values%
1485 // https://tc39.github.io/ecma262/#sec-createunmappedargumentsobject
1486 // https://tc39.github.io/ecma262/#sec-createmappedargumentsobject
1487 iterators.Arguments = iterators.Array;
1489 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
1490 addToUnscopables('keys');
1491 addToUnscopables('values');
1492 addToUnscopables('entries');
1494 var nativeJoin = [].join;
1496 var ES3_STRINGS = indexedObject != Object;
1497 var STRICT_METHOD$2 = arrayMethodIsStrict('join', ',');
1499 // `Array.prototype.join` method
1500 // https://tc39.github.io/ecma262/#sec-array.prototype.join
1501 _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$2 }, {
1502 join: function join(separator) {
1503 return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
1507 var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
1509 var process$1 = global_1.process;
1510 var versions = process$1 && process$1.versions;
1511 var v8 = versions && versions.v8;
1515 match = v8.split('.');
1516 version = match[0] + match[1];
1517 } else if (engineUserAgent) {
1518 match = engineUserAgent.match(/Edge\/(\d+)/);
1519 if (!match || match[1] >= 74) {
1520 match = engineUserAgent.match(/Chrome\/(\d+)/);
1521 if (match) version = match[1];
1525 var engineV8Version = version && +version;
1527 var SPECIES$1 = wellKnownSymbol('species');
1529 var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
1530 // We can't use this feature detection in V8 since it causes
1531 // deoptimization and serious performance degradation
1532 // https://github.com/zloirock/core-js/issues/677
1533 return engineV8Version >= 51 || !fails(function () {
1535 var constructor = array.constructor = {};
1536 constructor[SPECIES$1] = function () {
1539 return array[METHOD_NAME](Boolean).foo !== 1;
1543 var $map = arrayIteration.map;
1547 var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('map');
1549 var USES_TO_LENGTH$2 = arrayMethodUsesToLength('map');
1551 // `Array.prototype.map` method
1552 // https://tc39.github.io/ecma262/#sec-array.prototype.map
1553 // with adding support of @@species
1554 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT || !USES_TO_LENGTH$2 }, {
1555 map: function map(callbackfn /* , thisArg */) {
1556 return $map(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
1560 var createProperty = function (object, key, value) {
1561 var propertyKey = toPrimitive(key);
1562 if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
1563 else object[propertyKey] = value;
1566 var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('slice');
1567 var USES_TO_LENGTH$3 = arrayMethodUsesToLength('slice', { ACCESSORS: true, 0: 0, 1: 2 });
1569 var SPECIES$2 = wellKnownSymbol('species');
1570 var nativeSlice = [].slice;
1571 var max$1 = Math.max;
1573 // `Array.prototype.slice` method
1574 // https://tc39.github.io/ecma262/#sec-array.prototype.slice
1575 // fallback for not array-like ES3 strings and DOM objects
1576 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 || !USES_TO_LENGTH$3 }, {
1577 slice: function slice(start, end) {
1578 var O = toIndexedObject(this);
1579 var length = toLength(O.length);
1580 var k = toAbsoluteIndex(start, length);
1581 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
1582 // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
1583 var Constructor, result, n;
1585 Constructor = O.constructor;
1586 // cross-realm fallback
1587 if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
1588 Constructor = undefined;
1589 } else if (isObject(Constructor)) {
1590 Constructor = Constructor[SPECIES$2];
1591 if (Constructor === null) Constructor = undefined;
1593 if (Constructor === Array || Constructor === undefined) {
1594 return nativeSlice.call(O, k, fin);
1597 result = new (Constructor === undefined ? Array : Constructor)(max$1(fin - k, 0));
1598 for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
1604 var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1606 var redefineAll = function (target, src, options) {
1607 for (var key in src) redefine(target, key, src[key], options);
1611 var anInstance = function (it, Constructor, name) {
1612 if (!(it instanceof Constructor)) {
1613 throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1617 // `ToIndex` abstract operation
1618 // https://tc39.github.io/ecma262/#sec-toindex
1619 var toIndex = function (it) {
1620 if (it === undefined) return 0;
1621 var number = toInteger(it);
1622 var length = toLength(number);
1623 if (number !== length) throw RangeError('Wrong length or index');
1627 // IEEE754 conversions based on https://github.com/feross/ieee754
1628 // eslint-disable-next-line no-shadow-restricted-names
1629 var Infinity$1 = 1 / 0;
1632 var floor$1 = Math.floor;
1636 var pack = function (number, mantissaLength, bytes) {
1637 var buffer = new Array(bytes);
1638 var exponentLength = bytes * 8 - mantissaLength - 1;
1639 var eMax = (1 << exponentLength) - 1;
1640 var eBias = eMax >> 1;
1641 var rt = mantissaLength === 23 ? pow(2, -24) - pow(2, -77) : 0;
1642 var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1644 var exponent, mantissa, c;
1645 number = abs(number);
1646 // eslint-disable-next-line no-self-compare
1647 if (number != number || number === Infinity$1) {
1648 // eslint-disable-next-line no-self-compare
1649 mantissa = number != number ? 1 : 0;
1652 exponent = floor$1(log(number) / LN2);
1653 if (number * (c = pow(2, -exponent)) < 1) {
1657 if (exponent + eBias >= 1) {
1660 number += rt * pow(2, 1 - eBias);
1662 if (number * c >= 2) {
1666 if (exponent + eBias >= eMax) {
1669 } else if (exponent + eBias >= 1) {
1670 mantissa = (number * c - 1) * pow(2, mantissaLength);
1671 exponent = exponent + eBias;
1673 mantissa = number * pow(2, eBias - 1) * pow(2, mantissaLength);
1677 for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1678 exponent = exponent << mantissaLength | mantissa;
1679 exponentLength += mantissaLength;
1680 for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1681 buffer[--index] |= sign * 128;
1685 var unpack = function (buffer, mantissaLength) {
1686 var bytes = buffer.length;
1687 var exponentLength = bytes * 8 - mantissaLength - 1;
1688 var eMax = (1 << exponentLength) - 1;
1689 var eBias = eMax >> 1;
1690 var nBits = exponentLength - 7;
1691 var index = bytes - 1;
1692 var sign = buffer[index--];
1693 var exponent = sign & 127;
1696 for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1697 mantissa = exponent & (1 << -nBits) - 1;
1698 exponent >>= -nBits;
1699 nBits += mantissaLength;
1700 for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1701 if (exponent === 0) {
1702 exponent = 1 - eBias;
1703 } else if (exponent === eMax) {
1704 return mantissa ? NaN : sign ? -Infinity$1 : Infinity$1;
1706 mantissa = mantissa + pow(2, mantissaLength);
1707 exponent = exponent - eBias;
1708 } return (sign ? -1 : 1) * mantissa * pow(2, exponent - mantissaLength);
1716 // `Array.prototype.fill` method implementation
1717 // https://tc39.github.io/ecma262/#sec-array.prototype.fill
1718 var arrayFill = function fill(value /* , start = 0, end = @length */) {
1719 var O = toObject(this);
1720 var length = toLength(O.length);
1721 var argumentsLength = arguments.length;
1722 var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1723 var end = argumentsLength > 2 ? arguments[2] : undefined;
1724 var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1725 while (endPos > index) O[index++] = value;
1729 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
1730 var defineProperty$4 = objectDefineProperty.f;
1735 var getInternalState$2 = internalState.get;
1736 var setInternalState$2 = internalState.set;
1737 var ARRAY_BUFFER = 'ArrayBuffer';
1738 var DATA_VIEW = 'DataView';
1739 var PROTOTYPE$2 = 'prototype';
1740 var WRONG_LENGTH = 'Wrong length';
1741 var WRONG_INDEX = 'Wrong index';
1742 var NativeArrayBuffer = global_1[ARRAY_BUFFER];
1743 var $ArrayBuffer = NativeArrayBuffer;
1744 var $DataView = global_1[DATA_VIEW];
1745 var $DataViewPrototype = $DataView && $DataView[PROTOTYPE$2];
1746 var ObjectPrototype$2 = Object.prototype;
1747 var RangeError$1 = global_1.RangeError;
1749 var packIEEE754 = ieee754.pack;
1750 var unpackIEEE754 = ieee754.unpack;
1752 var packInt8 = function (number) {
1753 return [number & 0xFF];
1756 var packInt16 = function (number) {
1757 return [number & 0xFF, number >> 8 & 0xFF];
1760 var packInt32 = function (number) {
1761 return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1764 var unpackInt32 = function (buffer) {
1765 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1768 var packFloat32 = function (number) {
1769 return packIEEE754(number, 23, 4);
1772 var packFloat64 = function (number) {
1773 return packIEEE754(number, 52, 8);
1776 var addGetter = function (Constructor, key) {
1777 defineProperty$4(Constructor[PROTOTYPE$2], key, { get: function () { return getInternalState$2(this)[key]; } });
1780 var get$1 = function (view, count, index, isLittleEndian) {
1781 var intIndex = toIndex(index);
1782 var store = getInternalState$2(view);
1783 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1784 var bytes = getInternalState$2(store.buffer).bytes;
1785 var start = intIndex + store.byteOffset;
1786 var pack = bytes.slice(start, start + count);
1787 return isLittleEndian ? pack : pack.reverse();
1790 var set$1 = function (view, count, index, conversion, value, isLittleEndian) {
1791 var intIndex = toIndex(index);
1792 var store = getInternalState$2(view);
1793 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1794 var bytes = getInternalState$2(store.buffer).bytes;
1795 var start = intIndex + store.byteOffset;
1796 var pack = conversion(+value);
1797 for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
1800 if (!arrayBufferNative) {
1801 $ArrayBuffer = function ArrayBuffer(length) {
1802 anInstance(this, $ArrayBuffer, ARRAY_BUFFER);
1803 var byteLength = toIndex(length);
1804 setInternalState$2(this, {
1805 bytes: arrayFill.call(new Array(byteLength), 0),
1806 byteLength: byteLength
1808 if (!descriptors) this.byteLength = byteLength;
1811 $DataView = function DataView(buffer, byteOffset, byteLength) {
1812 anInstance(this, $DataView, DATA_VIEW);
1813 anInstance(buffer, $ArrayBuffer, DATA_VIEW);
1814 var bufferLength = getInternalState$2(buffer).byteLength;
1815 var offset = toInteger(byteOffset);
1816 if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
1817 byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
1818 if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
1819 setInternalState$2(this, {
1821 byteLength: byteLength,
1825 this.buffer = buffer;
1826 this.byteLength = byteLength;
1827 this.byteOffset = offset;
1832 addGetter($ArrayBuffer, 'byteLength');
1833 addGetter($DataView, 'buffer');
1834 addGetter($DataView, 'byteLength');
1835 addGetter($DataView, 'byteOffset');
1838 redefineAll($DataView[PROTOTYPE$2], {
1839 getInt8: function getInt8(byteOffset) {
1840 return get$1(this, 1, byteOffset)[0] << 24 >> 24;
1842 getUint8: function getUint8(byteOffset) {
1843 return get$1(this, 1, byteOffset)[0];
1845 getInt16: function getInt16(byteOffset /* , littleEndian */) {
1846 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
1847 return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
1849 getUint16: function getUint16(byteOffset /* , littleEndian */) {
1850 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
1851 return bytes[1] << 8 | bytes[0];
1853 getInt32: function getInt32(byteOffset /* , littleEndian */) {
1854 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
1856 getUint32: function getUint32(byteOffset /* , littleEndian */) {
1857 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
1859 getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
1860 return unpackIEEE754(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
1862 getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
1863 return unpackIEEE754(get$1(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
1865 setInt8: function setInt8(byteOffset, value) {
1866 set$1(this, 1, byteOffset, packInt8, value);
1868 setUint8: function setUint8(byteOffset, value) {
1869 set$1(this, 1, byteOffset, packInt8, value);
1871 setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
1872 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
1874 setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
1875 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
1877 setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
1878 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
1880 setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
1881 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
1883 setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
1884 set$1(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
1886 setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
1887 set$1(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
1891 if (!fails(function () {
1892 NativeArrayBuffer(1);
1893 }) || !fails(function () {
1894 new NativeArrayBuffer(-1); // eslint-disable-line no-new
1895 }) || fails(function () {
1896 new NativeArrayBuffer(); // eslint-disable-line no-new
1897 new NativeArrayBuffer(1.5); // eslint-disable-line no-new
1898 new NativeArrayBuffer(NaN); // eslint-disable-line no-new
1899 return NativeArrayBuffer.name != ARRAY_BUFFER;
1901 $ArrayBuffer = function ArrayBuffer(length) {
1902 anInstance(this, $ArrayBuffer);
1903 return new NativeArrayBuffer(toIndex(length));
1905 var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE$2] = NativeArrayBuffer[PROTOTYPE$2];
1906 for (var keys$1 = getOwnPropertyNames(NativeArrayBuffer), j = 0, key; keys$1.length > j;) {
1907 if (!((key = keys$1[j++]) in $ArrayBuffer)) {
1908 createNonEnumerableProperty($ArrayBuffer, key, NativeArrayBuffer[key]);
1911 ArrayBufferPrototype.constructor = $ArrayBuffer;
1914 // WebKit bug - the same parent prototype for typed arrays and data view
1915 if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$2) {
1916 objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$2);
1919 // iOS Safari 7.x bug
1920 var testView = new $DataView(new $ArrayBuffer(2));
1921 var nativeSetInt8 = $DataViewPrototype.setInt8;
1922 testView.setInt8(0, 2147483648);
1923 testView.setInt8(1, 2147483649);
1924 if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
1925 setInt8: function setInt8(byteOffset, value) {
1926 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
1928 setUint8: function setUint8(byteOffset, value) {
1929 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
1931 }, { unsafe: true });
1934 setToStringTag($ArrayBuffer, ARRAY_BUFFER);
1935 setToStringTag($DataView, DATA_VIEW);
1938 ArrayBuffer: $ArrayBuffer,
1942 var SPECIES$3 = wellKnownSymbol('species');
1944 var setSpecies = function (CONSTRUCTOR_NAME) {
1945 var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
1946 var defineProperty = objectDefineProperty.f;
1948 if (descriptors && Constructor && !Constructor[SPECIES$3]) {
1949 defineProperty(Constructor, SPECIES$3, {
1951 get: function () { return this; }
1956 var ARRAY_BUFFER$1 = 'ArrayBuffer';
1957 var ArrayBuffer$1 = arrayBuffer[ARRAY_BUFFER$1];
1958 var NativeArrayBuffer$1 = global_1[ARRAY_BUFFER$1];
1960 // `ArrayBuffer` constructor
1961 // https://tc39.github.io/ecma262/#sec-arraybuffer-constructor
1962 _export({ global: true, forced: NativeArrayBuffer$1 !== ArrayBuffer$1 }, {
1963 ArrayBuffer: ArrayBuffer$1
1966 setSpecies(ARRAY_BUFFER$1);
1968 var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
1971 test[TO_STRING_TAG$1] = 'z';
1973 var toStringTagSupport = String(test) === '[object z]';
1975 var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1977 var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
1979 // fallback for IE11 Script Access Denied error
1980 var tryGet = function (it, key) {
1983 } catch (error) { /* empty */ }
1986 // getting tag from ES6+ `Object.prototype.toString`
1987 var classof = toStringTagSupport ? classofRaw : function (it) {
1989 return it === undefined ? 'Undefined' : it === null ? 'Null'
1990 // @@toStringTag case
1991 : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag
1993 : CORRECT_ARGUMENTS ? classofRaw(O)
1994 // ES3 arguments fallback
1995 : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
1998 var defineProperty$5 = objectDefineProperty.f;
2004 var Int8Array$1 = global_1.Int8Array;
2005 var Int8ArrayPrototype = Int8Array$1 && Int8Array$1.prototype;
2006 var Uint8ClampedArray = global_1.Uint8ClampedArray;
2007 var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2008 var TypedArray = Int8Array$1 && objectGetPrototypeOf(Int8Array$1);
2009 var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2010 var ObjectPrototype$3 = Object.prototype;
2011 var isPrototypeOf = ObjectPrototype$3.isPrototypeOf;
2013 var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
2014 var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2015 // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2016 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferNative && !!objectSetPrototypeOf && classof(global_1.opera) !== 'Opera';
2017 var TYPED_ARRAY_TAG_REQIRED = false;
2020 var TypedArrayConstructorsList = {
2023 Uint8ClampedArray: 1,
2032 var isView = function isView(it) {
2033 var klass = classof(it);
2034 return klass === 'DataView' || has(TypedArrayConstructorsList, klass);
2037 var isTypedArray = function (it) {
2038 return isObject(it) && has(TypedArrayConstructorsList, classof(it));
2041 var aTypedArray = function (it) {
2042 if (isTypedArray(it)) return it;
2043 throw TypeError('Target is not a typed array');
2046 var aTypedArrayConstructor = function (C) {
2047 if (objectSetPrototypeOf) {
2048 if (isPrototypeOf.call(TypedArray, C)) return C;
2049 } else for (var ARRAY in TypedArrayConstructorsList) if (has(TypedArrayConstructorsList, NAME)) {
2050 var TypedArrayConstructor = global_1[ARRAY];
2051 if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2054 } throw TypeError('Target is not a typed array constructor');
2057 var exportTypedArrayMethod = function (KEY, property, forced) {
2058 if (!descriptors) return;
2059 if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2060 var TypedArrayConstructor = global_1[ARRAY];
2061 if (TypedArrayConstructor && has(TypedArrayConstructor.prototype, KEY)) {
2062 delete TypedArrayConstructor.prototype[KEY];
2065 if (!TypedArrayPrototype[KEY] || forced) {
2066 redefine(TypedArrayPrototype, KEY, forced ? property
2067 : NATIVE_ARRAY_BUFFER_VIEWS && Int8ArrayPrototype[KEY] || property);
2071 var exportTypedArrayStaticMethod = function (KEY, property, forced) {
2072 var ARRAY, TypedArrayConstructor;
2073 if (!descriptors) return;
2074 if (objectSetPrototypeOf) {
2075 if (forced) for (ARRAY in TypedArrayConstructorsList) {
2076 TypedArrayConstructor = global_1[ARRAY];
2077 if (TypedArrayConstructor && has(TypedArrayConstructor, KEY)) {
2078 delete TypedArrayConstructor[KEY];
2081 if (!TypedArray[KEY] || forced) {
2082 // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2084 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && Int8Array$1[KEY] || property);
2085 } catch (error) { /* empty */ }
2088 for (ARRAY in TypedArrayConstructorsList) {
2089 TypedArrayConstructor = global_1[ARRAY];
2090 if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2091 redefine(TypedArrayConstructor, KEY, property);
2096 for (NAME in TypedArrayConstructorsList) {
2097 if (!global_1[NAME]) NATIVE_ARRAY_BUFFER_VIEWS = false;
2100 // WebKit bug - typed arrays constructors prototype is Object.prototype
2101 if (!NATIVE_ARRAY_BUFFER_VIEWS || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2102 // eslint-disable-next-line no-shadow
2103 TypedArray = function TypedArray() {
2104 throw TypeError('Incorrect invocation');
2106 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2107 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME], TypedArray);
2111 if (!NATIVE_ARRAY_BUFFER_VIEWS || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype$3) {
2112 TypedArrayPrototype = TypedArray.prototype;
2113 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2114 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME].prototype, TypedArrayPrototype);
2118 // WebKit bug - one more object in Uint8ClampedArray prototype chain
2119 if (NATIVE_ARRAY_BUFFER_VIEWS && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2120 objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2123 if (descriptors && !has(TypedArrayPrototype, TO_STRING_TAG$3)) {
2124 TYPED_ARRAY_TAG_REQIRED = true;
2125 defineProperty$5(TypedArrayPrototype, TO_STRING_TAG$3, { get: function () {
2126 return isObject(this) ? this[TYPED_ARRAY_TAG] : undefined;
2128 for (NAME in TypedArrayConstructorsList) if (global_1[NAME]) {
2129 createNonEnumerableProperty(global_1[NAME], TYPED_ARRAY_TAG, NAME);
2133 var arrayBufferViewCore = {
2134 NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS,
2135 TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2136 aTypedArray: aTypedArray,
2137 aTypedArrayConstructor: aTypedArrayConstructor,
2138 exportTypedArrayMethod: exportTypedArrayMethod,
2139 exportTypedArrayStaticMethod: exportTypedArrayStaticMethod,
2141 isTypedArray: isTypedArray,
2142 TypedArray: TypedArray,
2143 TypedArrayPrototype: TypedArrayPrototype
2146 var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2148 // `ArrayBuffer.isView` method
2149 // https://tc39.github.io/ecma262/#sec-arraybuffer.isview
2150 _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2151 isView: arrayBufferViewCore.isView
2154 var SPECIES$4 = wellKnownSymbol('species');
2156 // `SpeciesConstructor` abstract operation
2157 // https://tc39.github.io/ecma262/#sec-speciesconstructor
2158 var speciesConstructor = function (O, defaultConstructor) {
2159 var C = anObject(O).constructor;
2161 return C === undefined || (S = anObject(C)[SPECIES$4]) == undefined ? defaultConstructor : aFunction$1(S);
2164 var ArrayBuffer$2 = arrayBuffer.ArrayBuffer;
2165 var DataView$1 = arrayBuffer.DataView;
2166 var nativeArrayBufferSlice = ArrayBuffer$2.prototype.slice;
2168 var INCORRECT_SLICE = fails(function () {
2169 return !new ArrayBuffer$2(2).slice(1, undefined).byteLength;
2172 // `ArrayBuffer.prototype.slice` method
2173 // https://tc39.github.io/ecma262/#sec-arraybuffer.prototype.slice
2174 _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2175 slice: function slice(start, end) {
2176 if (nativeArrayBufferSlice !== undefined && end === undefined) {
2177 return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2179 var length = anObject(this).byteLength;
2180 var first = toAbsoluteIndex(start, length);
2181 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2182 var result = new (speciesConstructor(this, ArrayBuffer$2))(toLength(fin - first));
2183 var viewSource = new DataView$1(this);
2184 var viewTarget = new DataView$1(result);
2186 while (first < fin) {
2187 viewTarget.setUint8(index++, viewSource.getUint8(first++));
2192 // `DataView` constructor
2193 // https://tc39.github.io/ecma262/#sec-dataview-constructor
2194 _export({ global: true, forced: !arrayBufferNative }, {
2195 DataView: arrayBuffer.DataView
2198 var defineProperty$6 = objectDefineProperty.f;
2200 var FunctionPrototype = Function.prototype;
2201 var FunctionPrototypeToString = FunctionPrototype.toString;
2202 var nameRE = /^\s*function ([^ (]*)/;
2203 var NAME$1 = 'name';
2205 // Function instances `.name` property
2206 // https://tc39.github.io/ecma262/#sec-function-instances-name
2207 if (descriptors && !(NAME$1 in FunctionPrototype)) {
2208 defineProperty$6(FunctionPrototype, NAME$1, {
2212 return FunctionPrototypeToString.call(this).match(nameRE)[1];
2220 // `Object.create` method
2221 // https://tc39.github.io/ecma262/#sec-object.create
2222 _export({ target: 'Object', stat: true, sham: !descriptors }, {
2223 create: objectCreate
2226 var nativeGetOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2228 var FAILS_ON_PRIMITIVES = fails(function () { return !Object.getOwnPropertyNames(1); });
2230 // `Object.getOwnPropertyNames` method
2231 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
2232 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, {
2233 getOwnPropertyNames: nativeGetOwnPropertyNames$2
2236 // `Object.prototype.toString` method implementation
2237 // https://tc39.github.io/ecma262/#sec-object.prototype.tostring
2238 var objectToString = toStringTagSupport ? {}.toString : function toString() {
2239 return '[object ' + classof(this) + ']';
2242 // `Object.prototype.toString` method
2243 // https://tc39.github.io/ecma262/#sec-object.prototype.tostring
2244 if (!toStringTagSupport) {
2245 redefine(Object.prototype, 'toString', objectToString, { unsafe: true });
2248 var nativePromiseConstructor = global_1.Promise;
2250 var ITERATOR$2 = wellKnownSymbol('iterator');
2251 var ArrayPrototype$1 = Array.prototype;
2253 // check on default Array iterator
2254 var isArrayIteratorMethod = function (it) {
2255 return it !== undefined && (iterators.Array === it || ArrayPrototype$1[ITERATOR$2] === it);
2258 var ITERATOR$3 = wellKnownSymbol('iterator');
2260 var getIteratorMethod = function (it) {
2261 if (it != undefined) return it[ITERATOR$3]
2263 || iterators[classof(it)];
2266 var iteratorClose = function (iterator) {
2267 var returnMethod = iterator['return'];
2268 if (returnMethod !== undefined) {
2269 return anObject(returnMethod.call(iterator)).value;
2273 var Result = function (stopped, result) {
2274 this.stopped = stopped;
2275 this.result = result;
2278 var iterate = function (iterable, unboundFunction, options) {
2279 var that = options && options.that;
2280 var AS_ENTRIES = !!(options && options.AS_ENTRIES);
2281 var IS_ITERATOR = !!(options && options.IS_ITERATOR);
2282 var INTERRUPTED = !!(options && options.INTERRUPTED);
2283 var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
2284 var iterator, iterFn, index, length, result, next, step;
2286 var stop = function (condition) {
2287 if (iterator) iteratorClose(iterator);
2288 return new Result(true, condition);
2291 var callFn = function (value) {
2294 return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
2295 } return INTERRUPTED ? fn(value, stop) : fn(value);
2299 iterator = iterable;
2301 iterFn = getIteratorMethod(iterable);
2302 if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2303 // optimisation for array iterators
2304 if (isArrayIteratorMethod(iterFn)) {
2305 for (index = 0, length = toLength(iterable.length); length > index; index++) {
2306 result = callFn(iterable[index]);
2307 if (result && result instanceof Result) return result;
2308 } return new Result(false);
2310 iterator = iterFn.call(iterable);
2313 next = iterator.next;
2314 while (!(step = next.call(iterator)).done) {
2316 result = callFn(step.value);
2318 iteratorClose(iterator);
2321 if (typeof result == 'object' && result && result instanceof Result) return result;
2322 } return new Result(false);
2325 var ITERATOR$4 = wellKnownSymbol('iterator');
2326 var SAFE_CLOSING = false;
2330 var iteratorWithReturn = {
2332 return { done: !!called++ };
2334 'return': function () {
2335 SAFE_CLOSING = true;
2338 iteratorWithReturn[ITERATOR$4] = function () {
2341 // eslint-disable-next-line no-throw-literal
2342 Array.from(iteratorWithReturn, function () { throw 2; });
2343 } catch (error) { /* empty */ }
2345 var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2346 if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2347 var ITERATION_SUPPORT = false;
2350 object[ITERATOR$4] = function () {
2353 return { done: ITERATION_SUPPORT = true };
2358 } catch (error) { /* empty */ }
2359 return ITERATION_SUPPORT;
2362 var engineIsIos = /(iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2364 var engineIsNode = classofRaw(global_1.process) == 'process';
2366 var location$1 = global_1.location;
2367 var set$2 = global_1.setImmediate;
2368 var clear = global_1.clearImmediate;
2369 var process$2 = global_1.process;
2370 var MessageChannel = global_1.MessageChannel;
2371 var Dispatch = global_1.Dispatch;
2374 var ONREADYSTATECHANGE = 'onreadystatechange';
2375 var defer, channel, port;
2377 var run = function (id) {
2378 // eslint-disable-next-line no-prototype-builtins
2379 if (queue.hasOwnProperty(id)) {
2386 var runner = function (id) {
2387 return function () {
2392 var listener = function (event) {
2396 var post = function (id) {
2397 // old engines have not location.origin
2398 global_1.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2401 // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2402 if (!set$2 || !clear) {
2403 set$2 = function setImmediate(fn) {
2406 while (arguments.length > i) args.push(arguments[i++]);
2407 queue[++counter] = function () {
2408 // eslint-disable-next-line no-new-func
2409 (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2414 clear = function clearImmediate(id) {
2419 defer = function (id) {
2420 process$2.nextTick(runner(id));
2422 // Sphere (JS game engine) Dispatch API
2423 } else if (Dispatch && Dispatch.now) {
2424 defer = function (id) {
2425 Dispatch.now(runner(id));
2427 // Browsers with MessageChannel, includes WebWorkers
2428 // except iOS - https://github.com/zloirock/core-js/issues/624
2429 } else if (MessageChannel && !engineIsIos) {
2430 channel = new MessageChannel();
2431 port = channel.port2;
2432 channel.port1.onmessage = listener;
2433 defer = functionBindContext(port.postMessage, port, 1);
2434 // Browsers with postMessage, skip WebWorkers
2435 // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2437 global_1.addEventListener &&
2438 typeof postMessage == 'function' &&
2439 !global_1.importScripts &&
2440 location$1 && location$1.protocol !== 'file:' &&
2444 global_1.addEventListener('message', listener, false);
2446 } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2447 defer = function (id) {
2448 html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2449 html.removeChild(this);
2453 // Rest old browsers
2455 defer = function (id) {
2456 setTimeout(runner(id), 0);
2466 var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
2467 var macrotask = task.set;
2471 var MutationObserver = global_1.MutationObserver || global_1.WebKitMutationObserver;
2472 var document$2 = global_1.document;
2473 var process$3 = global_1.process;
2474 var Promise$1 = global_1.Promise;
2475 // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2476 var queueMicrotaskDescriptor = getOwnPropertyDescriptor$2(global_1, 'queueMicrotask');
2477 var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2479 var flush, head, last, notify, toggle, node, promise, then;
2481 // modern engines have queueMicrotask method
2482 if (!queueMicrotask) {
2483 flush = function () {
2485 if (engineIsNode && (parent = process$3.domain)) parent.exit();
2493 else last = undefined;
2497 if (parent) parent.enter();
2500 // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2501 if (!engineIsIos && !engineIsNode && MutationObserver && document$2) {
2503 node = document$2.createTextNode('');
2504 new MutationObserver(flush).observe(node, { characterData: true });
2505 notify = function () {
2506 node.data = toggle = !toggle;
2508 // environments with maybe non-completely correct, but existent Promise
2509 } else if (Promise$1 && Promise$1.resolve) {
2510 // Promise.resolve without an argument throws an error in LG WebOS 2
2511 promise = Promise$1.resolve(undefined);
2512 then = promise.then;
2513 notify = function () {
2514 then.call(promise, flush);
2516 // Node.js without promises
2517 } else if (engineIsNode) {
2518 notify = function () {
2519 process$3.nextTick(flush);
2521 // for other environments - macrotask based on:
2524 // - window.postMessag
2525 // - onreadystatechange
2528 notify = function () {
2529 // strange IE + webpack dev server bug - use .call(global)
2530 macrotask.call(global_1, flush);
2535 var microtask = queueMicrotask || function (fn) {
2536 var task = { fn: fn, next: undefined };
2537 if (last) last.next = task;
2544 var PromiseCapability = function (C) {
2545 var resolve, reject;
2546 this.promise = new C(function ($$resolve, $$reject) {
2547 if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2548 resolve = $$resolve;
2551 this.resolve = aFunction$1(resolve);
2552 this.reject = aFunction$1(reject);
2555 // 25.4.1.5 NewPromiseCapability(C)
2556 var f$7 = function (C) {
2557 return new PromiseCapability(C);
2560 var newPromiseCapability = {
2564 var promiseResolve = function (C, x) {
2566 if (isObject(x) && x.constructor === C) return x;
2567 var promiseCapability = newPromiseCapability.f(C);
2568 var resolve = promiseCapability.resolve;
2570 return promiseCapability.promise;
2573 var hostReportErrors = function (a, b) {
2574 var console = global_1.console;
2575 if (console && console.error) {
2576 arguments.length === 1 ? console.error(a) : console.error(a, b);
2580 var perform = function (exec) {
2582 return { error: false, value: exec() };
2584 return { error: true, value: error };
2588 var task$1 = task.set;
2600 var SPECIES$5 = wellKnownSymbol('species');
2601 var PROMISE = 'Promise';
2602 var getInternalState$3 = internalState.get;
2603 var setInternalState$3 = internalState.set;
2604 var getInternalPromiseState = internalState.getterFor(PROMISE);
2605 var PromiseConstructor = nativePromiseConstructor;
2606 var TypeError$1 = global_1.TypeError;
2607 var document$3 = global_1.document;
2608 var process$4 = global_1.process;
2609 var $fetch = getBuiltIn('fetch');
2610 var newPromiseCapability$1 = newPromiseCapability.f;
2611 var newGenericPromiseCapability = newPromiseCapability$1;
2612 var DISPATCH_EVENT = !!(document$3 && document$3.createEvent && global_1.dispatchEvent);
2613 var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
2614 var UNHANDLED_REJECTION = 'unhandledrejection';
2615 var REJECTION_HANDLED = 'rejectionhandled';
2621 var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2623 var FORCED = isForced_1(PROMISE, function () {
2624 var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor);
2625 if (!GLOBAL_CORE_JS_PROMISE) {
2626 // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2627 // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2628 // We can't detect it synchronously, so just check versions
2629 if (engineV8Version === 66) return true;
2630 // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2631 if (!engineIsNode && !NATIVE_REJECTION_EVENT) return true;
2633 // We can't use @@species feature detection in V8 since it causes
2634 // deoptimization and performance degradation
2635 // https://github.com/zloirock/core-js/issues/679
2636 if (engineV8Version >= 51 && /native code/.test(PromiseConstructor)) return false;
2637 // Detect correctness of subclassing with @@species support
2638 var promise = PromiseConstructor.resolve(1);
2639 var FakePromise = function (exec) {
2640 exec(function () { /* empty */ }, function () { /* empty */ });
2642 var constructor = promise.constructor = {};
2643 constructor[SPECIES$5] = FakePromise;
2644 return !(promise.then(function () { /* empty */ }) instanceof FakePromise);
2647 var INCORRECT_ITERATION = FORCED || !checkCorrectnessOfIteration(function (iterable) {
2648 PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2652 var isThenable = function (it) {
2654 return isObject(it) && typeof (then = it.then) == 'function' ? then : false;
2657 var notify$1 = function (state, isReject) {
2658 if (state.notified) return;
2659 state.notified = true;
2660 var chain = state.reactions;
2661 microtask(function () {
2662 var value = state.value;
2663 var ok = state.state == FULFILLED;
2665 // variable length - can't use forEach
2666 while (chain.length > index) {
2667 var reaction = chain[index++];
2668 var handler = ok ? reaction.ok : reaction.fail;
2669 var resolve = reaction.resolve;
2670 var reject = reaction.reject;
2671 var domain = reaction.domain;
2672 var result, then, exited;
2676 if (state.rejection === UNHANDLED) onHandleUnhandled(state);
2677 state.rejection = HANDLED;
2679 if (handler === true) result = value;
2681 if (domain) domain.enter();
2682 result = handler(value); // can throw
2688 if (result === reaction.promise) {
2689 reject(TypeError$1('Promise-chain cycle'));
2690 } else if (then = isThenable(result)) {
2691 then.call(result, resolve, reject);
2692 } else resolve(result);
2693 } else reject(value);
2695 if (domain && !exited) domain.exit();
2699 state.reactions = [];
2700 state.notified = false;
2701 if (isReject && !state.rejection) onUnhandled(state);
2705 var dispatchEvent = function (name, promise, reason) {
2707 if (DISPATCH_EVENT) {
2708 event = document$3.createEvent('Event');
2709 event.promise = promise;
2710 event.reason = reason;
2711 event.initEvent(name, false, true);
2712 global_1.dispatchEvent(event);
2713 } else event = { promise: promise, reason: reason };
2714 if (!NATIVE_REJECTION_EVENT && (handler = global_1['on' + name])) handler(event);
2715 else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2718 var onUnhandled = function (state) {
2719 task$1.call(global_1, function () {
2720 var promise = state.facade;
2721 var value = state.value;
2722 var IS_UNHANDLED = isUnhandled(state);
2725 result = perform(function () {
2727 process$4.emit('unhandledRejection', value, promise);
2728 } else dispatchEvent(UNHANDLED_REJECTION, promise, value);
2730 // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2731 state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
2732 if (result.error) throw result.value;
2737 var isUnhandled = function (state) {
2738 return state.rejection !== HANDLED && !state.parent;
2741 var onHandleUnhandled = function (state) {
2742 task$1.call(global_1, function () {
2743 var promise = state.facade;
2745 process$4.emit('rejectionHandled', promise);
2746 } else dispatchEvent(REJECTION_HANDLED, promise, state.value);
2750 var bind = function (fn, state, unwrap) {
2751 return function (value) {
2752 fn(state, value, unwrap);
2756 var internalReject = function (state, value, unwrap) {
2757 if (state.done) return;
2759 if (unwrap) state = unwrap;
2760 state.value = value;
2761 state.state = REJECTED;
2762 notify$1(state, true);
2765 var internalResolve = function (state, value, unwrap) {
2766 if (state.done) return;
2768 if (unwrap) state = unwrap;
2770 if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
2771 var then = isThenable(value);
2773 microtask(function () {
2774 var wrapper = { done: false };
2777 bind(internalResolve, wrapper, state),
2778 bind(internalReject, wrapper, state)
2781 internalReject(wrapper, error, state);
2785 state.value = value;
2786 state.state = FULFILLED;
2787 notify$1(state, false);
2790 internalReject({ done: false }, error, state);
2794 // constructor polyfill
2796 // 25.4.3.1 Promise(executor)
2797 PromiseConstructor = function Promise(executor) {
2798 anInstance(this, PromiseConstructor, PROMISE);
2799 aFunction$1(executor);
2800 Internal.call(this);
2801 var state = getInternalState$3(this);
2803 executor(bind(internalResolve, state), bind(internalReject, state));
2805 internalReject(state, error);
2808 // eslint-disable-next-line no-unused-vars
2809 Internal = function Promise(executor) {
2810 setInternalState$3(this, {
2821 Internal.prototype = redefineAll(PromiseConstructor.prototype, {
2822 // `Promise.prototype.then` method
2823 // https://tc39.github.io/ecma262/#sec-promise.prototype.then
2824 then: function then(onFulfilled, onRejected) {
2825 var state = getInternalPromiseState(this);
2826 var reaction = newPromiseCapability$1(speciesConstructor(this, PromiseConstructor));
2827 reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
2828 reaction.fail = typeof onRejected == 'function' && onRejected;
2829 reaction.domain = engineIsNode ? process$4.domain : undefined;
2830 state.parent = true;
2831 state.reactions.push(reaction);
2832 if (state.state != PENDING) notify$1(state, false);
2833 return reaction.promise;
2835 // `Promise.prototype.catch` method
2836 // https://tc39.github.io/ecma262/#sec-promise.prototype.catch
2837 'catch': function (onRejected) {
2838 return this.then(undefined, onRejected);
2841 OwnPromiseCapability = function () {
2842 var promise = new Internal();
2843 var state = getInternalState$3(promise);
2844 this.promise = promise;
2845 this.resolve = bind(internalResolve, state);
2846 this.reject = bind(internalReject, state);
2848 newPromiseCapability.f = newPromiseCapability$1 = function (C) {
2849 return C === PromiseConstructor || C === PromiseWrapper
2850 ? new OwnPromiseCapability(C)
2851 : newGenericPromiseCapability(C);
2854 if ( typeof nativePromiseConstructor == 'function') {
2855 nativeThen = nativePromiseConstructor.prototype.then;
2857 // wrap native Promise#then for native async functions
2858 redefine(nativePromiseConstructor.prototype, 'then', function then(onFulfilled, onRejected) {
2860 return new PromiseConstructor(function (resolve, reject) {
2861 nativeThen.call(that, resolve, reject);
2862 }).then(onFulfilled, onRejected);
2863 // https://github.com/zloirock/core-js/issues/640
2864 }, { unsafe: true });
2866 // wrap fetch result
2867 if (typeof $fetch == 'function') _export({ global: true, enumerable: true, forced: true }, {
2868 // eslint-disable-next-line no-unused-vars
2869 fetch: function fetch(input /* , init */) {
2870 return promiseResolve(PromiseConstructor, $fetch.apply(global_1, arguments));
2876 _export({ global: true, wrap: true, forced: FORCED }, {
2877 Promise: PromiseConstructor
2880 setToStringTag(PromiseConstructor, PROMISE, false);
2881 setSpecies(PROMISE);
2883 PromiseWrapper = getBuiltIn(PROMISE);
2886 _export({ target: PROMISE, stat: true, forced: FORCED }, {
2887 // `Promise.reject` method
2888 // https://tc39.github.io/ecma262/#sec-promise.reject
2889 reject: function reject(r) {
2890 var capability = newPromiseCapability$1(this);
2891 capability.reject.call(undefined, r);
2892 return capability.promise;
2896 _export({ target: PROMISE, stat: true, forced: FORCED }, {
2897 // `Promise.resolve` method
2898 // https://tc39.github.io/ecma262/#sec-promise.resolve
2899 resolve: function resolve(x) {
2900 return promiseResolve( this, x);
2904 _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION }, {
2905 // `Promise.all` method
2906 // https://tc39.github.io/ecma262/#sec-promise.all
2907 all: function all(iterable) {
2909 var capability = newPromiseCapability$1(C);
2910 var resolve = capability.resolve;
2911 var reject = capability.reject;
2912 var result = perform(function () {
2913 var $promiseResolve = aFunction$1(C.resolve);
2917 iterate(iterable, function (promise) {
2918 var index = counter++;
2919 var alreadyCalled = false;
2920 values.push(undefined);
2922 $promiseResolve.call(C, promise).then(function (value) {
2923 if (alreadyCalled) return;
2924 alreadyCalled = true;
2925 values[index] = value;
2926 --remaining || resolve(values);
2929 --remaining || resolve(values);
2931 if (result.error) reject(result.value);
2932 return capability.promise;
2934 // `Promise.race` method
2935 // https://tc39.github.io/ecma262/#sec-promise.race
2936 race: function race(iterable) {
2938 var capability = newPromiseCapability$1(C);
2939 var reject = capability.reject;
2940 var result = perform(function () {
2941 var $promiseResolve = aFunction$1(C.resolve);
2942 iterate(iterable, function (promise) {
2943 $promiseResolve.call(C, promise).then(capability.resolve, reject);
2946 if (result.error) reject(result.value);
2947 return capability.promise;
2951 // `RegExp.prototype.flags` getter implementation
2952 // https://tc39.github.io/ecma262/#sec-get-regexp.prototype.flags
2953 var regexpFlags = function () {
2954 var that = anObject(this);
2956 if (that.global) result += 'g';
2957 if (that.ignoreCase) result += 'i';
2958 if (that.multiline) result += 'm';
2959 if (that.dotAll) result += 's';
2960 if (that.unicode) result += 'u';
2961 if (that.sticky) result += 'y';
2965 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
2966 // so we use an intermediate function.
2968 return RegExp(s, f);
2971 var UNSUPPORTED_Y = fails(function () {
2972 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError
2973 var re = RE('a', 'y');
2975 return re.exec('abcd') != null;
2978 var BROKEN_CARET = fails(function () {
2979 // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
2980 var re = RE('^r', 'gy');
2982 return re.exec('str') != null;
2985 var regexpStickyHelpers = {
2986 UNSUPPORTED_Y: UNSUPPORTED_Y,
2987 BROKEN_CARET: BROKEN_CARET
2990 var nativeExec = RegExp.prototype.exec;
2991 // This always refers to the native implementation, because the
2992 // String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,
2993 // which loads this file before patching the method.
2994 var nativeReplace = String.prototype.replace;
2996 var patchedExec = nativeExec;
2998 var UPDATES_LAST_INDEX_WRONG = (function () {
3001 nativeExec.call(re1, 'a');
3002 nativeExec.call(re2, 'a');
3003 return re1.lastIndex !== 0 || re2.lastIndex !== 0;
3006 var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
3008 // nonparticipating capturing group, copied from es5-shim's String#split patch.
3009 var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
3011 var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$1;
3014 patchedExec = function exec(str) {
3016 var lastIndex, reCopy, match, i;
3017 var sticky = UNSUPPORTED_Y$1 && re.sticky;
3018 var flags = regexpFlags.call(re);
3019 var source = re.source;
3024 flags = flags.replace('y', '');
3025 if (flags.indexOf('g') === -1) {
3029 strCopy = String(str).slice(re.lastIndex);
3030 // Support anchored sticky behavior.
3031 if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
3032 source = '(?: ' + source + ')';
3033 strCopy = ' ' + strCopy;
3036 // ^(? + rx + ) is needed, in combination with some str slicing, to
3037 // simulate the 'y' flag.
3038 reCopy = new RegExp('^(?:' + source + ')', flags);
3041 if (NPCG_INCLUDED) {
3042 reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
3044 if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
3046 match = nativeExec.call(sticky ? reCopy : re, strCopy);
3050 match.input = match.input.slice(charsAdded);
3051 match[0] = match[0].slice(charsAdded);
3052 match.index = re.lastIndex;
3053 re.lastIndex += match[0].length;
3054 } else re.lastIndex = 0;
3055 } else if (UPDATES_LAST_INDEX_WRONG && match) {
3056 re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
3058 if (NPCG_INCLUDED && match && match.length > 1) {
3059 // Fix browsers whose `exec` methods don't consistently return `undefined`
3060 // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
3061 nativeReplace.call(match[0], reCopy, function () {
3062 for (i = 1; i < arguments.length - 2; i++) {
3063 if (arguments[i] === undefined) match[i] = undefined;
3072 var regexpExec = patchedExec;
3074 _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
3078 var TO_STRING$1 = 'toString';
3079 var RegExpPrototype = RegExp.prototype;
3080 var nativeToString = RegExpPrototype[TO_STRING$1];
3082 var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
3083 // FF44- RegExp#toString has a wrong name
3084 var INCORRECT_NAME = nativeToString.name != TO_STRING$1;
3086 // `RegExp.prototype.toString` method
3087 // https://tc39.github.io/ecma262/#sec-regexp.prototype.tostring
3088 if (NOT_GENERIC || INCORRECT_NAME) {
3089 redefine(RegExp.prototype, TO_STRING$1, function toString() {
3090 var R = anObject(this);
3091 var p = String(R.source);
3093 var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype) ? regexpFlags.call(R) : rf);
3094 return '/' + p + '/' + f;
3095 }, { unsafe: true });
3098 // `String.prototype.{ codePointAt, at }` methods implementation
3099 var createMethod$2 = function (CONVERT_TO_STRING) {
3100 return function ($this, pos) {
3101 var S = String(requireObjectCoercible($this));
3102 var position = toInteger(pos);
3103 var size = S.length;
3105 if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
3106 first = S.charCodeAt(position);
3107 return first < 0xD800 || first > 0xDBFF || position + 1 === size
3108 || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
3109 ? CONVERT_TO_STRING ? S.charAt(position) : first
3110 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
3114 var stringMultibyte = {
3115 // `String.prototype.codePointAt` method
3116 // https://tc39.github.io/ecma262/#sec-string.prototype.codepointat
3117 codeAt: createMethod$2(false),
3118 // `String.prototype.at` method
3119 // https://github.com/mathiasbynens/String.prototype.at
3120 charAt: createMethod$2(true)
3123 var charAt = stringMultibyte.charAt;
3127 var STRING_ITERATOR = 'String Iterator';
3128 var setInternalState$4 = internalState.set;
3129 var getInternalState$4 = internalState.getterFor(STRING_ITERATOR);
3131 // `String.prototype[@@iterator]` method
3132 // https://tc39.github.io/ecma262/#sec-string.prototype-@@iterator
3133 defineIterator(String, 'String', function (iterated) {
3134 setInternalState$4(this, {
3135 type: STRING_ITERATOR,
3136 string: String(iterated),
3139 // `%StringIteratorPrototype%.next` method
3140 // https://tc39.github.io/ecma262/#sec-%stringiteratorprototype%.next
3141 }, function next() {
3142 var state = getInternalState$4(this);
3143 var string = state.string;
3144 var index = state.index;
3146 if (index >= string.length) return { value: undefined, done: true };
3147 point = charAt(string, index);
3148 state.index += point.length;
3149 return { value: point, done: false };
3152 // TODO: Remove from `core-js@4` since it's moved to entry points
3160 var SPECIES$6 = wellKnownSymbol('species');
3162 var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
3163 // #replace needs built-in support for named groups.
3164 // #match works fine because it just return the exec results, even if it has
3165 // a "grops" property.
3167 re.exec = function () {
3169 result.groups = { a: '7' };
3172 return ''.replace(re, '$<a>') !== '7';
3175 // IE <= 11 replaces $0 with the whole match, as if it was $&
3176 // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
3177 var REPLACE_KEEPS_$0 = (function () {
3178 return 'a'.replace(/./, '$0') === '$0';
3181 var REPLACE = wellKnownSymbol('replace');
3182 // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
3183 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
3185 return /./[REPLACE]('a', '$0') === '';
3190 // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
3191 // Weex JS has frozen built-in prototypes, so use try / catch wrapper
3192 var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
3194 var originalExec = re.exec;
3195 re.exec = function () { return originalExec.apply(this, arguments); };
3196 var result = 'ab'.split(re);
3197 return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
3200 var fixRegexpWellKnownSymbolLogic = function (KEY, length, exec, sham) {
3201 var SYMBOL = wellKnownSymbol(KEY);
3203 var DELEGATES_TO_SYMBOL = !fails(function () {
3204 // String methods call symbol-named RegEp methods
3206 O[SYMBOL] = function () { return 7; };
3207 return ''[KEY](O) != 7;
3210 var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
3211 // Symbol-named RegExp methods call .exec
3212 var execCalled = false;
3215 if (KEY === 'split') {
3216 // We can't use real regex here since it causes deoptimization
3217 // and serious performance degradation in V8
3218 // https://github.com/zloirock/core-js/issues/306
3220 // RegExp[@@split] doesn't call the regex's exec method, but first creates
3221 // a new one. We need to return the patched regex when creating the new one.
3222 re.constructor = {};
3223 re.constructor[SPECIES$6] = function () { return re; };
3225 re[SYMBOL] = /./[SYMBOL];
3228 re.exec = function () { execCalled = true; return null; };
3235 !DELEGATES_TO_SYMBOL ||
3236 !DELEGATES_TO_EXEC ||
3237 (KEY === 'replace' && !(
3238 REPLACE_SUPPORTS_NAMED_GROUPS &&
3240 !REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
3242 (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC)
3244 var nativeRegExpMethod = /./[SYMBOL];
3245 var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
3246 if (regexp.exec === regexpExec) {
3247 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
3248 // The native String method already delegates to @@method (this
3249 // polyfilled function), leasing to infinite recursion.
3250 // We avoid it by directly calling the native @@method method.
3251 return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
3253 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
3255 return { done: false };
3257 REPLACE_KEEPS_$0: REPLACE_KEEPS_$0,
3258 REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
3260 var stringMethod = methods[0];
3261 var regexMethod = methods[1];
3263 redefine(String.prototype, KEY, stringMethod);
3264 redefine(RegExp.prototype, SYMBOL, length == 2
3265 // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
3266 // 21.2.5.11 RegExp.prototype[@@split](string, limit)
3267 ? function (string, arg) { return regexMethod.call(string, this, arg); }
3268 // 21.2.5.6 RegExp.prototype[@@match](string)
3269 // 21.2.5.9 RegExp.prototype[@@search](string)
3270 : function (string) { return regexMethod.call(string, this); }
3274 if (sham) createNonEnumerableProperty(RegExp.prototype[SYMBOL], 'sham', true);
3277 var charAt$1 = stringMultibyte.charAt;
3279 // `AdvanceStringIndex` abstract operation
3280 // https://tc39.github.io/ecma262/#sec-advancestringindex
3281 var advanceStringIndex = function (S, index, unicode) {
3282 return index + (unicode ? charAt$1(S, index).length : 1);
3285 // `RegExpExec` abstract operation
3286 // https://tc39.github.io/ecma262/#sec-regexpexec
3287 var regexpExecAbstract = function (R, S) {
3289 if (typeof exec === 'function') {
3290 var result = exec.call(R, S);
3291 if (typeof result !== 'object') {
3292 throw TypeError('RegExp exec method returned something other than an Object or null');
3297 if (classofRaw(R) !== 'RegExp') {
3298 throw TypeError('RegExp#exec called on incompatible receiver');
3301 return regexpExec.call(R, S);
3304 var max$2 = Math.max;
3305 var min$2 = Math.min;
3306 var floor$2 = Math.floor;
3307 var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d\d?|<[^>]*>)/g;
3308 var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d\d?)/g;
3310 var maybeToString = function (it) {
3311 return it === undefined ? it : String(it);
3315 fixRegexpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) {
3316 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
3317 var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
3318 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
3321 // `String.prototype.replace` method
3322 // https://tc39.github.io/ecma262/#sec-string.prototype.replace
3323 function replace(searchValue, replaceValue) {
3324 var O = requireObjectCoercible(this);
3325 var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
3326 return replacer !== undefined
3327 ? replacer.call(searchValue, O, replaceValue)
3328 : nativeReplace.call(String(O), searchValue, replaceValue);
3330 // `RegExp.prototype[@@replace]` method
3331 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
3332 function (regexp, replaceValue) {
3334 (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) ||
3335 (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1)
3337 var res = maybeCallNative(nativeReplace, regexp, this, replaceValue);
3338 if (res.done) return res.value;
3341 var rx = anObject(regexp);
3342 var S = String(this);
3344 var functionalReplace = typeof replaceValue === 'function';
3345 if (!functionalReplace) replaceValue = String(replaceValue);
3347 var global = rx.global;
3349 var fullUnicode = rx.unicode;
3354 var result = regexpExecAbstract(rx, S);
3355 if (result === null) break;
3357 results.push(result);
3360 var matchStr = String(result[0]);
3361 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
3364 var accumulatedResult = '';
3365 var nextSourcePosition = 0;
3366 for (var i = 0; i < results.length; i++) {
3367 result = results[i];
3369 var matched = String(result[0]);
3370 var position = max$2(min$2(toInteger(result.index), S.length), 0);
3372 // NOTE: This is equivalent to
3373 // captures = result.slice(1).map(maybeToString)
3374 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
3375 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
3376 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
3377 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
3378 var namedCaptures = result.groups;
3379 if (functionalReplace) {
3380 var replacerArgs = [matched].concat(captures, position, S);
3381 if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
3382 var replacement = String(replaceValue.apply(undefined, replacerArgs));
3384 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
3386 if (position >= nextSourcePosition) {
3387 accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
3388 nextSourcePosition = position + matched.length;
3391 return accumulatedResult + S.slice(nextSourcePosition);
3395 // https://tc39.github.io/ecma262/#sec-getsubstitution
3396 function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
3397 var tailPos = position + matched.length;
3398 var m = captures.length;
3399 var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
3400 if (namedCaptures !== undefined) {
3401 namedCaptures = toObject(namedCaptures);
3402 symbols = SUBSTITUTION_SYMBOLS;
3404 return nativeReplace.call(replacement, symbols, function (match, ch) {
3406 switch (ch.charAt(0)) {
3407 case '$': return '$';
3408 case '&': return matched;
3409 case '`': return str.slice(0, position);
3410 case "'": return str.slice(tailPos);
3412 capture = namedCaptures[ch.slice(1, -1)];
3416 if (n === 0) return match;
3418 var f = floor$2(n / 10);
3419 if (f === 0) return match;
3420 if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
3423 capture = captures[n - 1];
3425 return capture === undefined ? '' : capture;
3430 var MATCH = wellKnownSymbol('match');
3432 // `IsRegExp` abstract operation
3433 // https://tc39.github.io/ecma262/#sec-isregexp
3434 var isRegexp = function (it) {
3436 return isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
3439 var arrayPush = [].push;
3440 var min$3 = Math.min;
3441 var MAX_UINT32 = 0xFFFFFFFF;
3443 // babel-minify transpiles RegExp('x', 'y') -> /x/y and it causes SyntaxError
3444 var SUPPORTS_Y = !fails(function () { return !RegExp(MAX_UINT32, 'y'); });
3447 fixRegexpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCallNative) {
3450 'abbc'.split(/(b)*/)[1] == 'c' ||
3451 'test'.split(/(?:)/, -1).length != 4 ||
3452 'ab'.split(/(?:ab)*/).length != 2 ||
3453 '.'.split(/(.?)(.?)/).length != 4 ||
3454 '.'.split(/()()/).length > 1 ||
3455 ''.split(/.?/).length
3457 // based on es5-shim implementation, need to rework it
3458 internalSplit = function (separator, limit) {
3459 var string = String(requireObjectCoercible(this));
3460 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
3461 if (lim === 0) return [];
3462 if (separator === undefined) return [string];
3463 // If `separator` is not a regex, use native split
3464 if (!isRegexp(separator)) {
3465 return nativeSplit.call(string, separator, lim);
3468 var flags = (separator.ignoreCase ? 'i' : '') +
3469 (separator.multiline ? 'm' : '') +
3470 (separator.unicode ? 'u' : '') +
3471 (separator.sticky ? 'y' : '');
3472 var lastLastIndex = 0;
3473 // Make `global` and avoid `lastIndex` issues by working with a copy
3474 var separatorCopy = new RegExp(separator.source, flags + 'g');
3475 var match, lastIndex, lastLength;
3476 while (match = regexpExec.call(separatorCopy, string)) {
3477 lastIndex = separatorCopy.lastIndex;
3478 if (lastIndex > lastLastIndex) {
3479 output.push(string.slice(lastLastIndex, match.index));
3480 if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
3481 lastLength = match[0].length;
3482 lastLastIndex = lastIndex;
3483 if (output.length >= lim) break;
3485 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
3487 if (lastLastIndex === string.length) {
3488 if (lastLength || !separatorCopy.test('')) output.push('');
3489 } else output.push(string.slice(lastLastIndex));
3490 return output.length > lim ? output.slice(0, lim) : output;
3493 } else if ('0'.split(undefined, 0).length) {
3494 internalSplit = function (separator, limit) {
3495 return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
3497 } else internalSplit = nativeSplit;
3500 // `String.prototype.split` method
3501 // https://tc39.github.io/ecma262/#sec-string.prototype.split
3502 function split(separator, limit) {
3503 var O = requireObjectCoercible(this);
3504 var splitter = separator == undefined ? undefined : separator[SPLIT];
3505 return splitter !== undefined
3506 ? splitter.call(separator, O, limit)
3507 : internalSplit.call(String(O), separator, limit);
3509 // `RegExp.prototype[@@split]` method
3510 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@split
3512 // NOTE: This cannot be properly polyfilled in engines that don't support
3514 function (regexp, limit) {
3515 var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit);
3516 if (res.done) return res.value;
3518 var rx = anObject(regexp);
3519 var S = String(this);
3520 var C = speciesConstructor(rx, RegExp);
3522 var unicodeMatching = rx.unicode;
3523 var flags = (rx.ignoreCase ? 'i' : '') +
3524 (rx.multiline ? 'm' : '') +
3525 (rx.unicode ? 'u' : '') +
3526 (SUPPORTS_Y ? 'y' : 'g');
3528 // ^(? + rx + ) is needed, in combination with some S slicing, to
3529 // simulate the 'y' flag.
3530 var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags);
3531 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
3532 if (lim === 0) return [];
3533 if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
3537 while (q < S.length) {
3538 splitter.lastIndex = SUPPORTS_Y ? q : 0;
3539 var z = regexpExecAbstract(splitter, SUPPORTS_Y ? S : S.slice(q));
3543 (e = min$3(toLength(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p
3545 q = advanceStringIndex(S, q, unicodeMatching);
3547 A.push(S.slice(p, q));
3548 if (A.length === lim) return A;
3549 for (var i = 1; i <= z.length - 1; i++) {
3551 if (A.length === lim) return A;
3562 // a string of all valid unicode whitespaces
3563 // eslint-disable-next-line max-len
3564 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';
3566 var whitespace = '[' + whitespaces + ']';
3567 var ltrim = RegExp('^' + whitespace + whitespace + '*');
3568 var rtrim = RegExp(whitespace + whitespace + '*$');
3570 // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
3571 var createMethod$3 = function (TYPE) {
3572 return function ($this) {
3573 var string = String(requireObjectCoercible($this));
3574 if (TYPE & 1) string = string.replace(ltrim, '');
3575 if (TYPE & 2) string = string.replace(rtrim, '');
3581 // `String.prototype.{ trimLeft, trimStart }` methods
3582 // https://tc39.github.io/ecma262/#sec-string.prototype.trimstart
3583 start: createMethod$3(1),
3584 // `String.prototype.{ trimRight, trimEnd }` methods
3585 // https://tc39.github.io/ecma262/#sec-string.prototype.trimend
3586 end: createMethod$3(2),
3587 // `String.prototype.trim` method
3588 // https://tc39.github.io/ecma262/#sec-string.prototype.trim
3589 trim: createMethod$3(3)
3592 var non = '\u200B\u0085\u180E';
3594 // check that a method works with the correct list
3595 // of whitespaces and has a correct name
3596 var stringTrimForced = function (METHOD_NAME) {
3597 return fails(function () {
3598 return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
3602 var $trim = stringTrim.trim;
3605 // `String.prototype.trim` method
3606 // https://tc39.github.io/ecma262/#sec-string.prototype.trim
3607 _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
3608 trim: function trim() {
3613 /* eslint-disable no-new */
3617 var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3619 var ArrayBuffer$3 = global_1.ArrayBuffer;
3620 var Int8Array$2 = global_1.Int8Array;
3622 var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS$2 || !fails(function () {
3624 }) || !fails(function () {
3625 new Int8Array$2(-1);
3626 }) || !checkCorrectnessOfIteration(function (iterable) {
3628 new Int8Array$2(null);
3629 new Int8Array$2(1.5);
3630 new Int8Array$2(iterable);
3631 }, true) || fails(function () {
3632 // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3633 return new Int8Array$2(new ArrayBuffer$3(2), 1, undefined).length !== 1;
3636 var toPositiveInteger = function (it) {
3637 var result = toInteger(it);
3638 if (result < 0) throw RangeError("The argument can't be less than 0");
3642 var toOffset = function (it, BYTES) {
3643 var offset = toPositiveInteger(it);
3644 if (offset % BYTES) throw RangeError('Wrong offset');
3648 var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3650 var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3651 var O = toObject(source);
3652 var argumentsLength = arguments.length;
3653 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3654 var mapping = mapfn !== undefined;
3655 var iteratorMethod = getIteratorMethod(O);
3656 var i, length, result, step, iterator, next;
3657 if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3658 iterator = iteratorMethod.call(O);
3659 next = iterator.next;
3661 while (!(step = next.call(iterator)).done) {
3665 if (mapping && argumentsLength > 2) {
3666 mapfn = functionBindContext(mapfn, arguments[2], 2);
3668 length = toLength(O.length);
3669 result = new (aTypedArrayConstructor$1(this))(length);
3670 for (i = 0; length > i; i++) {
3671 result[i] = mapping ? mapfn(O[i], i) : O[i];
3676 // makes subclassing work correct for wrapped built-ins
3677 var inheritIfRequired = function ($this, dummy, Wrapper) {
3678 var NewTarget, NewTargetPrototype;
3680 // it can work only with native `setPrototypeOf`
3681 objectSetPrototypeOf &&
3682 // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3683 typeof (NewTarget = dummy.constructor) == 'function' &&
3684 NewTarget !== Wrapper &&
3685 isObject(NewTargetPrototype = NewTarget.prototype) &&
3686 NewTargetPrototype !== Wrapper.prototype
3687 ) objectSetPrototypeOf($this, NewTargetPrototype);
3691 var typedArrayConstructor = createCommonjsModule(function (module) {
3710 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3712 var forEach = arrayIteration.forEach;
3719 var getInternalState = internalState.get;
3720 var setInternalState = internalState.set;
3721 var nativeDefineProperty = objectDefineProperty.f;
3722 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3723 var round = Math.round;
3724 var RangeError = global_1.RangeError;
3725 var ArrayBuffer = arrayBuffer.ArrayBuffer;
3726 var DataView = arrayBuffer.DataView;
3727 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3728 var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3729 var TypedArray = arrayBufferViewCore.TypedArray;
3730 var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3731 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3732 var isTypedArray = arrayBufferViewCore.isTypedArray;
3733 var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3734 var WRONG_LENGTH = 'Wrong length';
3736 var fromList = function (C, list) {
3738 var length = list.length;
3739 var result = new (aTypedArrayConstructor(C))(length);
3740 while (length > index) result[index] = list[index++];
3744 var addGetter = function (it, key) {
3745 nativeDefineProperty(it, key, { get: function () {
3746 return getInternalState(this)[key];
3750 var isArrayBuffer = function (it) {
3752 return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3755 var isTypedArrayIndex = function (target, key) {
3756 return isTypedArray(target)
3757 && typeof key != 'symbol'
3759 && String(+key) == String(key);
3762 var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3763 return isTypedArrayIndex(target, key = toPrimitive(key, true))
3764 ? createPropertyDescriptor(2, target[key])
3765 : nativeGetOwnPropertyDescriptor(target, key);
3768 var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3769 if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3770 && isObject(descriptor)
3771 && has(descriptor, 'value')
3772 && !has(descriptor, 'get')
3773 && !has(descriptor, 'set')
3774 // TODO: add validation descriptor w/o calling accessors
3775 && !descriptor.configurable
3776 && (!has(descriptor, 'writable') || descriptor.writable)
3777 && (!has(descriptor, 'enumerable') || descriptor.enumerable)
3779 target[key] = descriptor.value;
3781 } return nativeDefineProperty(target, key, descriptor);
3785 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3786 objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3787 objectDefineProperty.f = wrappedDefineProperty;
3788 addGetter(TypedArrayPrototype, 'buffer');
3789 addGetter(TypedArrayPrototype, 'byteOffset');
3790 addGetter(TypedArrayPrototype, 'byteLength');
3791 addGetter(TypedArrayPrototype, 'length');
3794 _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3795 getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3796 defineProperty: wrappedDefineProperty
3799 module.exports = function (TYPE, wrapper, CLAMPED) {
3800 var BYTES = TYPE.match(/\d+$/)[0] / 8;
3801 var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3802 var GETTER = 'get' + TYPE;
3803 var SETTER = 'set' + TYPE;
3804 var NativeTypedArrayConstructor = global_1[CONSTRUCTOR_NAME];
3805 var TypedArrayConstructor = NativeTypedArrayConstructor;
3806 var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3809 var getter = function (that, index) {
3810 var data = getInternalState(that);
3811 return data.view[GETTER](index * BYTES + data.byteOffset, true);
3814 var setter = function (that, index, value) {
3815 var data = getInternalState(that);
3816 if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3817 data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3820 var addElement = function (that, index) {
3821 nativeDefineProperty(that, index, {
3823 return getter(this, index);
3825 set: function (value) {
3826 return setter(this, index, value);
3832 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3833 TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3834 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3837 var buffer, byteLength, length;
3838 if (!isObject(data)) {
3839 length = toIndex(data);
3840 byteLength = length * BYTES;
3841 buffer = new ArrayBuffer(byteLength);
3842 } else if (isArrayBuffer(data)) {
3844 byteOffset = toOffset(offset, BYTES);
3845 var $len = data.byteLength;
3846 if ($length === undefined) {
3847 if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3848 byteLength = $len - byteOffset;
3849 if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3851 byteLength = toLength($length) * BYTES;
3852 if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3854 length = byteLength / BYTES;
3855 } else if (isTypedArray(data)) {
3856 return fromList(TypedArrayConstructor, data);
3858 return typedArrayFrom.call(TypedArrayConstructor, data);
3860 setInternalState(that, {
3862 byteOffset: byteOffset,
3863 byteLength: byteLength,
3865 view: new DataView(buffer)
3867 while (index < length) addElement(that, index++);
3870 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3871 TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3872 } else if (typedArrayConstructorsRequireWrappers) {
3873 TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3874 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3875 return inheritIfRequired(function () {
3876 if (!isObject(data)) return new NativeTypedArrayConstructor(toIndex(data));
3877 if (isArrayBuffer(data)) return $length !== undefined
3878 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3879 : typedArrayOffset !== undefined
3880 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3881 : new NativeTypedArrayConstructor(data);
3882 if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3883 return typedArrayFrom.call(TypedArrayConstructor, data);
3884 }(), dummy, TypedArrayConstructor);
3887 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3888 forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3889 if (!(key in TypedArrayConstructor)) {
3890 createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3893 TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3896 if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3897 createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3900 if (TYPED_ARRAY_TAG) {
3901 createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3904 exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3907 global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3910 if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3911 createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3914 if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3915 createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3918 setSpecies(CONSTRUCTOR_NAME);
3920 } else module.exports = function () { /* empty */ };
3923 // `Uint8Array` constructor
3924 // https://tc39.github.io/ecma262/#sec-typedarray-objects
3925 typedArrayConstructor('Uint8', function (init) {
3926 return function Uint8Array(data, byteOffset, length) {
3927 return init(this, data, byteOffset, length);
3931 var min$4 = Math.min;
3933 // `Array.prototype.copyWithin` method implementation
3934 // https://tc39.github.io/ecma262/#sec-array.prototype.copywithin
3935 var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3936 var O = toObject(this);
3937 var len = toLength(O.length);
3938 var to = toAbsoluteIndex(target, len);
3939 var from = toAbsoluteIndex(start, len);
3940 var end = arguments.length > 2 ? arguments[2] : undefined;
3941 var count = min$4((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3943 if (from < to && to < from + count) {
3948 while (count-- > 0) {
3949 if (from in O) O[to] = O[from];
3956 var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
3957 var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
3959 // `%TypedArray%.prototype.copyWithin` method
3960 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.copywithin
3961 exportTypedArrayMethod$1('copyWithin', function copyWithin(target, start /* , end */) {
3962 return arrayCopyWithin.call(aTypedArray$1(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3965 var $every = arrayIteration.every;
3967 var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3968 var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
3970 // `%TypedArray%.prototype.every` method
3971 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
3972 exportTypedArrayMethod$2('every', function every(callbackfn /* , thisArg */) {
3973 return $every(aTypedArray$2(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3976 var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3977 var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3979 // `%TypedArray%.prototype.fill` method
3980 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.fill
3981 // eslint-disable-next-line no-unused-vars
3982 exportTypedArrayMethod$3('fill', function fill(value /* , start, end */) {
3983 return arrayFill.apply(aTypedArray$3(this), arguments);
3986 var $filter = arrayIteration.filter;
3989 var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3990 var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3991 var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3993 // `%TypedArray%.prototype.filter` method
3994 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter
3995 exportTypedArrayMethod$4('filter', function filter(callbackfn /* , thisArg */) {
3996 var list = $filter(aTypedArray$4(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3997 var C = speciesConstructor(this, this.constructor);
3999 var length = list.length;
4000 var result = new (aTypedArrayConstructor$2(C))(length);
4001 while (length > index) result[index] = list[index++];
4005 var $find = arrayIteration.find;
4007 var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
4008 var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
4010 // `%TypedArray%.prototype.find` method
4011 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find
4012 exportTypedArrayMethod$5('find', function find(predicate /* , thisArg */) {
4013 return $find(aTypedArray$5(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
4016 var $findIndex = arrayIteration.findIndex;
4018 var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
4019 var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
4021 // `%TypedArray%.prototype.findIndex` method
4022 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findindex
4023 exportTypedArrayMethod$6('findIndex', function findIndex(predicate /* , thisArg */) {
4024 return $findIndex(aTypedArray$6(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
4027 var $forEach$2 = arrayIteration.forEach;
4029 var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
4030 var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
4032 // `%TypedArray%.prototype.forEach` method
4033 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.foreach
4034 exportTypedArrayMethod$7('forEach', function forEach(callbackfn /* , thisArg */) {
4035 $forEach$2(aTypedArray$7(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
4038 var $includes = arrayIncludes.includes;
4040 var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
4041 var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
4043 // `%TypedArray%.prototype.includes` method
4044 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.includes
4045 exportTypedArrayMethod$8('includes', function includes(searchElement /* , fromIndex */) {
4046 return $includes(aTypedArray$8(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
4049 var $indexOf$1 = arrayIncludes.indexOf;
4051 var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
4052 var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
4054 // `%TypedArray%.prototype.indexOf` method
4055 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.indexof
4056 exportTypedArrayMethod$9('indexOf', function indexOf(searchElement /* , fromIndex */) {
4057 return $indexOf$1(aTypedArray$9(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
4060 var ITERATOR$5 = wellKnownSymbol('iterator');
4061 var Uint8Array$1 = global_1.Uint8Array;
4062 var arrayValues = es_array_iterator.values;
4063 var arrayKeys = es_array_iterator.keys;
4064 var arrayEntries = es_array_iterator.entries;
4065 var aTypedArray$a = arrayBufferViewCore.aTypedArray;
4066 var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
4067 var nativeTypedArrayIterator = Uint8Array$1 && Uint8Array$1.prototype[ITERATOR$5];
4069 var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
4070 && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
4072 var typedArrayValues = function values() {
4073 return arrayValues.call(aTypedArray$a(this));
4076 // `%TypedArray%.prototype.entries` method
4077 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.entries
4078 exportTypedArrayMethod$a('entries', function entries() {
4079 return arrayEntries.call(aTypedArray$a(this));
4081 // `%TypedArray%.prototype.keys` method
4082 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.keys
4083 exportTypedArrayMethod$a('keys', function keys() {
4084 return arrayKeys.call(aTypedArray$a(this));
4086 // `%TypedArray%.prototype.values` method
4087 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.values
4088 exportTypedArrayMethod$a('values', typedArrayValues, !CORRECT_ITER_NAME);
4089 // `%TypedArray%.prototype[@@iterator]` method
4090 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype-@@iterator
4091 exportTypedArrayMethod$a(ITERATOR$5, typedArrayValues, !CORRECT_ITER_NAME);
4093 var aTypedArray$b = arrayBufferViewCore.aTypedArray;
4094 var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
4095 var $join = [].join;
4097 // `%TypedArray%.prototype.join` method
4098 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.join
4099 // eslint-disable-next-line no-unused-vars
4100 exportTypedArrayMethod$b('join', function join(separator) {
4101 return $join.apply(aTypedArray$b(this), arguments);
4104 var min$5 = Math.min;
4105 var nativeLastIndexOf = [].lastIndexOf;
4106 var NEGATIVE_ZERO$1 = !!nativeLastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
4107 var STRICT_METHOD$3 = arrayMethodIsStrict('lastIndexOf');
4108 // For preventing possible almost infinite loop in non-standard implementations, test the forward version of the method
4109 var USES_TO_LENGTH$4 = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
4110 var FORCED$1 = NEGATIVE_ZERO$1 || !STRICT_METHOD$3 || !USES_TO_LENGTH$4;
4112 // `Array.prototype.lastIndexOf` method implementation
4113 // https://tc39.github.io/ecma262/#sec-array.prototype.lastindexof
4114 var arrayLastIndexOf = FORCED$1 ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
4116 if (NEGATIVE_ZERO$1) return nativeLastIndexOf.apply(this, arguments) || 0;
4117 var O = toIndexedObject(this);
4118 var length = toLength(O.length);
4119 var index = length - 1;
4120 if (arguments.length > 1) index = min$5(index, toInteger(arguments[1]));
4121 if (index < 0) index = length + index;
4122 for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
4124 } : nativeLastIndexOf;
4126 var aTypedArray$c = arrayBufferViewCore.aTypedArray;
4127 var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
4129 // `%TypedArray%.prototype.lastIndexOf` method
4130 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.lastindexof
4131 // eslint-disable-next-line no-unused-vars
4132 exportTypedArrayMethod$c('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
4133 return arrayLastIndexOf.apply(aTypedArray$c(this), arguments);
4136 var $map$1 = arrayIteration.map;
4139 var aTypedArray$d = arrayBufferViewCore.aTypedArray;
4140 var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
4141 var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
4143 // `%TypedArray%.prototype.map` method
4144 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.map
4145 exportTypedArrayMethod$d('map', function map(mapfn /* , thisArg */) {
4146 return $map$1(aTypedArray$d(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
4147 return new (aTypedArrayConstructor$3(speciesConstructor(O, O.constructor)))(length);
4151 // `Array.prototype.{ reduce, reduceRight }` methods implementation
4152 var createMethod$4 = function (IS_RIGHT) {
4153 return function (that, callbackfn, argumentsLength, memo) {
4154 aFunction$1(callbackfn);
4155 var O = toObject(that);
4156 var self = indexedObject(O);
4157 var length = toLength(O.length);
4158 var index = IS_RIGHT ? length - 1 : 0;
4159 var i = IS_RIGHT ? -1 : 1;
4160 if (argumentsLength < 2) while (true) {
4161 if (index in self) {
4167 if (IS_RIGHT ? index < 0 : length <= index) {
4168 throw TypeError('Reduce of empty array with no initial value');
4171 for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
4172 memo = callbackfn(memo, self[index], index, O);
4179 // `Array.prototype.reduce` method
4180 // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
4181 left: createMethod$4(false),
4182 // `Array.prototype.reduceRight` method
4183 // https://tc39.github.io/ecma262/#sec-array.prototype.reduceright
4184 right: createMethod$4(true)
4187 var $reduce = arrayReduce.left;
4189 var aTypedArray$e = arrayBufferViewCore.aTypedArray;
4190 var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
4192 // `%TypedArray%.prototype.reduce` method
4193 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce
4194 exportTypedArrayMethod$e('reduce', function reduce(callbackfn /* , initialValue */) {
4195 return $reduce(aTypedArray$e(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
4198 var $reduceRight = arrayReduce.right;
4200 var aTypedArray$f = arrayBufferViewCore.aTypedArray;
4201 var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
4203 // `%TypedArray%.prototype.reduceRicht` method
4204 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright
4205 exportTypedArrayMethod$f('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
4206 return $reduceRight(aTypedArray$f(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
4209 var aTypedArray$g = arrayBufferViewCore.aTypedArray;
4210 var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
4211 var floor$3 = Math.floor;
4213 // `%TypedArray%.prototype.reverse` method
4214 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reverse
4215 exportTypedArrayMethod$g('reverse', function reverse() {
4217 var length = aTypedArray$g(that).length;
4218 var middle = floor$3(length / 2);
4221 while (index < middle) {
4222 value = that[index];
4223 that[index++] = that[--length];
4224 that[length] = value;
4228 var aTypedArray$h = arrayBufferViewCore.aTypedArray;
4229 var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
4231 var FORCED$2 = fails(function () {
4232 // eslint-disable-next-line no-undef
4233 new Int8Array(1).set({});
4236 // `%TypedArray%.prototype.set` method
4237 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.set
4238 exportTypedArrayMethod$h('set', function set(arrayLike /* , offset */) {
4239 aTypedArray$h(this);
4240 var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
4241 var length = this.length;
4242 var src = toObject(arrayLike);
4243 var len = toLength(src.length);
4245 if (len + offset > length) throw RangeError('Wrong length');
4246 while (index < len) this[offset + index] = src[index++];
4249 var aTypedArray$i = arrayBufferViewCore.aTypedArray;
4250 var aTypedArrayConstructor$4 = arrayBufferViewCore.aTypedArrayConstructor;
4251 var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
4252 var $slice = [].slice;
4254 var FORCED$3 = fails(function () {
4255 // eslint-disable-next-line no-undef
4256 new Int8Array(1).slice();
4259 // `%TypedArray%.prototype.slice` method
4260 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice
4261 exportTypedArrayMethod$i('slice', function slice(start, end) {
4262 var list = $slice.call(aTypedArray$i(this), start, end);
4263 var C = speciesConstructor(this, this.constructor);
4265 var length = list.length;
4266 var result = new (aTypedArrayConstructor$4(C))(length);
4267 while (length > index) result[index] = list[index++];
4271 var $some = arrayIteration.some;
4273 var aTypedArray$j = arrayBufferViewCore.aTypedArray;
4274 var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
4276 // `%TypedArray%.prototype.some` method
4277 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some
4278 exportTypedArrayMethod$j('some', function some(callbackfn /* , thisArg */) {
4279 return $some(aTypedArray$j(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
4282 var aTypedArray$k = arrayBufferViewCore.aTypedArray;
4283 var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
4284 var $sort = [].sort;
4286 // `%TypedArray%.prototype.sort` method
4287 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort
4288 exportTypedArrayMethod$k('sort', function sort(comparefn) {
4289 return $sort.call(aTypedArray$k(this), comparefn);
4292 var aTypedArray$l = arrayBufferViewCore.aTypedArray;
4293 var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
4295 // `%TypedArray%.prototype.subarray` method
4296 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.subarray
4297 exportTypedArrayMethod$l('subarray', function subarray(begin, end) {
4298 var O = aTypedArray$l(this);
4299 var length = O.length;
4300 var beginIndex = toAbsoluteIndex(begin, length);
4301 return new (speciesConstructor(O, O.constructor))(
4303 O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
4304 toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
4308 var Int8Array$3 = global_1.Int8Array;
4309 var aTypedArray$m = arrayBufferViewCore.aTypedArray;
4310 var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
4311 var $toLocaleString = [].toLocaleString;
4312 var $slice$1 = [].slice;
4314 // iOS Safari 6.x fails here
4315 var TO_LOCALE_STRING_BUG = !!Int8Array$3 && fails(function () {
4316 $toLocaleString.call(new Int8Array$3(1));
4319 var FORCED$4 = fails(function () {
4320 return [1, 2].toLocaleString() != new Int8Array$3([1, 2]).toLocaleString();
4321 }) || !fails(function () {
4322 Int8Array$3.prototype.toLocaleString.call([1, 2]);
4325 // `%TypedArray%.prototype.toLocaleString` method
4326 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tolocalestring
4327 exportTypedArrayMethod$m('toLocaleString', function toLocaleString() {
4328 return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice$1.call(aTypedArray$m(this)) : aTypedArray$m(this), arguments);
4331 var exportTypedArrayMethod$n = arrayBufferViewCore.exportTypedArrayMethod;
4335 var Uint8Array$2 = global_1.Uint8Array;
4336 var Uint8ArrayPrototype = Uint8Array$2 && Uint8Array$2.prototype || {};
4337 var arrayToString = [].toString;
4338 var arrayJoin = [].join;
4340 if (fails(function () { arrayToString.call({}); })) {
4341 arrayToString = function toString() {
4342 return arrayJoin.call(this);
4346 var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
4348 // `%TypedArray%.prototype.toString` method
4349 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tostring
4350 exportTypedArrayMethod$n('toString', arrayToString, IS_NOT_ARRAY_METHOD);
4352 // iterable DOM collections
4353 // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
4354 var domIterables = {
4356 CSSStyleDeclaration: 0,
4362 DataTransferItemList: 0,
4364 HTMLAllCollection: 0,
4367 HTMLSelectElement: 0,
4372 PaintRequestList: 0,
4380 SVGTransformList: 0,
4381 SourceBufferList: 0,
4383 TextTrackCueList: 0,
4388 for (var COLLECTION_NAME in domIterables) {
4389 var Collection = global_1[COLLECTION_NAME];
4390 var CollectionPrototype = Collection && Collection.prototype;
4391 // some Chrome versions have non-configurable methods on DOMTokenList
4392 if (CollectionPrototype && CollectionPrototype.forEach !== arrayForEach) try {
4393 createNonEnumerableProperty(CollectionPrototype, 'forEach', arrayForEach);
4395 CollectionPrototype.forEach = arrayForEach;
4399 var ITERATOR$6 = wellKnownSymbol('iterator');
4400 var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
4401 var ArrayValues = es_array_iterator.values;
4403 for (var COLLECTION_NAME$1 in domIterables) {
4404 var Collection$1 = global_1[COLLECTION_NAME$1];
4405 var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
4406 if (CollectionPrototype$1) {
4407 // some Chrome versions have non-configurable methods on DOMTokenList
4408 if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try {
4409 createNonEnumerableProperty(CollectionPrototype$1, ITERATOR$6, ArrayValues);
4411 CollectionPrototype$1[ITERATOR$6] = ArrayValues;
4413 if (!CollectionPrototype$1[TO_STRING_TAG$4]) {
4414 createNonEnumerableProperty(CollectionPrototype$1, TO_STRING_TAG$4, COLLECTION_NAME$1);
4416 if (domIterables[COLLECTION_NAME$1]) for (var METHOD_NAME in es_array_iterator) {
4417 // some Chrome versions have non-configurable methods on DOMTokenList
4418 if (CollectionPrototype$1[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
4419 createNonEnumerableProperty(CollectionPrototype$1, METHOD_NAME, es_array_iterator[METHOD_NAME]);
4421 CollectionPrototype$1[METHOD_NAME] = es_array_iterator[METHOD_NAME];
4427 var slice = [].slice;
4428 var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
4430 var wrap$1 = function (scheduler) {
4431 return function (handler, timeout /* , ...arguments */) {
4432 var boundArgs = arguments.length > 2;
4433 var args = boundArgs ? slice.call(arguments, 2) : undefined;
4434 return scheduler(boundArgs ? function () {
4435 // eslint-disable-next-line no-new-func
4436 (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
4437 } : handler, timeout);
4441 // ie9- setTimeout & setInterval additional parameters fix
4442 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
4443 _export({ global: true, bind: true, forced: MSIE }, {
4444 // `setTimeout` method
4445 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
4446 setTimeout: wrap$1(global_1.setTimeout),
4447 // `setInterval` method
4448 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
4449 setInterval: wrap$1(global_1.setInterval)
4452 var ITERATOR$7 = wellKnownSymbol('iterator');
4454 var nativeUrl = !fails(function () {
4455 var url = new URL('b?a=1&b=2&c=3', 'http://a');
4456 var searchParams = url.searchParams;
4458 url.pathname = 'c%20d';
4459 searchParams.forEach(function (value, key) {
4460 searchParams['delete']('b');
4461 result += key + value;
4463 return (isPure && !url.toJSON)
4464 || !searchParams.sort
4465 || url.href !== 'http://a/c%20d?a=1&c=3'
4466 || searchParams.get('c') !== '3'
4467 || String(new URLSearchParams('?a=1')) !== 'a=1'
4468 || !searchParams[ITERATOR$7]
4470 || new URL('https://a@b').username !== 'a'
4471 || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
4472 // not punycoded in Edge
4473 || new URL('http://тест').host !== 'xn--e1aybc'
4474 // not escaped in Chrome 62-
4475 || new URL('http://a#б').hash !== '#%D0%B1'
4476 // fails in Chrome 66-
4477 || result !== 'a1c3'
4479 || new URL('http://x', undefined).host !== 'x';
4482 var nativeAssign = Object.assign;
4483 var defineProperty$7 = Object.defineProperty;
4485 // `Object.assign` method
4486 // https://tc39.github.io/ecma262/#sec-object.assign
4487 var objectAssign = !nativeAssign || fails(function () {
4488 // should have correct order of operations (Edge bug)
4489 if (descriptors && nativeAssign({ b: 1 }, nativeAssign(defineProperty$7({}, 'a', {
4492 defineProperty$7(this, 'b', {
4497 }), { b: 2 })).b !== 1) return true;
4498 // should work with symbols and should have deterministic property order (V8 bug)
4501 // eslint-disable-next-line no-undef
4502 var symbol = Symbol();
4503 var alphabet = 'abcdefghijklmnopqrst';
4505 alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4506 return nativeAssign({}, A)[symbol] != 7 || objectKeys(nativeAssign({}, B)).join('') != alphabet;
4507 }) ? function assign(target, source) { // eslint-disable-line no-unused-vars
4508 var T = toObject(target);
4509 var argumentsLength = arguments.length;
4511 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4512 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4513 while (argumentsLength > index) {
4514 var S = indexedObject(arguments[index++]);
4515 var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4516 var length = keys.length;
4519 while (length > j) {
4521 if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4526 // call something on iterator step with safe closing on error
4527 var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
4529 return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
4530 // 7.4.6 IteratorClose(iterator, completion)
4532 iteratorClose(iterator);
4537 // `Array.from` method implementation
4538 // https://tc39.github.io/ecma262/#sec-array.from
4539 var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4540 var O = toObject(arrayLike);
4541 var C = typeof this == 'function' ? this : Array;
4542 var argumentsLength = arguments.length;
4543 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4544 var mapping = mapfn !== undefined;
4545 var iteratorMethod = getIteratorMethod(O);
4547 var length, result, step, iterator, next, value;
4548 if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4549 // if the target is not iterable or it's an array with the default iterator - use a simple case
4550 if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4551 iterator = iteratorMethod.call(O);
4552 next = iterator.next;
4554 for (;!(step = next.call(iterator)).done; index++) {
4555 value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4556 createProperty(result, index, value);
4559 length = toLength(O.length);
4560 result = new C(length);
4561 for (;length > index; index++) {
4562 value = mapping ? mapfn(O[index], index) : O[index];
4563 createProperty(result, index, value);
4566 result.length = index;
4570 // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4571 var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4577 var initialBias = 72;
4578 var initialN = 128; // 0x80
4579 var delimiter = '-'; // '\x2D'
4580 var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4581 var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4582 var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4583 var baseMinusTMin = base - tMin;
4584 var floor$4 = Math.floor;
4585 var stringFromCharCode = String.fromCharCode;
4588 * Creates an array containing the numeric code points of each Unicode
4589 * character in the string. While JavaScript uses UCS-2 internally,
4590 * this function will convert a pair of surrogate halves (each of which
4591 * UCS-2 exposes as separate characters) into a single code point,
4594 var ucs2decode = function (string) {
4597 var length = string.length;
4598 while (counter < length) {
4599 var value = string.charCodeAt(counter++);
4600 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4601 // It's a high surrogate, and there is a next character.
4602 var extra = string.charCodeAt(counter++);
4603 if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4604 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4606 // It's an unmatched surrogate; only append this code unit, in case the
4607 // next code unit is the high surrogate of a surrogate pair.
4619 * Converts a digit/integer into a basic code point.
4621 var digitToBasic = function (digit) {
4622 // 0..25 map to ASCII a..z or A..Z
4623 // 26..35 map to ASCII 0..9
4624 return digit + 22 + 75 * (digit < 26);
4628 * Bias adaptation function as per section 3.4 of RFC 3492.
4629 * https://tools.ietf.org/html/rfc3492#section-3.4
4631 var adapt = function (delta, numPoints, firstTime) {
4633 delta = firstTime ? floor$4(delta / damp) : delta >> 1;
4634 delta += floor$4(delta / numPoints);
4635 for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4636 delta = floor$4(delta / baseMinusTMin);
4638 return floor$4(k + (baseMinusTMin + 1) * delta / (delta + skew));
4642 * Converts a string of Unicode symbols (e.g. a domain name label) to a
4643 * Punycode string of ASCII-only symbols.
4645 // eslint-disable-next-line max-statements
4646 var encode = function (input) {
4649 // Convert the input in UCS-2 to an array of Unicode code points.
4650 input = ucs2decode(input);
4652 // Cache the length.
4653 var inputLength = input.length;
4655 // Initialize the state.
4658 var bias = initialBias;
4659 var i, currentValue;
4661 // Handle the basic code points.
4662 for (i = 0; i < input.length; i++) {
4663 currentValue = input[i];
4664 if (currentValue < 0x80) {
4665 output.push(stringFromCharCode(currentValue));
4669 var basicLength = output.length; // number of basic code points.
4670 var handledCPCount = basicLength; // number of code points that have been handled;
4672 // Finish the basic string with a delimiter unless it's empty.
4674 output.push(delimiter);
4677 // Main encoding loop:
4678 while (handledCPCount < inputLength) {
4679 // All non-basic code points < n have been handled already. Find the next larger one:
4681 for (i = 0; i < input.length; i++) {
4682 currentValue = input[i];
4683 if (currentValue >= n && currentValue < m) {
4688 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4689 var handledCPCountPlusOne = handledCPCount + 1;
4690 if (m - n > floor$4((maxInt - delta) / handledCPCountPlusOne)) {
4691 throw RangeError(OVERFLOW_ERROR);
4694 delta += (m - n) * handledCPCountPlusOne;
4697 for (i = 0; i < input.length; i++) {
4698 currentValue = input[i];
4699 if (currentValue < n && ++delta > maxInt) {
4700 throw RangeError(OVERFLOW_ERROR);
4702 if (currentValue == n) {
4703 // Represent delta as a generalized variable-length integer.
4705 for (var k = base; /* no condition */; k += base) {
4706 var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4708 var qMinusT = q - t;
4709 var baseMinusT = base - t;
4710 output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4711 q = floor$4(qMinusT / baseMinusT);
4714 output.push(stringFromCharCode(digitToBasic(q)));
4715 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4724 return output.join('');
4727 var stringPunycodeToAscii = function (input) {
4729 var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4731 for (i = 0; i < labels.length; i++) {
4733 encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4735 return encoded.join('.');
4738 var getIterator = function (it) {
4739 var iteratorMethod = getIteratorMethod(it);
4740 if (typeof iteratorMethod != 'function') {
4741 throw TypeError(String(it) + ' is not iterable');
4742 } return anObject(iteratorMethod.call(it));
4745 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4767 var $fetch$1 = getBuiltIn('fetch');
4768 var Headers = getBuiltIn('Headers');
4769 var ITERATOR$8 = wellKnownSymbol('iterator');
4770 var URL_SEARCH_PARAMS = 'URLSearchParams';
4771 var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4772 var setInternalState$5 = internalState.set;
4773 var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4774 var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4777 var sequences = Array(4);
4779 var percentSequence = function (bytes) {
4780 return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4783 var percentDecode = function (sequence) {
4785 return decodeURIComponent(sequence);
4791 var deserialize = function (it) {
4792 var result = it.replace(plus, ' ');
4795 return decodeURIComponent(result);
4798 result = result.replace(percentSequence(bytes--), percentDecode);
4804 var find = /[!'()~]|%20/g;
4815 var replacer = function (match) {
4816 return replace[match];
4819 var serialize = function (it) {
4820 return encodeURIComponent(it).replace(find, replacer);
4823 var parseSearchParams = function (result, query) {
4825 var attributes = query.split('&');
4827 var attribute, entry;
4828 while (index < attributes.length) {
4829 attribute = attributes[index++];
4830 if (attribute.length) {
4831 entry = attribute.split('=');
4833 key: deserialize(entry.shift()),
4834 value: deserialize(entry.join('='))
4841 var updateSearchParams = function (query) {
4842 this.entries.length = 0;
4843 parseSearchParams(this.entries, query);
4846 var validateArgumentsLength = function (passed, required) {
4847 if (passed < required) throw TypeError('Not enough arguments');
4850 var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4851 setInternalState$5(this, {
4852 type: URL_SEARCH_PARAMS_ITERATOR,
4853 iterator: getIterator(getInternalParamsState(params).entries),
4856 }, 'Iterator', function next() {
4857 var state = getInternalIteratorState(this);
4858 var kind = state.kind;
4859 var step = state.iterator.next();
4860 var entry = step.value;
4862 step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4866 // `URLSearchParams` constructor
4867 // https://url.spec.whatwg.org/#interface-urlsearchparams
4868 var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4869 anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4870 var init = arguments.length > 0 ? arguments[0] : undefined;
4873 var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4875 setInternalState$5(that, {
4876 type: URL_SEARCH_PARAMS,
4878 updateURL: function () { /* empty */ },
4879 updateSearchParams: updateSearchParams
4882 if (init !== undefined) {
4883 if (isObject(init)) {
4884 iteratorMethod = getIteratorMethod(init);
4885 if (typeof iteratorMethod === 'function') {
4886 iterator = iteratorMethod.call(init);
4887 next = iterator.next;
4888 while (!(step = next.call(iterator)).done) {
4889 entryIterator = getIterator(anObject(step.value));
4890 entryNext = entryIterator.next;
4892 (first = entryNext.call(entryIterator)).done ||
4893 (second = entryNext.call(entryIterator)).done ||
4894 !entryNext.call(entryIterator).done
4895 ) throw TypeError('Expected sequence with length 2');
4896 entries.push({ key: first.value + '', value: second.value + '' });
4898 } else for (key in init) if (has(init, key)) entries.push({ key: key, value: init[key] + '' });
4900 parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4905 var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4907 redefineAll(URLSearchParamsPrototype, {
4908 // `URLSearchParams.prototype.append` method
4909 // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4910 append: function append(name, value) {
4911 validateArgumentsLength(arguments.length, 2);
4912 var state = getInternalParamsState(this);
4913 state.entries.push({ key: name + '', value: value + '' });
4916 // `URLSearchParams.prototype.delete` method
4917 // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4918 'delete': function (name) {
4919 validateArgumentsLength(arguments.length, 1);
4920 var state = getInternalParamsState(this);
4921 var entries = state.entries;
4922 var key = name + '';
4924 while (index < entries.length) {
4925 if (entries[index].key === key) entries.splice(index, 1);
4930 // `URLSearchParams.prototype.get` method
4931 // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4932 get: function get(name) {
4933 validateArgumentsLength(arguments.length, 1);
4934 var entries = getInternalParamsState(this).entries;
4935 var key = name + '';
4937 for (; index < entries.length; index++) {
4938 if (entries[index].key === key) return entries[index].value;
4942 // `URLSearchParams.prototype.getAll` method
4943 // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4944 getAll: function getAll(name) {
4945 validateArgumentsLength(arguments.length, 1);
4946 var entries = getInternalParamsState(this).entries;
4947 var key = name + '';
4950 for (; index < entries.length; index++) {
4951 if (entries[index].key === key) result.push(entries[index].value);
4955 // `URLSearchParams.prototype.has` method
4956 // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4957 has: function has(name) {
4958 validateArgumentsLength(arguments.length, 1);
4959 var entries = getInternalParamsState(this).entries;
4960 var key = name + '';
4962 while (index < entries.length) {
4963 if (entries[index++].key === key) return true;
4967 // `URLSearchParams.prototype.set` method
4968 // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4969 set: function set(name, value) {
4970 validateArgumentsLength(arguments.length, 1);
4971 var state = getInternalParamsState(this);
4972 var entries = state.entries;
4974 var key = name + '';
4975 var val = value + '';
4978 for (; index < entries.length; index++) {
4979 entry = entries[index];
4980 if (entry.key === key) {
4981 if (found) entries.splice(index--, 1);
4988 if (!found) entries.push({ key: key, value: val });
4991 // `URLSearchParams.prototype.sort` method
4992 // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4993 sort: function sort() {
4994 var state = getInternalParamsState(this);
4995 var entries = state.entries;
4996 // Array#sort is not stable in some engines
4997 var slice = entries.slice();
4998 var entry, entriesIndex, sliceIndex;
5000 for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
5001 entry = slice[sliceIndex];
5002 for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
5003 if (entries[entriesIndex].key > entry.key) {
5004 entries.splice(entriesIndex, 0, entry);
5008 if (entriesIndex === sliceIndex) entries.push(entry);
5012 // `URLSearchParams.prototype.forEach` method
5013 forEach: function forEach(callback /* , thisArg */) {
5014 var entries = getInternalParamsState(this).entries;
5015 var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
5018 while (index < entries.length) {
5019 entry = entries[index++];
5020 boundFunction(entry.value, entry.key, this);
5023 // `URLSearchParams.prototype.keys` method
5024 keys: function keys() {
5025 return new URLSearchParamsIterator(this, 'keys');
5027 // `URLSearchParams.prototype.values` method
5028 values: function values() {
5029 return new URLSearchParamsIterator(this, 'values');
5031 // `URLSearchParams.prototype.entries` method
5032 entries: function entries() {
5033 return new URLSearchParamsIterator(this, 'entries');
5035 }, { enumerable: true });
5037 // `URLSearchParams.prototype[@@iterator]` method
5038 redefine(URLSearchParamsPrototype, ITERATOR$8, URLSearchParamsPrototype.entries);
5040 // `URLSearchParams.prototype.toString` method
5041 // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
5042 redefine(URLSearchParamsPrototype, 'toString', function toString() {
5043 var entries = getInternalParamsState(this).entries;
5047 while (index < entries.length) {
5048 entry = entries[index++];
5049 result.push(serialize(entry.key) + '=' + serialize(entry.value));
5050 } return result.join('&');
5051 }, { enumerable: true });
5053 setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
5055 _export({ global: true, forced: !nativeUrl }, {
5056 URLSearchParams: URLSearchParamsConstructor
5059 // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
5060 // https://github.com/zloirock/core-js/issues/674
5061 if (!nativeUrl && typeof $fetch$1 == 'function' && typeof Headers == 'function') {
5062 _export({ global: true, enumerable: true, forced: true }, {
5063 fetch: function fetch(input /* , init */) {
5065 var init, body, headers;
5066 if (arguments.length > 1) {
5067 init = arguments[1];
5068 if (isObject(init)) {
5070 if (classof(body) === URL_SEARCH_PARAMS) {
5071 headers = init.headers ? new Headers(init.headers) : new Headers();
5072 if (!headers.has('content-type')) {
5073 headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
5075 init = objectCreate(init, {
5076 body: createPropertyDescriptor(0, String(body)),
5077 headers: createPropertyDescriptor(0, headers)
5082 } return $fetch$1.apply(this, args);
5087 var web_urlSearchParams = {
5088 URLSearchParams: URLSearchParamsConstructor,
5089 getState: getInternalParamsState
5092 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
5104 var codeAt = stringMultibyte.codeAt;
5110 var NativeURL = global_1.URL;
5111 var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
5112 var getInternalSearchParamsState = web_urlSearchParams.getState;
5113 var setInternalState$6 = internalState.set;
5114 var getInternalURLState = internalState.getterFor('URL');
5115 var floor$5 = Math.floor;
5116 var pow$1 = Math.pow;
5118 var INVALID_AUTHORITY = 'Invalid authority';
5119 var INVALID_SCHEME = 'Invalid scheme';
5120 var INVALID_HOST = 'Invalid host';
5121 var INVALID_PORT = 'Invalid port';
5123 var ALPHA = /[A-Za-z]/;
5124 var ALPHANUMERIC = /[\d+-.A-Za-z]/;
5126 var HEX_START = /^(0x|0X)/;
5127 var OCT = /^[0-7]+$/;
5129 var HEX = /^[\dA-Fa-f]+$/;
5130 // eslint-disable-next-line no-control-regex
5131 var FORBIDDEN_HOST_CODE_POINT = /[\u0000\u0009\u000A\u000D #%/:?@[\\]]/;
5132 // eslint-disable-next-line no-control-regex
5133 var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\u0000\u0009\u000A\u000D #/:?@[\\]]/;
5134 // eslint-disable-next-line no-control-regex
5135 var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
5136 // eslint-disable-next-line no-control-regex
5137 var TAB_AND_NEW_LINE = /[\u0009\u000A\u000D]/g;
5140 var parseHost = function (url, input) {
5141 var result, codePoints, index;
5142 if (input.charAt(0) == '[') {
5143 if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
5144 result = parseIPv6(input.slice(1, -1));
5145 if (!result) return INVALID_HOST;
5148 } else if (!isSpecial(url)) {
5149 if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
5151 codePoints = arrayFrom(input);
5152 for (index = 0; index < codePoints.length; index++) {
5153 result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
5157 input = stringPunycodeToAscii(input);
5158 if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
5159 result = parseIPv4(input);
5160 if (result === null) return INVALID_HOST;
5165 var parseIPv4 = function (input) {
5166 var parts = input.split('.');
5167 var partsLength, numbers, index, part, radix, number, ipv4;
5168 if (parts.length && parts[parts.length - 1] == '') {
5171 partsLength = parts.length;
5172 if (partsLength > 4) return input;
5174 for (index = 0; index < partsLength; index++) {
5175 part = parts[index];
5176 if (part == '') return input;
5178 if (part.length > 1 && part.charAt(0) == '0') {
5179 radix = HEX_START.test(part) ? 16 : 8;
5180 part = part.slice(radix == 8 ? 1 : 2);
5185 if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
5186 number = parseInt(part, radix);
5188 numbers.push(number);
5190 for (index = 0; index < partsLength; index++) {
5191 number = numbers[index];
5192 if (index == partsLength - 1) {
5193 if (number >= pow$1(256, 5 - partsLength)) return null;
5194 } else if (number > 255) return null;
5196 ipv4 = numbers.pop();
5197 for (index = 0; index < numbers.length; index++) {
5198 ipv4 += numbers[index] * pow$1(256, 3 - index);
5203 // eslint-disable-next-line max-statements
5204 var parseIPv6 = function (input) {
5205 var address = [0, 0, 0, 0, 0, 0, 0, 0];
5207 var compress = null;
5209 var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
5211 var char = function () {
5212 return input.charAt(pointer);
5215 if (char() == ':') {
5216 if (input.charAt(1) != ':') return;
5219 compress = pieceIndex;
5222 if (pieceIndex == 8) return;
5223 if (char() == ':') {
5224 if (compress !== null) return;
5227 compress = pieceIndex;
5231 while (length < 4 && HEX.test(char())) {
5232 value = value * 16 + parseInt(char(), 16);
5236 if (char() == '.') {
5237 if (length == 0) return;
5239 if (pieceIndex > 6) return;
5243 if (numbersSeen > 0) {
5244 if (char() == '.' && numbersSeen < 4) pointer++;
5247 if (!DIGIT.test(char())) return;
5248 while (DIGIT.test(char())) {
5249 number = parseInt(char(), 10);
5250 if (ipv4Piece === null) ipv4Piece = number;
5251 else if (ipv4Piece == 0) return;
5252 else ipv4Piece = ipv4Piece * 10 + number;
5253 if (ipv4Piece > 255) return;
5256 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
5258 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
5260 if (numbersSeen != 4) return;
5262 } else if (char() == ':') {
5264 if (!char()) return;
5265 } else if (char()) return;
5266 address[pieceIndex++] = value;
5268 if (compress !== null) {
5269 swaps = pieceIndex - compress;
5271 while (pieceIndex != 0 && swaps > 0) {
5272 swap = address[pieceIndex];
5273 address[pieceIndex--] = address[compress + swaps - 1];
5274 address[compress + --swaps] = swap;
5276 } else if (pieceIndex != 8) return;
5280 var findLongestZeroSequence = function (ipv6) {
5281 var maxIndex = null;
5283 var currStart = null;
5286 for (; index < 8; index++) {
5287 if (ipv6[index] !== 0) {
5288 if (currLength > maxLength) {
5289 maxIndex = currStart;
5290 maxLength = currLength;
5295 if (currStart === null) currStart = index;
5299 if (currLength > maxLength) {
5300 maxIndex = currStart;
5301 maxLength = currLength;
5306 var serializeHost = function (host) {
5307 var result, index, compress, ignore0;
5309 if (typeof host == 'number') {
5311 for (index = 0; index < 4; index++) {
5312 result.unshift(host % 256);
5313 host = floor$5(host / 256);
5314 } return result.join('.');
5316 } else if (typeof host == 'object') {
5318 compress = findLongestZeroSequence(host);
5319 for (index = 0; index < 8; index++) {
5320 if (ignore0 && host[index] === 0) continue;
5321 if (ignore0) ignore0 = false;
5322 if (compress === index) {
5323 result += index ? ':' : '::';
5326 result += host[index].toString(16);
5327 if (index < 7) result += ':';
5330 return '[' + result + ']';
5334 var C0ControlPercentEncodeSet = {};
5335 var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
5336 ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
5338 var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
5339 '#': 1, '?': 1, '{': 1, '}': 1
5341 var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
5342 '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
5345 var percentEncode = function (char, set) {
5346 var code = codeAt(char, 0);
5347 return code > 0x20 && code < 0x7F && !has(set, char) ? char : encodeURIComponent(char);
5350 var specialSchemes = {
5359 var isSpecial = function (url) {
5360 return has(specialSchemes, url.scheme);
5363 var includesCredentials = function (url) {
5364 return url.username != '' || url.password != '';
5367 var cannotHaveUsernamePasswordPort = function (url) {
5368 return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
5371 var isWindowsDriveLetter = function (string, normalized) {
5373 return string.length == 2 && ALPHA.test(string.charAt(0))
5374 && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
5377 var startsWithWindowsDriveLetter = function (string) {
5379 return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
5380 string.length == 2 ||
5381 ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
5385 var shortenURLsPath = function (url) {
5386 var path = url.path;
5387 var pathSize = path.length;
5388 if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
5393 var isSingleDot = function (segment) {
5394 return segment === '.' || segment.toLowerCase() === '%2e';
5397 var isDoubleDot = function (segment) {
5398 segment = segment.toLowerCase();
5399 return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
5403 var SCHEME_START = {};
5406 var SPECIAL_RELATIVE_OR_AUTHORITY = {};
5407 var PATH_OR_AUTHORITY = {};
5409 var RELATIVE_SLASH = {};
5410 var SPECIAL_AUTHORITY_SLASHES = {};
5411 var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
5417 var FILE_SLASH = {};
5419 var PATH_START = {};
5421 var CANNOT_BE_A_BASE_URL_PATH = {};
5425 // eslint-disable-next-line max-statements
5426 var parseURL = function (url, input, stateOverride, base) {
5427 var state = stateOverride || SCHEME_START;
5431 var seenBracket = false;
5432 var seenPasswordToken = false;
5433 var codePoints, char, bufferCodePoints, failure;
5435 if (!stateOverride) {
5443 url.fragment = null;
5444 url.cannotBeABaseURL = false;
5445 input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
5448 input = input.replace(TAB_AND_NEW_LINE, '');
5450 codePoints = arrayFrom(input);
5452 while (pointer <= codePoints.length) {
5453 char = codePoints[pointer];
5456 if (char && ALPHA.test(char)) {
5457 buffer += char.toLowerCase();
5459 } else if (!stateOverride) {
5462 } else return INVALID_SCHEME;
5466 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
5467 buffer += char.toLowerCase();
5468 } else if (char == ':') {
5469 if (stateOverride && (
5470 (isSpecial(url) != has(specialSchemes, buffer)) ||
5471 (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
5472 (url.scheme == 'file' && !url.host)
5474 url.scheme = buffer;
5475 if (stateOverride) {
5476 if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5480 if (url.scheme == 'file') {
5482 } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5483 state = SPECIAL_RELATIVE_OR_AUTHORITY;
5484 } else if (isSpecial(url)) {
5485 state = SPECIAL_AUTHORITY_SLASHES;
5486 } else if (codePoints[pointer + 1] == '/') {
5487 state = PATH_OR_AUTHORITY;
5490 url.cannotBeABaseURL = true;
5492 state = CANNOT_BE_A_BASE_URL_PATH;
5494 } else if (!stateOverride) {
5499 } else return INVALID_SCHEME;
5503 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5504 if (base.cannotBeABaseURL && char == '#') {
5505 url.scheme = base.scheme;
5506 url.path = base.path.slice();
5507 url.query = base.query;
5509 url.cannotBeABaseURL = true;
5513 state = base.scheme == 'file' ? FILE : RELATIVE;
5516 case SPECIAL_RELATIVE_OR_AUTHORITY:
5517 if (char == '/' && codePoints[pointer + 1] == '/') {
5518 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5525 case PATH_OR_AUTHORITY:
5535 url.scheme = base.scheme;
5537 url.username = base.username;
5538 url.password = base.password;
5539 url.host = base.host;
5540 url.port = base.port;
5541 url.path = base.path.slice();
5542 url.query = base.query;
5543 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5544 state = RELATIVE_SLASH;
5545 } else if (char == '?') {
5546 url.username = base.username;
5547 url.password = base.password;
5548 url.host = base.host;
5549 url.port = base.port;
5550 url.path = base.path.slice();
5553 } else if (char == '#') {
5554 url.username = base.username;
5555 url.password = base.password;
5556 url.host = base.host;
5557 url.port = base.port;
5558 url.path = base.path.slice();
5559 url.query = base.query;
5563 url.username = base.username;
5564 url.password = base.password;
5565 url.host = base.host;
5566 url.port = base.port;
5567 url.path = base.path.slice();
5573 case RELATIVE_SLASH:
5574 if (isSpecial(url) && (char == '/' || char == '\\')) {
5575 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5576 } else if (char == '/') {
5579 url.username = base.username;
5580 url.password = base.password;
5581 url.host = base.host;
5582 url.port = base.port;
5587 case SPECIAL_AUTHORITY_SLASHES:
5588 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5589 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5593 case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5594 if (char != '/' && char != '\\') {
5601 if (seenAt) buffer = '%40' + buffer;
5603 bufferCodePoints = arrayFrom(buffer);
5604 for (var i = 0; i < bufferCodePoints.length; i++) {
5605 var codePoint = bufferCodePoints[i];
5606 if (codePoint == ':' && !seenPasswordToken) {
5607 seenPasswordToken = true;
5610 var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5611 if (seenPasswordToken) url.password += encodedCodePoints;
5612 else url.username += encodedCodePoints;
5616 char == EOF || char == '/' || char == '?' || char == '#' ||
5617 (char == '\\' && isSpecial(url))
5619 if (seenAt && buffer == '') return INVALID_AUTHORITY;
5620 pointer -= arrayFrom(buffer).length + 1;
5623 } else buffer += char;
5628 if (stateOverride && url.scheme == 'file') {
5631 } else if (char == ':' && !seenBracket) {
5632 if (buffer == '') return INVALID_HOST;
5633 failure = parseHost(url, buffer);
5634 if (failure) return failure;
5637 if (stateOverride == HOSTNAME) return;
5639 char == EOF || char == '/' || char == '?' || char == '#' ||
5640 (char == '\\' && isSpecial(url))
5642 if (isSpecial(url) && buffer == '') return INVALID_HOST;
5643 if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5644 failure = parseHost(url, buffer);
5645 if (failure) return failure;
5648 if (stateOverride) return;
5651 if (char == '[') seenBracket = true;
5652 else if (char == ']') seenBracket = false;
5657 if (DIGIT.test(char)) {
5660 char == EOF || char == '/' || char == '?' || char == '#' ||
5661 (char == '\\' && isSpecial(url)) ||
5665 var port = parseInt(buffer, 10);
5666 if (port > 0xFFFF) return INVALID_PORT;
5667 url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5670 if (stateOverride) return;
5673 } else return INVALID_PORT;
5677 url.scheme = 'file';
5678 if (char == '/' || char == '\\') state = FILE_SLASH;
5679 else if (base && base.scheme == 'file') {
5681 url.host = base.host;
5682 url.path = base.path.slice();
5683 url.query = base.query;
5684 } else if (char == '?') {
5685 url.host = base.host;
5686 url.path = base.path.slice();
5689 } else if (char == '#') {
5690 url.host = base.host;
5691 url.path = base.path.slice();
5692 url.query = base.query;
5696 if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5697 url.host = base.host;
5698 url.path = base.path.slice();
5699 shortenURLsPath(url);
5710 if (char == '/' || char == '\\') {
5714 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5715 if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5716 else url.host = base.host;
5722 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5723 if (!stateOverride && isWindowsDriveLetter(buffer)) {
5725 } else if (buffer == '') {
5727 if (stateOverride) return;
5730 failure = parseHost(url, buffer);
5731 if (failure) return failure;
5732 if (url.host == 'localhost') url.host = '';
5733 if (stateOverride) return;
5737 } else buffer += char;
5741 if (isSpecial(url)) {
5743 if (char != '/' && char != '\\') continue;
5744 } else if (!stateOverride && char == '?') {
5747 } else if (!stateOverride && char == '#') {
5750 } else if (char != EOF) {
5752 if (char != '/') continue;
5757 char == EOF || char == '/' ||
5758 (char == '\\' && isSpecial(url)) ||
5759 (!stateOverride && (char == '?' || char == '#'))
5761 if (isDoubleDot(buffer)) {
5762 shortenURLsPath(url);
5763 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5766 } else if (isSingleDot(buffer)) {
5767 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5771 if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5772 if (url.host) url.host = '';
5773 buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5775 url.path.push(buffer);
5778 if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5779 while (url.path.length > 1 && url.path[0] === '') {
5786 } else if (char == '#') {
5791 buffer += percentEncode(char, pathPercentEncodeSet);
5794 case CANNOT_BE_A_BASE_URL_PATH:
5798 } else if (char == '#') {
5801 } else if (char != EOF) {
5802 url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5806 if (!stateOverride && char == '#') {
5809 } else if (char != EOF) {
5810 if (char == "'" && isSpecial(url)) url.query += '%27';
5811 else if (char == '#') url.query += '%23';
5812 else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5816 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5824 // `URL` constructor
5825 // https://url.spec.whatwg.org/#url-class
5826 var URLConstructor = function URL(url /* , base */) {
5827 var that = anInstance(this, URLConstructor, 'URL');
5828 var base = arguments.length > 1 ? arguments[1] : undefined;
5829 var urlString = String(url);
5830 var state = setInternalState$6(that, { type: 'URL' });
5831 var baseState, failure;
5832 if (base !== undefined) {
5833 if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5835 failure = parseURL(baseState = {}, String(base));
5836 if (failure) throw TypeError(failure);
5839 failure = parseURL(state, urlString, null, baseState);
5840 if (failure) throw TypeError(failure);
5841 var searchParams = state.searchParams = new URLSearchParams$1();
5842 var searchParamsState = getInternalSearchParamsState(searchParams);
5843 searchParamsState.updateSearchParams(state.query);
5844 searchParamsState.updateURL = function () {
5845 state.query = String(searchParams) || null;
5848 that.href = serializeURL.call(that);
5849 that.origin = getOrigin.call(that);
5850 that.protocol = getProtocol.call(that);
5851 that.username = getUsername.call(that);
5852 that.password = getPassword.call(that);
5853 that.host = getHost.call(that);
5854 that.hostname = getHostname.call(that);
5855 that.port = getPort.call(that);
5856 that.pathname = getPathname.call(that);
5857 that.search = getSearch.call(that);
5858 that.searchParams = getSearchParams.call(that);
5859 that.hash = getHash.call(that);
5863 var URLPrototype = URLConstructor.prototype;
5865 var serializeURL = function () {
5866 var url = getInternalURLState(this);
5867 var scheme = url.scheme;
5868 var username = url.username;
5869 var password = url.password;
5870 var host = url.host;
5871 var port = url.port;
5872 var path = url.path;
5873 var query = url.query;
5874 var fragment = url.fragment;
5875 var output = scheme + ':';
5876 if (host !== null) {
5878 if (includesCredentials(url)) {
5879 output += username + (password ? ':' + password : '') + '@';
5881 output += serializeHost(host);
5882 if (port !== null) output += ':' + port;
5883 } else if (scheme == 'file') output += '//';
5884 output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5885 if (query !== null) output += '?' + query;
5886 if (fragment !== null) output += '#' + fragment;
5890 var getOrigin = function () {
5891 var url = getInternalURLState(this);
5892 var scheme = url.scheme;
5893 var port = url.port;
5894 if (scheme == 'blob') try {
5895 return new URL(scheme.path[0]).origin;
5899 if (scheme == 'file' || !isSpecial(url)) return 'null';
5900 return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5903 var getProtocol = function () {
5904 return getInternalURLState(this).scheme + ':';
5907 var getUsername = function () {
5908 return getInternalURLState(this).username;
5911 var getPassword = function () {
5912 return getInternalURLState(this).password;
5915 var getHost = function () {
5916 var url = getInternalURLState(this);
5917 var host = url.host;
5918 var port = url.port;
5919 return host === null ? ''
5920 : port === null ? serializeHost(host)
5921 : serializeHost(host) + ':' + port;
5924 var getHostname = function () {
5925 var host = getInternalURLState(this).host;
5926 return host === null ? '' : serializeHost(host);
5929 var getPort = function () {
5930 var port = getInternalURLState(this).port;
5931 return port === null ? '' : String(port);
5934 var getPathname = function () {
5935 var url = getInternalURLState(this);
5936 var path = url.path;
5937 return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5940 var getSearch = function () {
5941 var query = getInternalURLState(this).query;
5942 return query ? '?' + query : '';
5945 var getSearchParams = function () {
5946 return getInternalURLState(this).searchParams;
5949 var getHash = function () {
5950 var fragment = getInternalURLState(this).fragment;
5951 return fragment ? '#' + fragment : '';
5954 var accessorDescriptor = function (getter, setter) {
5955 return { get: getter, set: setter, configurable: true, enumerable: true };
5959 objectDefineProperties(URLPrototype, {
5960 // `URL.prototype.href` accessors pair
5961 // https://url.spec.whatwg.org/#dom-url-href
5962 href: accessorDescriptor(serializeURL, function (href) {
5963 var url = getInternalURLState(this);
5964 var urlString = String(href);
5965 var failure = parseURL(url, urlString);
5966 if (failure) throw TypeError(failure);
5967 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5969 // `URL.prototype.origin` getter
5970 // https://url.spec.whatwg.org/#dom-url-origin
5971 origin: accessorDescriptor(getOrigin),
5972 // `URL.prototype.protocol` accessors pair
5973 // https://url.spec.whatwg.org/#dom-url-protocol
5974 protocol: accessorDescriptor(getProtocol, function (protocol) {
5975 var url = getInternalURLState(this);
5976 parseURL(url, String(protocol) + ':', SCHEME_START);
5978 // `URL.prototype.username` accessors pair
5979 // https://url.spec.whatwg.org/#dom-url-username
5980 username: accessorDescriptor(getUsername, function (username) {
5981 var url = getInternalURLState(this);
5982 var codePoints = arrayFrom(String(username));
5983 if (cannotHaveUsernamePasswordPort(url)) return;
5985 for (var i = 0; i < codePoints.length; i++) {
5986 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5989 // `URL.prototype.password` accessors pair
5990 // https://url.spec.whatwg.org/#dom-url-password
5991 password: accessorDescriptor(getPassword, function (password) {
5992 var url = getInternalURLState(this);
5993 var codePoints = arrayFrom(String(password));
5994 if (cannotHaveUsernamePasswordPort(url)) return;
5996 for (var i = 0; i < codePoints.length; i++) {
5997 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
6000 // `URL.prototype.host` accessors pair
6001 // https://url.spec.whatwg.org/#dom-url-host
6002 host: accessorDescriptor(getHost, function (host) {
6003 var url = getInternalURLState(this);
6004 if (url.cannotBeABaseURL) return;
6005 parseURL(url, String(host), HOST);
6007 // `URL.prototype.hostname` accessors pair
6008 // https://url.spec.whatwg.org/#dom-url-hostname
6009 hostname: accessorDescriptor(getHostname, function (hostname) {
6010 var url = getInternalURLState(this);
6011 if (url.cannotBeABaseURL) return;
6012 parseURL(url, String(hostname), HOSTNAME);
6014 // `URL.prototype.port` accessors pair
6015 // https://url.spec.whatwg.org/#dom-url-port
6016 port: accessorDescriptor(getPort, function (port) {
6017 var url = getInternalURLState(this);
6018 if (cannotHaveUsernamePasswordPort(url)) return;
6019 port = String(port);
6020 if (port == '') url.port = null;
6021 else parseURL(url, port, PORT);
6023 // `URL.prototype.pathname` accessors pair
6024 // https://url.spec.whatwg.org/#dom-url-pathname
6025 pathname: accessorDescriptor(getPathname, function (pathname) {
6026 var url = getInternalURLState(this);
6027 if (url.cannotBeABaseURL) return;
6029 parseURL(url, pathname + '', PATH_START);
6031 // `URL.prototype.search` accessors pair
6032 // https://url.spec.whatwg.org/#dom-url-search
6033 search: accessorDescriptor(getSearch, function (search) {
6034 var url = getInternalURLState(this);
6035 search = String(search);
6039 if ('?' == search.charAt(0)) search = search.slice(1);
6041 parseURL(url, search, QUERY);
6043 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
6045 // `URL.prototype.searchParams` getter
6046 // https://url.spec.whatwg.org/#dom-url-searchparams
6047 searchParams: accessorDescriptor(getSearchParams),
6048 // `URL.prototype.hash` accessors pair
6049 // https://url.spec.whatwg.org/#dom-url-hash
6050 hash: accessorDescriptor(getHash, function (hash) {
6051 var url = getInternalURLState(this);
6052 hash = String(hash);
6054 url.fragment = null;
6057 if ('#' == hash.charAt(0)) hash = hash.slice(1);
6059 parseURL(url, hash, FRAGMENT);
6064 // `URL.prototype.toJSON` method
6065 // https://url.spec.whatwg.org/#dom-url-tojson
6066 redefine(URLPrototype, 'toJSON', function toJSON() {
6067 return serializeURL.call(this);
6068 }, { enumerable: true });
6070 // `URL.prototype.toString` method
6071 // https://url.spec.whatwg.org/#URL-stringification-behavior
6072 redefine(URLPrototype, 'toString', function toString() {
6073 return serializeURL.call(this);
6074 }, { enumerable: true });
6077 var nativeCreateObjectURL = NativeURL.createObjectURL;
6078 var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
6079 // `URL.createObjectURL` method
6080 // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
6081 // eslint-disable-next-line no-unused-vars
6082 if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
6083 return nativeCreateObjectURL.apply(NativeURL, arguments);
6085 // `URL.revokeObjectURL` method
6086 // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
6087 // eslint-disable-next-line no-unused-vars
6088 if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
6089 return nativeRevokeObjectURL.apply(NativeURL, arguments);
6093 setToStringTag(URLConstructor, 'URL');
6095 _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
6099 function _typeof(obj) {
6100 "@babel/helpers - typeof";
6102 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
6103 _typeof = function (obj) {
6107 _typeof = function (obj) {
6108 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
6112 return _typeof(obj);
6115 function _classCallCheck(instance, Constructor) {
6116 if (!(instance instanceof Constructor)) {
6117 throw new TypeError("Cannot call a class as a function");
6121 function _defineProperties(target, props) {
6122 for (var i = 0; i < props.length; i++) {
6123 var descriptor = props[i];
6124 descriptor.enumerable = descriptor.enumerable || false;
6125 descriptor.configurable = true;
6126 if ("value" in descriptor) descriptor.writable = true;
6127 Object.defineProperty(target, descriptor.key, descriptor);
6131 function _createClass(Constructor, protoProps, staticProps) {
6132 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
6133 if (staticProps) _defineProperties(Constructor, staticProps);
6137 function _defineProperty(obj, key, value) {
6139 Object.defineProperty(obj, key, {
6152 function _slicedToArray(arr, i) {
6153 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
6156 function _toConsumableArray(arr) {
6157 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
6160 function _arrayWithoutHoles(arr) {
6161 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
6164 function _arrayWithHoles(arr) {
6165 if (Array.isArray(arr)) return arr;
6168 function _iterableToArray(iter) {
6169 if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
6172 function _iterableToArrayLimit(arr, i) {
6173 if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
6180 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
6181 _arr.push(_s.value);
6183 if (i && _arr.length === i) break;
6190 if (!_n && _i["return"] != null) _i["return"]();
6199 function _unsupportedIterableToArray(o, minLen) {
6201 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
6202 var n = Object.prototype.toString.call(o).slice(8, -1);
6203 if (n === "Object" && o.constructor) n = o.constructor.name;
6204 if (n === "Map" || n === "Set") return Array.from(o);
6205 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
6208 function _arrayLikeToArray(arr, len) {
6209 if (len == null || len > arr.length) len = arr.length;
6211 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
6216 function _nonIterableSpread() {
6217 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6220 function _nonIterableRest() {
6221 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6224 function _createForOfIteratorHelper(o, allowArrayLike) {
6227 if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
6228 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
6232 var F = function () {};
6237 if (i >= o.length) return {
6252 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6255 var normalCompletion = true,
6260 it = o[Symbol.iterator]();
6263 var step = it.next();
6264 normalCompletion = step.done;
6273 if (!normalCompletion && it.return != null) it.return();
6275 if (didErr) throw err;
6281 var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6283 searchParams: 'URLSearchParams' in global$1,
6284 iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6285 blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6293 formData: 'FormData' in global$1,
6294 arrayBuffer: 'ArrayBuffer' in global$1
6297 function isDataView(obj) {
6298 return obj && DataView.prototype.isPrototypeOf(obj);
6301 if (support.arrayBuffer) {
6302 var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6304 var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6305 return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6309 function normalizeName(name) {
6310 if (typeof name !== 'string') {
6311 name = String(name);
6314 if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6315 throw new TypeError('Invalid character in header field name');
6318 return name.toLowerCase();
6321 function normalizeValue(value) {
6322 if (typeof value !== 'string') {
6323 value = String(value);
6327 } // Build a destructive iterator for the value list
6330 function iteratorFor(items) {
6332 next: function next() {
6333 var value = items.shift();
6335 done: value === undefined,
6341 if (support.iterable) {
6342 iterator[Symbol.iterator] = function () {
6350 function Headers$1(headers) {
6353 if (headers instanceof Headers$1) {
6354 headers.forEach(function (value, name) {
6355 this.append(name, value);
6357 } else if (Array.isArray(headers)) {
6358 headers.forEach(function (header) {
6359 this.append(header[0], header[1]);
6361 } else if (headers) {
6362 Object.getOwnPropertyNames(headers).forEach(function (name) {
6363 this.append(name, headers[name]);
6368 Headers$1.prototype.append = function (name, value) {
6369 name = normalizeName(name);
6370 value = normalizeValue(value);
6371 var oldValue = this.map[name];
6372 this.map[name] = oldValue ? oldValue + ', ' + value : value;
6375 Headers$1.prototype['delete'] = function (name) {
6376 delete this.map[normalizeName(name)];
6379 Headers$1.prototype.get = function (name) {
6380 name = normalizeName(name);
6381 return this.has(name) ? this.map[name] : null;
6384 Headers$1.prototype.has = function (name) {
6385 return this.map.hasOwnProperty(normalizeName(name));
6388 Headers$1.prototype.set = function (name, value) {
6389 this.map[normalizeName(name)] = normalizeValue(value);
6392 Headers$1.prototype.forEach = function (callback, thisArg) {
6393 for (var name in this.map) {
6394 if (this.map.hasOwnProperty(name)) {
6395 callback.call(thisArg, this.map[name], name, this);
6400 Headers$1.prototype.keys = function () {
6402 this.forEach(function (value, name) {
6405 return iteratorFor(items);
6408 Headers$1.prototype.values = function () {
6410 this.forEach(function (value) {
6413 return iteratorFor(items);
6416 Headers$1.prototype.entries = function () {
6418 this.forEach(function (value, name) {
6419 items.push([name, value]);
6421 return iteratorFor(items);
6424 if (support.iterable) {
6425 Headers$1.prototype[Symbol.iterator] = Headers$1.prototype.entries;
6428 function consumed(body) {
6429 if (body.bodyUsed) {
6430 return Promise.reject(new TypeError('Already read'));
6433 body.bodyUsed = true;
6436 function fileReaderReady(reader) {
6437 return new Promise(function (resolve, reject) {
6438 reader.onload = function () {
6439 resolve(reader.result);
6442 reader.onerror = function () {
6443 reject(reader.error);
6448 function readBlobAsArrayBuffer(blob) {
6449 var reader = new FileReader();
6450 var promise = fileReaderReady(reader);
6451 reader.readAsArrayBuffer(blob);
6455 function readBlobAsText(blob) {
6456 var reader = new FileReader();
6457 var promise = fileReaderReady(reader);
6458 reader.readAsText(blob);
6462 function readArrayBufferAsText(buf) {
6463 var view = new Uint8Array(buf);
6464 var chars = new Array(view.length);
6466 for (var i = 0; i < view.length; i++) {
6467 chars[i] = String.fromCharCode(view[i]);
6470 return chars.join('');
6473 function bufferClone(buf) {
6475 return buf.slice(0);
6477 var view = new Uint8Array(buf.byteLength);
6478 view.set(new Uint8Array(buf));
6484 this.bodyUsed = false;
6486 this._initBody = function (body) {
6488 fetch-mock wraps the Response object in an ES6 Proxy to
6489 provide useful test harness features such as flush. However, on
6490 ES5 browsers without fetch or Proxy support pollyfills must be used;
6491 the proxy-pollyfill is unable to proxy an attribute unless it exists
6492 on the object before the Proxy is created. This change ensures
6493 Response.bodyUsed exists on the instance, while maintaining the
6494 semantic of setting Request.bodyUsed in the constructor before
6495 _initBody is called.
6497 this.bodyUsed = this.bodyUsed;
6498 this._bodyInit = body;
6501 this._bodyText = '';
6502 } else if (typeof body === 'string') {
6503 this._bodyText = body;
6504 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6505 this._bodyBlob = body;
6506 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6507 this._bodyFormData = body;
6508 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6509 this._bodyText = body.toString();
6510 } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6511 this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6513 this._bodyInit = new Blob([this._bodyArrayBuffer]);
6514 } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6515 this._bodyArrayBuffer = bufferClone(body);
6517 this._bodyText = body = Object.prototype.toString.call(body);
6520 if (!this.headers.get('content-type')) {
6521 if (typeof body === 'string') {
6522 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6523 } else if (this._bodyBlob && this._bodyBlob.type) {
6524 this.headers.set('content-type', this._bodyBlob.type);
6525 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6526 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6532 this.blob = function () {
6533 var rejected = consumed(this);
6539 if (this._bodyBlob) {
6540 return Promise.resolve(this._bodyBlob);
6541 } else if (this._bodyArrayBuffer) {
6542 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6543 } else if (this._bodyFormData) {
6544 throw new Error('could not read FormData body as blob');
6546 return Promise.resolve(new Blob([this._bodyText]));
6550 this.arrayBuffer = function () {
6551 if (this._bodyArrayBuffer) {
6552 var isConsumed = consumed(this);
6558 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6559 return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6561 return Promise.resolve(this._bodyArrayBuffer);
6564 return this.blob().then(readBlobAsArrayBuffer);
6569 this.text = function () {
6570 var rejected = consumed(this);
6576 if (this._bodyBlob) {
6577 return readBlobAsText(this._bodyBlob);
6578 } else if (this._bodyArrayBuffer) {
6579 return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6580 } else if (this._bodyFormData) {
6581 throw new Error('could not read FormData body as text');
6583 return Promise.resolve(this._bodyText);
6587 if (support.formData) {
6588 this.formData = function () {
6589 return this.text().then(decode);
6593 this.json = function () {
6594 return this.text().then(JSON.parse);
6598 } // HTTP methods whose capitalization should be normalized
6601 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6603 function normalizeMethod(method) {
6604 var upcased = method.toUpperCase();
6605 return methods.indexOf(upcased) > -1 ? upcased : method;
6608 function Request(input, options) {
6609 if (!(this instanceof Request)) {
6610 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6613 options = options || {};
6614 var body = options.body;
6616 if (input instanceof Request) {
6617 if (input.bodyUsed) {
6618 throw new TypeError('Already read');
6621 this.url = input.url;
6622 this.credentials = input.credentials;
6624 if (!options.headers) {
6625 this.headers = new Headers$1(input.headers);
6628 this.method = input.method;
6629 this.mode = input.mode;
6630 this.signal = input.signal;
6632 if (!body && input._bodyInit != null) {
6633 body = input._bodyInit;
6634 input.bodyUsed = true;
6637 this.url = String(input);
6640 this.credentials = options.credentials || this.credentials || 'same-origin';
6642 if (options.headers || !this.headers) {
6643 this.headers = new Headers$1(options.headers);
6646 this.method = normalizeMethod(options.method || this.method || 'GET');
6647 this.mode = options.mode || this.mode || null;
6648 this.signal = options.signal || this.signal;
6649 this.referrer = null;
6651 if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6652 throw new TypeError('Body not allowed for GET or HEAD requests');
6655 this._initBody(body);
6657 if (this.method === 'GET' || this.method === 'HEAD') {
6658 if (options.cache === 'no-store' || options.cache === 'no-cache') {
6659 // Search for a '_' parameter in the query string
6660 var reParamSearch = /([?&])_=[^&]*/;
6662 if (reParamSearch.test(this.url)) {
6663 // If it already exists then set the value with the current time
6664 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6666 // Otherwise add a new '_' parameter to the end with the current time
6667 var reQueryString = /\?/;
6668 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6674 Request.prototype.clone = function () {
6675 return new Request(this, {
6676 body: this._bodyInit
6680 function decode(body) {
6681 var form = new FormData();
6682 body.trim().split('&').forEach(function (bytes) {
6684 var split = bytes.split('=');
6685 var name = split.shift().replace(/\+/g, ' ');
6686 var value = split.join('=').replace(/\+/g, ' ');
6687 form.append(decodeURIComponent(name), decodeURIComponent(value));
6693 function parseHeaders(rawHeaders) {
6694 var headers = new Headers$1(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6695 // https://tools.ietf.org/html/rfc7230#section-3.2
6697 var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
6698 // https://github.com/github/fetch/issues/748
6699 // https://github.com/zloirock/core-js/issues/751
6701 preProcessedHeaders.split('\r').map(function (header) {
6702 return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
6703 }).forEach(function (line) {
6704 var parts = line.split(':');
6705 var key = parts.shift().trim();
6708 var value = parts.join(':').trim();
6709 headers.append(key, value);
6715 Body.call(Request.prototype);
6716 function Response(bodyInit, options) {
6717 if (!(this instanceof Response)) {
6718 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6725 this.type = 'default';
6726 this.status = options.status === undefined ? 200 : options.status;
6727 this.ok = this.status >= 200 && this.status < 300;
6728 this.statusText = 'statusText' in options ? options.statusText : '';
6729 this.headers = new Headers$1(options.headers);
6730 this.url = options.url || '';
6732 this._initBody(bodyInit);
6734 Body.call(Response.prototype);
6736 Response.prototype.clone = function () {
6737 return new Response(this._bodyInit, {
6738 status: this.status,
6739 statusText: this.statusText,
6740 headers: new Headers$1(this.headers),
6745 Response.error = function () {
6746 var response = new Response(null, {
6750 response.type = 'error';
6754 var redirectStatuses = [301, 302, 303, 307, 308];
6756 Response.redirect = function (url, status) {
6757 if (redirectStatuses.indexOf(status) === -1) {
6758 throw new RangeError('Invalid status code');
6761 return new Response(null, {
6769 var DOMException$1 = global$1.DOMException;
6772 new DOMException$1();
6774 DOMException$1 = function DOMException(message, name) {
6775 this.message = message;
6777 var error = Error(message);
6778 this.stack = error.stack;
6781 DOMException$1.prototype = Object.create(Error.prototype);
6782 DOMException$1.prototype.constructor = DOMException$1;
6785 function fetch$1(input, init) {
6786 return new Promise(function (resolve, reject) {
6787 var request = new Request(input, init);
6789 if (request.signal && request.signal.aborted) {
6790 return reject(new DOMException$1('Aborted', 'AbortError'));
6793 var xhr = new XMLHttpRequest();
6795 function abortXhr() {
6799 xhr.onload = function () {
6802 statusText: xhr.statusText,
6803 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
6805 options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
6806 var body = 'response' in xhr ? xhr.response : xhr.responseText;
6807 setTimeout(function () {
6808 resolve(new Response(body, options));
6812 xhr.onerror = function () {
6813 setTimeout(function () {
6814 reject(new TypeError('Network request failed'));
6818 xhr.ontimeout = function () {
6819 setTimeout(function () {
6820 reject(new TypeError('Network request failed'));
6824 xhr.onabort = function () {
6825 setTimeout(function () {
6826 reject(new DOMException$1('Aborted', 'AbortError'));
6830 function fixUrl(url) {
6832 return url === '' && global$1.location.href ? global$1.location.href : url;
6838 xhr.open(request.method, fixUrl(request.url), true);
6840 if (request.credentials === 'include') {
6841 xhr.withCredentials = true;
6842 } else if (request.credentials === 'omit') {
6843 xhr.withCredentials = false;
6846 if ('responseType' in xhr) {
6848 xhr.responseType = 'blob';
6849 } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
6850 xhr.responseType = 'arraybuffer';
6854 if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers$1)) {
6855 Object.getOwnPropertyNames(init.headers).forEach(function (name) {
6856 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
6859 request.headers.forEach(function (value, name) {
6860 xhr.setRequestHeader(name, value);
6864 if (request.signal) {
6865 request.signal.addEventListener('abort', abortXhr);
6867 xhr.onreadystatechange = function () {
6868 // DONE (success or failure)
6869 if (xhr.readyState === 4) {
6870 request.signal.removeEventListener('abort', abortXhr);
6875 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
6878 fetch$1.polyfill = true;
6880 if (!global$1.fetch) {
6881 global$1.fetch = fetch$1;
6882 global$1.Headers = Headers$1;
6883 global$1.Request = Request;
6884 global$1.Response = Response;
6887 // `Symbol.toStringTag` well-known symbol
6888 // https://tc39.github.io/ecma262/#sec-symbol.tostringtag
6889 defineWellKnownSymbol('toStringTag');
6891 var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('splice');
6892 var USES_TO_LENGTH$5 = arrayMethodUsesToLength('splice', { ACCESSORS: true, 0: 0, 1: 2 });
6894 var max$3 = Math.max;
6895 var min$6 = Math.min;
6896 var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
6897 var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
6899 // `Array.prototype.splice` method
6900 // https://tc39.github.io/ecma262/#sec-array.prototype.splice
6901 // with adding support of @@species
6902 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 || !USES_TO_LENGTH$5 }, {
6903 splice: function splice(start, deleteCount /* , ...items */) {
6904 var O = toObject(this);
6905 var len = toLength(O.length);
6906 var actualStart = toAbsoluteIndex(start, len);
6907 var argumentsLength = arguments.length;
6908 var insertCount, actualDeleteCount, A, k, from, to;
6909 if (argumentsLength === 0) {
6910 insertCount = actualDeleteCount = 0;
6911 } else if (argumentsLength === 1) {
6913 actualDeleteCount = len - actualStart;
6915 insertCount = argumentsLength - 2;
6916 actualDeleteCount = min$6(max$3(toInteger(deleteCount), 0), len - actualStart);
6918 if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER) {
6919 throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
6921 A = arraySpeciesCreate(O, actualDeleteCount);
6922 for (k = 0; k < actualDeleteCount; k++) {
6923 from = actualStart + k;
6924 if (from in O) createProperty(A, k, O[from]);
6926 A.length = actualDeleteCount;
6927 if (insertCount < actualDeleteCount) {
6928 for (k = actualStart; k < len - actualDeleteCount; k++) {
6929 from = k + actualDeleteCount;
6930 to = k + insertCount;
6931 if (from in O) O[to] = O[from];
6934 for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
6935 } else if (insertCount > actualDeleteCount) {
6936 for (k = len - actualDeleteCount; k > actualStart; k--) {
6937 from = k + actualDeleteCount - 1;
6938 to = k + insertCount - 1;
6939 if (from in O) O[to] = O[from];
6943 for (k = 0; k < insertCount; k++) {
6944 O[k + actualStart] = arguments[k + 2];
6946 O.length = len - actualDeleteCount + insertCount;
6951 // JSON[@@toStringTag] property
6952 // https://tc39.github.io/ecma262/#sec-json-@@tostringtag
6953 setToStringTag(global_1.JSON, 'JSON', true);
6955 // Math[@@toStringTag] property
6956 // https://tc39.github.io/ecma262/#sec-math-@@tostringtag
6957 setToStringTag(Math, 'Math', true);
6959 // `Object.defineProperty` method
6960 // https://tc39.github.io/ecma262/#sec-object.defineproperty
6961 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
6962 defineProperty: objectDefineProperty.f
6965 var nativeGetOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
6968 var FAILS_ON_PRIMITIVES$1 = fails(function () { nativeGetOwnPropertyDescriptor$2(1); });
6969 var FORCED$5 = !descriptors || FAILS_ON_PRIMITIVES$1;
6971 // `Object.getOwnPropertyDescriptor` method
6972 // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptor
6973 _export({ target: 'Object', stat: true, forced: FORCED$5, sham: !descriptors }, {
6974 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
6975 return nativeGetOwnPropertyDescriptor$2(toIndexedObject(it), key);
6979 var FAILS_ON_PRIMITIVES$2 = fails(function () { objectGetPrototypeOf(1); });
6981 // `Object.getPrototypeOf` method
6982 // https://tc39.github.io/ecma262/#sec-object.getprototypeof
6983 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$2, sham: !correctPrototypeGetter }, {
6984 getPrototypeOf: function getPrototypeOf(it) {
6985 return objectGetPrototypeOf(toObject(it));
6989 // `Object.setPrototypeOf` method
6990 // https://tc39.github.io/ecma262/#sec-object.setprototypeof
6991 _export({ target: 'Object', stat: true }, {
6992 setPrototypeOf: objectSetPrototypeOf
6995 var slice$1 = [].slice;
6998 var construct = function (C, argsLength, args) {
6999 if (!(argsLength in factories)) {
7000 for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
7001 // eslint-disable-next-line no-new-func
7002 factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
7003 } return factories[argsLength](C, args);
7006 // `Function.prototype.bind` method implementation
7007 // https://tc39.github.io/ecma262/#sec-function.prototype.bind
7008 var functionBind = Function.bind || function bind(that /* , ...args */) {
7009 var fn = aFunction$1(this);
7010 var partArgs = slice$1.call(arguments, 1);
7011 var boundFunction = function bound(/* args... */) {
7012 var args = partArgs.concat(slice$1.call(arguments));
7013 return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
7015 if (isObject(fn.prototype)) boundFunction.prototype = fn.prototype;
7016 return boundFunction;
7019 var nativeConstruct = getBuiltIn('Reflect', 'construct');
7021 // `Reflect.construct` method
7022 // https://tc39.github.io/ecma262/#sec-reflect.construct
7023 // MS Edge supports only 2 arguments and argumentsList argument is optional
7024 // FF Nightly sets third argument as `new.target`, but does not create `this` from it
7025 var NEW_TARGET_BUG = fails(function () {
7026 function F() { /* empty */ }
7027 return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
7029 var ARGS_BUG = !fails(function () {
7030 nativeConstruct(function () { /* empty */ });
7032 var FORCED$6 = NEW_TARGET_BUG || ARGS_BUG;
7034 _export({ target: 'Reflect', stat: true, forced: FORCED$6, sham: FORCED$6 }, {
7035 construct: function construct(Target, args /* , newTarget */) {
7036 aFunction$1(Target);
7038 var newTarget = arguments.length < 3 ? Target : aFunction$1(arguments[2]);
7039 if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
7040 if (Target == newTarget) {
7041 // w/o altered newTarget, optimization for 0-4 arguments
7042 switch (args.length) {
7043 case 0: return new Target();
7044 case 1: return new Target(args[0]);
7045 case 2: return new Target(args[0], args[1]);
7046 case 3: return new Target(args[0], args[1], args[2]);
7047 case 4: return new Target(args[0], args[1], args[2], args[3]);
7049 // w/o altered newTarget, lot of arguments case
7051 $args.push.apply($args, args);
7052 return new (functionBind.apply(Target, $args))();
7054 // with altered newTarget, not support built-in constructors
7055 var proto = newTarget.prototype;
7056 var instance = objectCreate(isObject(proto) ? proto : Object.prototype);
7057 var result = Function.apply.call(Target, instance, args);
7058 return isObject(result) ? result : instance;
7062 // `Reflect.get` method
7063 // https://tc39.github.io/ecma262/#sec-reflect.get
7064 function get$2(target, propertyKey /* , receiver */) {
7065 var receiver = arguments.length < 3 ? target : arguments[2];
7066 var descriptor, prototype;
7067 if (anObject(target) === receiver) return target[propertyKey];
7068 if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has(descriptor, 'value')
7070 : descriptor.get === undefined
7072 : descriptor.get.call(receiver);
7073 if (isObject(prototype = objectGetPrototypeOf(target))) return get$2(prototype, propertyKey, receiver);
7076 _export({ target: 'Reflect', stat: true }, {
7080 (function (factory) {
7084 function _classCallCheck(instance, Constructor) {
7085 if (!(instance instanceof Constructor)) {
7086 throw new TypeError("Cannot call a class as a function");
7090 function _defineProperties(target, props) {
7091 for (var i = 0; i < props.length; i++) {
7092 var descriptor = props[i];
7093 descriptor.enumerable = descriptor.enumerable || false;
7094 descriptor.configurable = true;
7095 if ("value" in descriptor) descriptor.writable = true;
7096 Object.defineProperty(target, descriptor.key, descriptor);
7100 function _createClass(Constructor, protoProps, staticProps) {
7101 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7102 if (staticProps) _defineProperties(Constructor, staticProps);
7106 function _inherits(subClass, superClass) {
7107 if (typeof superClass !== "function" && superClass !== null) {
7108 throw new TypeError("Super expression must either be null or a function");
7111 subClass.prototype = Object.create(superClass && superClass.prototype, {
7118 if (superClass) _setPrototypeOf(subClass, superClass);
7121 function _getPrototypeOf(o) {
7122 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7123 return o.__proto__ || Object.getPrototypeOf(o);
7125 return _getPrototypeOf(o);
7128 function _setPrototypeOf(o, p) {
7129 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7134 return _setPrototypeOf(o, p);
7137 function _isNativeReflectConstruct() {
7138 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7139 if (Reflect.construct.sham) return false;
7140 if (typeof Proxy === "function") return true;
7143 Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
7150 function _assertThisInitialized(self) {
7151 if (self === void 0) {
7152 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7158 function _possibleConstructorReturn(self, call) {
7159 if (call && (_typeof(call) === "object" || typeof call === "function")) {
7163 return _assertThisInitialized(self);
7166 function _createSuper(Derived) {
7167 var hasNativeReflectConstruct = _isNativeReflectConstruct();
7169 return function _createSuperInternal() {
7170 var Super = _getPrototypeOf(Derived),
7173 if (hasNativeReflectConstruct) {
7174 var NewTarget = _getPrototypeOf(this).constructor;
7176 result = Reflect.construct(Super, arguments, NewTarget);
7178 result = Super.apply(this, arguments);
7181 return _possibleConstructorReturn(this, result);
7185 function _superPropBase(object, property) {
7186 while (!Object.prototype.hasOwnProperty.call(object, property)) {
7187 object = _getPrototypeOf(object);
7188 if (object === null) break;
7194 function _get(target, property, receiver) {
7195 if (typeof Reflect !== "undefined" && Reflect.get) {
7198 _get = function _get(target, property, receiver) {
7199 var base = _superPropBase(target, property);
7202 var desc = Object.getOwnPropertyDescriptor(base, property);
7205 return desc.get.call(receiver);
7212 return _get(target, property, receiver || target);
7215 var Emitter = /*#__PURE__*/function () {
7216 function Emitter() {
7217 _classCallCheck(this, Emitter);
7219 Object.defineProperty(this, 'listeners', {
7226 _createClass(Emitter, [{
7227 key: "addEventListener",
7228 value: function addEventListener(type, callback) {
7229 if (!(type in this.listeners)) {
7230 this.listeners[type] = [];
7233 this.listeners[type].push(callback);
7236 key: "removeEventListener",
7237 value: function removeEventListener(type, callback) {
7238 if (!(type in this.listeners)) {
7242 var stack = this.listeners[type];
7244 for (var i = 0, l = stack.length; i < l; i++) {
7245 if (stack[i] === callback) {
7252 key: "dispatchEvent",
7253 value: function dispatchEvent(event) {
7256 if (!(event.type in this.listeners)) {
7260 var debounce = function debounce(callback) {
7261 setTimeout(function () {
7262 return callback.call(_this, event);
7266 var stack = this.listeners[event.type];
7268 for (var i = 0, l = stack.length; i < l; i++) {
7272 return !event.defaultPrevented;
7279 var AbortSignal = /*#__PURE__*/function (_Emitter) {
7280 _inherits(AbortSignal, _Emitter);
7282 var _super = _createSuper(AbortSignal);
7284 function AbortSignal() {
7287 _classCallCheck(this, AbortSignal);
7289 _this2 = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7290 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7291 // the parent constructor directly instead as a workaround. For general details, see babel bug:
7292 // https://github.com/babel/babel/issues/3041
7293 // This hack was added as a fix for the issue described here:
7294 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7296 if (!_this2.listeners) {
7297 Emitter.call(_assertThisInitialized(_this2));
7298 } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7299 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7302 Object.defineProperty(_assertThisInitialized(_this2), 'aborted', {
7307 Object.defineProperty(_assertThisInitialized(_this2), 'onabort', {
7315 _createClass(AbortSignal, [{
7317 value: function toString() {
7318 return '[object AbortSignal]';
7321 key: "dispatchEvent",
7322 value: function dispatchEvent(event) {
7323 if (event.type === 'abort') {
7324 this.aborted = true;
7326 if (typeof this.onabort === 'function') {
7327 this.onabort.call(this, event);
7331 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7338 var AbortController = /*#__PURE__*/function () {
7339 function AbortController() {
7340 _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7341 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7344 Object.defineProperty(this, 'signal', {
7345 value: new AbortSignal(),
7351 _createClass(AbortController, [{
7353 value: function abort() {
7357 event = new Event('abort');
7359 if (typeof document !== 'undefined') {
7360 if (!document.createEvent) {
7361 // For Internet Explorer 8:
7362 event = document.createEventObject();
7363 event.type = 'abort';
7365 // For Internet Explorer 11:
7366 event = document.createEvent('Event');
7367 event.initEvent('abort', false, false);
7370 // Fallback where document isn't available:
7379 this.signal.dispatchEvent(event);
7383 value: function toString() {
7384 return '[object AbortController]';
7388 return AbortController;
7391 if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7392 // These are necessary to make sure that we get correct output for:
7393 // Object.prototype.toString.call(new AbortController())
7394 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7395 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7398 function polyfillNeeded(self) {
7399 if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7400 console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7402 } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7403 // defining window.Request, and this polyfill need to work on top of unfetch
7404 // so the below feature detection needs the !self.AbortController part.
7405 // The Request.prototype check is also needed because Safari versions 11.1.2
7406 // up to and including 12.1.x has a window.AbortController present but still
7407 // does NOT correctly implement abortable fetch:
7408 // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7411 return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7414 * Note: the "fetch.Request" default value is available for fetch imported from
7415 * the "node-fetch" package and not in browsers. This is OK since browsers
7416 * will be importing umd-polyfill.js from that path "self" is passed the
7417 * decorator so the default value will not be used (because browsers that define
7418 * fetch also has Request). One quirky setup where self.fetch exists but
7419 * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7420 * on top of IE11; for this case the browser will try to use the fetch.Request
7421 * default value which in turn will be undefined but then then "if (Request)"
7422 * will ensure that you get a patched fetch but still no Request (as expected).
7423 * @param {fetch, Request = fetch.Request}
7424 * @returns {fetch: abortableFetch, Request: AbortableRequest}
7428 function abortableFetchDecorator(patchTargets) {
7429 if ('function' === typeof patchTargets) {
7435 var _patchTargets = patchTargets,
7436 fetch = _patchTargets.fetch,
7437 _patchTargets$Request = _patchTargets.Request,
7438 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7439 NativeAbortController = _patchTargets.AbortController,
7440 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7441 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7443 if (!polyfillNeeded({
7445 Request: NativeRequest,
7446 AbortController: NativeAbortController,
7447 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7455 var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7456 // defining window.Request, and this polyfill need to work on top of unfetch
7457 // hence we only patch it if it's available. Also we don't patch it if signal
7458 // is already available on the Request prototype because in this case support
7459 // is present and the patching below can cause a crash since it assigns to
7460 // request.signal which is technically a read-only property. This latter error
7461 // happens when you run the main5.js node-fetch example in the repo
7462 // "abortcontroller-polyfill-examples". The exact error is:
7463 // request.signal = init.signal;
7465 // TypeError: Cannot set property signal of #<Request> which has only a getter
7467 if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7468 Request = function Request(input, init) {
7471 if (init && init.signal) {
7472 signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7473 // been installed because if we're running on top of a browser with a
7474 // working native AbortController (i.e. the polyfill was installed due to
7475 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7476 // fake AbortSignal to the native fetch will trigger:
7477 // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7482 var request = new NativeRequest(input, init);
7485 Object.defineProperty(request, 'signal', {
7496 Request.prototype = NativeRequest.prototype;
7499 var realFetch = fetch;
7501 var abortableFetch = function abortableFetch(input, init) {
7502 var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7508 abortError = new DOMException('Aborted', 'AbortError');
7510 // IE 11 does not support calling the DOMException constructor, use a
7511 // regular error object on it instead.
7512 abortError = new Error('Aborted');
7513 abortError.name = 'AbortError';
7514 } // Return early if already aborted, thus avoiding making an HTTP request
7517 if (signal.aborted) {
7518 return Promise.reject(abortError);
7519 } // Turn an event into a promise, reject it once `abort` is dispatched
7522 var cancellation = new Promise(function (_, reject) {
7523 signal.addEventListener('abort', function () {
7524 return reject(abortError);
7530 if (init && init.signal) {
7531 // Never pass .signal to the native implementation when the polyfill has
7532 // been installed because if we're running on top of a browser with a
7533 // working native AbortController (i.e. the polyfill was installed due to
7534 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7535 // fake AbortSignal to the native fetch will trigger:
7536 // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7538 } // Return the fastest promise (don't need to wait for request to finish)
7541 return Promise.race([cancellation, realFetch(input, init)]);
7544 return realFetch(input, init);
7548 fetch: abortableFetch,
7554 if (!polyfillNeeded(self)) {
7559 console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7563 var _abortableFetch = abortableFetchDecorator(self),
7564 fetch = _abortableFetch.fetch,
7565 Request = _abortableFetch.Request;
7568 self.Request = Request;
7569 Object.defineProperty(self, 'AbortController', {
7573 value: AbortController
7575 Object.defineProperty(self, 'AbortSignal', {
7581 })(typeof self !== 'undefined' ? self : commonjsGlobal);
7584 function actionAddEntity(way) {
7585 return function (graph) {
7586 return graph.replace(way);
7590 var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7591 var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7592 var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7594 // We can't use this feature detection in V8 since it causes
7595 // deoptimization and serious performance degradation
7596 // https://github.com/zloirock/core-js/issues/679
7597 var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7599 array[IS_CONCAT_SPREADABLE] = false;
7600 return array.concat()[0] !== array;
7603 var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7605 var isConcatSpreadable = function (O) {
7606 if (!isObject(O)) return false;
7607 var spreadable = O[IS_CONCAT_SPREADABLE];
7608 return spreadable !== undefined ? !!spreadable : isArray(O);
7611 var FORCED$7 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7613 // `Array.prototype.concat` method
7614 // https://tc39.github.io/ecma262/#sec-array.prototype.concat
7615 // with adding support of @@isConcatSpreadable and @@species
7616 _export({ target: 'Array', proto: true, forced: FORCED$7 }, {
7617 concat: function concat(arg) { // eslint-disable-line no-unused-vars
7618 var O = toObject(this);
7619 var A = arraySpeciesCreate(O, 0);
7621 var i, k, length, len, E;
7622 for (i = -1, length = arguments.length; i < length; i++) {
7623 E = i === -1 ? O : arguments[i];
7624 if (isConcatSpreadable(E)) {
7625 len = toLength(E.length);
7626 if (n + len > MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7627 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7629 if (n >= MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7630 createProperty(A, n++, E);
7638 // `Object.assign` method
7639 // https://tc39.github.io/ecma262/#sec-object.assign
7640 _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7641 assign: objectAssign
7644 var $filter$1 = arrayIteration.filter;
7648 var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('filter');
7650 var USES_TO_LENGTH$6 = arrayMethodUsesToLength('filter');
7652 // `Array.prototype.filter` method
7653 // https://tc39.github.io/ecma262/#sec-array.prototype.filter
7654 // with adding support of @@species
7655 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 || !USES_TO_LENGTH$6 }, {
7656 filter: function filter(callbackfn /* , thisArg */) {
7657 return $filter$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7661 var nativeReverse = [].reverse;
7662 var test$1 = [1, 2];
7664 // `Array.prototype.reverse` method
7665 // https://tc39.github.io/ecma262/#sec-array.prototype.reverse
7666 // fix for Safari 12.0 bug
7667 // https://bugs.webkit.org/show_bug.cgi?id=188794
7668 _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7669 reverse: function reverse() {
7670 // eslint-disable-next-line no-self-assign
7671 if (isArray(this)) this.length = this.length;
7672 return nativeReverse.call(this);
7676 var FAILS_ON_PRIMITIVES$3 = fails(function () { objectKeys(1); });
7678 // `Object.keys` method
7679 // https://tc39.github.io/ecma262/#sec-object.keys
7680 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3 }, {
7681 keys: function keys(it) {
7682 return objectKeys(toObject(it));
7686 var trim = stringTrim.trim;
7689 var $parseFloat = global_1.parseFloat;
7690 var FORCED$8 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7692 // `parseFloat` method
7693 // https://tc39.github.io/ecma262/#sec-parsefloat-string
7694 var numberParseFloat = FORCED$8 ? function parseFloat(string) {
7695 var trimmedString = trim(String(string));
7696 var result = $parseFloat(trimmedString);
7697 return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7700 // `parseFloat` method
7701 // https://tc39.github.io/ecma262/#sec-parsefloat-string
7702 _export({ global: true, forced: parseFloat != numberParseFloat }, {
7703 parseFloat: numberParseFloat
7707 Order the nodes of a way in reverse order and reverse any direction dependent tags
7708 other than `oneway`. (We assume that correcting a backwards oneway is the primary
7709 reason for reversing a way.)
7711 In addition, numeric-valued `incline` tags are negated.
7713 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7714 or adjusted tags that don't seem to be used in practice were omitted.
7717 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7718 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7719 http://wiki.openstreetmap.org/wiki/Key:incline
7720 http://wiki.openstreetmap.org/wiki/Route#Members
7721 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7722 http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7723 http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7725 function actionReverse(entityID, options) {
7726 var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7727 var numeric = /^([+\-]?)(?=[\d.])/;
7728 var directionKey = /direction$/;
7729 var turn_lanes = /^turn:lanes:?/;
7730 var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7731 var valueReplacements = {
7736 forward: 'backward',
7737 backward: 'forward',
7738 forwards: 'backward',
7739 backwards: 'forward'
7741 var roleReplacements = {
7742 forward: 'backward',
7743 backward: 'forward',
7744 forwards: 'backward',
7745 backwards: 'forward'
7747 var onewayReplacements = {
7752 var compassReplacements = {
7771 function reverseKey(key) {
7772 for (var i = 0; i < keyReplacements.length; ++i) {
7773 var replacement = keyReplacements[i];
7775 if (replacement[0].test(key)) {
7776 return key.replace(replacement[0], replacement[1]);
7783 function reverseValue(key, value, includeAbsolute) {
7784 if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7786 if (turn_lanes.test(key)) {
7788 } else if (key === 'incline' && numeric.test(value)) {
7789 return value.replace(numeric, function (_, sign) {
7790 return sign === '-' ? '' : '-';
7792 } else if (options && options.reverseOneway && key === 'oneway') {
7793 return onewayReplacements[value] || value;
7794 } else if (includeAbsolute && directionKey.test(key)) {
7795 if (compassReplacements[value]) return compassReplacements[value];
7796 var degrees = parseFloat(value);
7798 if (typeof degrees === 'number' && !isNaN(degrees)) {
7799 if (degrees < 180) {
7805 return degrees.toString();
7809 return valueReplacements[value] || value;
7810 } // Reverse the direction of tags attached to the nodes - #3076
7813 function reverseNodeTags(graph, nodeIDs) {
7814 for (var i = 0; i < nodeIDs.length; i++) {
7815 var node = graph.hasEntity(nodeIDs[i]);
7816 if (!node || !Object.keys(node.tags).length) continue;
7819 for (var key in node.tags) {
7820 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
7823 graph = graph.replace(node.update({
7831 function reverseWay(graph, way) {
7832 var nodes = way.nodes.slice().reverse();
7836 for (var key in way.tags) {
7837 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
7840 graph.parentRelations(way).forEach(function (relation) {
7841 relation.members.forEach(function (member, index) {
7842 if (member.id === way.id && (role = roleReplacements[member.role])) {
7843 relation = relation.updateMember({
7846 graph = graph.replace(relation);
7849 }); // Reverse any associated directions on nodes on the way and then replace
7850 // the way itself with the reversed node ids and updated way tags
7852 return reverseNodeTags(graph, nodes).replace(way.update({
7858 var action = function action(graph) {
7859 var entity = graph.entity(entityID);
7861 if (entity.type === 'way') {
7862 return reverseWay(graph, entity);
7865 return reverseNodeTags(graph, [entityID]);
7868 action.disabled = function (graph) {
7869 var entity = graph.hasEntity(entityID);
7870 if (!entity || entity.type === 'way') return false;
7872 for (var key in entity.tags) {
7873 var value = entity.tags[key];
7875 if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
7880 return 'nondirectional_node';
7883 action.entityID = function () {
7890 function osmIsInterestingTag(key) {
7891 return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
7892 key.indexOf('tiger:') !== 0;
7894 var osmAreaKeys = {};
7895 function osmSetAreaKeys(value) {
7896 osmAreaKeys = value;
7897 } // returns an object with the tag from `tags` that implies an area geometry, if any
7899 function osmTagSuggestingArea(tags) {
7900 if (tags.area === 'yes') return {
7903 if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
7904 // are a few exceptions that should be treated as areas, even in the
7905 // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
7920 var returnTags = {};
7922 for (var key in tags) {
7923 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
7924 returnTags[key] = tags[key];
7928 if (key in lineKeys && tags[key] in lineKeys[key]) {
7929 returnTags[key] = tags[key];
7935 } // Tags that indicate a node can be a standalone point
7936 // e.g. { amenity: { bar: true, parking: true, ... } ... }
7938 var osmPointTags = {};
7939 function osmSetPointTags(value) {
7940 osmPointTags = value;
7941 } // Tags that indicate a node can be part of a way
7942 // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
7944 var osmVertexTags = {};
7945 function osmSetVertexTags(value) {
7946 osmVertexTags = value;
7948 function osmNodeGeometriesForTags(nodeTags) {
7949 var geometries = {};
7951 for (var key in nodeTags) {
7952 if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
7953 geometries.point = true;
7956 if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
7957 geometries.vertex = true;
7958 } // break early if both are already supported
7961 if (geometries.point && geometries.vertex) break;
7966 var osmOneWayTags = {
7971 'magic_carpet': true,
7986 'goods_conveyor': true,
7987 'piste:halfpipe': true
8001 'tidal_channel': true
8003 }; // solid and smooth surfaces akin to the assumed default road surface in OSM
8005 var osmPavedTags = {
8010 'concrete:lanes': true,
8011 'concrete:plates': true
8016 }; // solid, if somewhat uncommon surfaces with a high range of smoothness
8018 var osmSemipavedTags = {
8020 'cobblestone': true,
8021 'cobblestone:flattened': true,
8022 'unhewn_cobblestone': true,
8024 'paving_stones': true,
8029 var osmRightSideIsInsideTags = {
8032 'coastline': 'coastline'
8035 'retaining_wall': true,
8046 }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8047 // (does not include `raceway`)
8049 var osmRoutableHighwayTagValues = {
8056 motorway_link: true,
8059 secondary_link: true,
8060 tertiary_link: true,
8065 living_street: true,
8074 }; // "highway" tag values that generally do not allow motor vehicles
8076 var osmPathHighwayTagValues = {
8084 }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8086 var osmRailwayTrackTagValues = {
8097 }; // "waterway" tag values for line features representing water flow
8099 var osmFlowingWaterwayTagValues = {
8109 var trim$1 = stringTrim.trim;
8112 var $parseInt = global_1.parseInt;
8113 var hex$1 = /^[+-]?0[Xx]/;
8114 var FORCED$9 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8116 // `parseInt` method
8117 // https://tc39.github.io/ecma262/#sec-parseint-string-radix
8118 var numberParseInt = FORCED$9 ? function parseInt(string, radix) {
8119 var S = trim$1(String(string));
8120 return $parseInt(S, (radix >>> 0) || (hex$1.test(S) ? 16 : 10));
8123 // `parseInt` method
8124 // https://tc39.github.io/ecma262/#sec-parseint-string-radix
8125 _export({ global: true, forced: parseInt != numberParseInt }, {
8126 parseInt: numberParseInt
8129 var freezing = !fails(function () {
8130 return Object.isExtensible(Object.preventExtensions({}));
8133 var internalMetadata = createCommonjsModule(function (module) {
8134 var defineProperty = objectDefineProperty.f;
8138 var METADATA = uid('meta');
8141 var isExtensible = Object.isExtensible || function () {
8145 var setMetadata = function (it) {
8146 defineProperty(it, METADATA, { value: {
8147 objectID: 'O' + ++id, // object ID
8148 weakData: {} // weak collections IDs
8152 var fastKey = function (it, create) {
8153 // return a primitive with prefix
8154 if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8155 if (!has(it, METADATA)) {
8156 // can't set metadata to uncaught frozen object
8157 if (!isExtensible(it)) return 'F';
8158 // not necessary to add metadata
8159 if (!create) return 'E';
8160 // add missing metadata
8163 } return it[METADATA].objectID;
8166 var getWeakData = function (it, create) {
8167 if (!has(it, METADATA)) {
8168 // can't set metadata to uncaught frozen object
8169 if (!isExtensible(it)) return true;
8170 // not necessary to add metadata
8171 if (!create) return false;
8172 // add missing metadata
8174 // return the store of weak collections IDs
8175 } return it[METADATA].weakData;
8178 // add metadata on freeze-family methods calling
8179 var onFreeze = function (it) {
8180 if (freezing && meta.REQUIRED && isExtensible(it) && !has(it, METADATA)) setMetadata(it);
8184 var meta = module.exports = {
8187 getWeakData: getWeakData,
8191 hiddenKeys[METADATA] = true;
8194 var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8195 var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8196 var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8197 var ADDER = IS_MAP ? 'set' : 'add';
8198 var NativeConstructor = global_1[CONSTRUCTOR_NAME];
8199 var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8200 var Constructor = NativeConstructor;
8203 var fixMethod = function (KEY) {
8204 var nativeMethod = NativePrototype[KEY];
8205 redefine(NativePrototype, KEY,
8206 KEY == 'add' ? function add(value) {
8207 nativeMethod.call(this, value === 0 ? 0 : value);
8209 } : KEY == 'delete' ? function (key) {
8210 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8211 } : KEY == 'get' ? function get(key) {
8212 return IS_WEAK && !isObject(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8213 } : KEY == 'has' ? function has(key) {
8214 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8215 } : function set(key, value) {
8216 nativeMethod.call(this, key === 0 ? 0 : key, value);
8222 // eslint-disable-next-line max-len
8223 if (isForced_1(CONSTRUCTOR_NAME, typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8224 new NativeConstructor().entries().next();
8226 // create collection constructor
8227 Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8228 internalMetadata.REQUIRED = true;
8229 } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8230 var instance = new Constructor();
8231 // early implementations not supports chaining
8232 var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8233 // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8234 var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8235 // most early implementations doesn't supports iterables, most modern - not close it correctly
8236 // eslint-disable-next-line no-new
8237 var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8238 // for early implementations -0 and +0 not the same
8239 var BUGGY_ZERO = !IS_WEAK && fails(function () {
8240 // V8 ~ Chromium 42- fails only with 5+ elements
8241 var $instance = new NativeConstructor();
8243 while (index--) $instance[ADDER](index, index);
8244 return !$instance.has(-0);
8247 if (!ACCEPT_ITERABLES) {
8248 Constructor = wrapper(function (dummy, iterable) {
8249 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8250 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8251 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8254 Constructor.prototype = NativePrototype;
8255 NativePrototype.constructor = Constructor;
8258 if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8259 fixMethod('delete');
8261 IS_MAP && fixMethod('get');
8264 if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8266 // weak collections should not contains .clear method
8267 if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8270 exported[CONSTRUCTOR_NAME] = Constructor;
8271 _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8273 setToStringTag(Constructor, CONSTRUCTOR_NAME);
8275 if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8280 var defineProperty$8 = objectDefineProperty.f;
8289 var fastKey = internalMetadata.fastKey;
8292 var setInternalState$7 = internalState.set;
8293 var internalStateGetterFor = internalState.getterFor;
8295 var collectionStrong = {
8296 getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8297 var C = wrapper(function (that, iterable) {
8298 anInstance(that, C, CONSTRUCTOR_NAME);
8299 setInternalState$7(that, {
8300 type: CONSTRUCTOR_NAME,
8301 index: objectCreate(null),
8306 if (!descriptors) that.size = 0;
8307 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8310 var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8312 var define = function (that, key, value) {
8313 var state = getInternalState(that);
8314 var entry = getEntry(that, key);
8315 var previous, index;
8316 // change existing entry
8318 entry.value = value;
8321 state.last = entry = {
8322 index: index = fastKey(key, true),
8325 previous: previous = state.last,
8329 if (!state.first) state.first = entry;
8330 if (previous) previous.next = entry;
8331 if (descriptors) state.size++;
8334 if (index !== 'F') state.index[index] = entry;
8338 var getEntry = function (that, key) {
8339 var state = getInternalState(that);
8341 var index = fastKey(key);
8343 if (index !== 'F') return state.index[index];
8344 // frozen object case
8345 for (entry = state.first; entry; entry = entry.next) {
8346 if (entry.key == key) return entry;
8350 redefineAll(C.prototype, {
8351 // 23.1.3.1 Map.prototype.clear()
8352 // 23.2.3.2 Set.prototype.clear()
8353 clear: function clear() {
8355 var state = getInternalState(that);
8356 var data = state.index;
8357 var entry = state.first;
8359 entry.removed = true;
8360 if (entry.previous) entry.previous = entry.previous.next = undefined;
8361 delete data[entry.index];
8364 state.first = state.last = undefined;
8365 if (descriptors) state.size = 0;
8368 // 23.1.3.3 Map.prototype.delete(key)
8369 // 23.2.3.4 Set.prototype.delete(value)
8370 'delete': function (key) {
8372 var state = getInternalState(that);
8373 var entry = getEntry(that, key);
8375 var next = entry.next;
8376 var prev = entry.previous;
8377 delete state.index[entry.index];
8378 entry.removed = true;
8379 if (prev) prev.next = next;
8380 if (next) next.previous = prev;
8381 if (state.first == entry) state.first = next;
8382 if (state.last == entry) state.last = prev;
8383 if (descriptors) state.size--;
8387 // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
8388 // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
8389 forEach: function forEach(callbackfn /* , that = undefined */) {
8390 var state = getInternalState(this);
8391 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8393 while (entry = entry ? entry.next : state.first) {
8394 boundFunction(entry.value, entry.key, this);
8395 // revert to the last existing entry
8396 while (entry && entry.removed) entry = entry.previous;
8399 // 23.1.3.7 Map.prototype.has(key)
8400 // 23.2.3.7 Set.prototype.has(value)
8401 has: function has(key) {
8402 return !!getEntry(this, key);
8406 redefineAll(C.prototype, IS_MAP ? {
8407 // 23.1.3.6 Map.prototype.get(key)
8408 get: function get(key) {
8409 var entry = getEntry(this, key);
8410 return entry && entry.value;
8412 // 23.1.3.9 Map.prototype.set(key, value)
8413 set: function set(key, value) {
8414 return define(this, key === 0 ? 0 : key, value);
8417 // 23.2.3.1 Set.prototype.add(value)
8418 add: function add(value) {
8419 return define(this, value = value === 0 ? 0 : value, value);
8422 if (descriptors) defineProperty$8(C.prototype, 'size', {
8424 return getInternalState(this).size;
8429 setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8430 var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8431 var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8432 var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8433 // add .keys, .values, .entries, [@@iterator]
8434 // 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
8435 defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8436 setInternalState$7(this, {
8437 type: ITERATOR_NAME,
8439 state: getInternalCollectionState(iterated),
8444 var state = getInternalIteratorState(this);
8445 var kind = state.kind;
8446 var entry = state.last;
8447 // revert to the last existing entry
8448 while (entry && entry.removed) entry = entry.previous;
8450 if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8451 // or finish the iteration
8452 state.target = undefined;
8453 return { value: undefined, done: true };
8455 // return step by kind
8456 if (kind == 'keys') return { value: entry.key, done: false };
8457 if (kind == 'values') return { value: entry.value, done: false };
8458 return { value: [entry.key, entry.value], done: false };
8459 }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8461 // add [@@species], 23.1.2.2, 23.2.2.2
8462 setSpecies(CONSTRUCTOR_NAME);
8466 // `Set` constructor
8467 // https://tc39.github.io/ecma262/#sec-set-objects
8468 var es_set = collection('Set', function (init) {
8469 return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8470 }, collectionStrong);
8472 function d3_ascending (a, b) {
8473 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8476 function d3_bisector (f) {
8480 if (f.length === 1) {
8481 delta = function delta(d, x) {
8485 compare = ascendingComparator(f);
8488 function left(a, x, lo, hi) {
8489 if (lo == null) lo = 0;
8490 if (hi == null) hi = a.length;
8493 var mid = lo + hi >>> 1;
8494 if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8500 function right(a, x, lo, hi) {
8501 if (lo == null) lo = 0;
8502 if (hi == null) hi = a.length;
8505 var mid = lo + hi >>> 1;
8506 if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8512 function center(a, x, lo, hi) {
8513 if (lo == null) lo = 0;
8514 if (hi == null) hi = a.length;
8515 var i = left(a, x, lo, hi - 1);
8516 return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8526 function ascendingComparator(f) {
8527 return function (d, x) {
8528 return d3_ascending(f(d), x);
8532 // `Symbol.asyncIterator` well-known symbol
8533 // https://tc39.github.io/ecma262/#sec-symbol.asynciterator
8534 defineWellKnownSymbol('asyncIterator');
8536 var runtime_1 = createCommonjsModule(function (module) {
8538 * Copyright (c) 2014-present, Facebook, Inc.
8540 * This source code is licensed under the MIT license found in the
8541 * LICENSE file in the root directory of this source tree.
8543 var runtime = function (exports) {
8545 var Op = Object.prototype;
8546 var hasOwn = Op.hasOwnProperty;
8547 var undefined$1; // More compressible than void 0.
8549 var $Symbol = typeof Symbol === "function" ? Symbol : {};
8550 var iteratorSymbol = $Symbol.iterator || "@@iterator";
8551 var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8552 var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8554 function define(obj, key, value) {
8555 Object.defineProperty(obj, key, {
8565 // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8568 define = function define(obj, key, value) {
8569 return obj[key] = value;
8573 function wrap(innerFn, outerFn, self, tryLocsList) {
8574 // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8575 var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8576 var generator = Object.create(protoGenerator.prototype);
8577 var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8578 // .throw, and .return methods.
8580 generator._invoke = makeInvokeMethod(innerFn, self, context);
8584 exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8585 // record like context.tryEntries[i].completion. This interface could
8586 // have been (and was previously) designed to take a closure to be
8587 // invoked without arguments, but in all the cases we care about we
8588 // already have an existing method we want to call, so there's no need
8589 // to create a new function object. We can even get away with assuming
8590 // the method takes exactly one argument, since that happens to be true
8591 // in every case, so we don't have to touch the arguments object. The
8592 // only additional allocation required is the completion record, which
8593 // has a stable shape and so hopefully should be cheap to allocate.
8595 function tryCatch(fn, obj, arg) {
8599 arg: fn.call(obj, arg)
8609 var GenStateSuspendedStart = "suspendedStart";
8610 var GenStateSuspendedYield = "suspendedYield";
8611 var GenStateExecuting = "executing";
8612 var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8613 // breaking out of the dispatch switch statement.
8615 var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8616 // .constructor.prototype properties for functions that return Generator
8617 // objects. For full spec compliance, you may wish to configure your
8618 // minifier not to mangle the names of these two functions.
8620 function Generator() {}
8622 function GeneratorFunction() {}
8624 function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8625 // don't natively support it.
8628 var IteratorPrototype = {};
8630 IteratorPrototype[iteratorSymbol] = function () {
8634 var getProto = Object.getPrototypeOf;
8635 var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8637 if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8638 // This environment has a native %IteratorPrototype%; use it instead
8640 IteratorPrototype = NativeIteratorPrototype;
8643 var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8644 GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8645 GeneratorFunctionPrototype.constructor = GeneratorFunction;
8646 GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8647 // Iterator interface in terms of a single ._invoke method.
8649 function defineIteratorMethods(prototype) {
8650 ["next", "throw", "return"].forEach(function (method) {
8651 define(prototype, method, function (arg) {
8652 return this._invoke(method, arg);
8657 exports.isGeneratorFunction = function (genFun) {
8658 var ctor = typeof genFun === "function" && genFun.constructor;
8659 return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8660 // do is to check its .name property.
8661 (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8664 exports.mark = function (genFun) {
8665 if (Object.setPrototypeOf) {
8666 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8668 genFun.__proto__ = GeneratorFunctionPrototype;
8669 define(genFun, toStringTagSymbol, "GeneratorFunction");
8672 genFun.prototype = Object.create(Gp);
8674 }; // Within the body of any async function, `await x` is transformed to
8675 // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8676 // `hasOwn.call(value, "__await")` to determine if the yielded value is
8677 // meant to be awaited.
8680 exports.awrap = function (arg) {
8686 function AsyncIterator(generator, PromiseImpl) {
8687 function invoke(method, arg, resolve, reject) {
8688 var record = tryCatch(generator[method], generator, arg);
8690 if (record.type === "throw") {
8693 var result = record.arg;
8694 var value = result.value;
8696 if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8697 return PromiseImpl.resolve(value.__await).then(function (value) {
8698 invoke("next", value, resolve, reject);
8700 invoke("throw", err, resolve, reject);
8704 return PromiseImpl.resolve(value).then(function (unwrapped) {
8705 // When a yielded Promise is resolved, its final value becomes
8706 // the .value of the Promise<{value,done}> result for the
8707 // current iteration.
8708 result.value = unwrapped;
8710 }, function (error) {
8711 // If a rejected Promise was yielded, throw the rejection back
8712 // into the async generator function so it can be handled there.
8713 return invoke("throw", error, resolve, reject);
8718 var previousPromise;
8720 function enqueue(method, arg) {
8721 function callInvokeWithMethodAndArg() {
8722 return new PromiseImpl(function (resolve, reject) {
8723 invoke(method, arg, resolve, reject);
8727 return previousPromise = // If enqueue has been called before, then we want to wait until
8728 // all previous Promises have been resolved before calling invoke,
8729 // so that results are always delivered in the correct order. If
8730 // enqueue has not been called before, then it is important to
8731 // call invoke immediately, without waiting on a callback to fire,
8732 // so that the async generator function has the opportunity to do
8733 // any necessary setup in a predictable way. This predictability
8734 // is why the Promise constructor synchronously invokes its
8735 // executor callback, and why async functions synchronously
8736 // execute code before the first await. Since we implement simple
8737 // async functions in terms of async generators, it is especially
8738 // important to get this right, even though it requires care.
8739 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8740 // invocations of the iterator.
8741 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8742 } // Define the unified helper method that is used to implement .next,
8743 // .throw, and .return (see defineIteratorMethods).
8746 this._invoke = enqueue;
8749 defineIteratorMethods(AsyncIterator.prototype);
8751 AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8755 exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8756 // AsyncIterator objects; they just return a Promise for the value of
8757 // the final result produced by the iterator.
8759 exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8760 if (PromiseImpl === void 0) PromiseImpl = Promise;
8761 var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8762 return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8763 : iter.next().then(function (result) {
8764 return result.done ? result.value : iter.next();
8768 function makeInvokeMethod(innerFn, self, context) {
8769 var state = GenStateSuspendedStart;
8770 return function invoke(method, arg) {
8771 if (state === GenStateExecuting) {
8772 throw new Error("Generator is already running");
8775 if (state === GenStateCompleted) {
8776 if (method === "throw") {
8778 } // Be forgiving, per 25.3.3.3.3 of the spec:
8779 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
8782 return doneResult();
8785 context.method = method;
8789 var delegate = context.delegate;
8792 var delegateResult = maybeInvokeDelegate(delegate, context);
8794 if (delegateResult) {
8795 if (delegateResult === ContinueSentinel) continue;
8796 return delegateResult;
8800 if (context.method === "next") {
8801 // Setting context._sent for legacy support of Babel's
8802 // function.sent implementation.
8803 context.sent = context._sent = context.arg;
8804 } else if (context.method === "throw") {
8805 if (state === GenStateSuspendedStart) {
8806 state = GenStateCompleted;
8810 context.dispatchException(context.arg);
8811 } else if (context.method === "return") {
8812 context.abrupt("return", context.arg);
8815 state = GenStateExecuting;
8816 var record = tryCatch(innerFn, self, context);
8818 if (record.type === "normal") {
8819 // If an exception is thrown from innerFn, we leave state ===
8820 // GenStateExecuting and loop back for another invocation.
8821 state = context.done ? GenStateCompleted : GenStateSuspendedYield;
8823 if (record.arg === ContinueSentinel) {
8831 } else if (record.type === "throw") {
8832 state = GenStateCompleted; // Dispatch the exception by looping back around to the
8833 // context.dispatchException(context.arg) call above.
8835 context.method = "throw";
8836 context.arg = record.arg;
8840 } // Call delegate.iterator[context.method](context.arg) and handle the
8841 // result, either by returning a { value, done } result from the
8842 // delegate iterator, or by modifying context.method and context.arg,
8843 // setting context.delegate to null, and returning the ContinueSentinel.
8846 function maybeInvokeDelegate(delegate, context) {
8847 var method = delegate.iterator[context.method];
8849 if (method === undefined$1) {
8850 // A .throw or .return when the delegate iterator has no .throw
8851 // method always terminates the yield* loop.
8852 context.delegate = null;
8854 if (context.method === "throw") {
8855 // Note: ["return"] must be used for ES3 parsing compatibility.
8856 if (delegate.iterator["return"]) {
8857 // If the delegate iterator has a return method, give it a
8858 // chance to clean up.
8859 context.method = "return";
8860 context.arg = undefined$1;
8861 maybeInvokeDelegate(delegate, context);
8863 if (context.method === "throw") {
8864 // If maybeInvokeDelegate(context) changed context.method from
8865 // "return" to "throw", let that override the TypeError below.
8866 return ContinueSentinel;
8870 context.method = "throw";
8871 context.arg = new TypeError("The iterator does not provide a 'throw' method");
8874 return ContinueSentinel;
8877 var record = tryCatch(method, delegate.iterator, context.arg);
8879 if (record.type === "throw") {
8880 context.method = "throw";
8881 context.arg = record.arg;
8882 context.delegate = null;
8883 return ContinueSentinel;
8886 var info = record.arg;
8889 context.method = "throw";
8890 context.arg = new TypeError("iterator result is not an object");
8891 context.delegate = null;
8892 return ContinueSentinel;
8896 // Assign the result of the finished delegate to the temporary
8897 // variable specified by delegate.resultName (see delegateYield).
8898 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
8900 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
8901 // exception, let the outer generator proceed normally. If
8902 // context.method was "next", forget context.arg since it has been
8903 // "consumed" by the delegate iterator. If context.method was
8904 // "return", allow the original .return call to continue in the
8907 if (context.method !== "return") {
8908 context.method = "next";
8909 context.arg = undefined$1;
8912 // Re-yield the result returned by the delegate method.
8914 } // The delegate iterator is finished, so forget it and continue with
8915 // the outer generator.
8918 context.delegate = null;
8919 return ContinueSentinel;
8920 } // Define Generator.prototype.{next,throw,return} in terms of the
8921 // unified ._invoke helper method.
8924 defineIteratorMethods(Gp);
8925 define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
8926 // @@iterator function is called on it. Some browsers' implementations of the
8927 // iterator prototype chain incorrectly implement this, causing the Generator
8928 // object to not be returned from this call. This ensures that doesn't happen.
8929 // See https://github.com/facebook/regenerator/issues/274 for more details.
8931 Gp[iteratorSymbol] = function () {
8935 Gp.toString = function () {
8936 return "[object Generator]";
8939 function pushTryEntry(locs) {
8945 entry.catchLoc = locs[1];
8949 entry.finallyLoc = locs[2];
8950 entry.afterLoc = locs[3];
8953 this.tryEntries.push(entry);
8956 function resetTryEntry(entry) {
8957 var record = entry.completion || {};
8958 record.type = "normal";
8960 entry.completion = record;
8963 function Context(tryLocsList) {
8964 // The root entry object (effectively a try statement without a catch
8965 // or a finally block) gives us a place to store values thrown from
8966 // locations where there is no enclosing try statement.
8967 this.tryEntries = [{
8970 tryLocsList.forEach(pushTryEntry, this);
8974 exports.keys = function (object) {
8977 for (var key in object) {
8981 keys.reverse(); // Rather than returning an object with a next method, we keep
8982 // things simple and return the next function itself.
8984 return function next() {
8985 while (keys.length) {
8986 var key = keys.pop();
8988 if (key in object) {
8993 } // To avoid creating an additional object, we just hang the .value
8994 // and .done properties off the next function object itself. This
8995 // also ensures that the minifier will not anonymize the function.
9003 function values(iterable) {
9005 var iteratorMethod = iterable[iteratorSymbol];
9007 if (iteratorMethod) {
9008 return iteratorMethod.call(iterable);
9011 if (typeof iterable.next === "function") {
9015 if (!isNaN(iterable.length)) {
9017 next = function next() {
9018 while (++i < iterable.length) {
9019 if (hasOwn.call(iterable, i)) {
9020 next.value = iterable[i];
9026 next.value = undefined$1;
9031 return next.next = next;
9033 } // Return an iterator with no values.
9041 exports.values = values;
9043 function doneResult() {
9050 Context.prototype = {
9051 constructor: Context,
9052 reset: function reset(skipTempReset) {
9054 this.next = 0; // Resetting context._sent for legacy support of Babel's
9055 // function.sent implementation.
9057 this.sent = this._sent = undefined$1;
9059 this.delegate = null;
9060 this.method = "next";
9061 this.arg = undefined$1;
9062 this.tryEntries.forEach(resetTryEntry);
9064 if (!skipTempReset) {
9065 for (var name in this) {
9066 // Not sure about the optimal order of these conditions:
9067 if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9068 this[name] = undefined$1;
9073 stop: function stop() {
9075 var rootEntry = this.tryEntries[0];
9076 var rootRecord = rootEntry.completion;
9078 if (rootRecord.type === "throw") {
9079 throw rootRecord.arg;
9084 dispatchException: function dispatchException(exception) {
9091 function handle(loc, caught) {
9092 record.type = "throw";
9093 record.arg = exception;
9097 // If the dispatched exception was caught by a catch block,
9098 // then let that catch block handle the exception normally.
9099 context.method = "next";
9100 context.arg = undefined$1;
9106 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9107 var entry = this.tryEntries[i];
9108 var record = entry.completion;
9110 if (entry.tryLoc === "root") {
9111 // Exception thrown outside of any try block that could handle
9112 // it, so set the completion value of the entire function to
9113 // throw the exception.
9114 return handle("end");
9117 if (entry.tryLoc <= this.prev) {
9118 var hasCatch = hasOwn.call(entry, "catchLoc");
9119 var hasFinally = hasOwn.call(entry, "finallyLoc");
9121 if (hasCatch && hasFinally) {
9122 if (this.prev < entry.catchLoc) {
9123 return handle(entry.catchLoc, true);
9124 } else if (this.prev < entry.finallyLoc) {
9125 return handle(entry.finallyLoc);
9127 } else if (hasCatch) {
9128 if (this.prev < entry.catchLoc) {
9129 return handle(entry.catchLoc, true);
9131 } else if (hasFinally) {
9132 if (this.prev < entry.finallyLoc) {
9133 return handle(entry.finallyLoc);
9136 throw new Error("try statement without catch or finally");
9141 abrupt: function abrupt(type, arg) {
9142 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9143 var entry = this.tryEntries[i];
9145 if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9146 var finallyEntry = entry;
9151 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9152 // Ignore the finally entry if control is not jumping to a
9153 // location outside the try/catch block.
9154 finallyEntry = null;
9157 var record = finallyEntry ? finallyEntry.completion : {};
9162 this.method = "next";
9163 this.next = finallyEntry.finallyLoc;
9164 return ContinueSentinel;
9167 return this.complete(record);
9169 complete: function complete(record, afterLoc) {
9170 if (record.type === "throw") {
9174 if (record.type === "break" || record.type === "continue") {
9175 this.next = record.arg;
9176 } else if (record.type === "return") {
9177 this.rval = this.arg = record.arg;
9178 this.method = "return";
9180 } else if (record.type === "normal" && afterLoc) {
9181 this.next = afterLoc;
9184 return ContinueSentinel;
9186 finish: function finish(finallyLoc) {
9187 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9188 var entry = this.tryEntries[i];
9190 if (entry.finallyLoc === finallyLoc) {
9191 this.complete(entry.completion, entry.afterLoc);
9192 resetTryEntry(entry);
9193 return ContinueSentinel;
9197 "catch": function _catch(tryLoc) {
9198 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9199 var entry = this.tryEntries[i];
9201 if (entry.tryLoc === tryLoc) {
9202 var record = entry.completion;
9204 if (record.type === "throw") {
9205 var thrown = record.arg;
9206 resetTryEntry(entry);
9211 } // The context.catch method must only be called with a location
9212 // argument that corresponds to a known catch block.
9215 throw new Error("illegal catch attempt");
9217 delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9219 iterator: values(iterable),
9220 resultName: resultName,
9224 if (this.method === "next") {
9225 // Deliberately forget the last sent value so that we don't
9226 // accidentally pass it on to the delegate.
9227 this.arg = undefined$1;
9230 return ContinueSentinel;
9232 }; // Regardless of whether this script is executing as a CommonJS module
9233 // or not, return the runtime object so that we can declare the variable
9234 // regeneratorRuntime in the outer scope, which allows this module to be
9235 // injected easily by `bin/regenerator --include-runtime script.js`.
9238 }( // If this script is executing as a CommonJS module, use module.exports
9239 // as the regeneratorRuntime namespace. Otherwise create a new empty
9240 // object. Either way, the resulting object will be used to initialize
9241 // the regeneratorRuntime variable at the top of this file.
9245 regeneratorRuntime = runtime;
9246 } catch (accidentalStrictMode) {
9247 // This module should not be running in strict mode, so the above
9248 // assignment should always work unless something is misconfigured. Just
9249 // in case runtime.js accidentally runs in strict mode, we can escape
9250 // strict mode using a global Function call. This could conceivably fail
9251 // if a Content Security Policy forbids using Function, but in that case
9252 // the proper solution is to fix the accidental strict mode problem. If
9253 // you've misconfigured your bundler to force strict mode and applied a
9254 // CSP to forbid Function, and you're not willing to fix either of those
9255 // problems, please detail your unique predicament in a GitHub issue.
9256 Function("r", "regeneratorRuntime = r")(runtime);
9260 var _marked = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9262 function number (x) {
9263 return x === null ? NaN : +x;
9265 function numbers(values, valueof) {
9266 var _iterator, _step, value, index, _iterator2, _step2, _value;
9268 return regeneratorRuntime.wrap(function numbers$(_context) {
9270 switch (_context.prev = _context.next) {
9272 if (!(valueof === undefined)) {
9277 _iterator = _createForOfIteratorHelper(values);
9283 if ((_step = _iterator.n()).done) {
9288 value = _step.value;
9290 if (!(value != null && (value = +value) >= value)) {
9308 _context.t0 = _context["catch"](2);
9310 _iterator.e(_context.t0);
9317 return _context.finish(16);
9325 _iterator2 = _createForOfIteratorHelper(values);
9331 if ((_step2 = _iterator2.n()).done) {
9336 _value = _step2.value;
9338 if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9356 _context.t1 = _context["catch"](23);
9358 _iterator2.e(_context.t1);
9365 return _context.finish(37);
9369 return _context.stop();
9372 }, _marked, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9375 var ascendingBisect = d3_bisector(d3_ascending);
9376 var bisectRight = ascendingBisect.right;
9377 var bisectCenter = d3_bisector(number).center;
9379 // `Array.prototype.fill` method
9380 // https://tc39.github.io/ecma262/#sec-array.prototype.fill
9381 _export({ target: 'Array', proto: true }, {
9385 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
9386 addToUnscopables('fill');
9388 var INCORRECT_ITERATION$1 = !checkCorrectnessOfIteration(function (iterable) {
9389 Array.from(iterable);
9392 // `Array.from` method
9393 // https://tc39.github.io/ecma262/#sec-array.from
9394 _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION$1 }, {
9398 var $some$1 = arrayIteration.some;
9402 var STRICT_METHOD$4 = arrayMethodIsStrict('some');
9403 var USES_TO_LENGTH$7 = arrayMethodUsesToLength('some');
9405 // `Array.prototype.some` method
9406 // https://tc39.github.io/ecma262/#sec-array.prototype.some
9407 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$4 || !USES_TO_LENGTH$7 }, {
9408 some: function some(callbackfn /* , thisArg */) {
9409 return $some$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9413 // `Float64Array` constructor
9414 // https://tc39.github.io/ecma262/#sec-typedarray-objects
9415 typedArrayConstructor('Float64', function (init) {
9416 return function Float64Array(data, byteOffset, length) {
9417 return init(this, data, byteOffset, length);
9421 var exportTypedArrayStaticMethod$1 = arrayBufferViewCore.exportTypedArrayStaticMethod;
9424 // `%TypedArray%.from` method
9425 // https://tc39.github.io/ecma262/#sec-%typedarray%.from
9426 exportTypedArrayStaticMethod$1('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9428 function d3_descending (a, b) {
9429 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9432 // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9433 var Adder = /*#__PURE__*/function () {
9435 _classCallCheck(this, Adder);
9437 this._partials = new Float64Array(32);
9441 _createClass(Adder, [{
9443 value: function add(x) {
9444 var p = this._partials;
9447 for (var j = 0; j < this._n && j < 32; j++) {
9450 lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9451 if (lo) p[i++] = lo;
9461 value: function valueOf() {
9462 var p = this._partials;
9480 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9483 if (y == x - hi) hi = x;
9494 // `Map` constructor
9495 // https://tc39.github.io/ecma262/#sec-map-objects
9496 var es_map = collection('Map', function (init) {
9497 return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9498 }, collectionStrong);
9500 var e10 = Math.sqrt(50),
9503 function ticks (start, stop, count) {
9509 stop = +stop, start = +start, count = +count;
9510 if (start === stop && count > 0) return [start];
9511 if (reverse = stop < start) n = start, start = stop, stop = n;
9512 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9515 start = Math.ceil(start / step);
9516 stop = Math.floor(stop / step);
9517 ticks = new Array(n = Math.ceil(stop - start + 1));
9520 ticks[i] = (start + i) * step;
9524 start = Math.ceil(start * step);
9525 stop = Math.floor(stop * step);
9526 ticks = new Array(n = Math.ceil(stop - start + 1));
9529 ticks[i] = (start + i) / step;
9533 if (reverse) ticks.reverse();
9536 function tickIncrement(start, stop, count) {
9537 var step = (stop - start) / Math.max(0, count),
9538 power = Math.floor(Math.log(step) / Math.LN10),
9539 error = step / Math.pow(10, power);
9540 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);
9542 function tickStep(start, stop, count) {
9543 var step0 = Math.abs(stop - start) / Math.max(0, count),
9544 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9545 error = step0 / step1;
9546 if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9547 return stop < start ? -step1 : step1;
9550 function max$4(values, valueof) {
9553 if (valueof === undefined) {
9554 var _iterator = _createForOfIteratorHelper(values),
9558 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9559 var value = _step.value;
9561 if (value != null && (max < value || max === undefined && value >= value)) {
9573 var _iterator2 = _createForOfIteratorHelper(values),
9577 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9578 var _value = _step2.value;
9580 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9594 function min$7(values, valueof) {
9597 if (valueof === undefined) {
9598 var _iterator = _createForOfIteratorHelper(values),
9602 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9603 var value = _step.value;
9605 if (value != null && (min > value || min === undefined && value >= value)) {
9617 var _iterator2 = _createForOfIteratorHelper(values),
9621 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9622 var _value = _step2.value;
9624 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9638 // ISC license, Copyright 2018 Vladimir Agafonkin.
9640 function quickselect(array, k) {
9641 var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9642 var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9643 var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9645 while (right > left) {
9646 if (right - left > 600) {
9647 var n = right - left + 1;
9648 var m = k - left + 1;
9649 var z = Math.log(n);
9650 var s = 0.5 * Math.exp(2 * z / 3);
9651 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9652 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9653 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9654 quickselect(array, k, newLeft, newRight, compare);
9660 swap(array, left, k);
9661 if (compare(array[right], t) > 0) swap(array, left, right);
9664 swap(array, i, j), ++i, --j;
9666 while (compare(array[i], t) < 0) {
9670 while (compare(array[j], t) > 0) {
9675 if (compare(array[left], t) === 0) swap(array, left, j);else ++j, swap(array, j, right);
9676 if (j <= k) left = j + 1;
9677 if (k <= j) right = j - 1;
9683 function swap(array, i, j) {
9685 array[i] = array[j];
9689 function quantile(values, p, valueof) {
9690 values = Float64Array.from(numbers(values, valueof));
9691 if (!(n = values.length)) return;
9692 if ((p = +p) <= 0 || n < 2) return min$7(values);
9693 if (p >= 1) return max$4(values);
9697 value0 = max$4(quickselect(values, i0).subarray(0, i0 + 1)),
9698 value1 = min$7(values.subarray(i0 + 1));
9699 return value0 + (value1 - value0) * (i - i0);
9702 function d3_median (values, valueof) {
9703 return quantile(values, 0.5, valueof);
9706 var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
9708 function flatten(arrays) {
9709 var _iterator, _step, array;
9711 return regeneratorRuntime.wrap(function flatten$(_context) {
9713 switch (_context.prev = _context.next) {
9715 _iterator = _createForOfIteratorHelper(arrays);
9721 if ((_step = _iterator.n()).done) {
9726 array = _step.value;
9727 return _context.delegateYield(array, "t0", 6);
9739 _context.t1 = _context["catch"](1);
9741 _iterator.e(_context.t1);
9748 return _context.finish(13);
9752 return _context.stop();
9755 }, _marked$1, null, [[1, 10, 13, 16]]);
9758 function merge(arrays) {
9759 return Array.from(flatten(arrays));
9762 function range (start, stop, step) {
9763 start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
9765 n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
9766 range = new Array(n);
9769 range[i] = start + i * step;
9776 var nativeSort = test$2.sort;
9779 var FAILS_ON_UNDEFINED = fails(function () {
9780 test$2.sort(undefined);
9783 var FAILS_ON_NULL = fails(function () {
9787 var STRICT_METHOD$5 = arrayMethodIsStrict('sort');
9789 var FORCED$a = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$5;
9791 // `Array.prototype.sort` method
9792 // https://tc39.github.io/ecma262/#sec-array.prototype.sort
9793 _export({ target: 'Array', proto: true, forced: FORCED$a }, {
9794 sort: function sort(comparefn) {
9795 return comparefn === undefined
9796 ? nativeSort.call(toObject(this))
9797 : nativeSort.call(toObject(this), aFunction$1(comparefn));
9801 // `SameValue` abstract operation
9802 // https://tc39.github.io/ecma262/#sec-samevalue
9803 var sameValue = Object.is || function is(x, y) {
9804 // eslint-disable-next-line no-self-compare
9805 return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
9808 var $hypot = Math.hypot;
9809 var abs$1 = Math.abs;
9810 var sqrt = Math.sqrt;
9813 // https://bugs.chromium.org/p/v8/issues/detail?id=9546
9814 var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
9816 // `Math.hypot` method
9817 // https://tc39.github.io/ecma262/#sec-math.hypot
9818 _export({ target: 'Math', stat: true, forced: BUGGY }, {
9819 hypot: function hypot(value1, value2) { // eslint-disable-line no-unused-vars
9822 var aLen = arguments.length;
9826 arg = abs$1(arguments[i++]);
9829 sum = sum * div * div + 1;
9831 } else if (arg > 0) {
9836 return larg === Infinity ? Infinity : larg * sqrt(sum);
9840 // `Math.sign` method implementation
9841 // https://tc39.github.io/ecma262/#sec-math.sign
9842 var mathSign = Math.sign || function sign(x) {
9843 // eslint-disable-next-line no-self-compare
9844 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
9847 // `Math.sign` method
9848 // https://tc39.github.io/ecma262/#sec-math.sign
9849 _export({ target: 'Math', stat: true }, {
9854 var epsilon2 = 1e-12;
9856 var halfPi = pi / 2;
9857 var quarterPi = pi / 4;
9859 var degrees = 180 / pi;
9860 var radians = pi / 180;
9861 var abs$2 = Math.abs;
9862 var atan = Math.atan;
9863 var atan2 = Math.atan2;
9866 var hypot = Math.hypot;
9867 var log$1 = Math.log;
9869 var sign = Math.sign || function (x) {
9870 return x > 0 ? 1 : x < 0 ? -1 : 0;
9872 var sqrt$1 = Math.sqrt;
9875 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
9878 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
9883 function streamGeometry(geometry, stream) {
9884 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
9885 streamGeometryType[geometry.type](geometry, stream);
9889 var streamObjectType = {
9890 Feature: function Feature(object, stream) {
9891 streamGeometry(object.geometry, stream);
9893 FeatureCollection: function FeatureCollection(object, stream) {
9894 var features = object.features,
9896 n = features.length;
9899 streamGeometry(features[i].geometry, stream);
9903 var streamGeometryType = {
9904 Sphere: function Sphere(object, stream) {
9907 Point: function Point(object, stream) {
9908 object = object.coordinates;
9909 stream.point(object[0], object[1], object[2]);
9911 MultiPoint: function MultiPoint(object, stream) {
9912 var coordinates = object.coordinates,
9914 n = coordinates.length;
9917 object = coordinates[i], stream.point(object[0], object[1], object[2]);
9920 LineString: function LineString(object, stream) {
9921 streamLine(object.coordinates, stream, 0);
9923 MultiLineString: function MultiLineString(object, stream) {
9924 var coordinates = object.coordinates,
9926 n = coordinates.length;
9929 streamLine(coordinates[i], stream, 0);
9932 Polygon: function Polygon(object, stream) {
9933 streamPolygon(object.coordinates, stream);
9935 MultiPolygon: function MultiPolygon(object, stream) {
9936 var coordinates = object.coordinates,
9938 n = coordinates.length;
9941 streamPolygon(coordinates[i], stream);
9944 GeometryCollection: function GeometryCollection(object, stream) {
9945 var geometries = object.geometries,
9947 n = geometries.length;
9950 streamGeometry(geometries[i], stream);
9955 function streamLine(coordinates, stream, closed) {
9957 n = coordinates.length - closed,
9962 coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
9968 function streamPolygon(coordinates, stream) {
9970 n = coordinates.length;
9971 stream.polygonStart();
9974 streamLine(coordinates[i], stream, 1);
9977 stream.polygonEnd();
9980 function d3_geoStream (object, stream) {
9981 if (object && streamObjectType.hasOwnProperty(object.type)) {
9982 streamObjectType[object.type](object, stream);
9984 streamGeometry(object, stream);
9988 var areaRingSum = new Adder(); // hello?
9990 var areaSum = new Adder(),
10000 polygonStart: function polygonStart() {
10001 areaRingSum = new Adder();
10002 areaStream.lineStart = areaRingStart;
10003 areaStream.lineEnd = areaRingEnd;
10005 polygonEnd: function polygonEnd() {
10006 var areaRing = +areaRingSum;
10007 areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
10008 this.lineStart = this.lineEnd = this.point = noop;
10010 sphere: function sphere() {
10015 function areaRingStart() {
10016 areaStream.point = areaPointFirst;
10019 function areaRingEnd() {
10020 areaPoint(lambda00, phi00);
10023 function areaPointFirst(lambda, phi) {
10024 areaStream.point = areaPoint;
10025 lambda00 = lambda, phi00 = phi;
10026 lambda *= radians, phi *= radians;
10027 lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi);
10030 function areaPoint(lambda, phi) {
10031 lambda *= radians, phi *= radians;
10032 phi = phi / 2 + quarterPi; // half the angular distance from south pole
10033 // Spherical excess E for a spherical triangle with vertices: south pole,
10034 // previous point, current point. Uses a formula derived from Cagnoli’s
10035 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10037 var dLambda = lambda - lambda0,
10038 sdLambda = dLambda >= 0 ? 1 : -1,
10039 adLambda = sdLambda * dLambda,
10042 k = sinPhi0 * sinPhi,
10043 u = cosPhi0 * cosPhi + k * cos(adLambda),
10044 v = k * sdLambda * sin(adLambda);
10045 areaRingSum.add(atan2(v, u)); // Advance the previous points.
10047 lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
10050 function d3_geoArea (object) {
10051 areaSum = new Adder();
10052 d3_geoStream(object, areaStream);
10053 return areaSum * 2;
10056 function spherical(cartesian) {
10057 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10059 function cartesian(spherical) {
10060 var lambda = spherical[0],
10061 phi = spherical[1],
10063 return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10065 function cartesianDot(a, b) {
10066 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10068 function cartesianCross(a, b) {
10069 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]];
10072 function cartesianAddInPlace(a, b) {
10073 a[0] += b[0], a[1] += b[1], a[2] += b[2];
10075 function cartesianScale(vector, k) {
10076 return [vector[0] * k, vector[1] * k, vector[2] * k];
10079 function cartesianNormalizeInPlace(d) {
10080 var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10081 d[0] /= l, d[1] /= l, d[2] /= l;
10084 var lambda0$1, phi0, lambda1, phi1, // bounds
10085 lambda2, // previous lambda-coordinate
10086 lambda00$1, phi00$1, // first point
10087 p0, // previous 3D point
10088 deltaSum, ranges, range$1;
10089 var boundsStream = {
10090 point: boundsPoint,
10091 lineStart: boundsLineStart,
10092 lineEnd: boundsLineEnd,
10093 polygonStart: function polygonStart() {
10094 boundsStream.point = boundsRingPoint;
10095 boundsStream.lineStart = boundsRingStart;
10096 boundsStream.lineEnd = boundsRingEnd;
10097 deltaSum = new Adder();
10098 areaStream.polygonStart();
10100 polygonEnd: function polygonEnd() {
10101 areaStream.polygonEnd();
10102 boundsStream.point = boundsPoint;
10103 boundsStream.lineStart = boundsLineStart;
10104 boundsStream.lineEnd = boundsLineEnd;
10105 if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon) phi1 = 90;else if (deltaSum < -epsilon) phi0 = -90;
10106 range$1[0] = lambda0$1, range$1[1] = lambda1;
10108 sphere: function sphere() {
10109 lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10113 function boundsPoint(lambda, phi) {
10114 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10115 if (phi < phi0) phi0 = phi;
10116 if (phi > phi1) phi1 = phi;
10119 function linePoint(lambda, phi) {
10120 var p = cartesian([lambda * radians, phi * radians]);
10123 var normal = cartesianCross(p0, p),
10124 equatorial = [normal[1], -normal[0], 0],
10125 inflection = cartesianCross(equatorial, normal);
10126 cartesianNormalizeInPlace(inflection);
10127 inflection = spherical(inflection);
10128 var delta = lambda - lambda2,
10129 sign = delta > 0 ? 1 : -1,
10130 lambdai = inflection[0] * degrees * sign,
10132 antimeridian = abs$2(delta) > 180;
10134 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10135 phii = inflection[1] * degrees;
10136 if (phii > phi1) phi1 = phii;
10137 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10138 phii = -inflection[1] * degrees;
10139 if (phii < phi0) phi0 = phii;
10141 if (phi < phi0) phi0 = phi;
10142 if (phi > phi1) phi1 = phi;
10145 if (antimeridian) {
10146 if (lambda < lambda2) {
10147 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10149 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10152 if (lambda1 >= lambda0$1) {
10153 if (lambda < lambda0$1) lambda0$1 = lambda;
10154 if (lambda > lambda1) lambda1 = lambda;
10156 if (lambda > lambda2) {
10157 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10159 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10164 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10167 if (phi < phi0) phi0 = phi;
10168 if (phi > phi1) phi1 = phi;
10169 p0 = p, lambda2 = lambda;
10172 function boundsLineStart() {
10173 boundsStream.point = linePoint;
10176 function boundsLineEnd() {
10177 range$1[0] = lambda0$1, range$1[1] = lambda1;
10178 boundsStream.point = boundsPoint;
10182 function boundsRingPoint(lambda, phi) {
10184 var delta = lambda - lambda2;
10185 deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10187 lambda00$1 = lambda, phi00$1 = phi;
10190 areaStream.point(lambda, phi);
10191 linePoint(lambda, phi);
10194 function boundsRingStart() {
10195 areaStream.lineStart();
10198 function boundsRingEnd() {
10199 boundsRingPoint(lambda00$1, phi00$1);
10200 areaStream.lineEnd();
10201 if (abs$2(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180);
10202 range$1[0] = lambda0$1, range$1[1] = lambda1;
10204 } // Finds the left-right distance between two longitudes.
10205 // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10206 // the distance between ±180° to be 360°.
10209 function angle(lambda0, lambda1) {
10210 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10213 function rangeCompare(a, b) {
10214 return a[0] - b[0];
10217 function rangeContains(range, x) {
10218 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10221 function d3_geoBounds (feature) {
10222 var i, n, a, b, merged, deltaMax, delta;
10223 phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10225 d3_geoStream(feature, boundsStream); // First, sort ranges by their minimum longitudes.
10227 if (n = ranges.length) {
10228 ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10230 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10233 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10234 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10235 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10237 merged.push(a = b);
10239 } // Finally, find the largest gap between the merged ranges.
10240 // The final bounding box will be the inverse of this gap.
10243 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10245 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10249 ranges = range$1 = null;
10250 return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10253 var W0, W1, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, lambda00$2, phi00$2, // first point
10254 x0, y0, z0; // previous point
10256 var centroidStream = {
10258 point: centroidPoint,
10259 lineStart: centroidLineStart,
10260 lineEnd: centroidLineEnd,
10261 polygonStart: function polygonStart() {
10262 centroidStream.lineStart = centroidRingStart;
10263 centroidStream.lineEnd = centroidRingEnd;
10265 polygonEnd: function polygonEnd() {
10266 centroidStream.lineStart = centroidLineStart;
10267 centroidStream.lineEnd = centroidLineEnd;
10269 }; // Arithmetic mean of Cartesian vectors.
10271 function centroidPoint(lambda, phi) {
10272 lambda *= radians, phi *= radians;
10273 var cosPhi = cos(phi);
10274 centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
10277 function centroidPointCartesian(x, y, z) {
10279 X0 += (x - X0) / W0;
10280 Y0 += (y - Y0) / W0;
10281 Z0 += (z - Z0) / W0;
10284 function centroidLineStart() {
10285 centroidStream.point = centroidLinePointFirst;
10288 function centroidLinePointFirst(lambda, phi) {
10289 lambda *= radians, phi *= radians;
10290 var cosPhi = cos(phi);
10291 x0 = cosPhi * cos(lambda);
10292 y0 = cosPhi * sin(lambda);
10294 centroidStream.point = centroidLinePoint;
10295 centroidPointCartesian(x0, y0, z0);
10298 function centroidLinePoint(lambda, phi) {
10299 lambda *= radians, phi *= radians;
10300 var cosPhi = cos(phi),
10301 x = cosPhi * cos(lambda),
10302 y = cosPhi * sin(lambda),
10304 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);
10306 X1 += w * (x0 + (x0 = x));
10307 Y1 += w * (y0 + (y0 = y));
10308 Z1 += w * (z0 + (z0 = z));
10309 centroidPointCartesian(x0, y0, z0);
10312 function centroidLineEnd() {
10313 centroidStream.point = centroidPoint;
10314 } // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
10315 // J. Applied Mechanics 42, 239 (1975).
10318 function centroidRingStart() {
10319 centroidStream.point = centroidRingPointFirst;
10322 function centroidRingEnd() {
10323 centroidRingPoint(lambda00$2, phi00$2);
10324 centroidStream.point = centroidPoint;
10327 function centroidRingPointFirst(lambda, phi) {
10328 lambda00$2 = lambda, phi00$2 = phi;
10329 lambda *= radians, phi *= radians;
10330 centroidStream.point = centroidRingPoint;
10331 var cosPhi = cos(phi);
10332 x0 = cosPhi * cos(lambda);
10333 y0 = cosPhi * sin(lambda);
10335 centroidPointCartesian(x0, y0, z0);
10338 function centroidRingPoint(lambda, phi) {
10339 lambda *= radians, phi *= radians;
10340 var cosPhi = cos(phi),
10341 x = cosPhi * cos(lambda),
10342 y = cosPhi * sin(lambda),
10344 cx = y0 * z - z0 * y,
10345 cy = z0 * x - x0 * z,
10346 cz = x0 * y - y0 * x,
10347 m = hypot(cx, cy, cz),
10349 // line weight = angle
10350 v = m && -w / m; // area weight multiplier
10356 X1 += w * (x0 + (x0 = x));
10357 Y1 += w * (y0 + (y0 = y));
10358 Z1 += w * (z0 + (z0 = z));
10359 centroidPointCartesian(x0, y0, z0);
10362 function d3_geoCentroid (object) {
10363 W0 = W1 = X0 = Y0 = Z0 = X1 = Y1 = Z1 = 0;
10367 d3_geoStream(object, centroidStream);
10371 m = hypot(x, y, z); // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
10373 if (m < epsilon2) {
10374 x = X1, y = Y1, z = Z1; // If the feature has zero length, fall back to arithmetic mean of point vectors.
10376 if (W1 < epsilon) x = X0, y = Y0, z = Z0;
10377 m = hypot(x, y, z); // If the feature still has an undefined ccentroid, then return.
10379 if (m < epsilon2) return [NaN, NaN];
10382 return [atan2(y, x) * degrees, asin(z / m) * degrees];
10385 function compose (a, b) {
10386 function compose(x, y) {
10387 return x = a(x, y), b(x[0], x[1]);
10390 if (a.invert && b.invert) compose.invert = function (x, y) {
10391 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10396 function rotationIdentity(lambda, phi) {
10397 return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10400 rotationIdentity.invert = rotationIdentity;
10401 function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10402 return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10405 function forwardRotationLambda(deltaLambda) {
10406 return function (lambda, phi) {
10407 return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10411 function rotationLambda(deltaLambda) {
10412 var rotation = forwardRotationLambda(deltaLambda);
10413 rotation.invert = forwardRotationLambda(-deltaLambda);
10417 function rotationPhiGamma(deltaPhi, deltaGamma) {
10418 var cosDeltaPhi = cos(deltaPhi),
10419 sinDeltaPhi = sin(deltaPhi),
10420 cosDeltaGamma = cos(deltaGamma),
10421 sinDeltaGamma = sin(deltaGamma);
10423 function rotation(lambda, phi) {
10424 var cosPhi = cos(phi),
10425 x = cos(lambda) * cosPhi,
10426 y = sin(lambda) * cosPhi,
10428 k = z * cosDeltaPhi + x * sinDeltaPhi;
10429 return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10432 rotation.invert = function (lambda, phi) {
10433 var cosPhi = cos(phi),
10434 x = cos(lambda) * cosPhi,
10435 y = sin(lambda) * cosPhi,
10437 k = z * cosDeltaGamma - y * sinDeltaGamma;
10438 return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10444 function rotation (rotate) {
10445 rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10447 function forward(coordinates) {
10448 coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10449 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10452 forward.invert = function (coordinates) {
10453 coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10454 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10460 function circleStream(stream, radius, delta, direction, t0, t1) {
10461 if (!delta) return;
10462 var cosRadius = cos(radius),
10463 sinRadius = sin(radius),
10464 step = direction * delta;
10467 t0 = radius + direction * tau;
10468 t1 = radius - step / 2;
10470 t0 = circleRadius(cosRadius, t0);
10471 t1 = circleRadius(cosRadius, t1);
10472 if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10475 for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10476 point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10477 stream.point(point[0], point[1]);
10479 } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10481 function circleRadius(cosRadius, point) {
10482 point = cartesian(point), point[0] -= cosRadius;
10483 cartesianNormalizeInPlace(point);
10484 var radius = acos(-point[1]);
10485 return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
10488 function clipBuffer () {
10492 point: function point(x, y, m) {
10493 line.push([x, y, m]);
10495 lineStart: function lineStart() {
10496 lines.push(line = []);
10499 rejoin: function rejoin() {
10500 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10502 result: function result() {
10503 var result = lines;
10511 function pointEqual (a, b) {
10512 return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon;
10515 function Intersection(point, points, other, entry) {
10518 this.o = other; // another intersection
10520 this.e = entry; // is an entry?
10522 this.v = false; // visited
10524 this.n = this.p = null; // next & previous
10525 } // A generalized polygon clipping algorithm: given a polygon that has been cut
10526 // into its visible line segments, and rejoins the segments by interpolating
10527 // along the clip edge.
10530 function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10535 segments.forEach(function (segment) {
10536 if ((n = segment.length - 1) <= 0) return;
10542 if (pointEqual(p0, p1)) {
10543 if (!p0[2] && !p1[2]) {
10544 stream.lineStart();
10546 for (i = 0; i < n; ++i) {
10547 stream.point((p0 = segment[i])[0], p0[1]);
10552 } // handle degenerate cases by moving the point
10555 p1[0] += 2 * epsilon;
10558 subject.push(x = new Intersection(p0, segment, null, true));
10559 clip.push(x.o = new Intersection(p0, null, x, false));
10560 subject.push(x = new Intersection(p1, segment, null, false));
10561 clip.push(x.o = new Intersection(p1, null, x, true));
10563 if (!subject.length) return;
10564 clip.sort(compareIntersection);
10568 for (i = 0, n = clip.length; i < n; ++i) {
10569 clip[i].e = startInside = !startInside;
10572 var start = subject[0],
10577 // Find first unvisited intersection.
10578 var current = start,
10581 while (current.v) {
10582 if ((current = current.n) === start) return;
10585 points = current.z;
10586 stream.lineStart();
10589 current.v = current.o.v = true;
10593 for (i = 0, n = points.length; i < n; ++i) {
10594 stream.point((point = points[i])[0], point[1]);
10597 interpolate(current.x, current.n.x, 1, stream);
10600 current = current.n;
10603 points = current.p.z;
10605 for (i = points.length - 1; i >= 0; --i) {
10606 stream.point((point = points[i])[0], point[1]);
10609 interpolate(current.x, current.p.x, -1, stream);
10612 current = current.p;
10615 current = current.o;
10616 points = current.z;
10617 isSubject = !isSubject;
10618 } while (!current.v);
10624 function link(array) {
10625 if (!(n = array.length)) return;
10632 a.n = b = array[i];
10637 a.n = b = array[0];
10641 function longitude(point) {
10642 if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10645 function polygonContains (polygon, point) {
10646 var lambda = longitude(point),
10649 normal = [sin(lambda), -cos(lambda), 0],
10652 var sum = new Adder();
10653 if (sinPhi === 1) phi = halfPi + epsilon;else if (sinPhi === -1) phi = -halfPi - epsilon;
10655 for (var i = 0, n = polygon.length; i < n; ++i) {
10656 if (!(m = (ring = polygon[i]).length)) continue;
10659 point0 = ring[m - 1],
10660 lambda0 = longitude(point0),
10661 phi0 = point0[1] / 2 + quarterPi,
10662 sinPhi0 = sin(phi0),
10663 cosPhi0 = cos(phi0);
10665 for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10666 var point1 = ring[j],
10667 lambda1 = longitude(point1),
10668 phi1 = point1[1] / 2 + quarterPi,
10669 sinPhi1 = sin(phi1),
10670 cosPhi1 = cos(phi1),
10671 delta = lambda1 - lambda0,
10672 sign = delta >= 0 ? 1 : -1,
10673 absDelta = sign * delta,
10674 antimeridian = absDelta > pi,
10675 k = sinPhi0 * sinPhi1;
10676 sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10677 angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10678 // and are the latitudes smaller than the parallel (phi)?
10680 if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10681 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10682 cartesianNormalizeInPlace(arc);
10683 var intersection = cartesianCross(normal, arc);
10684 cartesianNormalizeInPlace(intersection);
10685 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10687 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10688 winding += antimeridian ^ delta >= 0 ? 1 : -1;
10692 } // First, determine whether the South pole is inside or outside:
10694 // It is inside if:
10695 // * the polygon winds around it in a clockwise direction.
10696 // * the polygon does not (cumulatively) wind around it, but has a negative
10697 // (counter-clockwise) area.
10699 // Second, count the (signed) number of times a segment crosses a lambda
10700 // from the point to the South pole. If it is zero, then the point is the
10701 // same side as the South pole.
10704 return (angle < -epsilon || angle < epsilon && sum < -epsilon2) ^ winding & 1;
10707 function clip (pointVisible, clipLine, interpolate, start) {
10708 return function (sink) {
10709 var line = clipLine(sink),
10710 ringBuffer = clipBuffer(),
10711 ringSink = clipLine(ringBuffer),
10712 polygonStarted = false,
10718 lineStart: lineStart,
10720 polygonStart: function polygonStart() {
10721 clip.point = pointRing;
10722 clip.lineStart = ringStart;
10723 clip.lineEnd = ringEnd;
10727 polygonEnd: function polygonEnd() {
10728 clip.point = point;
10729 clip.lineStart = lineStart;
10730 clip.lineEnd = lineEnd;
10731 segments = merge(segments);
10732 var startInside = polygonContains(polygon, start);
10734 if (segments.length) {
10735 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10736 clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10737 } else if (startInside) {
10738 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10740 interpolate(null, null, 1, sink);
10744 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10745 segments = polygon = null;
10747 sphere: function sphere() {
10748 sink.polygonStart();
10750 interpolate(null, null, 1, sink);
10756 function point(lambda, phi) {
10757 if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10760 function pointLine(lambda, phi) {
10761 line.point(lambda, phi);
10764 function lineStart() {
10765 clip.point = pointLine;
10769 function lineEnd() {
10770 clip.point = point;
10774 function pointRing(lambda, phi) {
10775 ring.push([lambda, phi]);
10776 ringSink.point(lambda, phi);
10779 function ringStart() {
10780 ringSink.lineStart();
10784 function ringEnd() {
10785 pointRing(ring[0][0], ring[0][1]);
10786 ringSink.lineEnd();
10787 var clean = ringSink.clean(),
10788 ringSegments = ringBuffer.result(),
10790 n = ringSegments.length,
10795 polygon.push(ring);
10797 if (!n) return; // No intersections.
10800 segment = ringSegments[0];
10802 if ((m = segment.length - 1) > 0) {
10803 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10806 for (i = 0; i < m; ++i) {
10807 sink.point((point = segment[i])[0], point[1]);
10814 } // Rejoin connected segments.
10815 // TODO reuse ringBuffer.rejoin()?
10818 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10819 segments.push(ringSegments.filter(validSegment));
10826 function validSegment(segment) {
10827 return segment.length > 1;
10828 } // Intersections are sorted along the clip edge. For both antimeridian cutting
10829 // and circle clipping, the same comparison is used.
10832 function compareIntersection(a, b) {
10833 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]);
10836 var clipAntimeridian = clip(function () {
10838 }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
10839 // intersections or the line was empty; 1 - no intersections; 2 - there were
10840 // intersections, and the first and last segments should be rejoined.
10842 function clipAntimeridianLine(stream) {
10846 _clean; // no intersections
10850 lineStart: function lineStart() {
10851 stream.lineStart();
10854 point: function point(lambda1, phi1) {
10855 var sign1 = lambda1 > 0 ? pi : -pi,
10856 delta = abs$2(lambda1 - lambda0);
10858 if (abs$2(delta - pi) < epsilon) {
10859 // line crosses a pole
10860 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
10861 stream.point(sign0, phi0);
10863 stream.lineStart();
10864 stream.point(sign1, phi0);
10865 stream.point(lambda1, phi0);
10867 } else if (sign0 !== sign1 && delta >= pi) {
10868 // line crosses antimeridian
10869 if (abs$2(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies
10871 if (abs$2(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon;
10872 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
10873 stream.point(sign0, phi0);
10875 stream.lineStart();
10876 stream.point(sign1, phi0);
10880 stream.point(lambda0 = lambda1, phi0 = phi1);
10883 lineEnd: function lineEnd() {
10885 lambda0 = phi0 = NaN;
10887 clean: function clean() {
10888 return 2 - _clean; // if intersections, rejoin first and last segments
10893 function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
10896 sinLambda0Lambda1 = sin(lambda0 - lambda1);
10897 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;
10900 function clipAntimeridianInterpolate(from, to, direction, stream) {
10903 if (from == null) {
10904 phi = direction * halfPi;
10905 stream.point(-pi, phi);
10906 stream.point(0, phi);
10907 stream.point(pi, phi);
10908 stream.point(pi, 0);
10909 stream.point(pi, -phi);
10910 stream.point(0, -phi);
10911 stream.point(-pi, -phi);
10912 stream.point(-pi, 0);
10913 stream.point(-pi, phi);
10914 } else if (abs$2(from[0] - to[0]) > epsilon) {
10915 var lambda = from[0] < to[0] ? pi : -pi;
10916 phi = direction * lambda / 2;
10917 stream.point(-lambda, phi);
10918 stream.point(0, phi);
10919 stream.point(lambda, phi);
10921 stream.point(to[0], to[1]);
10925 function clipCircle (radius) {
10926 var cr = cos(radius),
10927 delta = 6 * radians,
10928 smallRadius = cr > 0,
10929 notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case
10931 function interpolate(from, to, direction, stream) {
10932 circleStream(stream, radius, delta, direction, from, to);
10935 function visible(lambda, phi) {
10936 return cos(lambda) * cos(phi) > cr;
10937 } // Takes a line and cuts into visible segments. Return values used for polygon
10938 // clipping: 0 - there were intersections or the line was empty; 1 - no
10939 // intersections 2 - there were intersections, and the first and last segments
10940 // should be rejoined.
10943 function clipLine(stream) {
10944 var point0, // previous point
10945 c0, // code for previous point
10946 v0, // visibility of previous point
10947 v00, // visibility of first point
10948 _clean; // no intersections
10952 lineStart: function lineStart() {
10956 point: function point(lambda, phi) {
10957 var point1 = [lambda, phi],
10959 v = visible(lambda, phi),
10960 c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
10961 if (!point0 && (v00 = v0 = v)) stream.lineStart();
10964 point2 = intersect(point0, point1);
10965 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
10972 // outside going in
10973 stream.lineStart();
10974 point2 = intersect(point1, point0);
10975 stream.point(point2[0], point2[1]);
10977 // inside going out
10978 point2 = intersect(point0, point1);
10979 stream.point(point2[0], point2[1], 2);
10984 } else if (notHemisphere && point0 && smallRadius ^ v) {
10985 var t; // If the codes for two points are different, or are both zero,
10986 // and there this segment intersects with the small circle.
10988 if (!(c & c0) && (t = intersect(point1, point0, true))) {
10992 stream.lineStart();
10993 stream.point(t[0][0], t[0][1]);
10994 stream.point(t[1][0], t[1][1]);
10997 stream.point(t[1][0], t[1][1]);
10999 stream.lineStart();
11000 stream.point(t[0][0], t[0][1], 3);
11005 if (v && (!point0 || !pointEqual(point0, point1))) {
11006 stream.point(point1[0], point1[1]);
11009 point0 = point1, v0 = v, c0 = c;
11011 lineEnd: function lineEnd() {
11012 if (v0) stream.lineEnd();
11015 // Rejoin first and last segments if there were intersections and the first
11016 // and last points were visible.
11017 clean: function clean() {
11018 return _clean | (v00 && v0) << 1;
11021 } // Intersects the great circle between a and b with the clip circle.
11024 function intersect(a, b, two) {
11025 var pa = cartesian(a),
11026 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
11027 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
11029 var n1 = [1, 0, 0],
11031 n2 = cartesianCross(pa, pb),
11032 n2n2 = cartesianDot(n2, n2),
11034 // cartesianDot(n1, n2),
11035 determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11037 if (!determinant) return !two && a;
11038 var c1 = cr * n2n2 / determinant,
11039 c2 = -cr * n1n2 / determinant,
11040 n1xn2 = cartesianCross(n1, n2),
11041 A = cartesianScale(n1, c1),
11042 B = cartesianScale(n2, c2);
11043 cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11046 w = cartesianDot(A, u),
11047 uu = cartesianDot(u, u),
11048 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11049 if (t2 < 0) return;
11050 var t = sqrt$1(t2),
11051 q = cartesianScale(u, (-w - t) / uu);
11052 cartesianAddInPlace(q, A);
11054 if (!two) return q; // Two intersection points.
11056 var lambda0 = a[0],
11061 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11062 var delta = lambda1 - lambda0,
11063 polar = abs$2(delta - pi) < epsilon,
11064 meridian = polar || delta < epsilon;
11065 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11067 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)) {
11068 var q1 = cartesianScale(u, (-w + t) / uu);
11069 cartesianAddInPlace(q1, A);
11070 return [q, spherical(q1)];
11072 } // Generates a 4-bit vector representing the location of a point relative to
11073 // the small circle's bounding box.
11076 function code(lambda, phi) {
11077 var r = smallRadius ? radius : pi - radius,
11079 if (lambda < -r) code |= 1; // left
11080 else if (lambda > r) code |= 2; // right
11082 if (phi < -r) code |= 4; // below
11083 else if (phi > r) code |= 8; // above
11088 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11091 function clipLine (a, b, x0, y0, x1, y1) {
11102 if (!dx && r > 0) return;
11106 if (r < t0) return;
11107 if (r < t1) t1 = r;
11108 } else if (dx > 0) {
11109 if (r > t1) return;
11110 if (r > t0) t0 = r;
11114 if (!dx && r < 0) return;
11118 if (r > t1) return;
11119 if (r > t0) t0 = r;
11120 } else if (dx > 0) {
11121 if (r < t0) return;
11122 if (r < t1) t1 = r;
11126 if (!dy && r > 0) return;
11130 if (r < t0) return;
11131 if (r < t1) t1 = r;
11132 } else if (dy > 0) {
11133 if (r > t1) return;
11134 if (r > t0) t0 = r;
11138 if (!dy && r < 0) return;
11142 if (r > t1) return;
11143 if (r > t0) t0 = r;
11144 } else if (dy > 0) {
11145 if (r < t0) return;
11146 if (r < t1) t1 = r;
11149 if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11150 if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11155 clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11156 // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11158 function clipRectangle(x0, y0, x1, y1) {
11159 function visible(x, y) {
11160 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11163 function interpolate(from, to, direction, stream) {
11167 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11169 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11170 } while ((a = (a + direction + 4) % 4) !== a1);
11172 stream.point(to[0], to[1]);
11176 function corner(p, direction) {
11177 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
11180 function compareIntersection(a, b) {
11181 return comparePoint(a.x, b.x);
11184 function comparePoint(a, b) {
11185 var ca = corner(a, 1),
11187 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];
11190 return function (stream) {
11191 var activeStream = stream,
11192 bufferStream = clipBuffer(),
11208 lineStart: lineStart,
11210 polygonStart: polygonStart,
11211 polygonEnd: polygonEnd
11214 function point(x, y) {
11215 if (visible(x, y)) activeStream.point(x, y);
11218 function polygonInside() {
11221 for (var i = 0, n = polygon.length; i < n; ++i) {
11222 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11223 a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11226 if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11228 if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11234 } // Buffer geometry within a polygon and then clip it en masse.
11237 function polygonStart() {
11238 activeStream = bufferStream, segments = [], polygon = [], clean = true;
11241 function polygonEnd() {
11242 var startInside = polygonInside(),
11243 cleanInside = clean && startInside,
11244 visible = (segments = merge(segments)).length;
11246 if (cleanInside || visible) {
11247 stream.polygonStart();
11250 stream.lineStart();
11251 interpolate(null, null, 1, stream);
11256 clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11259 stream.polygonEnd();
11262 activeStream = stream, segments = polygon = ring = null;
11265 function lineStart() {
11266 clipStream.point = linePoint;
11267 if (polygon) polygon.push(ring = []);
11271 } // TODO rather than special-case polygons, simply handle them separately.
11272 // Ideally, coincident intersection points should be jittered to avoid
11273 // clipping issues.
11276 function lineEnd() {
11278 linePoint(x__, y__);
11279 if (v__ && v_) bufferStream.rejoin();
11280 segments.push(bufferStream.result());
11283 clipStream.point = point;
11284 if (v_) activeStream.lineEnd();
11287 function linePoint(x, y) {
11288 var v = visible(x, y);
11289 if (polygon) ring.push([x, y]);
11292 x__ = x, y__ = y, v__ = v;
11296 activeStream.lineStart();
11297 activeStream.point(x, y);
11300 if (v && v_) activeStream.point(x, y);else {
11301 var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11302 b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11304 if (clipLine(a, b, x0, y0, x1, y1)) {
11306 activeStream.lineStart();
11307 activeStream.point(a[0], a[1]);
11310 activeStream.point(b[0], b[1]);
11311 if (!v) activeStream.lineEnd();
11314 activeStream.lineStart();
11315 activeStream.point(x, y);
11321 x_ = x, y_ = y, v_ = v;
11328 var lengthSum, lambda0$2, sinPhi0$1, cosPhi0$1;
11329 var lengthStream = {
11332 lineStart: lengthLineStart,
11334 polygonStart: noop,
11338 function lengthLineStart() {
11339 lengthStream.point = lengthPointFirst;
11340 lengthStream.lineEnd = lengthLineEnd;
11343 function lengthLineEnd() {
11344 lengthStream.point = lengthStream.lineEnd = noop;
11347 function lengthPointFirst(lambda, phi) {
11348 lambda *= radians, phi *= radians;
11349 lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi);
11350 lengthStream.point = lengthPoint;
11353 function lengthPoint(lambda, phi) {
11354 lambda *= radians, phi *= radians;
11355 var sinPhi = sin(phi),
11357 delta = abs$2(lambda - lambda0$2),
11358 cosDelta = cos(delta),
11359 sinDelta = sin(delta),
11360 x = cosPhi * sinDelta,
11361 y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
11362 z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
11363 lengthSum.add(atan2(sqrt$1(x * x + y * y), z));
11364 lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
11367 function d3_geoLength (object) {
11368 lengthSum = new Adder();
11369 d3_geoStream(object, lengthStream);
11373 var identity = (function (x) {
11377 var areaSum$1 = new Adder(),
11378 areaRingSum$1 = new Adder(),
11383 var areaStream$1 = {
11387 polygonStart: function polygonStart() {
11388 areaStream$1.lineStart = areaRingStart$1;
11389 areaStream$1.lineEnd = areaRingEnd$1;
11391 polygonEnd: function polygonEnd() {
11392 areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop;
11393 areaSum$1.add(abs$2(areaRingSum$1));
11394 areaRingSum$1 = new Adder();
11396 result: function result() {
11397 var area = areaSum$1 / 2;
11398 areaSum$1 = new Adder();
11403 function areaRingStart$1() {
11404 areaStream$1.point = areaPointFirst$1;
11407 function areaPointFirst$1(x, y) {
11408 areaStream$1.point = areaPoint$1;
11409 x00 = x0$1 = x, y00 = y0$1 = y;
11412 function areaPoint$1(x, y) {
11413 areaRingSum$1.add(y0$1 * x - x0$1 * y);
11414 x0$1 = x, y0$1 = y;
11417 function areaRingEnd$1() {
11418 areaPoint$1(x00, y00);
11421 var x0$2 = Infinity,
11425 var boundsStream$1 = {
11426 point: boundsPoint$1,
11429 polygonStart: noop,
11431 result: function result() {
11432 var bounds = [[x0$2, y0$2], [x1, y1]];
11433 x1 = y1 = -(y0$2 = x0$2 = Infinity);
11438 function boundsPoint$1(x, y) {
11439 if (x < x0$2) x0$2 = x;
11440 if (x > x1) x1 = x;
11441 if (y < y0$2) y0$2 = y;
11442 if (y > y1) y1 = y;
11458 var centroidStream$1 = {
11459 point: centroidPoint$1,
11460 lineStart: centroidLineStart$1,
11461 lineEnd: centroidLineEnd$1,
11462 polygonStart: function polygonStart() {
11463 centroidStream$1.lineStart = centroidRingStart$1;
11464 centroidStream$1.lineEnd = centroidRingEnd$1;
11466 polygonEnd: function polygonEnd() {
11467 centroidStream$1.point = centroidPoint$1;
11468 centroidStream$1.lineStart = centroidLineStart$1;
11469 centroidStream$1.lineEnd = centroidLineEnd$1;
11471 result: function result() {
11472 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];
11473 X0$1 = Y0$1 = Z0$1 = X1$1 = Y1$1 = Z1$1 = X2$1 = Y2$1 = Z2$1 = 0;
11478 function centroidPoint$1(x, y) {
11484 function centroidLineStart$1() {
11485 centroidStream$1.point = centroidPointFirstLine;
11488 function centroidPointFirstLine(x, y) {
11489 centroidStream$1.point = centroidPointLine;
11490 centroidPoint$1(x0$3 = x, y0$3 = y);
11493 function centroidPointLine(x, y) {
11496 z = sqrt$1(dx * dx + dy * dy);
11497 X1$1 += z * (x0$3 + x) / 2;
11498 Y1$1 += z * (y0$3 + y) / 2;
11500 centroidPoint$1(x0$3 = x, y0$3 = y);
11503 function centroidLineEnd$1() {
11504 centroidStream$1.point = centroidPoint$1;
11507 function centroidRingStart$1() {
11508 centroidStream$1.point = centroidPointFirstRing;
11511 function centroidRingEnd$1() {
11512 centroidPointRing(x00$1, y00$1);
11515 function centroidPointFirstRing(x, y) {
11516 centroidStream$1.point = centroidPointRing;
11517 centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
11520 function centroidPointRing(x, y) {
11523 z = sqrt$1(dx * dx + dy * dy);
11524 X1$1 += z * (x0$3 + x) / 2;
11525 Y1$1 += z * (y0$3 + y) / 2;
11527 z = y0$3 * x - x0$3 * y;
11528 X2$1 += z * (x0$3 + x);
11529 Y2$1 += z * (y0$3 + y);
11531 centroidPoint$1(x0$3 = x, y0$3 = y);
11534 function PathContext(context) {
11535 this._context = context;
11537 PathContext.prototype = {
11539 pointRadius: function pointRadius(_) {
11540 return this._radius = _, this;
11542 polygonStart: function polygonStart() {
11545 polygonEnd: function polygonEnd() {
11548 lineStart: function lineStart() {
11551 lineEnd: function lineEnd() {
11552 if (this._line === 0) this._context.closePath();
11555 point: function point(x, y) {
11556 switch (this._point) {
11559 this._context.moveTo(x, y);
11567 this._context.lineTo(x, y);
11574 this._context.moveTo(x + this._radius, y);
11576 this._context.arc(x, y, this._radius, 0, tau);
11585 var lengthSum$1 = new Adder(),
11591 var lengthStream$1 = {
11593 lineStart: function lineStart() {
11594 lengthStream$1.point = lengthPointFirst$1;
11596 lineEnd: function lineEnd() {
11597 if (lengthRing) lengthPoint$1(x00$2, y00$2);
11598 lengthStream$1.point = noop;
11600 polygonStart: function polygonStart() {
11603 polygonEnd: function polygonEnd() {
11606 result: function result() {
11607 var length = +lengthSum$1;
11608 lengthSum$1 = new Adder();
11613 function lengthPointFirst$1(x, y) {
11614 lengthStream$1.point = lengthPoint$1;
11615 x00$2 = x0$4 = x, y00$2 = y0$4 = y;
11618 function lengthPoint$1(x, y) {
11619 x0$4 -= x, y0$4 -= y;
11620 lengthSum$1.add(sqrt$1(x0$4 * x0$4 + y0$4 * y0$4));
11621 x0$4 = x, y0$4 = y;
11624 function PathString() {
11627 PathString.prototype = {
11629 _circle: circle(4.5),
11630 pointRadius: function pointRadius(_) {
11631 if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11634 polygonStart: function polygonStart() {
11637 polygonEnd: function polygonEnd() {
11640 lineStart: function lineStart() {
11643 lineEnd: function lineEnd() {
11644 if (this._line === 0) this._string.push("Z");
11647 point: function point(x, y) {
11648 switch (this._point) {
11651 this._string.push("M", x, ",", y);
11659 this._string.push("L", x, ",", y);
11666 if (this._circle == null) this._circle = circle(this._radius);
11668 this._string.push("M", x, ",", y, this._circle);
11674 result: function result() {
11675 if (this._string.length) {
11676 var result = this._string.join("");
11686 function circle(radius) {
11687 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11690 function d3_geoPath (projection, context) {
11691 var pointRadius = 4.5,
11695 function path(object) {
11697 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11698 d3_geoStream(object, projectionStream(contextStream));
11701 return contextStream.result();
11704 path.area = function (object) {
11705 d3_geoStream(object, projectionStream(areaStream$1));
11706 return areaStream$1.result();
11709 path.measure = function (object) {
11710 d3_geoStream(object, projectionStream(lengthStream$1));
11711 return lengthStream$1.result();
11714 path.bounds = function (object) {
11715 d3_geoStream(object, projectionStream(boundsStream$1));
11716 return boundsStream$1.result();
11719 path.centroid = function (object) {
11720 d3_geoStream(object, projectionStream(centroidStream$1));
11721 return centroidStream$1.result();
11724 path.projection = function (_) {
11725 return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
11728 path.context = function (_) {
11729 if (!arguments.length) return context;
11730 contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11731 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11735 path.pointRadius = function (_) {
11736 if (!arguments.length) return pointRadius;
11737 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11741 return path.projection(projection).context(context);
11744 function d3_geoTransform (methods) {
11746 stream: transformer(methods)
11749 function transformer(methods) {
11750 return function (stream) {
11751 var s = new TransformStream();
11753 for (var key in methods) {
11754 s[key] = methods[key];
11762 function TransformStream() {}
11764 TransformStream.prototype = {
11765 constructor: TransformStream,
11766 point: function point(x, y) {
11767 this.stream.point(x, y);
11769 sphere: function sphere() {
11770 this.stream.sphere();
11772 lineStart: function lineStart() {
11773 this.stream.lineStart();
11775 lineEnd: function lineEnd() {
11776 this.stream.lineEnd();
11778 polygonStart: function polygonStart() {
11779 this.stream.polygonStart();
11781 polygonEnd: function polygonEnd() {
11782 this.stream.polygonEnd();
11786 function fit(projection, fitBounds, object) {
11787 var clip = projection.clipExtent && projection.clipExtent();
11788 projection.scale(150).translate([0, 0]);
11789 if (clip != null) projection.clipExtent(null);
11790 d3_geoStream(object, projection.stream(boundsStream$1));
11791 fitBounds(boundsStream$1.result());
11792 if (clip != null) projection.clipExtent(clip);
11796 function fitExtent(projection, extent, object) {
11797 return fit(projection, function (b) {
11798 var w = extent[1][0] - extent[0][0],
11799 h = extent[1][1] - extent[0][1],
11800 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11801 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11802 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11803 projection.scale(150 * k).translate([x, y]);
11806 function fitSize(projection, size, object) {
11807 return fitExtent(projection, [[0, 0], size], object);
11809 function fitWidth(projection, width, object) {
11810 return fit(projection, function (b) {
11812 k = w / (b[1][0] - b[0][0]),
11813 x = (w - k * (b[1][0] + b[0][0])) / 2,
11815 projection.scale(150 * k).translate([x, y]);
11818 function fitHeight(projection, height, object) {
11819 return fit(projection, function (b) {
11821 k = h / (b[1][1] - b[0][1]),
11823 y = (h - k * (b[1][1] + b[0][1])) / 2;
11824 projection.scale(150 * k).translate([x, y]);
11829 // maximum depth of subdivision
11830 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11832 function resample (project, delta2) {
11833 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11836 function resampleNone(project) {
11837 return transformer({
11838 point: function point(x, y) {
11840 this.stream.point(x[0], x[1]);
11845 function resample$1(project, delta2) {
11846 function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
11849 d2 = dx * dx + dy * dy;
11851 if (d2 > 4 * delta2 && depth--) {
11855 m = sqrt$1(a * a + b * b + c * c),
11856 phi2 = asin(c /= m),
11857 lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
11858 p = project(lambda2, phi2),
11863 dz = dy * dx2 - dx * dy2;
11865 if (dz * dz / d2 > delta2 // perpendicular projected distance
11866 || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
11867 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
11868 // angular distance
11869 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
11870 stream.point(x2, y2);
11871 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
11876 return function (stream) {
11877 var lambda00, x00, y00, a00, b00, c00, // first point
11878 lambda0, x0, y0, a0, b0, c0; // previous point
11880 var resampleStream = {
11882 lineStart: lineStart,
11884 polygonStart: function polygonStart() {
11885 stream.polygonStart();
11886 resampleStream.lineStart = ringStart;
11888 polygonEnd: function polygonEnd() {
11889 stream.polygonEnd();
11890 resampleStream.lineStart = lineStart;
11894 function point(x, y) {
11896 stream.point(x[0], x[1]);
11899 function lineStart() {
11901 resampleStream.point = linePoint;
11902 stream.lineStart();
11905 function linePoint(lambda, phi) {
11906 var c = cartesian([lambda, phi]),
11907 p = project(lambda, phi);
11908 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);
11909 stream.point(x0, y0);
11912 function lineEnd() {
11913 resampleStream.point = point;
11917 function ringStart() {
11919 resampleStream.point = ringPoint;
11920 resampleStream.lineEnd = ringEnd;
11923 function ringPoint(lambda, phi) {
11924 linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
11925 resampleStream.point = linePoint;
11928 function ringEnd() {
11929 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
11930 resampleStream.lineEnd = lineEnd;
11934 return resampleStream;
11938 var transformRadians = transformer({
11939 point: function point(x, y) {
11940 this.stream.point(x * radians, y * radians);
11944 function transformRotate(rotate) {
11945 return transformer({
11946 point: function point(x, y) {
11947 var r = rotate(x, y);
11948 return this.stream.point(r[0], r[1]);
11953 function scaleTranslate(k, dx, dy, sx, sy) {
11954 function transform(x, y) {
11957 return [dx + k * x, dy - k * y];
11960 transform.invert = function (x, y) {
11961 return [(x - dx) / k * sx, (dy - y) / k * sy];
11967 function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
11968 if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
11969 var cosAlpha = cos(alpha),
11970 sinAlpha = sin(alpha),
11975 ci = (sinAlpha * dy - cosAlpha * dx) / k,
11976 fi = (sinAlpha * dx + cosAlpha * dy) / k;
11978 function transform(x, y) {
11981 return [a * x - b * y + dx, dy - b * x - a * y];
11984 transform.invert = function (x, y) {
11985 return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
11991 function projection(project) {
11992 return projectionMutator(function () {
11996 function projectionMutator(projectAt) {
12012 // post-rotate angle
12018 preclip = clipAntimeridian,
12024 postclip = identity,
12025 // post-clip extent
12030 projectRotateTransform,
12034 function projection(point) {
12035 return projectRotateTransform(point[0] * radians, point[1] * radians);
12038 function invert(point) {
12039 point = projectRotateTransform.invert(point[0], point[1]);
12040 return point && [point[0] * degrees, point[1] * degrees];
12043 projection.stream = function (stream) {
12044 return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12047 projection.preclip = function (_) {
12048 return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12051 projection.postclip = function (_) {
12052 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12055 projection.clipAngle = function (_) {
12056 return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
12059 projection.clipExtent = function (_) {
12060 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]];
12063 projection.scale = function (_) {
12064 return arguments.length ? (k = +_, recenter()) : k;
12067 projection.translate = function (_) {
12068 return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12071 projection.center = function (_) {
12072 return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
12075 projection.rotate = function (_) {
12076 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];
12079 projection.angle = function (_) {
12080 return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
12083 projection.reflectX = function (_) {
12084 return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12087 projection.reflectY = function (_) {
12088 return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12091 projection.precision = function (_) {
12092 return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2);
12095 projection.fitExtent = function (extent, object) {
12096 return fitExtent(projection, extent, object);
12099 projection.fitSize = function (size, object) {
12100 return fitSize(projection, size, object);
12103 projection.fitWidth = function (width, object) {
12104 return fitWidth(projection, width, object);
12107 projection.fitHeight = function (height, object) {
12108 return fitHeight(projection, height, object);
12111 function recenter() {
12112 var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12113 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12114 rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12115 projectTransform = compose(project, transform);
12116 projectRotateTransform = compose(rotate, projectTransform);
12117 projectResample = resample(projectTransform, delta2);
12122 cache = cacheStream = null;
12126 return function () {
12127 project = projectAt.apply(this, arguments);
12128 projection.invert = project.invert && invert;
12133 function mercatorRaw(lambda, phi) {
12134 return [lambda, log$1(tan((halfPi + phi) / 2))];
12137 mercatorRaw.invert = function (x, y) {
12138 return [x, 2 * atan(exp(y)) - halfPi];
12141 function mercator () {
12142 return mercatorProjection(mercatorRaw).scale(961 / tau);
12144 function mercatorProjection(project) {
12145 var m = projection(project),
12148 translate = m.translate,
12149 clipExtent = m.clipExtent,
12155 m.scale = function (_) {
12156 return arguments.length ? (scale(_), reclip()) : scale();
12159 m.translate = function (_) {
12160 return arguments.length ? (translate(_), reclip()) : translate();
12163 m.center = function (_) {
12164 return arguments.length ? (center(_), reclip()) : center();
12167 m.clipExtent = function (_) {
12168 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]];
12171 function reclip() {
12172 var k = pi * scale(),
12173 t = m(rotation(m.rotate()).invert([0, 0]));
12174 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)]]);
12180 function d3_geoIdentity () {
12186 // scale, translate and reflect
12198 transform = transformer({
12199 point: function point(x, y) {
12200 var p = projection([x, y]);
12201 this.stream.point(p[0], p[1]);
12204 postclip = identity,
12211 cache = cacheStream = null;
12215 function projection(p) {
12220 var t = y * ca - x * sa;
12221 x = x * ca + y * sa;
12225 return [x + tx, y + ty];
12228 projection.invert = function (p) {
12233 var t = y * ca + x * sa;
12234 x = x * ca - y * sa;
12238 return [x / kx, y / ky];
12241 projection.stream = function (stream) {
12242 return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12245 projection.postclip = function (_) {
12246 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12249 projection.clipExtent = function (_) {
12250 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]];
12253 projection.scale = function (_) {
12254 return arguments.length ? (k = +_, reset()) : k;
12257 projection.translate = function (_) {
12258 return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12261 projection.angle = function (_) {
12262 return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
12265 projection.reflectX = function (_) {
12266 return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12269 projection.reflectY = function (_) {
12270 return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12273 projection.fitExtent = function (extent, object) {
12274 return fitExtent(projection, extent, object);
12277 projection.fitSize = function (size, object) {
12278 return fitSize(projection, size, object);
12281 projection.fitWidth = function (width, object) {
12282 return fitWidth(projection, width, object);
12285 projection.fitHeight = function (height, object) {
12286 return fitHeight(projection, height, object);
12293 var TAU = 2 * Math.PI;
12294 var EQUATORIAL_RADIUS = 6356752.314245179;
12295 var POLAR_RADIUS = 6378137.0;
12296 function geoLatToMeters(dLat) {
12297 return dLat * (TAU * POLAR_RADIUS / 360);
12299 function geoLonToMeters(dLon, atLat) {
12300 return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12302 function geoMetersToLat(m) {
12303 return m / (TAU * POLAR_RADIUS / 360);
12305 function geoMetersToLon(m, atLat) {
12306 return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12308 function geoMetersToOffset(meters, tileSize) {
12309 tileSize = tileSize || 256;
12310 return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS)];
12312 function geoOffsetToMeters(offset, tileSize) {
12313 tileSize = tileSize || 256;
12314 return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS / tileSize];
12315 } // Equirectangular approximation of spherical distances on Earth
12317 function geoSphericalDistance(a, b) {
12318 var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12319 var y = geoLatToMeters(a[1] - b[1]);
12320 return Math.sqrt(x * x + y * y);
12323 function geoScaleToZoom(k, tileSize) {
12324 tileSize = tileSize || 256;
12325 var log2ts = Math.log(tileSize) * Math.LOG2E;
12326 return Math.log(k * TAU) / Math.LN2 - log2ts;
12329 function geoZoomToScale(z, tileSize) {
12330 tileSize = tileSize || 256;
12331 return tileSize * Math.pow(2, z) / TAU;
12332 } // returns info about the node from `nodes` closest to the given `point`
12334 function geoSphericalClosestNode(nodes, point) {
12335 var minDistance = Infinity,
12339 for (var i in nodes) {
12340 distance = geoSphericalDistance(nodes[i].loc, point);
12342 if (distance < minDistance) {
12343 minDistance = distance;
12348 if (indexOfMin !== undefined) {
12351 distance: minDistance,
12352 node: nodes[indexOfMin]
12359 function geoExtent(min, max) {
12360 if (!(this instanceof geoExtent)) {
12361 return new geoExtent(min, max);
12362 } else if (min instanceof geoExtent) {
12364 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12368 this[0] = min || [Infinity, Infinity];
12369 this[1] = max || min || [-Infinity, -Infinity];
12372 geoExtent.prototype = new Array(2);
12373 Object.assign(geoExtent.prototype, {
12374 equals: function equals(obj) {
12375 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];
12377 extend: function extend(obj) {
12378 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12379 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])]);
12381 _extend: function _extend(extent) {
12382 this[0][0] = Math.min(extent[0][0], this[0][0]);
12383 this[0][1] = Math.min(extent[0][1], this[0][1]);
12384 this[1][0] = Math.max(extent[1][0], this[1][0]);
12385 this[1][1] = Math.max(extent[1][1], this[1][1]);
12387 area: function area() {
12388 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12390 center: function center() {
12391 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12393 rectangle: function rectangle() {
12394 return [this[0][0], this[0][1], this[1][0], this[1][1]];
12396 bbox: function bbox() {
12404 polygon: function polygon() {
12405 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]]];
12407 contains: function contains(obj) {
12408 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12409 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];
12411 intersects: function intersects(obj) {
12412 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12413 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];
12415 intersection: function intersection(obj) {
12416 if (!this.intersects(obj)) return new geoExtent();
12417 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])]);
12419 percentContainedIn: function percentContainedIn(obj) {
12420 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12421 var a1 = this.intersection(obj).area();
12422 var a2 = this.area();
12424 if (a1 === Infinity || a2 === Infinity) {
12426 } else if (a1 === 0 || a2 === 0) {
12427 if (obj.contains(this)) {
12436 padByMeters: function padByMeters(meters) {
12437 var dLat = geoMetersToLat(meters);
12438 var dLon = geoMetersToLon(meters, this.center()[1]);
12439 return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12441 toParam: function toParam() {
12442 return this.rectangle().join(',');
12446 var $every$1 = arrayIteration.every;
12450 var STRICT_METHOD$6 = arrayMethodIsStrict('every');
12451 var USES_TO_LENGTH$8 = arrayMethodUsesToLength('every');
12453 // `Array.prototype.every` method
12454 // https://tc39.github.io/ecma262/#sec-array.prototype.every
12455 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$6 || !USES_TO_LENGTH$8 }, {
12456 every: function every(callbackfn /* , thisArg */) {
12457 return $every$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12461 var $reduce$1 = arrayReduce.left;
12467 var STRICT_METHOD$7 = arrayMethodIsStrict('reduce');
12468 var USES_TO_LENGTH$9 = arrayMethodUsesToLength('reduce', { 1: 0 });
12469 // Chrome 80-82 has a critical bug
12470 // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
12471 var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
12473 // `Array.prototype.reduce` method
12474 // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
12475 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$7 || !USES_TO_LENGTH$9 || CHROME_BUG }, {
12476 reduce: function reduce(callbackfn /* , initialValue */) {
12477 return $reduce$1(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12481 function d3_polygonArea (polygon) {
12483 n = polygon.length,
12485 b = polygon[n - 1],
12491 area += a[1] * b[0] - a[0] * b[1];
12497 function d3_polygonCentroid (polygon) {
12499 n = polygon.length,
12503 b = polygon[n - 1],
12510 k += c = a[0] * b[1] - b[0] * a[1];
12511 x += (a[0] + b[0]) * c;
12512 y += (a[1] + b[1]) * c;
12515 return k *= 3, [x / k, y / k];
12518 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12519 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12520 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12521 // negative if clockwise, and zero if the points are collinear.
12522 function cross (a, b, c) {
12523 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12526 function lexicographicOrder(a, b) {
12527 return a[0] - b[0] || a[1] - b[1];
12528 } // Computes the upper convex hull per the monotone chain algorithm.
12529 // Assumes points.length >= 3, is sorted by x, unique in y.
12530 // Returns an array of indices into points in left-to-right order.
12533 function computeUpperHullIndexes(points) {
12534 var n = points.length,
12539 for (i = 2; i < n; ++i) {
12540 while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12544 indexes[size++] = i;
12547 return indexes.slice(0, size); // remove popped points
12550 function d3_polygonHull (points) {
12551 if ((n = points.length) < 3) return null;
12554 sortedPoints = new Array(n),
12555 flippedPoints = new Array(n);
12557 for (i = 0; i < n; ++i) {
12558 sortedPoints[i] = [+points[i][0], +points[i][1], i];
12561 sortedPoints.sort(lexicographicOrder);
12563 for (i = 0; i < n; ++i) {
12564 flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12567 var upperIndexes = computeUpperHullIndexes(sortedPoints),
12568 lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12570 var skipLeft = lowerIndexes[0] === upperIndexes[0],
12571 skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12572 hull = []; // Add upper hull in right-to-l order.
12573 // Then add lower hull in left-to-right order.
12575 for (i = upperIndexes.length - 1; i >= 0; --i) {
12576 hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12579 for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12580 hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12587 function geoVecEqual(a, b, epsilon) {
12589 return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12591 return a[0] === b[0] && a[1] === b[1];
12593 } // vector addition
12595 function geoVecAdd(a, b) {
12596 return [a[0] + b[0], a[1] + b[1]];
12597 } // vector subtraction
12599 function geoVecSubtract(a, b) {
12600 return [a[0] - b[0], a[1] - b[1]];
12601 } // vector scaling
12603 function geoVecScale(a, mag) {
12604 return [a[0] * mag, a[1] * mag];
12605 } // vector rounding (was: geoRoundCoordinates)
12607 function geoVecFloor(a) {
12608 return [Math.floor(a[0]), Math.floor(a[1])];
12609 } // linear interpolation
12611 function geoVecInterp(a, b, t) {
12612 return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12613 } // http://jsperf.com/id-dist-optimization
12615 function geoVecLength(a, b) {
12616 return Math.sqrt(geoVecLengthSquare(a, b));
12617 } // length of vector raised to the power two
12619 function geoVecLengthSquare(a, b) {
12621 var x = a[0] - b[0];
12622 var y = a[1] - b[1];
12623 return x * x + y * y;
12624 } // get a unit vector
12626 function geoVecNormalize(a) {
12627 var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12629 if (length !== 0) {
12630 return geoVecScale(a, 1 / length);
12634 } // Return the counterclockwise angle in the range (-pi, pi)
12635 // between the positive X axis and the line intersecting a and b.
12637 function geoVecAngle(a, b) {
12638 return Math.atan2(b[1] - a[1], b[0] - a[0]);
12641 function geoVecDot(a, b, origin) {
12642 origin = origin || [0, 0];
12643 var p = geoVecSubtract(a, origin);
12644 var q = geoVecSubtract(b, origin);
12645 return p[0] * q[0] + p[1] * q[1];
12646 } // normalized dot product
12648 function geoVecNormalizedDot(a, b, origin) {
12649 origin = origin || [0, 0];
12650 var p = geoVecNormalize(geoVecSubtract(a, origin));
12651 var q = geoVecNormalize(geoVecSubtract(b, origin));
12652 return geoVecDot(p, q);
12653 } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12654 // Returns a positive value, if OAB makes a counter-clockwise turn,
12655 // negative for clockwise turn, and zero if the points are collinear.
12657 function geoVecCross(a, b, origin) {
12658 origin = origin || [0, 0];
12659 var p = geoVecSubtract(a, origin);
12660 var q = geoVecSubtract(b, origin);
12661 return p[0] * q[1] - p[1] * q[0];
12662 } // find closest orthogonal projection of point onto points array
12664 function geoVecProject(a, points) {
12665 var min = Infinity;
12669 for (var i = 0; i < points.length - 1; i++) {
12671 var s = geoVecSubtract(points[i + 1], o);
12672 var v = geoVecSubtract(a, o);
12673 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12678 } else if (proj > 1) {
12681 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12684 var dist = geoVecLength(p, a);
12693 if (idx !== undefined) {
12704 // between the positive X axis and the line intersecting a and b.
12706 function geoAngle(a, b, projection) {
12707 return geoVecAngle(projection(a.loc), projection(b.loc));
12709 function geoEdgeEqual(a, b) {
12710 return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12711 } // Rotate all points counterclockwise around a pivot point by given angle
12713 function geoRotate(points, angle, around) {
12714 return points.map(function (point) {
12715 var radial = geoVecSubtract(point, around);
12716 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]];
12718 } // Choose the edge with the minimal distance from `point` to its orthogonal
12719 // projection onto that edge, if such a projection exists, or the distance to
12720 // the closest vertex on that edge. Returns an object with the `index` of the
12721 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12723 function geoChooseEdge(nodes, point, projection, activeID) {
12724 var dist = geoVecLength;
12725 var points = nodes.map(function (n) {
12726 return projection(n.loc);
12728 var ids = nodes.map(function (n) {
12731 var min = Infinity;
12735 for (var i = 0; i < points.length - 1; i++) {
12736 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12738 var s = geoVecSubtract(points[i + 1], o);
12739 var v = geoVecSubtract(point, o);
12740 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12745 } else if (proj > 1) {
12748 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12751 var d = dist(p, point);
12756 loc = projection.invert(p);
12760 if (idx !== undefined) {
12769 } // Test active (dragged or drawing) segments against inactive segments
12770 // This is used to test e.g. multipolygon rings that cross
12771 // `activeNodes` is the ring containing the activeID being dragged.
12772 // `inactiveNodes` is the other ring to test against
12774 function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12776 var inactives = [];
12777 var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12779 for (j = 0; j < activeNodes.length - 1; j++) {
12780 n1 = activeNodes[j];
12781 n2 = activeNodes[j + 1];
12782 segment = [n1.loc, n2.loc];
12784 if (n1.id === activeID || n2.id === activeID) {
12785 actives.push(segment);
12787 } // gather inactive segments
12790 for (j = 0; j < inactiveNodes.length - 1; j++) {
12791 n1 = inactiveNodes[j];
12792 n2 = inactiveNodes[j + 1];
12793 segment = [n1.loc, n2.loc];
12794 inactives.push(segment);
12798 for (j = 0; j < actives.length; j++) {
12799 for (k = 0; k < inactives.length; k++) {
12800 var p = actives[j];
12801 var q = inactives[k];
12802 var hit = geoLineIntersection(p, q);
12811 } // Test active (dragged or drawing) segments against inactive segments
12812 // This is used to test whether a way intersects with itself.
12814 function geoHasSelfIntersections(nodes, activeID) {
12816 var inactives = [];
12817 var j, k; // group active and passive segments along the nodes
12819 for (j = 0; j < nodes.length - 1; j++) {
12821 var n2 = nodes[j + 1];
12822 var segment = [n1.loc, n2.loc];
12824 if (n1.id === activeID || n2.id === activeID) {
12825 actives.push(segment);
12827 inactives.push(segment);
12832 for (j = 0; j < actives.length; j++) {
12833 for (k = 0; k < inactives.length; k++) {
12834 var p = actives[j];
12835 var q = inactives[k]; // skip if segments share an endpoint
12837 if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
12841 var hit = geoLineIntersection(p, q);
12844 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
12846 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
12856 } // Return the intersection point of 2 line segments.
12857 // From https://github.com/pgkelley4/line-segments-intersect
12858 // This uses the vector cross product approach described below:
12859 // http://stackoverflow.com/a/565282/786339
12861 function geoLineIntersection(a, b) {
12862 var p = [a[0][0], a[0][1]];
12863 var p2 = [a[1][0], a[1][1]];
12864 var q = [b[0][0], b[0][1]];
12865 var q2 = [b[1][0], b[1][1]];
12866 var r = geoVecSubtract(p2, p);
12867 var s = geoVecSubtract(q2, q);
12868 var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
12869 var denominator = geoVecCross(r, s);
12871 if (uNumerator && denominator) {
12872 var u = uNumerator / denominator;
12873 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
12875 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
12876 return geoVecInterp(p, p2, t);
12882 function geoPathIntersections(path1, path2) {
12883 var intersections = [];
12885 for (var i = 0; i < path1.length - 1; i++) {
12886 for (var j = 0; j < path2.length - 1; j++) {
12887 var a = [path1[i], path1[i + 1]];
12888 var b = [path2[j], path2[j + 1]];
12889 var hit = geoLineIntersection(a, b);
12892 intersections.push(hit);
12897 return intersections;
12899 function geoPathHasIntersections(path1, path2) {
12900 for (var i = 0; i < path1.length - 1; i++) {
12901 for (var j = 0; j < path2.length - 1; j++) {
12902 var a = [path1[i], path1[i + 1]];
12903 var b = [path2[j], path2[j + 1]];
12904 var hit = geoLineIntersection(a, b);
12913 } // Return whether point is contained in polygon.
12915 // `point` should be a 2-item array of coordinates.
12916 // `polygon` should be an array of 2-item arrays of coordinates.
12918 // From https://github.com/substack/point-in-polygon.
12919 // ray-casting algorithm based on
12920 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
12923 function geoPointInPolygon(point, polygon) {
12926 var inside = false;
12928 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
12929 var xi = polygon[i][0];
12930 var yi = polygon[i][1];
12931 var xj = polygon[j][0];
12932 var yj = polygon[j][1];
12933 var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
12934 if (intersect) inside = !inside;
12939 function geoPolygonContainsPolygon(outer, inner) {
12940 return inner.every(function (point) {
12941 return geoPointInPolygon(point, outer);
12944 function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
12945 function testPoints(outer, inner) {
12946 return inner.some(function (point) {
12947 return geoPointInPolygon(point, outer);
12951 return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
12952 } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
12953 // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
12955 function geoGetSmallestSurroundingRectangle(points) {
12956 var hull = d3_polygonHull(points);
12957 var centroid = d3_polygonCentroid(hull);
12958 var minArea = Infinity;
12959 var ssrExtent = [];
12963 for (var i = 0; i <= hull.length - 1; i++) {
12964 var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
12965 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
12966 var poly = geoRotate(hull, -angle, centroid);
12967 var extent = poly.reduce(function (extent, point) {
12968 return extent.extend(geoExtent(point));
12970 var area = extent.area();
12972 if (area < minArea) {
12974 ssrExtent = extent;
12982 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
12986 function geoPathLength(path) {
12989 for (var i = 0; i < path.length - 1; i++) {
12990 length += geoVecLength(path[i], path[i + 1]);
12994 } // If the given point is at the edge of the padded viewport,
12995 // return a vector that will nudge the viewport in that direction
12997 function geoViewportEdge(point, dimensions) {
12998 var pad = [80, 20, 50, 20]; // top, right, bottom, left
13002 if (point[0] > dimensions[0] - pad[1]) x = -10;
13003 if (point[0] < pad[3]) x = 10;
13004 if (point[1] > dimensions[1] - pad[2]) y = -10;
13005 if (point[1] < pad[0]) y = 10;
13015 value: function value() {}
13018 function dispatch() {
13019 for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
13020 if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
13024 return new Dispatch$1(_);
13027 function Dispatch$1(_) {
13031 function parseTypenames(typenames, types) {
13032 return typenames.trim().split(/^|\s+/).map(function (t) {
13034 i = t.indexOf(".");
13035 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13036 if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13044 Dispatch$1.prototype = dispatch.prototype = {
13045 constructor: Dispatch$1,
13046 on: function on(typename, callback) {
13048 T = parseTypenames(typename + "", _),
13051 n = T.length; // If no callback was specified, return the callback of the given type and name.
13053 if (arguments.length < 2) {
13055 if ((t = (typename = T[i]).type) && (t = get$3(_[t], typename.name))) return t;
13059 } // If a type was specified, set the callback for the given type and name.
13060 // Otherwise, if a null callback was specified, remove callbacks of the given name.
13063 if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13066 if (t = (typename = T[i]).type) _[t] = set$3(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13067 _[t] = set$3(_[t], typename.name, null);
13073 copy: function copy() {
13078 copy[t] = _[t].slice();
13081 return new Dispatch$1(copy);
13083 call: function call(type, that) {
13084 if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13085 args[i] = arguments[i + 2];
13087 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13089 for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13090 t[i].value.apply(that, args);
13093 apply: function apply(type, that, args) {
13094 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13096 for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13097 t[i].value.apply(that, args);
13102 function get$3(type, name) {
13103 for (var i = 0, n = type.length, c; i < n; ++i) {
13104 if ((c = type[i]).name === name) {
13110 function set$3(type, name, callback) {
13111 for (var i = 0, n = type.length; i < n; ++i) {
13112 if (type[i].name === name) {
13113 type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1));
13118 if (callback != null) type.push({
13125 var xhtml = "http://www.w3.org/1999/xhtml";
13127 svg: "http://www.w3.org/2000/svg",
13129 xlink: "http://www.w3.org/1999/xlink",
13130 xml: "http://www.w3.org/XML/1998/namespace",
13131 xmlns: "http://www.w3.org/2000/xmlns/"
13134 function namespace (name) {
13135 var prefix = name += "",
13136 i = prefix.indexOf(":");
13137 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13138 return namespaces.hasOwnProperty(prefix) ? {
13139 space: namespaces[prefix],
13141 } : name; // eslint-disable-line no-prototype-builtins
13144 function creatorInherit(name) {
13145 return function () {
13146 var document = this.ownerDocument,
13147 uri = this.namespaceURI;
13148 return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13152 function creatorFixed(fullname) {
13153 return function () {
13154 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13158 function creator (name) {
13159 var fullname = namespace(name);
13160 return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13165 function selector (selector) {
13166 return selector == null ? none : function () {
13167 return this.querySelector(selector);
13171 function selection_select (select) {
13172 if (typeof select !== "function") select = selector(select);
13174 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13175 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13176 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13177 if ("__data__" in node) subnode.__data__ = node.__data__;
13178 subgroup[i] = subnode;
13183 return new Selection(subgroups, this._parents);
13186 function array (x) {
13187 return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13188 : Array.from(x); // Map, Set, iterable, string, or anything else
13195 function selectorAll (selector) {
13196 return selector == null ? empty : function () {
13197 return this.querySelectorAll(selector);
13201 function arrayAll(select) {
13202 return function () {
13203 var group = select.apply(this, arguments);
13204 return group == null ? [] : array(group);
13208 function selection_selectAll (select) {
13209 if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13211 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13212 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13213 if (node = group[i]) {
13214 subgroups.push(select.call(node, node.__data__, i, group));
13215 parents.push(node);
13220 return new Selection(subgroups, parents);
13223 var $find$1 = arrayIteration.find;
13228 var SKIPS_HOLES = true;
13230 var USES_TO_LENGTH$a = arrayMethodUsesToLength(FIND);
13232 // Shouldn't skip holes
13233 if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES = false; });
13235 // `Array.prototype.find` method
13236 // https://tc39.github.io/ecma262/#sec-array.prototype.find
13237 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES || !USES_TO_LENGTH$a }, {
13238 find: function find(callbackfn /* , that = undefined */) {
13239 return $find$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13243 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
13244 addToUnscopables(FIND);
13246 function matcher (selector) {
13247 return function () {
13248 return this.matches(selector);
13251 function childMatcher(selector) {
13252 return function (node) {
13253 return node.matches(selector);
13257 var find$1 = Array.prototype.find;
13259 function childFind(match) {
13260 return function () {
13261 return find$1.call(this.children, match);
13265 function childFirst() {
13266 return this.firstElementChild;
13269 function selection_selectChild (match) {
13270 return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13273 var filter = Array.prototype.filter;
13275 function children() {
13276 return this.children;
13279 function childrenFilter(match) {
13280 return function () {
13281 return filter.call(this.children, match);
13285 function selection_selectChildren (match) {
13286 return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13289 function selection_filter (match) {
13290 if (typeof match !== "function") match = matcher(match);
13292 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13293 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13294 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13295 subgroup.push(node);
13300 return new Selection(subgroups, this._parents);
13303 function sparse (update) {
13304 return new Array(update.length);
13307 function selection_enter () {
13308 return new Selection(this._enter || this._groups.map(sparse), this._parents);
13310 function EnterNode(parent, datum) {
13311 this.ownerDocument = parent.ownerDocument;
13312 this.namespaceURI = parent.namespaceURI;
13314 this._parent = parent;
13315 this.__data__ = datum;
13317 EnterNode.prototype = {
13318 constructor: EnterNode,
13319 appendChild: function appendChild(child) {
13320 return this._parent.insertBefore(child, this._next);
13322 insertBefore: function insertBefore(child, next) {
13323 return this._parent.insertBefore(child, next);
13325 querySelector: function querySelector(selector) {
13326 return this._parent.querySelector(selector);
13328 querySelectorAll: function querySelectorAll(selector) {
13329 return this._parent.querySelectorAll(selector);
13333 function constant (x) {
13334 return function () {
13339 function bindIndex(parent, group, enter, update, exit, data) {
13342 groupLength = group.length,
13343 dataLength = data.length; // Put any non-null nodes that fit into update.
13344 // Put any null nodes into enter.
13345 // Put any remaining data into enter.
13347 for (; i < dataLength; ++i) {
13348 if (node = group[i]) {
13349 node.__data__ = data[i];
13352 enter[i] = new EnterNode(parent, data[i]);
13354 } // Put any non-null nodes that don’t fit into exit.
13357 for (; i < groupLength; ++i) {
13358 if (node = group[i]) {
13364 function bindKey(parent, group, enter, update, exit, data, key) {
13367 nodeByKeyValue = new Map(),
13368 groupLength = group.length,
13369 dataLength = data.length,
13370 keyValues = new Array(groupLength),
13371 keyValue; // Compute the key for each node.
13372 // If multiple nodes have the same key, the duplicates are added to exit.
13374 for (i = 0; i < groupLength; ++i) {
13375 if (node = group[i]) {
13376 keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13378 if (nodeByKeyValue.has(keyValue)) {
13381 nodeByKeyValue.set(keyValue, node);
13384 } // Compute the key for each datum.
13385 // If there a node associated with this key, join and add it to update.
13386 // If there is not (or the key is a duplicate), add it to enter.
13389 for (i = 0; i < dataLength; ++i) {
13390 keyValue = key.call(parent, data[i], i, data) + "";
13392 if (node = nodeByKeyValue.get(keyValue)) {
13394 node.__data__ = data[i];
13395 nodeByKeyValue["delete"](keyValue);
13397 enter[i] = new EnterNode(parent, data[i]);
13399 } // Add any remaining nodes that were not bound to data to exit.
13402 for (i = 0; i < groupLength; ++i) {
13403 if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13409 function datum(node) {
13410 return node.__data__;
13413 function selection_data (value, key) {
13414 if (!arguments.length) return Array.from(this, datum);
13415 var bind = key ? bindKey : bindIndex,
13416 parents = this._parents,
13417 groups = this._groups;
13418 if (typeof value !== "function") value = constant(value);
13420 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13421 var parent = parents[j],
13423 groupLength = group.length,
13424 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13425 dataLength = data.length,
13426 enterGroup = enter[j] = new Array(dataLength),
13427 updateGroup = update[j] = new Array(dataLength),
13428 exitGroup = exit[j] = new Array(groupLength);
13429 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13430 // appendChild can insert the materialized enter node before this node,
13431 // rather than at the end of the parent node.
13433 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13434 if (previous = enterGroup[i0]) {
13435 if (i0 >= i1) i1 = i0 + 1;
13437 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13440 previous._next = next || null;
13445 update = new Selection(update, parents);
13446 update._enter = enter;
13447 update._exit = exit;
13451 function selection_exit () {
13452 return new Selection(this._exit || this._groups.map(sparse), this._parents);
13455 function selection_join (onenter, onupdate, onexit) {
13456 var enter = this.enter(),
13458 exit = this.exit();
13459 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13460 if (onupdate != null) update = onupdate(update);
13461 if (onexit == null) exit.remove();else onexit(exit);
13462 return enter && update ? enter.merge(update).order() : update;
13465 function selection_merge (selection) {
13466 if (!(selection instanceof Selection)) throw new Error("invalid merge");
13468 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) {
13469 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13470 if (node = group0[i] || group1[i]) {
13476 for (; j < m0; ++j) {
13477 merges[j] = groups0[j];
13480 return new Selection(merges, this._parents);
13483 function selection_order () {
13484 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13485 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13486 if (node = group[i]) {
13487 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13496 function selection_sort (compare) {
13497 if (!compare) compare = ascending;
13499 function compareNode(a, b) {
13500 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13503 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13504 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13505 if (node = group[i]) {
13506 sortgroup[i] = node;
13510 sortgroup.sort(compareNode);
13513 return new Selection(sortgroups, this._parents).order();
13516 function ascending(a, b) {
13517 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13520 function selection_call () {
13521 var callback = arguments[0];
13522 arguments[0] = this;
13523 callback.apply(null, arguments);
13527 function selection_nodes () {
13528 return Array.from(this);
13531 function selection_node () {
13532 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13533 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13534 var node = group[i];
13535 if (node) return node;
13542 function selection_size () {
13545 var _iterator = _createForOfIteratorHelper(this),
13549 for (_iterator.s(); !(_step = _iterator.n()).done;) {
13550 var node = _step.value;
13552 } // eslint-disable-line no-unused-vars
13563 function selection_empty () {
13564 return !this.node();
13567 function selection_each (callback) {
13568 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13569 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13570 if (node = group[i]) callback.call(node, node.__data__, i, group);
13577 function attrRemove(name) {
13578 return function () {
13579 this.removeAttribute(name);
13583 function attrRemoveNS(fullname) {
13584 return function () {
13585 this.removeAttributeNS(fullname.space, fullname.local);
13589 function attrConstant(name, value) {
13590 return function () {
13591 this.setAttribute(name, value);
13595 function attrConstantNS(fullname, value) {
13596 return function () {
13597 this.setAttributeNS(fullname.space, fullname.local, value);
13601 function attrFunction(name, value) {
13602 return function () {
13603 var v = value.apply(this, arguments);
13604 if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13608 function attrFunctionNS(fullname, value) {
13609 return function () {
13610 var v = value.apply(this, arguments);
13611 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13615 function selection_attr (name, value) {
13616 var fullname = namespace(name);
13618 if (arguments.length < 2) {
13619 var node = this.node();
13620 return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13623 return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value));
13626 function defaultView (node) {
13627 return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13628 node.document && node // node is a Window
13629 || node.defaultView; // node is a Document
13632 function styleRemove(name) {
13633 return function () {
13634 this.style.removeProperty(name);
13638 function styleConstant(name, value, priority) {
13639 return function () {
13640 this.style.setProperty(name, value, priority);
13644 function styleFunction(name, value, priority) {
13645 return function () {
13646 var v = value.apply(this, arguments);
13647 if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13651 function selection_style (name, value, priority) {
13652 return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13654 function styleValue(node, name) {
13655 return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13658 function propertyRemove(name) {
13659 return function () {
13664 function propertyConstant(name, value) {
13665 return function () {
13666 this[name] = value;
13670 function propertyFunction(name, value) {
13671 return function () {
13672 var v = value.apply(this, arguments);
13673 if (v == null) delete this[name];else this[name] = v;
13677 function selection_property (name, value) {
13678 return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13681 function classArray(string) {
13682 return string.trim().split(/^|\s+/);
13685 function classList(node) {
13686 return node.classList || new ClassList(node);
13689 function ClassList(node) {
13691 this._names = classArray(node.getAttribute("class") || "");
13694 ClassList.prototype = {
13695 add: function add(name) {
13696 var i = this._names.indexOf(name);
13699 this._names.push(name);
13701 this._node.setAttribute("class", this._names.join(" "));
13704 remove: function remove(name) {
13705 var i = this._names.indexOf(name);
13708 this._names.splice(i, 1);
13710 this._node.setAttribute("class", this._names.join(" "));
13713 contains: function contains(name) {
13714 return this._names.indexOf(name) >= 0;
13718 function classedAdd(node, names) {
13719 var list = classList(node),
13724 list.add(names[i]);
13728 function classedRemove(node, names) {
13729 var list = classList(node),
13734 list.remove(names[i]);
13738 function classedTrue(names) {
13739 return function () {
13740 classedAdd(this, names);
13744 function classedFalse(names) {
13745 return function () {
13746 classedRemove(this, names);
13750 function classedFunction(names, value) {
13751 return function () {
13752 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13756 function selection_classed (name, value) {
13757 var names = classArray(name + "");
13759 if (arguments.length < 2) {
13760 var list = classList(this.node()),
13765 if (!list.contains(names[i])) return false;
13771 return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13774 function textRemove() {
13775 this.textContent = "";
13778 function textConstant(value) {
13779 return function () {
13780 this.textContent = value;
13784 function textFunction(value) {
13785 return function () {
13786 var v = value.apply(this, arguments);
13787 this.textContent = v == null ? "" : v;
13791 function selection_text (value) {
13792 return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent;
13795 function htmlRemove() {
13796 this.innerHTML = "";
13799 function htmlConstant(value) {
13800 return function () {
13801 this.innerHTML = value;
13805 function htmlFunction(value) {
13806 return function () {
13807 var v = value.apply(this, arguments);
13808 this.innerHTML = v == null ? "" : v;
13812 function selection_html (value) {
13813 return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13817 if (this.nextSibling) this.parentNode.appendChild(this);
13820 function selection_raise () {
13821 return this.each(raise);
13825 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13828 function selection_lower () {
13829 return this.each(lower);
13832 function selection_append (name) {
13833 var create = typeof name === "function" ? name : creator(name);
13834 return this.select(function () {
13835 return this.appendChild(create.apply(this, arguments));
13839 function constantNull() {
13843 function selection_insert (name, before) {
13844 var create = typeof name === "function" ? name : creator(name),
13845 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
13846 return this.select(function () {
13847 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
13851 function remove() {
13852 var parent = this.parentNode;
13853 if (parent) parent.removeChild(this);
13856 function selection_remove () {
13857 return this.each(remove);
13860 function selection_cloneShallow() {
13861 var clone = this.cloneNode(false),
13862 parent = this.parentNode;
13863 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13866 function selection_cloneDeep() {
13867 var clone = this.cloneNode(true),
13868 parent = this.parentNode;
13869 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13872 function selection_clone (deep) {
13873 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
13876 function selection_datum (value) {
13877 return arguments.length ? this.property("__data__", value) : this.node().__data__;
13880 function contextListener(listener) {
13881 return function (event) {
13882 listener.call(this, event, this.__data__);
13886 function parseTypenames$1(typenames) {
13887 return typenames.trim().split(/^|\s+/).map(function (t) {
13889 i = t.indexOf(".");
13890 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13898 function onRemove(typename) {
13899 return function () {
13900 var on = this.__on;
13903 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
13904 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
13905 this.removeEventListener(o.type, o.listener, o.options);
13911 if (++i) on.length = i;else delete this.__on;
13915 function onAdd(typename, value, options) {
13916 return function () {
13917 var on = this.__on,
13919 listener = contextListener(value);
13920 if (on) for (var j = 0, m = on.length; j < m; ++j) {
13921 if ((o = on[j]).type === typename.type && o.name === typename.name) {
13922 this.removeEventListener(o.type, o.listener, o.options);
13923 this.addEventListener(o.type, o.listener = listener, o.options = options);
13928 this.addEventListener(typename.type, listener, options);
13930 type: typename.type,
13931 name: typename.name,
13933 listener: listener,
13936 if (!on) this.__on = [o];else on.push(o);
13940 function selection_on (typename, value, options) {
13941 var typenames = parseTypenames$1(typename + ""),
13943 n = typenames.length,
13946 if (arguments.length < 2) {
13947 var on = this.node().__on;
13949 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
13950 for (i = 0, o = on[j]; i < n; ++i) {
13951 if ((t = typenames[i]).type === o.type && t.name === o.name) {
13959 on = value ? onAdd : onRemove;
13961 for (i = 0; i < n; ++i) {
13962 this.each(on(typenames[i], value, options));
13968 function dispatchEvent$1(node, type, params) {
13969 var window = defaultView(node),
13970 event = window.CustomEvent;
13972 if (typeof event === "function") {
13973 event = new event(type, params);
13975 event = window.document.createEvent("Event");
13976 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
13979 node.dispatchEvent(event);
13982 function dispatchConstant(type, params) {
13983 return function () {
13984 return dispatchEvent$1(this, type, params);
13988 function dispatchFunction(type, params) {
13989 return function () {
13990 return dispatchEvent$1(this, type, params.apply(this, arguments));
13994 function selection_dispatch (type, params) {
13995 return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
13998 var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(_callee);
14000 function _callee() {
14001 var groups, j, m, group, i, n, node;
14002 return regeneratorRuntime.wrap(function _callee$(_context) {
14004 switch (_context.prev = _context.next) {
14006 groups = this._groups, j = 0, m = groups.length;
14010 _context.next = 13;
14014 group = groups[j], i = 0, n = group.length;
14018 _context.next = 10;
14022 if (!(node = group[i])) {
14042 return _context.stop();
14045 }, _marked$2, this);
14049 function Selection(groups, parents) {
14050 this._groups = groups;
14051 this._parents = parents;
14054 function selection() {
14055 return new Selection([[document.documentElement]], root);
14058 function selection_selection() {
14062 Selection.prototype = selection.prototype = _defineProperty({
14063 constructor: Selection,
14064 select: selection_select,
14065 selectAll: selection_selectAll,
14066 selectChild: selection_selectChild,
14067 selectChildren: selection_selectChildren,
14068 filter: selection_filter,
14069 data: selection_data,
14070 enter: selection_enter,
14071 exit: selection_exit,
14072 join: selection_join,
14073 merge: selection_merge,
14074 selection: selection_selection,
14075 order: selection_order,
14076 sort: selection_sort,
14077 call: selection_call,
14078 nodes: selection_nodes,
14079 node: selection_node,
14080 size: selection_size,
14081 empty: selection_empty,
14082 each: selection_each,
14083 attr: selection_attr,
14084 style: selection_style,
14085 property: selection_property,
14086 classed: selection_classed,
14087 text: selection_text,
14088 html: selection_html,
14089 raise: selection_raise,
14090 lower: selection_lower,
14091 append: selection_append,
14092 insert: selection_insert,
14093 remove: selection_remove,
14094 clone: selection_clone,
14095 datum: selection_datum,
14097 dispatch: selection_dispatch
14098 }, Symbol.iterator, _callee);
14100 function select (selector) {
14101 return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root);
14104 function sourceEvent (event) {
14107 while (sourceEvent = event.sourceEvent) {
14108 event = sourceEvent;
14114 function pointer (event, node) {
14115 event = sourceEvent(event);
14116 if (node === undefined) node = event.currentTarget;
14119 var svg = node.ownerSVGElement || node;
14121 if (svg.createSVGPoint) {
14122 var point = svg.createSVGPoint();
14123 point.x = event.clientX, point.y = event.clientY;
14124 point = point.matrixTransform(node.getScreenCTM().inverse());
14125 return [point.x, point.y];
14128 if (node.getBoundingClientRect) {
14129 var rect = node.getBoundingClientRect();
14130 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14134 return [event.pageX, event.pageY];
14137 function selectAll (selector) {
14138 return typeof selector === "string" ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) : new Selection([selector == null ? [] : array(selector)], root);
14141 function nopropagation(event) {
14142 event.stopImmediatePropagation();
14144 function noevent (event) {
14145 event.preventDefault();
14146 event.stopImmediatePropagation();
14149 function dragDisable (view) {
14150 var root = view.document.documentElement,
14151 selection = select(view).on("dragstart.drag", noevent, true);
14153 if ("onselectstart" in root) {
14154 selection.on("selectstart.drag", noevent, true);
14156 root.__noselect = root.style.MozUserSelect;
14157 root.style.MozUserSelect = "none";
14160 function yesdrag(view, noclick) {
14161 var root = view.document.documentElement,
14162 selection = select(view).on("dragstart.drag", null);
14165 selection.on("click.drag", noevent, true);
14166 setTimeout(function () {
14167 selection.on("click.drag", null);
14171 if ("onselectstart" in root) {
14172 selection.on("selectstart.drag", null);
14174 root.style.MozUserSelect = root.__noselect;
14175 delete root.__noselect;
14179 var constant$1 = (function (x) {
14180 return function () {
14185 // `Object.defineProperties` method
14186 // https://tc39.github.io/ecma262/#sec-object.defineproperties
14187 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
14188 defineProperties: objectDefineProperties
14191 function DragEvent(type, _ref) {
14192 var sourceEvent = _ref.sourceEvent,
14193 subject = _ref.subject,
14194 target = _ref.target,
14195 identifier = _ref.identifier,
14196 active = _ref.active,
14201 dispatch = _ref.dispatch;
14202 Object.defineProperties(this, {
14209 value: sourceEvent,
14259 DragEvent.prototype.on = function () {
14260 var value = this._.on.apply(this._, arguments);
14262 return value === this._ ? this : value;
14265 function defaultFilter(event) {
14266 return !event.ctrlKey && !event.button;
14269 function defaultContainer() {
14270 return this.parentNode;
14273 function defaultSubject(event, d) {
14274 return d == null ? {
14280 function defaultTouchable() {
14281 return navigator.maxTouchPoints || "ontouchstart" in this;
14284 function d3_drag () {
14285 var filter = defaultFilter,
14286 container = defaultContainer,
14287 subject = defaultSubject,
14288 touchable = defaultTouchable,
14290 listeners = dispatch("start", "drag", "end"),
14296 clickDistance2 = 0;
14298 function drag(selection) {
14299 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)");
14302 function mousedowned(event, d) {
14303 if (touchending || !filter.call(this, event, d)) return;
14304 var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14305 if (!gesture) return;
14306 select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14307 dragDisable(event.view);
14308 nopropagation(event);
14309 mousemoving = false;
14310 mousedownx = event.clientX;
14311 mousedowny = event.clientY;
14312 gesture("start", event);
14315 function mousemoved(event) {
14318 if (!mousemoving) {
14319 var dx = event.clientX - mousedownx,
14320 dy = event.clientY - mousedowny;
14321 mousemoving = dx * dx + dy * dy > clickDistance2;
14324 gestures.mouse("drag", event);
14327 function mouseupped(event) {
14328 select(event.view).on("mousemove.drag mouseup.drag", null);
14329 yesdrag(event.view, mousemoving);
14331 gestures.mouse("end", event);
14334 function touchstarted(event, d) {
14335 if (!filter.call(this, event, d)) return;
14336 var touches = event.changedTouches,
14337 c = container.call(this, event, d),
14338 n = touches.length,
14342 for (i = 0; i < n; ++i) {
14343 if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14344 nopropagation(event);
14345 gesture("start", event, touches[i]);
14350 function touchmoved(event) {
14351 var touches = event.changedTouches,
14352 n = touches.length,
14356 for (i = 0; i < n; ++i) {
14357 if (gesture = gestures[touches[i].identifier]) {
14359 gesture("drag", event, touches[i]);
14364 function touchended(event) {
14365 var touches = event.changedTouches,
14366 n = touches.length,
14369 if (touchending) clearTimeout(touchending);
14370 touchending = setTimeout(function () {
14371 touchending = null;
14372 }, 500); // Ghost clicks are delayed!
14374 for (i = 0; i < n; ++i) {
14375 if (gesture = gestures[touches[i].identifier]) {
14376 nopropagation(event);
14377 gesture("end", event, touches[i]);
14382 function beforestart(that, container, event, d, identifier, touch) {
14383 var dispatch = listeners.copy(),
14384 p = pointer(touch || event, container),
14388 if ((s = subject.call(that, new DragEvent("beforestart", {
14389 sourceEvent: event,
14391 identifier: identifier,
14398 }), d)) == null) return;
14399 dx = s.x - p[0] || 0;
14400 dy = s.y - p[1] || 0;
14401 return function gesture(type, event, touch) {
14407 gestures[identifier] = gesture, n = active++;
14411 delete gestures[identifier], --active;
14415 p = pointer(touch || event, container), n = active;
14419 dispatch.call(type, that, new DragEvent(type, {
14420 sourceEvent: event,
14423 identifier: identifier,
14434 drag.filter = function (_) {
14435 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter;
14438 drag.container = function (_) {
14439 return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container;
14442 drag.subject = function (_) {
14443 return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject;
14446 drag.touchable = function (_) {
14447 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable;
14450 drag.on = function () {
14451 var value = listeners.on.apply(listeners, arguments);
14452 return value === listeners ? drag : value;
14455 drag.clickDistance = function (_) {
14456 return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14462 var defineProperty$9 = objectDefineProperty.f;
14463 var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14469 var setInternalState$8 = internalState.set;
14473 var MATCH$1 = wellKnownSymbol('match');
14474 var NativeRegExp = global_1.RegExp;
14475 var RegExpPrototype$1 = NativeRegExp.prototype;
14479 // "new" should create a new object, old webkit bug
14480 var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14482 var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y;
14484 var FORCED$b = descriptors && isForced_1('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y$2 || fails(function () {
14485 re2[MATCH$1] = false;
14486 // RegExp constructor can alter flags and IsRegExp works correct with @@match
14487 return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14490 // `RegExp` constructor
14491 // https://tc39.github.io/ecma262/#sec-regexp-constructor
14493 var RegExpWrapper = function RegExp(pattern, flags) {
14494 var thisIsRegExp = this instanceof RegExpWrapper;
14495 var patternIsRegExp = isRegexp(pattern);
14496 var flagsAreUndefined = flags === undefined;
14499 if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
14504 if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;
14505 } else if (pattern instanceof RegExpWrapper) {
14506 if (flagsAreUndefined) flags = regexpFlags.call(pattern);
14507 pattern = pattern.source;
14510 if (UNSUPPORTED_Y$2) {
14511 sticky = !!flags && flags.indexOf('y') > -1;
14512 if (sticky) flags = flags.replace(/y/g, '');
14515 var result = inheritIfRequired(
14516 CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
14517 thisIsRegExp ? this : RegExpPrototype$1,
14521 if (UNSUPPORTED_Y$2 && sticky) setInternalState$8(result, { sticky: sticky });
14525 var proxy = function (key) {
14526 key in RegExpWrapper || defineProperty$9(RegExpWrapper, key, {
14527 configurable: true,
14528 get: function () { return NativeRegExp[key]; },
14529 set: function (it) { NativeRegExp[key] = it; }
14532 var keys$2 = getOwnPropertyNames$1(NativeRegExp);
14534 while (keys$2.length > index) proxy(keys$2[index++]);
14535 RegExpPrototype$1.constructor = RegExpWrapper;
14536 RegExpWrapper.prototype = RegExpPrototype$1;
14537 redefine(global_1, 'RegExp', RegExpWrapper);
14540 // https://tc39.github.io/ecma262/#sec-get-regexp-@@species
14541 setSpecies('RegExp');
14543 function define (constructor, factory, prototype) {
14544 constructor.prototype = factory.prototype = prototype;
14545 prototype.constructor = constructor;
14547 function extend(parent, definition) {
14548 var prototype = Object.create(parent.prototype);
14550 for (var key in definition) {
14551 prototype[key] = definition[key];
14557 function Color() {}
14560 var _brighter = 1 / _darker;
14561 var reI = "\\s*([+-]?\\d+)\\s*",
14562 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14563 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14564 reHex = /^#([0-9a-f]{3,8})$/,
14565 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14566 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14567 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14568 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14569 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14570 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14572 aliceblue: 0xf0f8ff,
14573 antiquewhite: 0xfaebd7,
14575 aquamarine: 0x7fffd4,
14580 blanchedalmond: 0xffebcd,
14582 blueviolet: 0x8a2be2,
14584 burlywood: 0xdeb887,
14585 cadetblue: 0x5f9ea0,
14586 chartreuse: 0x7fff00,
14587 chocolate: 0xd2691e,
14589 cornflowerblue: 0x6495ed,
14590 cornsilk: 0xfff8dc,
14593 darkblue: 0x00008b,
14594 darkcyan: 0x008b8b,
14595 darkgoldenrod: 0xb8860b,
14596 darkgray: 0xa9a9a9,
14597 darkgreen: 0x006400,
14598 darkgrey: 0xa9a9a9,
14599 darkkhaki: 0xbdb76b,
14600 darkmagenta: 0x8b008b,
14601 darkolivegreen: 0x556b2f,
14602 darkorange: 0xff8c00,
14603 darkorchid: 0x9932cc,
14605 darksalmon: 0xe9967a,
14606 darkseagreen: 0x8fbc8f,
14607 darkslateblue: 0x483d8b,
14608 darkslategray: 0x2f4f4f,
14609 darkslategrey: 0x2f4f4f,
14610 darkturquoise: 0x00ced1,
14611 darkviolet: 0x9400d3,
14612 deeppink: 0xff1493,
14613 deepskyblue: 0x00bfff,
14616 dodgerblue: 0x1e90ff,
14617 firebrick: 0xb22222,
14618 floralwhite: 0xfffaf0,
14619 forestgreen: 0x228b22,
14621 gainsboro: 0xdcdcdc,
14622 ghostwhite: 0xf8f8ff,
14624 goldenrod: 0xdaa520,
14627 greenyellow: 0xadff2f,
14629 honeydew: 0xf0fff0,
14631 indianred: 0xcd5c5c,
14635 lavender: 0xe6e6fa,
14636 lavenderblush: 0xfff0f5,
14637 lawngreen: 0x7cfc00,
14638 lemonchiffon: 0xfffacd,
14639 lightblue: 0xadd8e6,
14640 lightcoral: 0xf08080,
14641 lightcyan: 0xe0ffff,
14642 lightgoldenrodyellow: 0xfafad2,
14643 lightgray: 0xd3d3d3,
14644 lightgreen: 0x90ee90,
14645 lightgrey: 0xd3d3d3,
14646 lightpink: 0xffb6c1,
14647 lightsalmon: 0xffa07a,
14648 lightseagreen: 0x20b2aa,
14649 lightskyblue: 0x87cefa,
14650 lightslategray: 0x778899,
14651 lightslategrey: 0x778899,
14652 lightsteelblue: 0xb0c4de,
14653 lightyellow: 0xffffe0,
14655 limegreen: 0x32cd32,
14659 mediumaquamarine: 0x66cdaa,
14660 mediumblue: 0x0000cd,
14661 mediumorchid: 0xba55d3,
14662 mediumpurple: 0x9370db,
14663 mediumseagreen: 0x3cb371,
14664 mediumslateblue: 0x7b68ee,
14665 mediumspringgreen: 0x00fa9a,
14666 mediumturquoise: 0x48d1cc,
14667 mediumvioletred: 0xc71585,
14668 midnightblue: 0x191970,
14669 mintcream: 0xf5fffa,
14670 mistyrose: 0xffe4e1,
14671 moccasin: 0xffe4b5,
14672 navajowhite: 0xffdead,
14676 olivedrab: 0x6b8e23,
14678 orangered: 0xff4500,
14680 palegoldenrod: 0xeee8aa,
14681 palegreen: 0x98fb98,
14682 paleturquoise: 0xafeeee,
14683 palevioletred: 0xdb7093,
14684 papayawhip: 0xffefd5,
14685 peachpuff: 0xffdab9,
14689 powderblue: 0xb0e0e6,
14691 rebeccapurple: 0x663399,
14693 rosybrown: 0xbc8f8f,
14694 royalblue: 0x4169e1,
14695 saddlebrown: 0x8b4513,
14697 sandybrown: 0xf4a460,
14698 seagreen: 0x2e8b57,
14699 seashell: 0xfff5ee,
14703 slateblue: 0x6a5acd,
14704 slategray: 0x708090,
14705 slategrey: 0x708090,
14707 springgreen: 0x00ff7f,
14708 steelblue: 0x4682b4,
14713 turquoise: 0x40e0d0,
14717 whitesmoke: 0xf5f5f5,
14719 yellowgreen: 0x9acd32
14721 define(Color, color, {
14722 copy: function copy(channels) {
14723 return Object.assign(new this.constructor(), this, channels);
14725 displayable: function displayable() {
14726 return this.rgb().displayable();
14728 hex: color_formatHex,
14729 // Deprecated! Use color.formatHex.
14730 formatHex: color_formatHex,
14731 formatHsl: color_formatHsl,
14732 formatRgb: color_formatRgb,
14733 toString: color_formatRgb
14736 function color_formatHex() {
14737 return this.rgb().formatHex();
14740 function color_formatHsl() {
14741 return hslConvert(this).formatHsl();
14744 function color_formatRgb() {
14745 return this.rgb().formatRgb();
14748 function color(format) {
14750 format = (format + "").trim().toLowerCase();
14751 return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
14752 : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
14753 : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
14754 : 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
14755 : null // invalid hex
14756 ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
14757 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
14758 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
14759 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
14760 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
14761 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
14762 : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
14763 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
14767 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
14770 function rgba(r, g, b, a) {
14771 if (a <= 0) r = g = b = NaN;
14772 return new Rgb(r, g, b, a);
14775 function rgbConvert(o) {
14776 if (!(o instanceof Color)) o = color(o);
14777 if (!o) return new Rgb();
14779 return new Rgb(o.r, o.g, o.b, o.opacity);
14781 function rgb(r, g, b, opacity) {
14782 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
14784 function Rgb(r, g, b, opacity) {
14788 this.opacity = +opacity;
14790 define(Rgb, rgb, extend(Color, {
14791 brighter: function brighter(k) {
14792 k = k == null ? _brighter : Math.pow(_brighter, k);
14793 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14795 darker: function darker(k) {
14796 k = k == null ? _darker : Math.pow(_darker, k);
14797 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14799 rgb: function rgb() {
14802 displayable: function displayable() {
14803 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;
14805 hex: rgb_formatHex,
14806 // Deprecated! Use color.formatHex.
14807 formatHex: rgb_formatHex,
14808 formatRgb: rgb_formatRgb,
14809 toString: rgb_formatRgb
14812 function rgb_formatHex() {
14813 return "#" + hex$2(this.r) + hex$2(this.g) + hex$2(this.b);
14816 function rgb_formatRgb() {
14817 var a = this.opacity;
14818 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14819 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 + ")");
14822 function hex$2(value) {
14823 value = Math.max(0, Math.min(255, Math.round(value) || 0));
14824 return (value < 16 ? "0" : "") + value.toString(16);
14827 function hsla(h, s, l, a) {
14828 if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
14829 return new Hsl(h, s, l, a);
14832 function hslConvert(o) {
14833 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
14834 if (!(o instanceof Color)) o = color(o);
14835 if (!o) return new Hsl();
14836 if (o instanceof Hsl) return o;
14841 min = Math.min(r, g, b),
14842 max = Math.max(r, g, b),
14845 l = (max + min) / 2;
14848 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;
14849 s /= l < 0.5 ? max + min : 2 - max - min;
14852 s = l > 0 && l < 1 ? 0 : h;
14855 return new Hsl(h, s, l, o.opacity);
14857 function hsl(h, s, l, opacity) {
14858 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
14861 function Hsl(h, s, l, opacity) {
14865 this.opacity = +opacity;
14868 define(Hsl, hsl, extend(Color, {
14869 brighter: function brighter(k) {
14870 k = k == null ? _brighter : Math.pow(_brighter, k);
14871 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14873 darker: function darker(k) {
14874 k = k == null ? _darker : Math.pow(_darker, k);
14875 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14877 rgb: function rgb() {
14878 var h = this.h % 360 + (this.h < 0) * 360,
14879 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
14881 m2 = l + (l < 0.5 ? l : 1 - l) * s,
14883 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);
14885 displayable: function displayable() {
14886 return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
14888 formatHsl: function formatHsl() {
14889 var a = this.opacity;
14890 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14891 return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
14894 /* From FvD 13.37, CSS Color Module Level 3 */
14896 function hsl2rgb(h, m1, m2) {
14897 return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
14900 var constant$2 = (function (x) {
14901 return function () {
14906 function linear(a, d) {
14907 return function (t) {
14912 function exponential(a, b, y) {
14913 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
14914 return Math.pow(a + t * b, y);
14917 function gamma(y) {
14918 return (y = +y) === 1 ? nogamma : function (a, b) {
14919 return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a);
14922 function nogamma(a, b) {
14924 return d ? linear(a, d) : constant$2(isNaN(a) ? b : a);
14927 var d3_interpolateRgb = (function rgbGamma(y) {
14928 var color = gamma(y);
14930 function rgb$1(start, end) {
14931 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
14932 g = color(start.g, end.g),
14933 b = color(start.b, end.b),
14934 opacity = nogamma(start.opacity, end.opacity);
14935 return function (t) {
14939 start.opacity = opacity(t);
14944 rgb$1.gamma = rgbGamma;
14948 function numberArray (a, b) {
14950 var n = a ? Math.min(b.length, a.length) : 0,
14953 return function (t) {
14954 for (i = 0; i < n; ++i) {
14955 c[i] = a[i] * (1 - t) + b[i] * t;
14961 function isNumberArray(x) {
14962 return ArrayBuffer.isView(x) && !(x instanceof DataView);
14965 function genericArray(a, b) {
14966 var nb = b ? b.length : 0,
14967 na = a ? Math.min(nb, a.length) : 0,
14972 for (i = 0; i < na; ++i) {
14973 x[i] = interpolate(a[i], b[i]);
14976 for (; i < nb; ++i) {
14980 return function (t) {
14981 for (i = 0; i < na; ++i) {
14989 function date (a, b) {
14990 var d = new Date();
14991 return a = +a, b = +b, function (t) {
14992 return d.setTime(a * (1 - t) + b * t), d;
14996 function d3_interpolateNumber (a, b) {
14997 return a = +a, b = +b, function (t) {
14998 return a * (1 - t) + b * t;
15002 function object (a, b) {
15006 if (a === null || _typeof(a) !== "object") a = {};
15007 if (b === null || _typeof(b) !== "object") b = {};
15011 i[k] = interpolate(a[k], b[k]);
15017 return function (t) {
15026 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
15027 reB = new RegExp(reA.source, "g");
15030 return function () {
15036 return function (t) {
15041 function interpolateString (a, b) {
15042 var bi = reA.lastIndex = reB.lastIndex = 0,
15043 // scan index for next number in b
15045 // current match in a
15047 // current match in b
15049 // string preceding current number in b, if any
15053 // string constants and placeholders
15054 q = []; // number interpolators
15055 // Coerce inputs to strings.
15057 a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15059 while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15060 if ((bs = bm.index) > bi) {
15061 // a string precedes the next number in b
15062 bs = b.slice(bi, bs);
15063 if (s[i]) s[i] += bs; // coalesce with previous string
15067 if ((am = am[0]) === (bm = bm[0])) {
15068 // numbers in a & b match
15069 if (s[i]) s[i] += bm; // coalesce with previous string
15072 // interpolate non-matching numbers
15076 x: d3_interpolateNumber(am, bm)
15080 bi = reB.lastIndex;
15081 } // Add remains of b.
15084 if (bi < b.length) {
15086 if (s[i]) s[i] += bs; // coalesce with previous string
15088 } // Special optimization for only a single match.
15089 // Otherwise, interpolate each of the numbers and rejoin the string.
15092 return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15093 for (var i = 0, o; i < b; ++i) {
15094 s[(o = q[i]).i] = o.x(t);
15101 function interpolate (a, b) {
15102 var t = _typeof(b),
15105 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);
15108 function interpolateRound (a, b) {
15109 return a = +a, b = +b, function (t) {
15110 return Math.round(a * (1 - t) + b * t);
15114 var degrees$1 = 180 / Math.PI;
15123 function decompose (a, b, c, d, e, f) {
15124 var scaleX, scaleY, skewX;
15125 if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15126 if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15127 if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15128 if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15132 rotate: Math.atan2(b, a) * degrees$1,
15133 skewX: Math.atan(skewX) * degrees$1,
15140 /* eslint-disable no-undef */
15142 function parseCss(value) {
15143 var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15144 return m.isIdentity ? identity$1 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15146 function parseSvg(value) {
15147 if (value == null) return identity$1;
15148 if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15149 svgNode.setAttribute("transform", value);
15150 if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1;
15151 value = value.matrix;
15152 return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15155 function interpolateTransform(parse, pxComma, pxParen, degParen) {
15157 return s.length ? s.pop() + " " : "";
15160 function translate(xa, ya, xb, yb, s, q) {
15161 if (xa !== xb || ya !== yb) {
15162 var i = s.push("translate(", null, pxComma, null, pxParen);
15165 x: d3_interpolateNumber(xa, xb)
15168 x: d3_interpolateNumber(ya, yb)
15170 } else if (xb || yb) {
15171 s.push("translate(" + xb + pxComma + yb + pxParen);
15175 function rotate(a, b, s, q) {
15177 if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15180 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15181 x: d3_interpolateNumber(a, b)
15184 s.push(pop(s) + "rotate(" + b + degParen);
15188 function skewX(a, b, s, q) {
15191 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15192 x: d3_interpolateNumber(a, b)
15195 s.push(pop(s) + "skewX(" + b + degParen);
15199 function scale(xa, ya, xb, yb, s, q) {
15200 if (xa !== xb || ya !== yb) {
15201 var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15204 x: d3_interpolateNumber(xa, xb)
15207 x: d3_interpolateNumber(ya, yb)
15209 } else if (xb !== 1 || yb !== 1) {
15210 s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15214 return function (a, b) {
15216 // string constants and placeholders
15217 q = []; // number interpolators
15219 a = parse(a), b = parse(b);
15220 translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15221 rotate(a.rotate, b.rotate, s, q);
15222 skewX(a.skewX, b.skewX, s, q);
15223 scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15224 a = b = null; // gc
15226 return function (t) {
15232 s[(o = q[i]).i] = o.x(t);
15240 var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15241 var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15243 var epsilon2$1 = 1e-12;
15246 return ((x = Math.exp(x)) + 1 / x) / 2;
15250 return ((x = Math.exp(x)) - 1 / x) / 2;
15254 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15257 var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15258 // p0 = [ux0, uy0, w0]
15259 // p1 = [ux1, uy1, w1]
15260 function zoom(p0, p1) {
15269 d2 = dx * dx + dy * dy,
15271 S; // Special case for u0 ≅ u1.
15273 if (d2 < epsilon2$1) {
15274 S = Math.log(w1 / w0) / rho;
15276 i = function i(t) {
15277 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15281 var d1 = Math.sqrt(d2),
15282 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15283 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15284 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15285 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15286 S = (r1 - r0) / rho;
15288 i = function i(t) {
15291 u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15292 return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15296 i.duration = S * 1000 * rho / Math.SQRT2;
15300 zoom.rho = function (_) {
15301 var _1 = Math.max(1e-3, +_),
15305 return zoomRho(_1, _2, _4);
15309 })(Math.SQRT2, 2, 4);
15311 function d3_quantize (interpolator, n) {
15312 var samples = new Array(n);
15314 for (var i = 0; i < n; ++i) {
15315 samples[i] = interpolator(i / (n - 1));
15321 // `Function.prototype.bind` method
15322 // https://tc39.github.io/ecma262/#sec-function.prototype.bind
15323 _export({ target: 'Function', proto: true }, {
15328 // is an animation frame pending?
15330 // is a timeout pending?
15332 // are any timers active?
15334 // how frequently we check for clock skew
15340 clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15341 setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15345 return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15348 function clearNow() {
15353 this._call = this._time = this._next = null;
15355 Timer.prototype = timer.prototype = {
15356 constructor: Timer,
15357 restart: function restart(callback, delay, time) {
15358 if (typeof callback !== "function") throw new TypeError("callback is not a function");
15359 time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
15361 if (!this._next && taskTail !== this) {
15362 if (taskTail) taskTail._next = this;else taskHead = this;
15366 this._call = callback;
15370 stop: function stop() {
15373 this._time = Infinity;
15378 function timer(callback, delay, time) {
15379 var t = new Timer();
15380 t.restart(callback, delay, time);
15383 function timerFlush() {
15384 now(); // Get the current time, if not already set.
15386 ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15392 if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15400 clockNow = (clockLast = clock.now()) + clockSkew;
15401 frame = timeout = 0;
15413 var now = clock.now(),
15414 delay = now - clockLast;
15415 if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15426 if (time > t1._time) time = t1._time;
15427 t0 = t1, t1 = t1._next;
15429 t2 = t1._next, t1._next = null;
15430 t1 = t0 ? t0._next = t2 : taskHead = t2;
15438 function sleep(time) {
15439 if (frame) return; // Soonest alarm already set, or will be.
15441 if (timeout) timeout = clearTimeout(timeout);
15442 var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15445 if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15446 if (interval) interval = clearInterval(interval);
15448 if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15449 frame = 1, setFrame(wake);
15453 function d3_timeout (callback, delay, time) {
15454 var t = new Timer();
15455 delay = delay == null ? 0 : +delay;
15456 t.restart(function (elapsed) {
15458 callback(elapsed + delay);
15463 var emptyOn = dispatch("start", "end", "cancel", "interrupt");
15464 var emptyTween = [];
15472 function schedule (node, name, id, index, group, timing) {
15473 var schedules = node.__transition;
15474 if (!schedules) node.__transition = {};else if (id in schedules) return;
15478 // For context during callback.
15480 // For context during callback.
15484 delay: timing.delay,
15485 duration: timing.duration,
15491 function init(node, id) {
15492 var schedule = get$4(node, id);
15493 if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15496 function set$4(node, id) {
15497 var schedule = get$4(node, id);
15498 if (schedule.state > STARTED) throw new Error("too late; already running");
15501 function get$4(node, id) {
15502 var schedule = node.__transition;
15503 if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15507 function create(node, id, self) {
15508 var schedules = node.__transition,
15509 tween; // Initialize the self timer when the transition is created.
15510 // Note the actual delay is not known until the first callback!
15512 schedules[id] = self;
15513 self.timer = timer(schedule, 0, self.time);
15515 function schedule(elapsed) {
15516 self.state = SCHEDULED;
15517 self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15519 if (self.delay <= elapsed) start(elapsed - self.delay);
15522 function start(elapsed) {
15523 var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15525 if (self.state !== SCHEDULED) return stop();
15527 for (i in schedules) {
15529 if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15530 // defer starting an interrupting transition until that transition has a
15531 // chance to tick (and possibly end); see d3/d3-transition#54!
15533 if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15535 if (o.state === RUNNING) {
15538 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15539 delete schedules[i];
15540 } // Cancel any pre-empted transitions.
15541 else if (+i < id) {
15544 o.on.call("cancel", node, node.__data__, o.index, o.group);
15545 delete schedules[i];
15547 } // Defer the first tick to end of the current frame; see d3/d3#1576.
15548 // Note the transition may be canceled after start and before the first tick!
15549 // Note this must be scheduled before the start event; see d3/d3-transition#16!
15550 // Assuming this is successful, subsequent callbacks go straight to tick.
15553 d3_timeout(function () {
15554 if (self.state === STARTED) {
15555 self.state = RUNNING;
15556 self.timer.restart(tick, self.delay, self.time);
15559 }); // Dispatch the start event.
15560 // Note this must be done before the tween are initialized.
15562 self.state = STARTING;
15563 self.on.call("start", node, node.__data__, self.index, self.group);
15564 if (self.state !== STARTING) return; // interrupted
15566 self.state = STARTED; // Initialize the tween, deleting null tween.
15568 tween = new Array(n = self.tween.length);
15570 for (i = 0, j = -1; i < n; ++i) {
15571 if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15576 tween.length = j + 1;
15579 function tick(elapsed) {
15580 var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15585 tween[i].call(node, t);
15586 } // Dispatch the end event.
15589 if (self.state === ENDING) {
15590 self.on.call("end", node, node.__data__, self.index, self.group);
15596 self.state = ENDED;
15598 delete schedules[id];
15600 for (var i in schedules) {
15602 } // eslint-disable-line no-unused-vars
15605 delete node.__transition;
15609 function interrupt (node, name) {
15610 var schedules = node.__transition,
15615 if (!schedules) return;
15616 name = name == null ? null : name + "";
15618 for (i in schedules) {
15619 if ((schedule = schedules[i]).name !== name) {
15624 active = schedule.state > STARTING && schedule.state < ENDING;
15625 schedule.state = ENDED;
15626 schedule.timer.stop();
15627 schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15628 delete schedules[i];
15631 if (empty) delete node.__transition;
15634 function selection_interrupt (name) {
15635 return this.each(function () {
15636 interrupt(this, name);
15640 function tweenRemove(id, name) {
15641 var tween0, tween1;
15642 return function () {
15643 var schedule = set$4(this, id),
15644 tween = schedule.tween; // If this node shared tween with the previous node,
15645 // just assign the updated shared tween and we’re done!
15646 // Otherwise, copy-on-write.
15648 if (tween !== tween0) {
15649 tween1 = tween0 = tween;
15651 for (var i = 0, n = tween1.length; i < n; ++i) {
15652 if (tween1[i].name === name) {
15653 tween1 = tween1.slice();
15654 tween1.splice(i, 1);
15660 schedule.tween = tween1;
15664 function tweenFunction(id, name, value) {
15665 var tween0, tween1;
15666 if (typeof value !== "function") throw new Error();
15667 return function () {
15668 var schedule = set$4(this, id),
15669 tween = schedule.tween; // If this node shared tween with the previous node,
15670 // just assign the updated shared tween and we’re done!
15671 // Otherwise, copy-on-write.
15673 if (tween !== tween0) {
15674 tween1 = (tween0 = tween).slice();
15679 }, i = 0, n = tween1.length; i < n; ++i) {
15680 if (tween1[i].name === name) {
15686 if (i === n) tween1.push(t);
15689 schedule.tween = tween1;
15693 function transition_tween (name, value) {
15697 if (arguments.length < 2) {
15698 var tween = get$4(this.node(), id).tween;
15700 for (var i = 0, n = tween.length, t; i < n; ++i) {
15701 if ((t = tween[i]).name === name) {
15709 return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15711 function tweenValue(transition, name, value) {
15712 var id = transition._id;
15713 transition.each(function () {
15714 var schedule = set$4(this, id);
15715 (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15717 return function (node) {
15718 return get$4(node, id).value[name];
15722 function interpolate$1 (a, b) {
15724 return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15727 function attrRemove$1(name) {
15728 return function () {
15729 this.removeAttribute(name);
15733 function attrRemoveNS$1(fullname) {
15734 return function () {
15735 this.removeAttributeNS(fullname.space, fullname.local);
15739 function attrConstant$1(name, interpolate, value1) {
15741 string1 = value1 + "",
15743 return function () {
15744 var string0 = this.getAttribute(name);
15745 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15749 function attrConstantNS$1(fullname, interpolate, value1) {
15751 string1 = value1 + "",
15753 return function () {
15754 var string0 = this.getAttributeNS(fullname.space, fullname.local);
15755 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15759 function attrFunction$1(name, interpolate, value) {
15760 var string00, string10, interpolate0;
15761 return function () {
15763 value1 = value(this),
15765 if (value1 == null) return void this.removeAttribute(name);
15766 string0 = this.getAttribute(name);
15767 string1 = value1 + "";
15768 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15772 function attrFunctionNS$1(fullname, interpolate, value) {
15773 var string00, string10, interpolate0;
15774 return function () {
15776 value1 = value(this),
15778 if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
15779 string0 = this.getAttributeNS(fullname.space, fullname.local);
15780 string1 = value1 + "";
15781 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15785 function transition_attr (name, value) {
15786 var fullname = namespace(name),
15787 i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
15788 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));
15791 function attrInterpolate(name, i) {
15792 return function (t) {
15793 this.setAttribute(name, i.call(this, t));
15797 function attrInterpolateNS(fullname, i) {
15798 return function (t) {
15799 this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
15803 function attrTweenNS(fullname, value) {
15807 var i = value.apply(this, arguments);
15808 if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
15812 tween._value = value;
15816 function attrTween(name, value) {
15820 var i = value.apply(this, arguments);
15821 if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
15825 tween._value = value;
15829 function transition_attrTween (name, value) {
15830 var key = "attr." + name;
15831 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
15832 if (value == null) return this.tween(key, null);
15833 if (typeof value !== "function") throw new Error();
15834 var fullname = namespace(name);
15835 return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
15838 function delayFunction(id, value) {
15839 return function () {
15840 init(this, id).delay = +value.apply(this, arguments);
15844 function delayConstant(id, value) {
15845 return value = +value, function () {
15846 init(this, id).delay = value;
15850 function transition_delay (value) {
15852 return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$4(this.node(), id).delay;
15855 function durationFunction(id, value) {
15856 return function () {
15857 set$4(this, id).duration = +value.apply(this, arguments);
15861 function durationConstant(id, value) {
15862 return value = +value, function () {
15863 set$4(this, id).duration = value;
15867 function transition_duration (value) {
15869 return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$4(this.node(), id).duration;
15872 function easeConstant(id, value) {
15873 if (typeof value !== "function") throw new Error();
15874 return function () {
15875 set$4(this, id).ease = value;
15879 function transition_ease (value) {
15881 return arguments.length ? this.each(easeConstant(id, value)) : get$4(this.node(), id).ease;
15884 function easeVarying(id, value) {
15885 return function () {
15886 var v = value.apply(this, arguments);
15887 if (typeof v !== "function") throw new Error();
15888 set$4(this, id).ease = v;
15892 function transition_easeVarying (value) {
15893 if (typeof value !== "function") throw new Error();
15894 return this.each(easeVarying(this._id, value));
15897 function transition_filter (match) {
15898 if (typeof match !== "function") match = matcher(match);
15900 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15901 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
15902 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
15903 subgroup.push(node);
15908 return new Transition(subgroups, this._parents, this._name, this._id);
15911 function transition_merge (transition) {
15912 if (transition._id !== this._id) throw new Error();
15914 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) {
15915 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
15916 if (node = group0[i] || group1[i]) {
15922 for (; j < m0; ++j) {
15923 merges[j] = groups0[j];
15926 return new Transition(merges, this._parents, this._name, this._id);
15929 function start(name) {
15930 return (name + "").trim().split(/^|\s+/).every(function (t) {
15931 var i = t.indexOf(".");
15932 if (i >= 0) t = t.slice(0, i);
15933 return !t || t === "start";
15937 function onFunction(id, name, listener) {
15940 sit = start(name) ? init : set$4;
15941 return function () {
15942 var schedule = sit(this, id),
15943 on = schedule.on; // If this node shared a dispatch with the previous node,
15944 // just assign the updated shared dispatch and we’re done!
15945 // Otherwise, copy-on-write.
15947 if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
15952 function transition_on (name, listener) {
15954 return arguments.length < 2 ? get$4(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
15957 function removeFunction(id) {
15958 return function () {
15959 var parent = this.parentNode;
15961 for (var i in this.__transition) {
15962 if (+i !== id) return;
15965 if (parent) parent.removeChild(this);
15969 function transition_remove () {
15970 return this.on("end.remove", removeFunction(this._id));
15973 function transition_select (select) {
15974 var name = this._name,
15976 if (typeof select !== "function") select = selector(select);
15978 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15979 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
15980 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
15981 if ("__data__" in node) subnode.__data__ = node.__data__;
15982 subgroup[i] = subnode;
15983 schedule(subgroup[i], name, id, i, subgroup, get$4(node, id));
15988 return new Transition(subgroups, this._parents, name, id);
15991 function transition_selectAll (select) {
15992 var name = this._name,
15994 if (typeof select !== "function") select = selectorAll(select);
15996 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
15997 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
15998 if (node = group[i]) {
15999 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$4(node, id), k = 0, l = children.length; k < l; ++k) {
16000 if (child = children[k]) {
16001 schedule(child, name, id, k, children, inherit);
16005 subgroups.push(children);
16006 parents.push(node);
16011 return new Transition(subgroups, parents, name, id);
16014 var Selection$1 = selection.prototype.constructor;
16015 function transition_selection () {
16016 return new Selection$1(this._groups, this._parents);
16019 function styleNull(name, interpolate) {
16020 var string00, string10, interpolate0;
16021 return function () {
16022 var string0 = styleValue(this, name),
16023 string1 = (this.style.removeProperty(name), styleValue(this, name));
16024 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
16028 function styleRemove$1(name) {
16029 return function () {
16030 this.style.removeProperty(name);
16034 function styleConstant$1(name, interpolate, value1) {
16036 string1 = value1 + "",
16038 return function () {
16039 var string0 = styleValue(this, name);
16040 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16044 function styleFunction$1(name, interpolate, value) {
16045 var string00, string10, interpolate0;
16046 return function () {
16047 var string0 = styleValue(this, name),
16048 value1 = value(this),
16049 string1 = value1 + "";
16050 if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16051 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16055 function styleMaybeRemove(id, name) {
16059 key = "style." + name,
16060 event = "end." + key,
16062 return function () {
16063 var schedule = set$4(this, id),
16065 listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined; // If this node shared a dispatch with the previous node,
16066 // just assign the updated shared dispatch and we’re done!
16067 // Otherwise, copy-on-write.
16069 if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16074 function transition_style (name, value, priority) {
16075 var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
16076 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);
16079 function styleInterpolate(name, i, priority) {
16080 return function (t) {
16081 this.style.setProperty(name, i.call(this, t), priority);
16085 function styleTween(name, value, priority) {
16089 var i = value.apply(this, arguments);
16090 if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16094 tween._value = value;
16098 function transition_styleTween (name, value, priority) {
16099 var key = "style." + (name += "");
16100 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16101 if (value == null) return this.tween(key, null);
16102 if (typeof value !== "function") throw new Error();
16103 return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16106 function textConstant$1(value) {
16107 return function () {
16108 this.textContent = value;
16112 function textFunction$1(value) {
16113 return function () {
16114 var value1 = value(this);
16115 this.textContent = value1 == null ? "" : value1;
16119 function transition_text (value) {
16120 return this.tween("text", typeof value === "function" ? textFunction$1(tweenValue(this, "text", value)) : textConstant$1(value == null ? "" : value + ""));
16123 function textInterpolate(i) {
16124 return function (t) {
16125 this.textContent = i.call(this, t);
16129 function textTween(value) {
16133 var i = value.apply(this, arguments);
16134 if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16138 tween._value = value;
16142 function transition_textTween (value) {
16144 if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16145 if (value == null) return this.tween(key, null);
16146 if (typeof value !== "function") throw new Error();
16147 return this.tween(key, textTween(value));
16150 function transition_transition () {
16151 var name = this._name,
16155 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16156 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16157 if (node = group[i]) {
16158 var inherit = get$4(node, id0);
16159 schedule(node, name, id1, i, group, {
16160 time: inherit.time + inherit.delay + inherit.duration,
16162 duration: inherit.duration,
16169 return new Transition(groups, this._parents, name, id1);
16172 function transition_end () {
16177 size = that.size();
16178 return new Promise(function (resolve, reject) {
16183 value: function value() {
16184 if (--size === 0) resolve();
16187 that.each(function () {
16188 var schedule = set$4(this, id),
16189 on = schedule.on; // If this node shared a dispatch with the previous node,
16190 // just assign the updated shared dispatch and we’re done!
16191 // Otherwise, copy-on-write.
16194 on1 = (on0 = on).copy();
16196 on1._.cancel.push(cancel);
16198 on1._.interrupt.push(cancel);
16200 on1._.end.push(end);
16204 }); // The selection was empty, resolve end immediately
16206 if (size === 0) resolve();
16211 function Transition(groups, parents, name, id) {
16212 this._groups = groups;
16213 this._parents = parents;
16217 function transition(name) {
16218 return selection().transition(name);
16223 var selection_prototype = selection.prototype;
16224 Transition.prototype = transition.prototype = _defineProperty({
16225 constructor: Transition,
16226 select: transition_select,
16227 selectAll: transition_selectAll,
16228 filter: transition_filter,
16229 merge: transition_merge,
16230 selection: transition_selection,
16231 transition: transition_transition,
16232 call: selection_prototype.call,
16233 nodes: selection_prototype.nodes,
16234 node: selection_prototype.node,
16235 size: selection_prototype.size,
16236 empty: selection_prototype.empty,
16237 each: selection_prototype.each,
16239 attr: transition_attr,
16240 attrTween: transition_attrTween,
16241 style: transition_style,
16242 styleTween: transition_styleTween,
16243 text: transition_text,
16244 textTween: transition_textTween,
16245 remove: transition_remove,
16246 tween: transition_tween,
16247 delay: transition_delay,
16248 duration: transition_duration,
16249 ease: transition_ease,
16250 easeVarying: transition_easeVarying,
16251 end: transition_end
16252 }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16254 var linear$1 = function linear(t) {
16258 function cubicInOut(t) {
16259 return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16262 var defaultTiming = {
16270 function inherit(node, id) {
16273 while (!(timing = node.__transition) || !(timing = timing[id])) {
16274 if (!(node = node.parentNode)) {
16275 throw new Error("transition ".concat(id, " not found"));
16282 function selection_transition (name) {
16285 if (name instanceof Transition) {
16286 id = name._id, name = name._name;
16288 id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
16291 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16292 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16293 if (node = group[i]) {
16294 schedule(node, name, id, i, group, timing || inherit(node, id));
16299 return new Transition(groups, this._parents, name, id);
16302 selection.prototype.interrupt = selection_interrupt;
16303 selection.prototype.transition = selection_transition;
16305 var constant$3 = (function (x) {
16306 return function () {
16311 function ZoomEvent(type, _ref) {
16312 var sourceEvent = _ref.sourceEvent,
16313 target = _ref.target,
16314 transform = _ref.transform,
16315 dispatch = _ref.dispatch;
16316 Object.defineProperties(this, {
16323 value: sourceEvent,
16343 function Transform(k, x, y) {
16348 Transform.prototype = {
16349 constructor: Transform,
16350 scale: function scale(k) {
16351 return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16353 translate: function translate(x, y) {
16354 return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16356 apply: function apply(point) {
16357 return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16359 applyX: function applyX(x) {
16360 return x * this.k + this.x;
16362 applyY: function applyY(y) {
16363 return y * this.k + this.y;
16365 invert: function invert(location) {
16366 return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16368 invertX: function invertX(x) {
16369 return (x - this.x) / this.k;
16371 invertY: function invertY(y) {
16372 return (y - this.y) / this.k;
16374 rescaleX: function rescaleX(x) {
16375 return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16377 rescaleY: function rescaleY(y) {
16378 return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16380 toString: function toString() {
16381 return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16384 var identity$2 = new Transform(1, 0, 0);
16386 function nopropagation$1(event) {
16387 event.stopImmediatePropagation();
16389 function noevent$1 (event) {
16390 event.preventDefault();
16391 event.stopImmediatePropagation();
16394 // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16396 function defaultFilter$1(event) {
16397 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16400 function defaultExtent() {
16403 if (e instanceof SVGElement) {
16404 e = e.ownerSVGElement || e;
16406 if (e.hasAttribute("viewBox")) {
16407 e = e.viewBox.baseVal;
16408 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16411 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16414 return [[0, 0], [e.clientWidth, e.clientHeight]];
16417 function defaultTransform() {
16418 return this.__zoom || identity$2;
16421 function defaultWheelDelta(event) {
16422 return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16425 function defaultTouchable$1() {
16426 return navigator.maxTouchPoints || "ontouchstart" in this;
16429 function defaultConstrain(transform, extent, translateExtent) {
16430 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16431 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16432 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16433 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16434 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));
16437 function d3_zoom () {
16438 var filter = defaultFilter$1,
16439 extent = defaultExtent,
16440 constrain = defaultConstrain,
16441 wheelDelta = defaultWheelDelta,
16442 touchable = defaultTouchable$1,
16443 scaleExtent = [0, Infinity],
16444 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16446 interpolate = interpolateZoom,
16447 listeners = dispatch("start", "zoom", "end"),
16453 clickDistance2 = 0,
16456 function zoom(selection) {
16457 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)");
16460 zoom.transform = function (collection, transform, point, event) {
16461 var selection = collection.selection ? collection.selection() : collection;
16462 selection.property("__zoom", defaultTransform);
16464 if (collection !== selection) {
16465 schedule(collection, transform, point, event);
16467 selection.interrupt().each(function () {
16468 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16473 zoom.scaleBy = function (selection, k, p, event) {
16474 zoom.scaleTo(selection, function () {
16475 var k0 = this.__zoom.k,
16476 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16481 zoom.scaleTo = function (selection, k, p, event) {
16482 zoom.transform(selection, function () {
16483 var e = extent.apply(this, arguments),
16485 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16486 p1 = t0.invert(p0),
16487 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16488 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16492 zoom.translateBy = function (selection, x, y, event) {
16493 zoom.transform(selection, function () {
16494 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);
16498 zoom.translateTo = function (selection, x, y, p, event) {
16499 zoom.transform(selection, function () {
16500 var e = extent.apply(this, arguments),
16502 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16503 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);
16507 function scale(transform, k) {
16508 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16509 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16512 function translate(transform, p0, p1) {
16513 var x = p0[0] - p1[0] * transform.k,
16514 y = p0[1] - p1[1] * transform.k;
16515 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16518 function centroid(extent) {
16519 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16522 function schedule(transition, transform, point, event) {
16523 transition.on("start.zoom", function () {
16524 gesture(this, arguments).event(event).start();
16525 }).on("interrupt.zoom end.zoom", function () {
16526 gesture(this, arguments).event(event).end();
16527 }).tween("zoom", function () {
16530 g = gesture(that, args).event(event),
16531 e = extent.apply(that, args),
16532 p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16533 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16535 b = typeof transform === "function" ? transform.apply(that, args) : transform,
16536 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16537 return function (t) {
16538 if (t === 1) t = b; // Avoid rounding error on end.
16542 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16549 function gesture(that, args, clean) {
16550 return !clean && that.__zooming || new Gesture(that, args);
16553 function Gesture(that, args) {
16557 this.sourceEvent = null;
16558 this.extent = extent.apply(that, args);
16562 Gesture.prototype = {
16563 event: function event(_event) {
16564 if (_event) this.sourceEvent = _event;
16567 start: function start() {
16568 if (++this.active === 1) {
16569 this.that.__zooming = this;
16570 this.emit("start");
16575 zoom: function zoom(key, transform) {
16576 if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16577 if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16578 if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16579 this.that.__zoom = transform;
16583 end: function end() {
16584 if (--this.active === 0) {
16585 delete this.that.__zooming;
16591 emit: function emit(type) {
16592 var d = select(this.that).datum();
16593 listeners.call(type, this.that, new ZoomEvent(type, {
16594 sourceEvent: this.sourceEvent,
16597 transform: this.that.__zoom,
16598 dispatch: listeners
16603 function wheeled(event) {
16604 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16605 args[_key - 1] = arguments[_key];
16608 if (!filter.apply(this, arguments)) return;
16609 var g = gesture(this, args).event(event),
16611 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16612 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16613 // If there were recent wheel events, reset the wheel idle timeout.
16616 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16617 g.mouse[1] = t.invert(g.mouse[0] = p);
16620 clearTimeout(g.wheel);
16621 } // If this wheel event won’t trigger a transform change, ignore it.
16622 else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16624 g.mouse = [p, t.invert(p)];
16630 g.wheel = setTimeout(wheelidled, wheelDelay);
16631 g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16633 function wheelidled() {
16639 function mousedowned(event) {
16640 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16641 args[_key2 - 1] = arguments[_key2];
16644 if (touchending || !filter.apply(this, arguments)) return;
16645 var g = gesture(this, args, true).event(event),
16646 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16647 p = pointer(event, currentTarget),
16648 currentTarget = event.currentTarget,
16649 x0 = event.clientX,
16650 y0 = event.clientY;
16651 dragDisable(event.view);
16652 nopropagation$1(event);
16653 g.mouse = [p, this.__zoom.invert(p)];
16657 function mousemoved(event) {
16661 var dx = event.clientX - x0,
16662 dy = event.clientY - y0;
16663 g.moved = dx * dx + dy * dy > clickDistance2;
16666 g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16669 function mouseupped(event) {
16670 v.on("mousemove.zoom mouseup.zoom", null);
16671 yesdrag(event.view, g.moved);
16673 g.event(event).end();
16677 function dblclicked(event) {
16678 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16679 args[_key3 - 1] = arguments[_key3];
16682 if (!filter.apply(this, arguments)) return;
16683 var t0 = this.__zoom,
16684 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16685 p1 = t0.invert(p0),
16686 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16687 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16689 if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16692 function touchstarted(event) {
16693 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16694 args[_key4 - 1] = arguments[_key4];
16697 if (!filter.apply(this, arguments)) return;
16698 var touches = event.touches,
16699 n = touches.length,
16700 g = gesture(this, args, event.changedTouches.length === n).event(event),
16705 nopropagation$1(event);
16707 for (i = 0; i < n; ++i) {
16708 t = touches[i], p = pointer(t, this);
16709 p = [p, this.__zoom.invert(p), t.identifier];
16710 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;
16713 if (touchstarting) touchstarting = clearTimeout(touchstarting);
16716 if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16717 touchstarting = null;
16724 function touchmoved(event) {
16725 if (!this.__zooming) return;
16727 for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16728 args[_key5 - 1] = arguments[_key5];
16731 var g = gesture(this, args).event(event),
16732 touches = event.changedTouches,
16733 n = touches.length,
16740 for (i = 0; i < n; ++i) {
16741 t = touches[i], p = pointer(t, this);
16742 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;
16748 var p0 = g.touch0[0],
16752 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
16753 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
16754 t = scale(t, Math.sqrt(dp / dl));
16755 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
16756 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
16757 } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
16759 g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
16762 function touchended(event) {
16763 for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
16764 args[_key6 - 1] = arguments[_key6];
16767 if (!this.__zooming) return;
16768 var g = gesture(this, args).event(event),
16769 touches = event.changedTouches,
16770 n = touches.length,
16773 nopropagation$1(event);
16774 if (touchending) clearTimeout(touchending);
16775 touchending = setTimeout(function () {
16776 touchending = null;
16779 for (i = 0; i < n; ++i) {
16781 if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
16784 if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
16785 if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
16786 g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
16788 if (g.taps === 2) {
16789 t = pointer(t, this);
16791 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
16792 var p = select(this).on("dblclick.zoom");
16793 if (p) p.apply(this, arguments);
16799 zoom.wheelDelta = function (_) {
16800 return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta;
16803 zoom.filter = function (_) {
16804 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter;
16807 zoom.touchable = function (_) {
16808 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable;
16811 zoom.extent = function (_) {
16812 return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
16815 zoom.scaleExtent = function (_) {
16816 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
16819 zoom.translateExtent = function (_) {
16820 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]]];
16823 zoom.constrain = function (_) {
16824 return arguments.length ? (constrain = _, zoom) : constrain;
16827 zoom.duration = function (_) {
16828 return arguments.length ? (duration = +_, zoom) : duration;
16831 zoom.interpolate = function (_) {
16832 return arguments.length ? (interpolate = _, zoom) : interpolate;
16835 zoom.on = function () {
16836 var value = listeners.on.apply(listeners, arguments);
16837 return value === listeners ? zoom : value;
16840 zoom.clickDistance = function (_) {
16841 return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
16844 zoom.tapDistance = function (_) {
16845 return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
16852 Bypasses features of D3's default projection stream pipeline that are unnecessary:
16853 * Antimeridian clipping
16854 * Spherical rotation
16858 function geoRawMercator() {
16859 var project = mercatorRaw;
16860 var k = 512 / Math.PI; // scale
16863 var y = 0; // translate
16865 var clipExtent = [[0, 0], [0, 0]];
16867 function projection(point) {
16868 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
16869 return [point[0] * k + x, y - point[1] * k];
16872 projection.invert = function (point) {
16873 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
16874 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
16877 projection.scale = function (_) {
16878 if (!arguments.length) return k;
16883 projection.translate = function (_) {
16884 if (!arguments.length) return [x, y];
16890 projection.clipExtent = function (_) {
16891 if (!arguments.length) return clipExtent;
16896 projection.transform = function (obj) {
16897 if (!arguments.length) return identity$2.translate(x, y).scale(k);
16904 projection.stream = d3_geoTransform({
16905 point: function point(x, y) {
16906 var vec = projection([x, y]);
16907 this.stream.point(vec[0], vec[1]);
16913 function geoOrthoNormalizedDotProduct(a, b, origin) {
16914 if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
16915 return 1; // coincident points, treat as straight and try to remove
16918 return geoVecNormalizedDot(a, b, origin);
16921 function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
16922 var val = Math.abs(dotp);
16924 if (val < epsilon) {
16925 return 0; // already orthogonal
16926 } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
16927 return 0; // straight angle, which is okay in this case
16928 } else if (val < lowerThreshold || val > upperThreshold) {
16929 return dotp; // can be adjusted
16931 return null; // ignore vertex
16935 function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
16937 var first = isClosed ? 0 : 1;
16938 var last = isClosed ? points.length : points.length - 1;
16939 var coords = points.map(function (p) {
16942 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16943 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16945 for (var i = first; i < last; i++) {
16946 var a = coords[(i - 1 + coords.length) % coords.length];
16947 var origin = coords[i];
16948 var b = coords[(i + 1) % coords.length];
16949 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
16950 if (dotp === null) continue; // ignore vertex
16952 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
16956 } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
16958 function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
16959 var max = -Infinity;
16960 var first = isClosed ? 0 : 1;
16961 var last = isClosed ? coords.length : coords.length - 1;
16963 for (var i = first; i < last; i++) {
16964 var a = coords[(i - 1 + coords.length) % coords.length];
16965 var origin = coords[i];
16966 var b = coords[(i + 1) % coords.length];
16967 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
16968 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
16969 if (angle > 45) angle = 90 - angle;
16970 if (angle >= lessThan) continue;
16971 if (angle > max) max = angle;
16974 if (max === -Infinity) return null;
16976 } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
16978 function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
16980 var first = isClosed ? 0 : 1;
16981 var last = isClosed ? coords.length : coords.length - 1;
16982 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16983 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16985 for (var i = first; i < last; i++) {
16986 var a = coords[(i - 1 + coords.length) % coords.length];
16987 var origin = coords[i];
16988 var b = coords[(i + 1) % coords.length];
16989 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
16990 if (dotp === null) continue; // ignore vertex
16992 if (Math.abs(dotp) > 0) return 1; // something to do
16994 score = 0; // already square
17000 var onFreeze = internalMetadata.onFreeze;
17002 var nativeFreeze = Object.freeze;
17003 var FAILS_ON_PRIMITIVES$4 = fails(function () { nativeFreeze(1); });
17005 // `Object.freeze` method
17006 // https://tc39.github.io/ecma262/#sec-object.freeze
17007 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4, sham: !freezing }, {
17008 freeze: function freeze(it) {
17009 return nativeFreeze && isObject(it) ? nativeFreeze(onFreeze(it)) : it;
17013 // Returns true if a and b have the same elements at the same indices.
17014 function utilArrayIdentical(a, b) {
17015 // an array is always identical to itself
17016 if (a === b) return true;
17018 if (i !== b.length) return false;
17021 if (a[i] !== b[i]) return false;
17025 } // http://2ality.com/2015/01/es6-set-operations.html
17026 // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
17027 // This operation is also sometimes called minus (-).
17028 // var a = [1,2,3];
17029 // var b = [4,3,2];
17030 // utilArrayDifference(a, b)
17032 // utilArrayDifference(b, a)
17035 function utilArrayDifference(a, b) {
17036 var other = new Set(b);
17037 return Array.from(new Set(a)).filter(function (v) {
17038 return !other.has(v);
17040 } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
17041 // var a = [1,2,3];
17042 // var b = [4,3,2];
17043 // utilArrayIntersection(a, b)
17046 function utilArrayIntersection(a, b) {
17047 var other = new Set(b);
17048 return Array.from(new Set(a)).filter(function (v) {
17049 return other.has(v);
17051 } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17052 // var a = [1,2,3];
17053 // var b = [4,3,2];
17054 // utilArrayUnion(a, b)
17057 function utilArrayUnion(a, b) {
17058 var result = new Set(a);
17059 b.forEach(function (v) {
17062 return Array.from(result);
17063 } // Returns an Array with all the duplicates removed
17064 // var a = [1,1,2,3,3];
17065 // utilArrayUniq(a)
17068 function utilArrayUniq(a) {
17069 return Array.from(new Set(a));
17070 } // Splits array into chunks of given chunk size
17071 // var a = [1,2,3,4,5,6,7];
17072 // utilArrayChunk(a, 3);
17073 // [[1,2,3],[4,5,6],[7]];
17075 function utilArrayChunk(a, chunkSize) {
17076 if (!chunkSize || chunkSize < 0) return [a.slice()];
17077 var result = new Array(Math.ceil(a.length / chunkSize));
17078 return Array.from(result, function (item, i) {
17079 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17081 } // Flattens two level array into a single level
17082 // var a = [[1,2,3],[4,5,6],[7]];
17083 // utilArrayFlatten(a);
17084 // [1,2,3,4,5,6,7];
17086 function utilArrayFlatten(a) {
17087 return a.reduce(function (acc, val) {
17088 return acc.concat(val);
17090 } // Groups the items of the Array according to the given key
17091 // `key` can be passed as a property or as a key function
17094 // { type: 'Dog', name: 'Spot' },
17095 // { type: 'Cat', name: 'Tiger' },
17096 // { type: 'Dog', name: 'Rover' },
17097 // { type: 'Cat', name: 'Leo' }
17100 // utilArrayGroupBy(pets, 'type')
17102 // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17103 // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17106 // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17108 // 3: [{type: 'Cat', name: 'Leo'}],
17109 // 4: [{type: 'Dog', name: 'Spot'}],
17110 // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17113 function utilArrayGroupBy(a, key) {
17114 return a.reduce(function (acc, item) {
17115 var group = typeof key === 'function' ? key(item) : item[key];
17116 (acc[group] = acc[group] || []).push(item);
17119 } // Returns an Array with all the duplicates removed
17120 // where uniqueness determined by the given key
17121 // `key` can be passed as a property or as a key function
17124 // { type: 'Dog', name: 'Spot' },
17125 // { type: 'Cat', name: 'Tiger' },
17126 // { type: 'Dog', name: 'Rover' },
17127 // { type: 'Cat', name: 'Leo' }
17130 // utilArrayUniqBy(pets, 'type')
17132 // { type: 'Dog', name: 'Spot' },
17133 // { type: 'Cat', name: 'Tiger' }
17136 // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17138 // { type: 'Dog', name: 'Spot' },
17139 // { type: 'Cat', name: 'Tiger' },
17140 // { type: 'Cat', name: 'Leo' }
17143 function utilArrayUniqBy(a, key) {
17144 var seen = new Set();
17145 return a.reduce(function (acc, item) {
17146 var val = typeof key === 'function' ? key(item) : item[key];
17148 if (val && !seen.has(val)) {
17158 fixRegexpWellKnownSymbolLogic('match', 1, function (MATCH, nativeMatch, maybeCallNative) {
17160 // `String.prototype.match` method
17161 // https://tc39.github.io/ecma262/#sec-string.prototype.match
17162 function match(regexp) {
17163 var O = requireObjectCoercible(this);
17164 var matcher = regexp == undefined ? undefined : regexp[MATCH];
17165 return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17167 // `RegExp.prototype[@@match]` method
17168 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@match
17169 function (regexp) {
17170 var res = maybeCallNative(nativeMatch, regexp, this);
17171 if (res.done) return res.value;
17173 var rx = anObject(regexp);
17174 var S = String(this);
17176 if (!rx.global) return regexpExecAbstract(rx, S);
17178 var fullUnicode = rx.unicode;
17183 while ((result = regexpExecAbstract(rx, S)) !== null) {
17184 var matchStr = String(result[0]);
17186 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17189 return n === 0 ? null : A;
17194 var remove$1 = removeDiacritics;
17195 var replacementList = [{
17203 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"
17209 chars: "\xC6\u01FC\u01E2"
17218 chars: "\uA738\uA73A"
17224 chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17227 chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17230 chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17236 chars: "\u01F1\u01C4"
17239 chars: "\u01F2\u01C5"
17242 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"
17245 chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17248 chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17251 chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17254 chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17257 chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17260 chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17263 chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17272 chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17275 chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17284 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"
17299 chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17302 chars: "\u24C6\uFF31\uA756\uA758\u024A"
17305 chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17308 chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17311 chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17320 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"
17323 chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17329 chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17332 chars: "\u24CD\uFF38\u1E8A\u1E8C"
17335 chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17338 chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17341 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"
17347 chars: "\xE6\u01FD\u01E3"
17356 chars: "\uA739\uA73B"
17362 chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17365 chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17368 chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17374 chars: "\u01F3\u01C6"
17377 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"
17380 chars: "\u24D5\uFF46\u1E1F\u0192"
17398 chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17401 chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17407 chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17410 chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17413 chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17416 chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17422 chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17425 chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17431 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"
17446 chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17449 chars: "\u24E0\uFF51\u024B\uA757\uA759"
17452 chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17455 chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17461 chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17470 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"
17473 chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17479 chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17482 chars: "\u24E7\uFF58\u1E8B\u1E8D"
17485 chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17488 chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17490 var diacriticsMap = {};
17492 for (var i = 0; i < replacementList.length; i += 1) {
17493 var chars = replacementList[i].chars;
17495 for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17496 diacriticsMap[chars[j$1]] = replacementList[i].base;
17500 function removeDiacritics(str) {
17501 return str.replace(/[^\u0000-\u007e]/g, function (c) {
17502 return diacriticsMap[c] || c;
17506 var replacementList_1 = replacementList;
17507 var diacriticsMap_1 = diacriticsMap;
17510 replacementList: replacementList_1,
17511 diacriticsMap: diacriticsMap_1
17514 var isArabic_1 = createCommonjsModule(function (module, exports) {
17516 Object.defineProperty(exports, "__esModule", {
17519 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
17522 function isArabic(_char) {
17523 if (_char.length > 1) {
17524 // allow the newer chars?
17525 throw new Error('isArabic works on only one-character strings');
17528 var code = _char.charCodeAt(0);
17530 for (var i = 0; i < arabicBlocks.length; i++) {
17531 var block = arabicBlocks[i];
17533 if (code >= block[0] && code <= block[1]) {
17541 exports.isArabic = isArabic;
17543 function isMath(_char2) {
17544 if (_char2.length > 2) {
17545 // allow the newer chars?
17546 throw new Error('isMath works on only one-character strings');
17549 var code = _char2.charCodeAt(0);
17551 return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17554 exports.isMath = isMath;
17557 var unicodeArabic = createCommonjsModule(function (module, exports) {
17559 Object.defineProperty(exports, "__esModule", {
17562 var arabicReference = {
17564 "normal": ["\u0627"],
17566 "normal": ["\u0627\u0653", "\u0622"],
17567 "isolated": "\uFE81",
17571 "normal": ["\u0627\u0654", "\u0623"],
17572 "isolated": "\uFE83",
17576 "normal": ["\u0627\u0655", "\u0625"],
17577 "isolated": "\uFE87",
17581 "normal": "\u0671",
17582 "isolated": "\uFB50",
17585 "wavy_hamza_above": ["\u0672"],
17586 "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17587 "high_hamza": ["\u0675", "\u0627\u0674"],
17588 "indic_two_above": ["\u0773"],
17589 "indic_three_above": ["\u0774"],
17591 "normal": ["\u0627\u064B"],
17593 "isolated": "\uFD3D"
17595 "isolated": "\uFE8D",
17599 "normal": ["\u0628"],
17600 "dotless": ["\u066E"],
17601 "three_dots_horizontally_below": ["\u0750"],
17602 "dot_below_three_dots_above": ["\u0751"],
17603 "three_dots_pointing_upwards_below": ["\u0752"],
17604 "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17605 "two_dots_below_dot_above": ["\u0754"],
17606 "inverted_small_v_below": ["\u0755"],
17607 "small_v": ["\u0756"],
17608 "small_v_below": ["\u08A0"],
17609 "hamza_above": ["\u08A1"],
17610 "small_meem_above": ["\u08B6"],
17611 "isolated": "\uFE8F",
17613 "initial": "\uFE91",
17617 "normal": ["\u0629"],
17618 "isolated": "\uFE93",
17622 "normal": ["\u062A"],
17623 "ring": ["\u067C"],
17624 "three_dots_above_downwards": ["\u067D"],
17625 "small_teh_above": ["\u08B8"],
17626 "isolated": "\uFE95",
17628 "initial": "\uFE97",
17632 "normal": ["\u062B"],
17633 "isolated": "\uFE99",
17635 "initial": "\uFE9B",
17639 "normal": ["\u062C"],
17640 "two_dots_above": ["\u08A2"],
17641 "isolated": "\uFE9D",
17643 "initial": "\uFE9F",
17647 "normal": ["\u062D"],
17648 "hamza_above": ["\u0681"],
17649 "two_dots_vertical_above": ["\u0682"],
17650 "three_dots_above": ["\u0685"],
17651 "two_dots_above": ["\u0757"],
17652 "three_dots_pointing_upwards_below": ["\u0758"],
17653 "small_tah_below": ["\u076E"],
17654 "small_tah_two_dots": ["\u076F"],
17655 "small_tah_above": ["\u0772"],
17656 "indic_four_below": ["\u077C"],
17657 "isolated": "\uFEA1",
17659 "initial": "\uFEA3",
17663 "normal": ["\u062E"],
17664 "isolated": "\uFEA5",
17666 "initial": "\uFEA7",
17670 "normal": ["\u062F"],
17671 "ring": ["\u0689"],
17672 "dot_below": ["\u068A"],
17673 "dot_below_small_tah": ["\u068B"],
17674 "three_dots_above_downwards": ["\u068F"],
17675 "four_dots_above": ["\u0690"],
17676 "inverted_v": ["\u06EE"],
17677 "two_dots_vertically_below_small_tah": ["\u0759"],
17678 "inverted_small_v_below": ["\u075A"],
17679 "three_dots_below": ["\u08AE"],
17680 "isolated": "\uFEA9",
17684 "normal": ["\u0630"],
17685 "isolated": "\uFEAB",
17689 "normal": ["\u0631"],
17690 "small_v": ["\u0692"],
17691 "ring": ["\u0693"],
17692 "dot_below": ["\u0694"],
17693 "small_v_below": ["\u0695"],
17694 "dot_below_dot_above": ["\u0696"],
17695 "two_dots_above": ["\u0697"],
17696 "four_dots_above": ["\u0699"],
17697 "inverted_v": ["\u06EF"],
17698 "stroke": ["\u075B"],
17699 "two_dots_vertically_above": ["\u076B"],
17700 "hamza_above": ["\u076C"],
17701 "small_tah_two_dots": ["\u0771"],
17702 "loop": ["\u08AA"],
17703 "small_noon_above": ["\u08B9"],
17704 "isolated": "\uFEAD",
17708 "normal": ["\u0632"],
17709 "inverted_v_above": ["\u08B2"],
17710 "isolated": "\uFEAF",
17714 "normal": ["\u0633"],
17715 "dot_below_dot_above": ["\u069A"],
17716 "three_dots_below": ["\u069B"],
17717 "three_dots_below_three_dots_above": ["\u069C"],
17718 "four_dots_above": ["\u075C"],
17719 "two_dots_vertically_above": ["\u076D"],
17720 "small_tah_two_dots": ["\u0770"],
17721 "indic_four_above": ["\u077D"],
17722 "inverted_v": ["\u077E"],
17723 "isolated": "\uFEB1",
17725 "initial": "\uFEB3",
17729 "normal": ["\u0634"],
17730 "dot_below": ["\u06FA"],
17731 "isolated": "\uFEB5",
17733 "initial": "\uFEB7",
17737 "normal": ["\u0635"],
17738 "two_dots_below": ["\u069D"],
17739 "three_dots_above": ["\u069E"],
17740 "three_dots_below": ["\u08AF"],
17741 "isolated": "\uFEB9",
17743 "initial": "\uFEBB",
17747 "normal": ["\u0636"],
17748 "dot_below": ["\u06FB"],
17749 "isolated": "\uFEBD",
17751 "initial": "\uFEBF",
17755 "normal": ["\u0637"],
17756 "three_dots_above": ["\u069F"],
17757 "two_dots_above": ["\u08A3"],
17758 "isolated": "\uFEC1",
17760 "initial": "\uFEC3",
17764 "normal": ["\u0638"],
17765 "isolated": "\uFEC5",
17767 "initial": "\uFEC7",
17771 "normal": ["\u0639"],
17772 "three_dots_above": ["\u06A0"],
17773 "two_dots_above": ["\u075D"],
17774 "three_dots_pointing_downwards_above": ["\u075E"],
17775 "two_dots_vertically_above": ["\u075F"],
17776 "three_dots_below": ["\u08B3"],
17777 "isolated": "\uFEC9",
17779 "initial": "\uFECB",
17783 "normal": ["\u063A"],
17784 "dot_below": ["\u06FC"],
17785 "isolated": "\uFECD",
17787 "initial": "\uFECF",
17791 "normal": ["\u0641"],
17792 "dotless": ["\u06A1"],
17793 "dot_moved_below": ["\u06A2"],
17794 "dot_below": ["\u06A3"],
17795 "three_dots_below": ["\u06A5"],
17796 "two_dots_below": ["\u0760"],
17797 "three_dots_pointing_upwards_below": ["\u0761"],
17798 "dot_below_three_dots_above": ["\u08A4"],
17799 "isolated": "\uFED1",
17801 "initial": "\uFED3",
17805 "normal": ["\u0642"],
17806 "dotless": ["\u066F"],
17807 "dot_above": ["\u06A7"],
17808 "three_dots_above": ["\u06A8"],
17809 "dot_below": ["\u08A5"],
17810 "isolated": "\uFED5",
17812 "initial": "\uFED7",
17816 "normal": ["\u0643"],
17817 "swash": ["\u06AA"],
17818 "ring": ["\u06AB"],
17819 "dot_above": ["\u06AC"],
17820 "three_dots_below": ["\u06AE"],
17821 "two_dots_above": ["\u077F"],
17822 "dot_below": ["\u08B4"],
17823 "isolated": "\uFED9",
17825 "initial": "\uFEDB",
17829 "normal": ["\u0644"],
17830 "small_v": ["\u06B5"],
17831 "dot_above": ["\u06B6"],
17832 "three_dots_above": ["\u06B7"],
17833 "three_dots_below": ["\u06B8"],
17835 "double_bar": ["\u08A6"],
17836 "isolated": "\uFEDD",
17838 "initial": "\uFEDF",
17842 "normal": ["\u0645"],
17843 "dot_above": ["\u0765"],
17844 "dot_below": ["\u0766"],
17845 "three_dots_above": ["\u08A7"],
17846 "isolated": "\uFEE1",
17848 "initial": "\uFEE3",
17852 "normal": ["\u0646"],
17853 "dot_below": ["\u06B9"],
17854 "ring": ["\u06BC"],
17855 "three_dots_above": ["\u06BD"],
17856 "two_dots_below": ["\u0767"],
17857 "small_tah": ["\u0768"],
17858 "small_v": ["\u0769"],
17859 "isolated": "\uFEE5",
17861 "initial": "\uFEE7",
17865 "normal": ["\u0647"],
17866 "isolated": "\uFEE9",
17868 "initial": "\uFEEB",
17872 "normal": ["\u0648"],
17874 "normal": ["\u0624", "\u0648\u0654"],
17875 "isolated": "\uFE85",
17878 "high_hamza": ["\u0676", "\u0648\u0674"],
17879 "ring": ["\u06C4"],
17880 "two_dots_above": ["\u06CA"],
17881 "dot_above": ["\u06CF"],
17882 "indic_two_above": ["\u0778"],
17883 "indic_three_above": ["\u0779"],
17884 "dot_within": ["\u08AB"],
17885 "isolated": "\uFEED",
17889 "normal": ["\u0649"],
17890 "hamza_above": ["\u0626", "\u064A\u0654"],
17891 "initial": "\uFBE8",
17892 "medial": "\uFBE9",
17893 "isolated": "\uFEEF",
17897 "normal": ["\u064A"],
17899 "normal": ["\u0626", "\u0649\u0654"],
17900 "isolated": "\uFE89",
17902 "initial": "\uFE8B",
17905 "two_dots_below_hamza_above": ["\u08A8"],
17906 "high_hamza": ["\u0678", "\u064A\u0674"],
17907 "tail": ["\u06CD"],
17908 "small_v": ["\u06CE"],
17909 "three_dots_below": ["\u06D1"],
17910 "two_dots_below_dot_above": ["\u08A9"],
17911 "two_dots_below_small_noon_above": ["\u08BA"],
17912 "isolated": "\uFEF1",
17914 "initial": "\uFEF3",
17918 "normal": ["\u0679"],
17919 "isolated": "\uFB66",
17921 "initial": "\uFB68",
17925 "normal": ["\u067A"],
17926 "isolated": "\uFB5E",
17928 "initial": "\uFB60",
17932 "normal": ["\u067B"],
17933 "isolated": "\uFB52",
17935 "initial": "\uFB54",
17939 "normal": ["\u067E"],
17940 "small_meem_above": ["\u08B7"],
17941 "isolated": "\uFB56",
17943 "initial": "\uFB58",
17947 "normal": ["\u067F"],
17948 "isolated": "\uFB62",
17950 "initial": "\uFB64",
17954 "normal": ["\u0680"],
17955 "isolated": "\uFB5A",
17957 "initial": "\uFB5C",
17961 "normal": ["\u0683"],
17962 "isolated": "\uFB76",
17964 "initial": "\uFB78",
17968 "normal": ["\u0684"],
17969 "isolated": "\uFB72",
17971 "initial": "\uFB74",
17975 "normal": ["\u0686"],
17976 "dot_above": ["\u06BF"],
17977 "isolated": "\uFB7A",
17979 "initial": "\uFB7C",
17983 "normal": ["\u0687"],
17984 "isolated": "\uFB7E",
17986 "initial": "\uFB80",
17990 "normal": ["\u0688"],
17991 "isolated": "\uFB88",
17995 "normal": ["\u068C"],
17996 "isolated": "\uFB84",
18000 "normal": ["\u068D"],
18001 "isolated": "\uFB82",
18005 "normal": ["\u068F", "\u068E"],
18006 "isolated": "\uFB86",
18010 "normal": ["\u0691"],
18011 "isolated": "\uFB8C",
18015 "normal": ["\u0698"],
18016 "isolated": "\uFB8A",
18020 "normal": ["\u06A4"],
18021 "isolated": "\uFB6A",
18023 "initial": "\uFB6C",
18027 "normal": ["\u06A6"],
18028 "isolated": "\uFB6E",
18030 "initial": "\uFB70",
18034 "normal": ["\u06A9"],
18035 "dot_above": ["\u0762"],
18036 "three_dots_above": ["\u0763"],
18037 "three_dots_pointing_upwards_below": ["\u0764"],
18038 "isolated": "\uFB8E",
18040 "initial": "\uFB90",
18044 "normal": ["\u06AD"],
18045 "isolated": "\uFBD3",
18047 "initial": "\uFBD5",
18051 "normal": ["\u06AF"],
18052 "ring": ["\u06B0"],
18053 "two_dots_below": ["\u06B2"],
18054 "three_dots_above": ["\u06B4"],
18055 "inverted_stroke": ["\u08B0"],
18056 "isolated": "\uFB92",
18058 "initial": "\uFB94",
18062 "normal": ["\u06B1"],
18063 "isolated": "\uFB9A",
18065 "initial": "\uFB9C",
18069 "normal": ["\u06B3"],
18070 "isolated": "\uFB96",
18072 "initial": "\uFB98",
18076 "normal": ["\u06BA"],
18077 "isolated": "\uFB9E",
18081 "normal": ["\u06BB"],
18082 "isolated": "\uFBA0",
18084 "initial": "\uFBA2",
18087 "heh doachashmee": {
18088 "normal": ["\u06BE"],
18089 "isolated": "\uFBAA",
18091 "initial": "\uFBAC",
18095 "normal": ["\u06C1"],
18096 "hamza_above": ["\u06C1\u0654", "\u06C2"],
18097 "isolated": "\uFBA6",
18099 "initial": "\uFBA8",
18102 "teh marbuta goal": {
18103 "normal": ["\u06C3"]
18106 "normal": ["\u06C5"],
18107 "isolated": "\uFBE0",
18111 "normal": ["\u06C6"],
18112 "isolated": "\uFBD9",
18116 "normal": ["\u06C7"],
18118 "normal": ["\u0677", "\u06C7\u0674"],
18119 "isolated": "\uFBDD"
18121 "isolated": "\uFBD7",
18125 "normal": ["\u06C8"],
18126 "isolated": "\uFBDB",
18130 "normal": ["\u06C9"],
18131 "isolated": "\uFBE2",
18135 "normal": ["\u06CB"],
18136 "isolated": "\uFBDE",
18140 "normal": ["\u06CC"],
18141 "indic_two_above": ["\u0775"],
18142 "indic_three_above": ["\u0776"],
18143 "indic_four_above": ["\u0777"],
18144 "isolated": "\uFBFC",
18146 "initial": "\uFBFE",
18150 "normal": ["\u06D0"],
18151 "isolated": "\uFBE4",
18153 "initial": "\uFBE6",
18157 "normal": ["\u06D2"],
18159 "normal": ["\u06D2\u0654", "\u06D3"],
18160 "isolated": "\uFBB0",
18163 "indic_two_above": ["\u077A"],
18164 "indic_three_above": ["\u077B"],
18165 "isolated": "\uFBAE",
18169 "normal": ["\u06D5"],
18170 "isolated": "\u06D5",
18173 "normal": ["\u06C0", "\u06D5\u0654"],
18174 "isolated": "\uFBA4",
18179 "normal": ["\u08AC"]
18182 "normal": ["\u08AD"]
18185 "normal": ["\u08B1"]
18188 "normal": ["\u08BB"]
18191 "normal": ["\u08BC"]
18194 "normal": ["\u08BD"]
18197 exports["default"] = arabicReference;
18200 var unicodeLigatures = createCommonjsModule(function (module, exports) {
18202 Object.defineProperty(exports, "__esModule", {
18205 var ligatureReference = {
18207 "isolated": "\uFBEA",
18211 "isolated": "\uFBEC",
18215 "isolated": "\uFBEE",
18219 "isolated": "\uFBF0",
18223 "isolated": "\uFBF2",
18227 "isolated": "\uFBF4",
18231 "isolated": "\uFBF6",
18233 "initial": "\uFBF8"
18236 "uighur_kirghiz": {
18237 "isolated": "\uFBF9",
18239 "initial": "\uFBFB"
18241 "isolated": "\uFC03",
18245 "isolated": "\uFC00",
18246 "initial": "\uFC97"
18249 "isolated": "\uFC01",
18250 "initial": "\uFC98"
18253 "isolated": "\uFC02",
18255 "initial": "\uFC9A",
18259 "isolated": "\uFC04",
18263 "isolated": "\uFC05",
18264 "initial": "\uFC9C"
18267 "isolated": "\uFC06",
18268 "initial": "\uFC9D"
18271 "isolated": "\uFC07",
18272 "initial": "\uFC9E"
18275 "isolated": "\uFC08",
18277 "initial": "\uFC9F",
18281 "isolated": "\uFC09",
18285 "isolated": "\uFC0A",
18289 "isolated": "\uFC0B",
18290 "initial": "\uFCA1"
18293 "isolated": "\uFC0C",
18294 "initial": "\uFCA2"
18297 "isolated": "\uFC0D",
18298 "initial": "\uFCA3"
18301 "isolated": "\uFC0E",
18303 "initial": "\uFCA4",
18307 "isolated": "\uFC0F",
18311 "isolated": "\uFC10",
18315 "isolated": "\uFC11"
18318 "isolated": "\uFC12",
18320 "initial": "\uFCA6",
18324 "isolated": "\uFC13",
18328 "isolated": "\uFC14"
18331 "isolated": "\uFC15",
18332 "initial": "\uFCA7"
18335 "isolated": "\uFC16",
18336 "initial": "\uFCA8"
18339 "isolated": "\uFC17",
18340 "initial": "\uFCA9"
18343 "isolated": "\uFC18",
18344 "initial": "\uFCAA"
18347 "isolated": "\uFC19",
18348 "initial": "\uFCAB"
18351 "isolated": "\uFC1A"
18354 "isolated": "\uFC1B",
18355 "initial": "\uFCAC"
18358 "isolated": "\uFC1C",
18359 "initial": "\uFCAD",
18363 "isolated": "\uFC1D",
18364 "initial": "\uFCAE",
18368 "isolated": "\uFC1E",
18369 "initial": "\uFCAF",
18373 "isolated": "\uFC1F",
18374 "initial": "\uFCB0",
18378 "isolated": "\uFC20",
18379 "initial": "\uFCB1"
18382 "isolated": "\uFC21",
18383 "initial": "\uFCB3"
18386 "isolated": "\uFC22",
18387 "initial": "\uFCB4"
18390 "isolated": "\uFC23",
18391 "initial": "\uFCB5"
18394 "isolated": "\uFC24",
18395 "initial": "\uFCB6"
18398 "isolated": "\uFC25",
18399 "initial": "\uFCB7"
18402 "isolated": "\uFC26",
18403 "initial": "\uFCB8"
18406 "isolated": "\uFC27",
18407 "initial": "\uFD33",
18411 "isolated": "\uFC28",
18412 "initial": "\uFCB9",
18416 "isolated": "\uFC29",
18417 "initial": "\uFCBA"
18420 "isolated": "\uFC2A",
18421 "initial": "\uFCBB"
18424 "isolated": "\uFC2B",
18425 "initial": "\uFCBC"
18428 "isolated": "\uFC2C",
18429 "initial": "\uFCBD"
18432 "isolated": "\uFC2D",
18433 "initial": "\uFCBE"
18436 "isolated": "\uFC2E",
18437 "initial": "\uFCBF"
18440 "isolated": "\uFC2F",
18441 "initial": "\uFCC0"
18444 "isolated": "\uFC30",
18445 "initial": "\uFCC1"
18448 "isolated": "\uFC31",
18452 "isolated": "\uFC32",
18456 "isolated": "\uFC33",
18457 "initial": "\uFCC2"
18460 "isolated": "\uFC34",
18461 "initial": "\uFCC3"
18464 "isolated": "\uFC35",
18468 "isolated": "\uFC36",
18472 "isolated": "\uFC37",
18476 "isolated": "\uFC38",
18477 "initial": "\uFCC4"
18480 "isolated": "\uFC39",
18481 "initial": "\uFCC5"
18484 "isolated": "\uFC3A",
18485 "initial": "\uFCC6"
18488 "isolated": "\uFC3B",
18490 "initial": "\uFCC7",
18494 "isolated": "\uFC3C",
18496 "initial": "\uFCC8",
18500 "isolated": "\uFC3D",
18504 "isolated": "\uFC3E",
18508 "isolated": "\uFC3F",
18509 "initial": "\uFCC9"
18512 "isolated": "\uFC40",
18513 "initial": "\uFCCA"
18516 "isolated": "\uFC41",
18517 "initial": "\uFCCB"
18520 "isolated": "\uFC42",
18522 "initial": "\uFCCC",
18526 "isolated": "\uFC43",
18530 "isolated": "\uFC44",
18534 "isolated": "\uFC45",
18535 "initial": "\uFCCE"
18538 "isolated": "\uFC46",
18539 "initial": "\uFCCF"
18542 "isolated": "\uFC47",
18543 "initial": "\uFCD0"
18546 "isolated": "\uFC48",
18548 "initial": "\uFCD1"
18551 "isolated": "\uFC49"
18554 "isolated": "\uFC4A"
18557 "isolated": "\uFC4B",
18558 "initial": "\uFCD2"
18561 "isolated": "\uFC4C",
18562 "initial": "\uFCD3"
18565 "isolated": "\uFC4D",
18566 "initial": "\uFCD4"
18569 "isolated": "\uFC4E",
18571 "initial": "\uFCD5",
18575 "isolated": "\uFC4F",
18579 "isolated": "\uFC50",
18583 "isolated": "\uFC51",
18584 "initial": "\uFCD7"
18587 "isolated": "\uFC52",
18588 "initial": "\uFCD8"
18591 "isolated": "\uFC53"
18594 "isolated": "\uFC54"
18597 "isolated": "\uFC55",
18598 "initial": "\uFCDA"
18601 "isolated": "\uFC56",
18602 "initial": "\uFCDB"
18605 "isolated": "\uFC57",
18606 "initial": "\uFCDC"
18609 "isolated": "\uFC58",
18611 "initial": "\uFCDD",
18615 "isolated": "\uFC59",
18619 "isolated": "\uFC5A",
18623 "isolated": "\uFC5B"
18626 "isolated": "\uFC5C"
18629 "isolated": "\uFC5D",
18633 "isolated": "\uFC5E"
18636 "isolated": "\uFC5F"
18639 "isolated": "\uFC60"
18642 "isolated": "\uFC61"
18645 "isolated": "\uFC62"
18648 "isolated": "\uFC63"
18711 "initial": "\uFC99"
18714 "initial": "\uFC9B",
18718 "initial": "\uFCA0",
18722 "initial": "\uFCA5",
18726 "initial": "\uFCB2"
18729 "initial": "\uFCCD"
18732 "initial": "\uFCD6",
18736 "initial": "\uFCD9"
18739 "initial": "\uFCDE",
18746 "medial": "\uFCE8",
18747 "initial": "\uFD31"
18750 "medial": "\uFCE9",
18751 "isolated": "\uFD0C",
18753 "initial": "\uFD30"
18756 "medial": "\uFCEA",
18757 "initial": "\uFD32"
18759 "\u0640\u064E\u0651": {
18762 "\u0640\u064F\u0651": {
18765 "\u0640\u0650\u0651": {
18769 "isolated": "\uFCF5",
18773 "isolated": "\uFCF6",
18777 "isolated": "\uFCF7",
18781 "isolated": "\uFCF8",
18785 "isolated": "\uFCF9",
18789 "isolated": "\uFCFA",
18793 "isolated": "\uFCFB"
18796 "isolated": "\uFCFC",
18800 "isolated": "\uFCFD",
18804 "isolated": "\uFCFE",
18808 "isolated": "\uFCFF",
18812 "isolated": "\uFD00",
18816 "isolated": "\uFD01",
18820 "isolated": "\uFD02",
18824 "isolated": "\uFD03",
18828 "isolated": "\uFD04",
18832 "isolated": "\uFD05",
18836 "isolated": "\uFD06",
18840 "isolated": "\uFD07",
18844 "isolated": "\uFD08",
18848 "isolated": "\uFD09",
18850 "initial": "\uFD2D",
18854 "isolated": "\uFD0A",
18856 "initial": "\uFD2E",
18860 "isolated": "\uFD0B",
18862 "initial": "\uFD2F",
18866 "isolated": "\uFD0D",
18870 "isolated": "\uFD0E",
18874 "isolated": "\uFD0F",
18878 "isolated": "\uFD10",
18884 "\u062A\u062C\u0645": {
18885 "initial": "\uFD50"
18887 "\u062A\u062D\u062C": {
18889 "initial": "\uFD52"
18891 "\u062A\u062D\u0645": {
18892 "initial": "\uFD53"
18894 "\u062A\u062E\u0645": {
18895 "initial": "\uFD54"
18897 "\u062A\u0645\u062C": {
18898 "initial": "\uFD55"
18900 "\u062A\u0645\u062D": {
18901 "initial": "\uFD56"
18903 "\u062A\u0645\u062E": {
18904 "initial": "\uFD57"
18906 "\u062C\u0645\u062D": {
18908 "initial": "\uFD59"
18910 "\u062D\u0645\u064A": {
18913 "\u062D\u0645\u0649": {
18916 "\u0633\u062D\u062C": {
18917 "initial": "\uFD5C"
18919 "\u0633\u062C\u062D": {
18920 "initial": "\uFD5D"
18922 "\u0633\u062C\u0649": {
18925 "\u0633\u0645\u062D": {
18927 "initial": "\uFD60"
18929 "\u0633\u0645\u062C": {
18930 "initial": "\uFD61"
18932 "\u0633\u0645\u0645": {
18934 "initial": "\uFD63"
18936 "\u0635\u062D\u062D": {
18938 "initial": "\uFD65"
18940 "\u0635\u0645\u0645": {
18942 "initial": "\uFDC5"
18944 "\u0634\u062D\u0645": {
18946 "initial": "\uFD68"
18948 "\u0634\u062C\u064A": {
18951 "\u0634\u0645\u062E": {
18953 "initial": "\uFD6B"
18955 "\u0634\u0645\u0645": {
18957 "initial": "\uFD6D"
18959 "\u0636\u062D\u0649": {
18962 "\u0636\u062E\u0645": {
18964 "initial": "\uFD70"
18966 "\u0636\u0645\u062D": {
18969 "\u0637\u0645\u062D": {
18970 "initial": "\uFD72"
18972 "\u0637\u0645\u0645": {
18973 "initial": "\uFD73"
18975 "\u0637\u0645\u064A": {
18978 "\u0639\u062C\u0645": {
18980 "initial": "\uFDC4"
18982 "\u0639\u0645\u0645": {
18984 "initial": "\uFD77"
18986 "\u0639\u0645\u0649": {
18989 "\u063A\u0645\u0645": {
18992 "\u063A\u0645\u064A": {
18995 "\u063A\u0645\u0649": {
18998 "\u0641\u062E\u0645": {
19000 "initial": "\uFD7D"
19002 "\u0642\u0645\u062D": {
19004 "initial": "\uFDB4"
19006 "\u0642\u0645\u0645": {
19009 "\u0644\u062D\u0645": {
19011 "initial": "\uFDB5"
19013 "\u0644\u062D\u064A": {
19016 "\u0644\u062D\u0649": {
19019 "\u0644\u062C\u062C": {
19020 "initial": "\uFD83",
19023 "\u0644\u062E\u0645": {
19025 "initial": "\uFD86"
19027 "\u0644\u0645\u062D": {
19029 "initial": "\uFD88"
19031 "\u0645\u062D\u062C": {
19032 "initial": "\uFD89"
19034 "\u0645\u062D\u0645": {
19035 "initial": "\uFD8A"
19037 "\u0645\u062D\u064A": {
19040 "\u0645\u062C\u062D": {
19041 "initial": "\uFD8C"
19043 "\u0645\u062C\u0645": {
19044 "initial": "\uFD8D"
19046 "\u0645\u062E\u062C": {
19047 "initial": "\uFD8E"
19049 "\u0645\u062E\u0645": {
19050 "initial": "\uFD8F"
19052 "\u0645\u062C\u062E": {
19053 "initial": "\uFD92"
19055 "\u0647\u0645\u062C": {
19056 "initial": "\uFD93"
19058 "\u0647\u0645\u0645": {
19059 "initial": "\uFD94"
19061 "\u0646\u062D\u0645": {
19062 "initial": "\uFD95"
19064 "\u0646\u062D\u0649": {
19067 "\u0646\u062C\u0645": {
19069 "initial": "\uFD98"
19071 "\u0646\u062C\u0649": {
19074 "\u0646\u0645\u064A": {
19077 "\u0646\u0645\u0649": {
19080 "\u064A\u0645\u0645": {
19082 "initial": "\uFD9D"
19084 "\u0628\u062E\u064A": {
19087 "\u062A\u062C\u064A": {
19090 "\u062A\u062C\u0649": {
19093 "\u062A\u062E\u064A": {
19096 "\u062A\u062E\u0649": {
19099 "\u062A\u0645\u064A": {
19102 "\u062A\u0645\u0649": {
19105 "\u062C\u0645\u064A": {
19108 "\u062C\u062D\u0649": {
19111 "\u062C\u0645\u0649": {
19114 "\u0633\u062E\u0649": {
19117 "\u0635\u062D\u064A": {
19120 "\u0634\u062D\u064A": {
19123 "\u0636\u062D\u064A": {
19126 "\u0644\u062C\u064A": {
19129 "\u0644\u0645\u064A": {
19132 "\u064A\u062D\u064A": {
19135 "\u064A\u062C\u064A": {
19138 "\u064A\u0645\u064A": {
19141 "\u0645\u0645\u064A": {
19144 "\u0642\u0645\u064A": {
19147 "\u0646\u062D\u064A": {
19150 "\u0639\u0645\u064A": {
19153 "\u0643\u0645\u064A": {
19156 "\u0646\u062C\u062D": {
19157 "initial": "\uFDB8",
19160 "\u0645\u062E\u064A": {
19163 "\u0644\u062C\u0645": {
19164 "initial": "\uFDBA",
19167 "\u0643\u0645\u0645": {
19169 "initial": "\uFDC3"
19171 "\u062C\u062D\u064A": {
19174 "\u062D\u062C\u064A": {
19177 "\u0645\u062C\u064A": {
19180 "\u0641\u0645\u064A": {
19183 "\u0628\u062D\u064A": {
19186 "\u0633\u062E\u064A": {
19189 "\u0646\u062C\u064A": {
19193 "isolated": "\uFEF5",
19197 "isolated": "\uFEF7",
19201 "isolated": "\uFEF9",
19205 "isolated": "\uFEFB",
19209 "\u0635\u0644\u06D2": "\uFDF0",
19210 "\u0642\u0644\u06D2": "\uFDF1",
19211 "\u0627\u0644\u0644\u0647": "\uFDF2",
19212 "\u0627\u0643\u0628\u0631": "\uFDF3",
19213 "\u0645\u062D\u0645\u062F": "\uFDF4",
19214 "\u0635\u0644\u0639\u0645": "\uFDF5",
19215 "\u0631\u0633\u0648\u0644": "\uFDF6",
19216 "\u0639\u0644\u064A\u0647": "\uFDF7",
19217 "\u0648\u0633\u0644\u0645": "\uFDF8",
19218 "\u0635\u0644\u0649": "\uFDF9",
19219 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19220 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19221 "\u0631\u06CC\u0627\u0644": "\uFDFC"
19224 exports["default"] = ligatureReference;
19227 var reference = createCommonjsModule(function (module, exports) {
19229 Object.defineProperty(exports, "__esModule", {
19232 var letterList = Object.keys(unicodeArabic["default"]);
19233 exports.letterList = letterList;
19234 var ligatureList = Object.keys(unicodeLigatures["default"]);
19235 exports.ligatureList = ligatureList;
19236 var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19237 exports.ligatureWordList = ligatureWordList;
19238 var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19239 exports.lams = lams;
19240 var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19241 exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19242 // console.log('-');
19243 // for (var a = 0; a < alefs.length; a++) {
19244 // console.log(a + ': ' + lams[l] + alefs[a]);
19248 var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19249 exports.tashkeel = tashkeel;
19251 function addToTashkeel(start, finish) {
19252 for (var i = start; i <= finish; i++) {
19253 exports.tashkeel = tashkeel += String.fromCharCode(i);
19257 addToTashkeel(0x0610, 0x061A);
19258 addToTashkeel(0x064B, 0x065F);
19259 addToTashkeel(0x06D6, 0x06DC);
19260 addToTashkeel(0x06E0, 0x06E4);
19261 addToTashkeel(0x06EA, 0x06ED);
19262 addToTashkeel(0x08D3, 0x08E1);
19263 addToTashkeel(0x08E3, 0x08FF);
19264 addToTashkeel(0xFE70, 0xFE7F);
19265 var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19266 exports.lineBreakers = lineBreakers;
19268 function addToLineBreakers(start, finish) {
19269 for (var i = start; i <= finish; i++) {
19270 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19274 addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19276 addToLineBreakers(0x0621, 0x0625);
19277 addToLineBreakers(0x062F, 0x0632);
19278 addToLineBreakers(0x0660, 0x066D); // numerals, math
19280 addToLineBreakers(0x0671, 0x0677);
19281 addToLineBreakers(0x0688, 0x0699);
19282 addToLineBreakers(0x06C3, 0x06CB);
19283 addToLineBreakers(0x06D2, 0x06F9);
19284 addToLineBreakers(0x0759, 0x075B);
19285 addToLineBreakers(0x08AA, 0x08AE);
19286 addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19287 // Presentation Forms A includes diacritics but they are meant to stand alone
19289 addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19292 addToLineBreakers(0x10E60, 0x10E7F);
19293 addToLineBreakers(0x1EC70, 0x1ECBF);
19294 addToLineBreakers(0x1EE00, 0x1EEFF);
19297 var GlyphSplitter_1 = createCommonjsModule(function (module, exports) {
19299 Object.defineProperty(exports, "__esModule", {
19303 function GlyphSplitter(word) {
19305 var lastLetter = '';
19306 word.split('').forEach(function (letter) {
19307 if (isArabic_1.isArabic(letter)) {
19308 if (reference.tashkeel.indexOf(letter) > -1) {
19309 letters[letters.length - 1] += letter;
19310 } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19312 letters[letters.length - 1] += letter;
19314 letters.push(letter);
19317 letters.push(letter);
19320 if (reference.tashkeel.indexOf(letter) === -1) {
19321 lastLetter = letter;
19327 exports.GlyphSplitter = GlyphSplitter;
19330 var BaselineSplitter_1 = createCommonjsModule(function (module, exports) {
19332 Object.defineProperty(exports, "__esModule", {
19336 function BaselineSplitter(word) {
19338 var lastLetter = '';
19339 word.split('').forEach(function (letter) {
19340 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19341 if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19342 letters[letters.length - 1] += letter;
19343 } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19344 letters.push(letter);
19346 letters[letters.length - 1] += letter;
19349 letters.push(letter);
19352 if (reference.tashkeel.indexOf(letter) === -1) {
19353 // don't allow tashkeel to hide line break
19354 lastLetter = letter;
19360 exports.BaselineSplitter = BaselineSplitter;
19363 var Normalization = createCommonjsModule(function (module, exports) {
19365 Object.defineProperty(exports, "__esModule", {
19369 function Normal(word, breakPresentationForm) {
19370 // default is to turn initial/isolated/medial/final presentation form to generic
19371 if (typeof breakPresentationForm === 'undefined') {
19372 breakPresentationForm = true;
19375 var returnable = '';
19376 word.split('').forEach(function (letter) {
19377 if (!isArabic_1.isArabic(letter)) {
19378 returnable += letter;
19382 for (var w = 0; w < reference.letterList.length; w++) {
19383 // ok so we are checking this potential lettertron
19384 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19385 var versions = Object.keys(letterForms);
19387 for (var v = 0; v < versions.length; v++) {
19388 var localVersion = letterForms[versions[v]];
19390 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19391 // look at this embedded object
19392 var embeddedForms = Object.keys(localVersion);
19394 for (var ef = 0; ef < embeddedForms.length; ef++) {
19395 var form = localVersion[embeddedForms[ef]];
19397 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19399 // console.log('embedded match');
19400 if (form === letter) {
19402 if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19403 // replace presentation form
19404 // console.log('keeping normal form of the letter');
19405 if (_typeof(localVersion['normal']) === 'object') {
19406 returnable += localVersion['normal'][0];
19408 returnable += localVersion['normal'];
19412 } // console.log('keeping this letter');
19415 returnable += letter;
19417 } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19419 returnable += form[0]; // console.log('added the first letter from the same array');
19425 } else if (localVersion === letter) {
19427 if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19428 // replace presentation form
19429 // console.log('keeping normal form of the letter');
19430 if (_typeof(letterForms['normal']) === 'object') {
19431 returnable += letterForms['normal'][0];
19433 returnable += letterForms['normal'];
19437 } // console.log('keeping this letter');
19440 returnable += letter;
19442 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19444 returnable += localVersion[0]; // console.log('added the first letter from the same array');
19452 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19453 var normalForm = reference.ligatureList[v2];
19455 if (normalForm !== 'words') {
19456 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19458 for (var f = 0; f < ligForms.length; f++) {
19459 if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19460 returnable += normalForm;
19465 } // try words ligatures
19468 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19469 var _normalForm = reference.ligatureWordList[v3];
19471 if (unicodeLigatures["default"].words[_normalForm] === letter) {
19472 returnable += _normalForm;
19477 returnable += letter; // console.log('kept the letter')
19482 exports.Normal = Normal;
19485 var CharShaper_1 = createCommonjsModule(function (module, exports) {
19487 Object.defineProperty(exports, "__esModule", {
19491 function CharShaper(letter, form) {
19492 if (!isArabic_1.isArabic(letter)) {
19494 throw new Error('Not Arabic');
19497 if (letter === "\u0621") {
19502 for (var w = 0; w < reference.letterList.length; w++) {
19503 // ok so we are checking this potential lettertron
19504 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19505 var versions = Object.keys(letterForms);
19507 for (var v = 0; v < versions.length; v++) {
19508 var localVersion = letterForms[versions[v]];
19510 if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19511 if (versions.indexOf(form) > -1) {
19512 return letterForms[form];
19514 } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19516 var embeddedVersions = Object.keys(localVersion);
19518 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19519 if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19520 if (embeddedVersions.indexOf(form) > -1) {
19521 return localVersion[form];
19530 exports.CharShaper = CharShaper;
19533 var WordShaper_1 = createCommonjsModule(function (module, exports) {
19535 Object.defineProperty(exports, "__esModule", {
19539 function WordShaper(word) {
19540 var state = 'initial';
19543 for (var w = 0; w < word.length; w++) {
19544 var nextLetter = ' ';
19546 for (var nxw = w + 1; nxw < word.length; nxw++) {
19547 if (!isArabic_1.isArabic(word[nxw])) {
19551 if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19552 nextLetter = word[nxw];
19557 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19558 // space or other non-Arabic
19561 } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19562 // tashkeel - add without changing state
19564 } else if (nextLetter === ' ' || // last Arabic letter in this word
19565 reference.lineBreakers.indexOf(word[w]) > -1) {
19566 // the current letter is known to break lines
19567 output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19569 } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19570 // LA letters - advance an additional letter after this
19571 output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19573 while (word[w] !== nextLetter) {
19579 output += CharShaper_1.CharShaper(word[w], state);
19587 exports.WordShaper = WordShaper;
19590 var ParentLetter_1 = createCommonjsModule(function (module, exports) {
19592 Object.defineProperty(exports, "__esModule", {
19596 function ParentLetter(letter) {
19597 if (!isArabic_1.isArabic(letter)) {
19598 throw new Error('Not an Arabic letter');
19601 for (var w = 0; w < reference.letterList.length; w++) {
19602 // ok so we are checking this potential lettertron
19603 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19604 var versions = Object.keys(letterForms);
19606 for (var v = 0; v < versions.length; v++) {
19607 var localVersion = letterForms[versions[v]];
19609 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19610 // look at this embedded object
19611 var embeddedForms = Object.keys(localVersion);
19613 for (var ef = 0; ef < embeddedForms.length; ef++) {
19614 var form = localVersion[embeddedForms[ef]];
19616 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19618 return localVersion;
19621 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19623 return letterForms;
19631 exports.ParentLetter = ParentLetter;
19633 function GrandparentLetter(letter) {
19634 if (!isArabic_1.isArabic(letter)) {
19635 throw new Error('Not an Arabic letter');
19638 for (var w = 0; w < reference.letterList.length; w++) {
19639 // ok so we are checking this potential lettertron
19640 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19641 var versions = Object.keys(letterForms);
19643 for (var v = 0; v < versions.length; v++) {
19644 var localVersion = letterForms[versions[v]];
19646 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19647 // look at this embedded object
19648 var embeddedForms = Object.keys(localVersion);
19650 for (var ef = 0; ef < embeddedForms.length; ef++) {
19651 var form = localVersion[embeddedForms[ef]];
19653 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19655 return letterForms;
19658 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19660 return letterForms;
19668 exports.GrandparentLetter = GrandparentLetter;
19671 var lib = createCommonjsModule(function (module, exports) {
19673 Object.defineProperty(exports, "__esModule", {
19676 exports.isArabic = isArabic_1.isArabic;
19677 exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter;
19678 exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter;
19679 exports.Normal = Normalization.Normal;
19680 exports.CharShaper = CharShaper_1.CharShaper;
19681 exports.WordShaper = WordShaper_1.WordShaper;
19682 exports.ParentLetter = ParentLetter_1.ParentLetter;
19683 exports.GrandparentLetter = ParentLetter_1.GrandparentLetter;
19686 var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19687 function fixRTLTextForSvg(inputText) {
19690 var arabicRegex = /[\u0600-\u06FF]/g;
19691 var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19692 var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19693 var thaanaVowel = /[\u07A6-\u07B0]/;
19694 var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19696 if (arabicRegex.test(inputText)) {
19697 inputText = lib.WordShaper(inputText);
19700 for (var n = 0; n < inputText.length; n++) {
19701 var c = inputText[n];
19703 if (arabicMath.test(c)) {
19704 // Arabic numbers go LTR
19705 ret += rtlBuffer.reverse().join('');
19708 if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19709 ret += rtlBuffer.reverse().join('');
19713 if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19714 rtlBuffer[rtlBuffer.length - 1] += c;
19715 } else if (rtlRegex.test(c) // include Arabic presentation forms
19716 || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19718 } else if (c === ' ' && rtlBuffer.length) {
19719 // whitespace within RTL text
19720 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19722 // non-RTL character
19723 ret += rtlBuffer.reverse().join('') + c;
19729 ret += rtlBuffer.reverse().join('');
19733 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19735 // `Object.{ entries, values }` methods implementation
19736 var createMethod$5 = function (TO_ENTRIES) {
19737 return function (it) {
19738 var O = toIndexedObject(it);
19739 var keys = objectKeys(O);
19740 var length = keys.length;
19744 while (length > i) {
19746 if (!descriptors || propertyIsEnumerable.call(O, key)) {
19747 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
19754 var objectToArray = {
19755 // `Object.entries` method
19756 // https://tc39.github.io/ecma262/#sec-object.entries
19757 entries: createMethod$5(true),
19758 // `Object.values` method
19759 // https://tc39.github.io/ecma262/#sec-object.values
19760 values: createMethod$5(false)
19763 var $values = objectToArray.values;
19765 // `Object.values` method
19766 // https://tc39.github.io/ecma262/#sec-object.values
19767 _export({ target: 'Object', stat: true }, {
19768 values: function values(O) {
19773 // https://github.com/openstreetmap/iD/issues/772
19774 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19778 _storage = localStorage;
19779 } catch (e) {} // eslint-disable-line no-empty
19782 _storage = _storage || function () {
19785 getItem: function getItem(k) {
19788 setItem: function setItem(k, v) {
19791 removeItem: function removeItem(k) {
19792 return delete s[k];
19796 // corePreferences is an interface for persisting basic key-value strings
19797 // within and between iD sessions on the same site.
19801 function corePreferences(k, v) {
19803 if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
19805 /* eslint-disable no-console */
19806 if (typeof console !== 'undefined') {
19807 console.error('localStorage quota exceeded');
19809 /* eslint-enable no-console */
19814 function responseText(response) {
19815 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19816 return response.text();
19819 function d3_text (input, init) {
19820 return fetch(input, init).then(responseText);
19823 function responseJson(response) {
19824 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19825 if (response.status === 204 || response.status === 205) return;
19826 return response.json();
19829 function d3_json (input, init) {
19830 return fetch(input, init).then(responseJson);
19833 function parser(type) {
19834 return function (input, init) {
19835 return d3_text(input, init).then(function (text) {
19836 return new DOMParser().parseFromString(text, type);
19841 var d3_xml = parser("application/xml");
19842 var svg = parser("image/svg+xml");
19844 var _mainFileFetcher = coreFileFetcher(); // singleton
19845 // coreFileFetcher asynchronously fetches data from JSON files
19848 function coreFileFetcher() {
19850 var _inflight = {};
19852 'address_formats': 'data/address_formats.min.json',
19853 'deprecated': 'data/deprecated.min.json',
19854 'discarded': 'data/discarded.min.json',
19855 'imagery': 'data/imagery.min.json',
19856 'intro_graph': 'data/intro_graph.min.json',
19857 'keepRight': 'data/keepRight.min.json',
19858 'languages': 'data/languages.min.json',
19859 'locales': 'data/locales.min.json',
19860 'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json',
19861 'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json',
19862 'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json',
19863 'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json',
19864 'preset_categories': 'data/preset_categories.min.json',
19865 'preset_defaults': 'data/preset_defaults.min.json',
19866 'preset_fields': 'data/preset_fields.min.json',
19867 'preset_presets': 'data/preset_presets.min.json',
19868 'phone_formats': 'data/phone_formats.min.json',
19869 'qa_data': 'data/qa_data.min.json',
19870 'shortcuts': 'data/shortcuts.min.json',
19871 'territory_languages': 'data/territory_languages.min.json',
19872 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
19874 var _cachedData = {}; // expose the cache; useful for tests
19876 _this.cache = function () {
19877 return _cachedData;
19878 }; // Returns a Promise to fetch data
19879 // (resolved with the data if we have it already)
19882 _this.get = function (which) {
19883 if (_cachedData[which]) {
19884 return Promise.resolve(_cachedData[which]);
19887 var file = _fileMap[which];
19889 var url = file && _this.asset(file);
19892 return Promise.reject("Unknown data file for \"".concat(which, "\""));
19895 var prom = _inflight[url];
19898 _inflight[url] = prom = d3_json(url).then(function (result) {
19899 delete _inflight[url];
19902 throw new Error("No data loaded for \"".concat(which, "\""));
19905 _cachedData[which] = result;
19907 })["catch"](function (err) {
19908 delete _inflight[url];
19914 }; // Accessor for the file map
19917 _this.fileMap = function (val) {
19918 if (!arguments.length) return _fileMap;
19923 var _assetPath = '';
19925 _this.assetPath = function (val) {
19926 if (!arguments.length) return _assetPath;
19931 var _assetMap = {};
19933 _this.assetMap = function (val) {
19934 if (!arguments.length) return _assetMap;
19939 _this.asset = function (val) {
19940 if (/^http(s)?:\/\//i.test(val)) return val;
19941 var filename = _assetPath + val;
19942 return _assetMap[filename] || filename;
19948 var $findIndex$1 = arrayIteration.findIndex;
19952 var FIND_INDEX = 'findIndex';
19953 var SKIPS_HOLES$1 = true;
19955 var USES_TO_LENGTH$b = arrayMethodUsesToLength(FIND_INDEX);
19957 // Shouldn't skip holes
19958 if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES$1 = false; });
19960 // `Array.prototype.findIndex` method
19961 // https://tc39.github.io/ecma262/#sec-array.prototype.findindex
19962 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 || !USES_TO_LENGTH$b }, {
19963 findIndex: function findIndex(callbackfn /* , that = undefined */) {
19964 return $findIndex$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
19968 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
19969 addToUnscopables(FIND_INDEX);
19971 var $includes$1 = arrayIncludes.includes;
19975 var USES_TO_LENGTH$c = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
19977 // `Array.prototype.includes` method
19978 // https://tc39.github.io/ecma262/#sec-array.prototype.includes
19979 _export({ target: 'Array', proto: true, forced: !USES_TO_LENGTH$c }, {
19980 includes: function includes(el /* , fromIndex = 0 */) {
19981 return $includes$1(this, el, arguments.length > 1 ? arguments[1] : undefined);
19985 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
19986 addToUnscopables('includes');
19988 var notARegexp = function (it) {
19989 if (isRegexp(it)) {
19990 throw TypeError("The method doesn't accept regular expressions");
19994 var MATCH$2 = wellKnownSymbol('match');
19996 var correctIsRegexpLogic = function (METHOD_NAME) {
19999 '/./'[METHOD_NAME](regexp);
20002 regexp[MATCH$2] = false;
20003 return '/./'[METHOD_NAME](regexp);
20004 } catch (error2) { /* empty */ }
20008 // `String.prototype.includes` method
20009 // https://tc39.github.io/ecma262/#sec-string.prototype.includes
20010 _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
20011 includes: function includes(searchString /* , position = 0 */) {
20012 return !!~String(requireObjectCoercible(this))
20013 .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
20019 function utilDetect(refresh) {
20020 if (_detected && !refresh) return _detected;
20022 var ua = navigator.userAgent;
20026 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
20029 _detected.browser = m[1];
20030 _detected.version = m[2];
20033 if (!_detected.browser) {
20034 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
20037 _detected.browser = 'msie';
20038 _detected.version = m[1];
20042 if (!_detected.browser) {
20043 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
20046 _detected.browser = 'Opera';
20047 _detected.version = m[2];
20051 if (!_detected.browser) {
20052 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20055 _detected.browser = m[1];
20056 _detected.version = m[2];
20057 m = ua.match(/version\/([\.\d]+)/i);
20058 if (m !== null) _detected.version = m[1];
20062 if (!_detected.browser) {
20063 _detected.browser = navigator.appName;
20064 _detected.version = navigator.appVersion;
20065 } // keep major.minor version only..
20068 _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
20069 // Legacy Opera has incomplete svg style support. See #715
20071 _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
20073 if (_detected.browser.toLowerCase() === 'msie') {
20074 _detected.ie = true;
20075 _detected.browser = 'Internet Explorer';
20076 _detected.support = parseFloat(_detected.version) >= 11;
20078 _detected.ie = false;
20079 _detected.support = true;
20082 _detected.filedrop = window.FileReader && 'ondrop' in window;
20083 _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20084 _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20087 if (/Win/.test(ua)) {
20088 _detected.os = 'win';
20089 _detected.platform = 'Windows';
20090 } else if (/Mac/.test(ua)) {
20091 _detected.os = 'mac';
20092 _detected.platform = 'Macintosh';
20093 } else if (/X11/.test(ua) || /Linux/.test(ua)) {
20094 _detected.os = 'linux';
20095 _detected.platform = 'Linux';
20097 _detected.os = 'win';
20098 _detected.platform = 'Unknown';
20101 _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
20102 // so assume any "mac" with multitouch is actually iOS
20103 navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
20105 // An array of locales requested by the browser in priority order.
20107 _detected.browserLocales = Array.from(new Set( // remove duplicates
20108 [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
20109 navigator.userLanguage]) // remove any undefined values
20110 .filter(Boolean)));
20113 var loc = window.top.location;
20114 var origin = loc.origin;
20117 // for unpatched IE11
20118 origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
20121 _detected.host = origin + loc.pathname;
20125 var getOwnPropertyNames$2 = objectGetOwnPropertyNames.f;
20126 var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
20127 var defineProperty$a = objectDefineProperty.f;
20128 var trim$2 = stringTrim.trim;
20130 var NUMBER = 'Number';
20131 var NativeNumber = global_1[NUMBER];
20132 var NumberPrototype = NativeNumber.prototype;
20134 // Opera ~12 has broken Object#toString
20135 var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20137 // `ToNumber` abstract operation
20138 // https://tc39.github.io/ecma262/#sec-tonumber
20139 var toNumber = function (argument) {
20140 var it = toPrimitive(argument, false);
20141 var first, third, radix, maxCode, digits, length, index, code;
20142 if (typeof it == 'string' && it.length > 2) {
20144 first = it.charCodeAt(0);
20145 if (first === 43 || first === 45) {
20146 third = it.charCodeAt(2);
20147 if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20148 } else if (first === 48) {
20149 switch (it.charCodeAt(1)) {
20150 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20151 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20152 default: return +it;
20154 digits = it.slice(2);
20155 length = digits.length;
20156 for (index = 0; index < length; index++) {
20157 code = digits.charCodeAt(index);
20158 // parseInt parses a string to a first unavailable symbol
20159 // but ToNumber should return NaN if a string contains unavailable symbols
20160 if (code < 48 || code > maxCode) return NaN;
20161 } return parseInt(digits, radix);
20166 // `Number` constructor
20167 // https://tc39.github.io/ecma262/#sec-number-constructor
20168 if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20169 var NumberWrapper = function Number(value) {
20170 var it = arguments.length < 1 ? 0 : value;
20172 return dummy instanceof NumberWrapper
20173 // check on 1..constructor(foo) case
20174 && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20175 ? inheritIfRequired(new NativeNumber(toNumber(it)), dummy, NumberWrapper) : toNumber(it);
20177 for (var keys$3 = descriptors ? getOwnPropertyNames$2(NativeNumber) : (
20179 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20180 // ES2015 (in case, if modules with ES2015 Number statics required before):
20181 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20182 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger'
20183 ).split(','), j$2 = 0, key$1; keys$3.length > j$2; j$2++) {
20184 if (has(NativeNumber, key$1 = keys$3[j$2]) && !has(NumberWrapper, key$1)) {
20185 defineProperty$a(NumberWrapper, key$1, getOwnPropertyDescriptor$3(NativeNumber, key$1));
20188 NumberWrapper.prototype = NumberPrototype;
20189 NumberPrototype.constructor = NumberWrapper;
20190 redefine(global_1, NUMBER, NumberWrapper);
20193 // `Number.MAX_SAFE_INTEGER` constant
20194 // https://tc39.github.io/ecma262/#sec-number.max_safe_integer
20195 _export({ target: 'Number', stat: true }, {
20196 MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
20199 var aesJs = createCommonjsModule(function (module, exports) {
20200 /*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
20203 function checkInt(value) {
20204 return parseInt(value) === value;
20207 function checkInts(arrayish) {
20208 if (!checkInt(arrayish.length)) {
20212 for (var i = 0; i < arrayish.length; i++) {
20213 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
20221 function coerceArray(arg, copy) {
20222 // ArrayBuffer view
20223 if (arg.buffer && arg.name === 'Uint8Array') {
20228 arg = Array.prototype.slice.call(arg);
20233 } // It's an array; check it is a valid representation of a byte
20236 if (Array.isArray(arg)) {
20237 if (!checkInts(arg)) {
20238 throw new Error('Array contains invalid value: ' + arg);
20241 return new Uint8Array(arg);
20242 } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
20245 if (checkInt(arg.length) && checkInts(arg)) {
20246 return new Uint8Array(arg);
20249 throw new Error('unsupported array-like object');
20252 function createArray(length) {
20253 return new Uint8Array(length);
20256 function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
20257 if (sourceStart != null || sourceEnd != null) {
20258 if (sourceArray.slice) {
20259 sourceArray = sourceArray.slice(sourceStart, sourceEnd);
20261 sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
20265 targetArray.set(sourceArray, targetStart);
20268 var convertUtf8 = function () {
20269 function toBytes(text) {
20272 text = encodeURI(text);
20274 while (i < text.length) {
20275 var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
20278 result.push(parseInt(text.substr(i, 2), 16));
20279 i += 2; // otherwise, just the actual byte
20285 return coerceArray(result);
20288 function fromBytes(bytes) {
20292 while (i < bytes.length) {
20296 result.push(String.fromCharCode(c));
20298 } else if (c > 191 && c < 224) {
20299 result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
20302 result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
20307 return result.join('');
20312 fromBytes: fromBytes
20316 var convertHex = function () {
20317 function toBytes(text) {
20320 for (var i = 0; i < text.length; i += 2) {
20321 result.push(parseInt(text.substr(i, 2), 16));
20325 } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
20328 var Hex = '0123456789abcdef';
20330 function fromBytes(bytes) {
20333 for (var i = 0; i < bytes.length; i++) {
20335 result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
20338 return result.join('');
20343 fromBytes: fromBytes
20345 }(); // Number of rounds by keysize
20348 var numberOfRounds = {
20352 }; // Round constant words
20354 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)
20356 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];
20357 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
20359 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];
20360 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];
20361 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];
20362 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
20364 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];
20365 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];
20366 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];
20367 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
20369 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];
20370 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];
20371 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];
20372 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];
20374 function convertToInt32(bytes) {
20377 for (var i = 0; i < bytes.length; i += 4) {
20378 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
20384 var AES = function AES(key) {
20385 if (!(this instanceof AES)) {
20386 throw Error('AES must be instanitated with `new`');
20389 Object.defineProperty(this, 'key', {
20390 value: coerceArray(key, true)
20396 AES.prototype._prepare = function () {
20397 var rounds = numberOfRounds[this.key.length];
20399 if (rounds == null) {
20400 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
20401 } // encryption round keys
20404 this._Ke = []; // decryption round keys
20408 for (var i = 0; i <= rounds; i++) {
20409 this._Ke.push([0, 0, 0, 0]);
20411 this._Kd.push([0, 0, 0, 0]);
20414 var roundKeyCount = (rounds + 1) * 4;
20415 var KC = this.key.length / 4; // convert the key into ints
20417 var tk = convertToInt32(this.key); // copy values into round key arrays
20421 for (var i = 0; i < KC; i++) {
20423 this._Ke[index][i % 4] = tk[i];
20424 this._Kd[rounds - index][i % 4] = tk[i];
20425 } // key expansion (fips-197 section 5.2)
20428 var rconpointer = 0;
20432 while (t < roundKeyCount) {
20434 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
20435 rconpointer += 1; // key expansion (for non-256 bit)
20438 for (var i = 1; i < KC; i++) {
20439 tk[i] ^= tk[i - 1];
20440 } // key expansion for 256-bit keys is "slightly different" (fips-197)
20443 for (var i = 1; i < KC / 2; i++) {
20444 tk[i] ^= tk[i - 1];
20447 tt = tk[KC / 2 - 1];
20448 tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
20450 for (var i = KC / 2 + 1; i < KC; i++) {
20451 tk[i] ^= tk[i - 1];
20453 } // copy values into round key arrays
20460 while (i < KC && t < roundKeyCount) {
20463 this._Ke[r][c] = tk[i];
20464 this._Kd[rounds - r][c] = tk[i++];
20467 } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
20470 for (var r = 1; r < rounds; r++) {
20471 for (var c = 0; c < 4; c++) {
20472 tt = this._Kd[r][c];
20473 this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
20478 AES.prototype.encrypt = function (plaintext) {
20479 if (plaintext.length != 16) {
20480 throw new Error('invalid plaintext size (must be 16 bytes)');
20483 var rounds = this._Ke.length - 1;
20484 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20486 var t = convertToInt32(plaintext);
20488 for (var i = 0; i < 4; i++) {
20489 t[i] ^= this._Ke[0][i];
20490 } // apply round transforms
20493 for (var r = 1; r < rounds; r++) {
20494 for (var i = 0; i < 4; i++) {
20495 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];
20499 } // the last round is special
20502 var result = createArray(16),
20505 for (var i = 0; i < 4; i++) {
20506 tt = this._Ke[rounds][i];
20507 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20508 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20509 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20510 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
20516 AES.prototype.decrypt = function (ciphertext) {
20517 if (ciphertext.length != 16) {
20518 throw new Error('invalid ciphertext size (must be 16 bytes)');
20521 var rounds = this._Kd.length - 1;
20522 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20524 var t = convertToInt32(ciphertext);
20526 for (var i = 0; i < 4; i++) {
20527 t[i] ^= this._Kd[0][i];
20528 } // apply round transforms
20531 for (var r = 1; r < rounds; r++) {
20532 for (var i = 0; i < 4; i++) {
20533 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];
20537 } // the last round is special
20540 var result = createArray(16),
20543 for (var i = 0; i < 4; i++) {
20544 tt = this._Kd[rounds][i];
20545 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20546 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20547 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20548 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
20554 * Mode Of Operation - Electonic Codebook (ECB)
20558 var ModeOfOperationECB = function ModeOfOperationECB(key) {
20559 if (!(this instanceof ModeOfOperationECB)) {
20560 throw Error('AES must be instanitated with `new`');
20563 this.description = "Electronic Code Block";
20565 this._aes = new AES(key);
20568 ModeOfOperationECB.prototype.encrypt = function (plaintext) {
20569 plaintext = coerceArray(plaintext);
20571 if (plaintext.length % 16 !== 0) {
20572 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20575 var ciphertext = createArray(plaintext.length);
20576 var block = createArray(16);
20578 for (var i = 0; i < plaintext.length; i += 16) {
20579 copyArray(plaintext, block, 0, i, i + 16);
20580 block = this._aes.encrypt(block);
20581 copyArray(block, ciphertext, i);
20587 ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
20588 ciphertext = coerceArray(ciphertext);
20590 if (ciphertext.length % 16 !== 0) {
20591 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20594 var plaintext = createArray(ciphertext.length);
20595 var block = createArray(16);
20597 for (var i = 0; i < ciphertext.length; i += 16) {
20598 copyArray(ciphertext, block, 0, i, i + 16);
20599 block = this._aes.decrypt(block);
20600 copyArray(block, plaintext, i);
20606 * Mode Of Operation - Cipher Block Chaining (CBC)
20610 var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
20611 if (!(this instanceof ModeOfOperationCBC)) {
20612 throw Error('AES must be instanitated with `new`');
20615 this.description = "Cipher Block Chaining";
20619 iv = createArray(16);
20620 } else if (iv.length != 16) {
20621 throw new Error('invalid initialation vector size (must be 16 bytes)');
20624 this._lastCipherblock = coerceArray(iv, true);
20625 this._aes = new AES(key);
20628 ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
20629 plaintext = coerceArray(plaintext);
20631 if (plaintext.length % 16 !== 0) {
20632 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20635 var ciphertext = createArray(plaintext.length);
20636 var block = createArray(16);
20638 for (var i = 0; i < plaintext.length; i += 16) {
20639 copyArray(plaintext, block, 0, i, i + 16);
20641 for (var j = 0; j < 16; j++) {
20642 block[j] ^= this._lastCipherblock[j];
20645 this._lastCipherblock = this._aes.encrypt(block);
20646 copyArray(this._lastCipherblock, ciphertext, i);
20652 ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
20653 ciphertext = coerceArray(ciphertext);
20655 if (ciphertext.length % 16 !== 0) {
20656 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20659 var plaintext = createArray(ciphertext.length);
20660 var block = createArray(16);
20662 for (var i = 0; i < ciphertext.length; i += 16) {
20663 copyArray(ciphertext, block, 0, i, i + 16);
20664 block = this._aes.decrypt(block);
20666 for (var j = 0; j < 16; j++) {
20667 plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
20670 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
20676 * Mode Of Operation - Cipher Feedback (CFB)
20680 var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
20681 if (!(this instanceof ModeOfOperationCFB)) {
20682 throw Error('AES must be instanitated with `new`');
20685 this.description = "Cipher Feedback";
20689 iv = createArray(16);
20690 } else if (iv.length != 16) {
20691 throw new Error('invalid initialation vector size (must be 16 size)');
20694 if (!segmentSize) {
20698 this.segmentSize = segmentSize;
20699 this._shiftRegister = coerceArray(iv, true);
20700 this._aes = new AES(key);
20703 ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
20704 if (plaintext.length % this.segmentSize != 0) {
20705 throw new Error('invalid plaintext size (must be segmentSize bytes)');
20708 var encrypted = coerceArray(plaintext, true);
20711 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
20712 xorSegment = this._aes.encrypt(this._shiftRegister);
20714 for (var j = 0; j < this.segmentSize; j++) {
20715 encrypted[i + j] ^= xorSegment[j];
20716 } // Shift the register
20719 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20720 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20726 ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
20727 if (ciphertext.length % this.segmentSize != 0) {
20728 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
20731 var plaintext = coerceArray(ciphertext, true);
20734 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
20735 xorSegment = this._aes.encrypt(this._shiftRegister);
20737 for (var j = 0; j < this.segmentSize; j++) {
20738 plaintext[i + j] ^= xorSegment[j];
20739 } // Shift the register
20742 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20743 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20749 * Mode Of Operation - Output Feedback (OFB)
20753 var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
20754 if (!(this instanceof ModeOfOperationOFB)) {
20755 throw Error('AES must be instanitated with `new`');
20758 this.description = "Output Feedback";
20762 iv = createArray(16);
20763 } else if (iv.length != 16) {
20764 throw new Error('invalid initialation vector size (must be 16 bytes)');
20767 this._lastPrecipher = coerceArray(iv, true);
20768 this._lastPrecipherIndex = 16;
20769 this._aes = new AES(key);
20772 ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
20773 var encrypted = coerceArray(plaintext, true);
20775 for (var i = 0; i < encrypted.length; i++) {
20776 if (this._lastPrecipherIndex === 16) {
20777 this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
20778 this._lastPrecipherIndex = 0;
20781 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
20785 }; // Decryption is symetric
20788 ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
20790 * Counter object for CTR common mode of operation
20793 var Counter = function Counter(initialValue) {
20794 if (!(this instanceof Counter)) {
20795 throw Error('Counter must be instanitated with `new`');
20796 } // We allow 0, but anything false-ish uses the default 1
20799 if (initialValue !== 0 && !initialValue) {
20803 if (typeof initialValue === 'number') {
20804 this._counter = createArray(16);
20805 this.setValue(initialValue);
20807 this.setBytes(initialValue);
20811 Counter.prototype.setValue = function (value) {
20812 if (typeof value !== 'number' || parseInt(value) != value) {
20813 throw new Error('invalid counter value (must be an integer)');
20814 } // We cannot safely handle numbers beyond the safe range for integers
20817 if (value > Number.MAX_SAFE_INTEGER) {
20818 throw new Error('integer value out of safe range');
20821 for (var index = 15; index >= 0; --index) {
20822 this._counter[index] = value % 256;
20823 value = parseInt(value / 256);
20827 Counter.prototype.setBytes = function (bytes) {
20828 bytes = coerceArray(bytes, true);
20830 if (bytes.length != 16) {
20831 throw new Error('invalid counter bytes size (must be 16 bytes)');
20834 this._counter = bytes;
20837 Counter.prototype.increment = function () {
20838 for (var i = 15; i >= 0; i--) {
20839 if (this._counter[i] === 255) {
20840 this._counter[i] = 0;
20842 this._counter[i]++;
20848 * Mode Of Operation - Counter (CTR)
20852 var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
20853 if (!(this instanceof ModeOfOperationCTR)) {
20854 throw Error('AES must be instanitated with `new`');
20857 this.description = "Counter";
20860 if (!(counter instanceof Counter)) {
20861 counter = new Counter(counter);
20864 this._counter = counter;
20865 this._remainingCounter = null;
20866 this._remainingCounterIndex = 16;
20867 this._aes = new AES(key);
20870 ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
20871 var encrypted = coerceArray(plaintext, true);
20873 for (var i = 0; i < encrypted.length; i++) {
20874 if (this._remainingCounterIndex === 16) {
20875 this._remainingCounter = this._aes.encrypt(this._counter._counter);
20876 this._remainingCounterIndex = 0;
20878 this._counter.increment();
20881 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
20885 }; // Decryption is symetric
20888 ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
20890 // See:https://tools.ietf.org/html/rfc2315
20892 function pkcs7pad(data) {
20893 data = coerceArray(data, true);
20894 var padder = 16 - data.length % 16;
20895 var result = createArray(data.length + padder);
20896 copyArray(data, result);
20898 for (var i = data.length; i < result.length; i++) {
20899 result[i] = padder;
20905 function pkcs7strip(data) {
20906 data = coerceArray(data, true);
20908 if (data.length < 16) {
20909 throw new Error('PKCS#7 invalid length');
20912 var padder = data[data.length - 1];
20915 throw new Error('PKCS#7 padding byte out of range');
20918 var length = data.length - padder;
20920 for (var i = 0; i < padder; i++) {
20921 if (data[length + i] !== padder) {
20922 throw new Error('PKCS#7 invalid padding byte');
20926 var result = createArray(length);
20927 copyArray(data, result, 0, 0, length);
20929 } ///////////////////////
20931 // The block cipher
20938 ecb: ModeOfOperationECB,
20939 cbc: ModeOfOperationCBC,
20940 cfb: ModeOfOperationCFB,
20941 ofb: ModeOfOperationOFB,
20942 ctr: ModeOfOperationCTR
20955 coerceArray: coerceArray,
20956 createArray: createArray,
20957 copyArray: copyArray
20962 module.exports = aesjs; // RequireJS/AMD
20963 // http://www.requirejs.org/docs/api.html
20964 // https://github.com/amdjs/amdjs-api/wiki/AMD
20969 // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
20970 // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16));
20971 // This default signing key is built into iD and can be used to mask/unmask sensitive values.
20973 var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
20974 function utilAesEncrypt(text, key) {
20975 key = key || DEFAULT_128;
20976 var textBytes = aesJs.utils.utf8.toBytes(text);
20977 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20978 var encryptedBytes = aesCtr.encrypt(textBytes);
20979 var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
20980 return encryptedHex;
20982 function utilAesDecrypt(encryptedHex, key) {
20983 key = key || DEFAULT_128;
20984 var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
20985 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20986 var decryptedBytes = aesCtr.decrypt(encryptedBytes);
20987 var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
20991 function utilCleanTags(tags) {
20994 for (var k in tags) {
20998 if (v !== undefined) {
20999 out[k] = cleanValue(k, v);
21005 function cleanValue(k, v) {
21006 function keepSpaces(k) {
21007 return /_hours|_times|:conditional$/.test(k);
21011 return /^(description|note|fixme)$/.test(k);
21014 if (skip(k)) return v;
21015 var cleaned = v.split(';').map(function (s) {
21017 }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
21018 // It is only intended to prevent obvious copy-paste errors. (#2323)
21019 // clean website- and email-like tags
21021 if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
21022 cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
21029 // Like selection.property('value', ...), but avoids no-op value sets,
21030 // which can result in layout/repaint thrashing in some situations.
21031 function utilGetSetValue(selection, value) {
21032 function d3_selection_value(value) {
21033 function valueNull() {
21037 function valueConstant() {
21038 if (this.value !== value) {
21039 this.value = value;
21043 function valueFunction() {
21044 var x = value.apply(this, arguments);
21046 if (x === null || x === undefined) {
21048 } else if (this.value !== x) {
21053 return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
21056 if (arguments.length === 1) {
21057 return selection.property('value');
21060 return selection.each(d3_selection_value(value));
21063 function utilKeybinding(namespace) {
21064 var _keybindings = {};
21066 function testBindings(d3_event, isCapturing) {
21067 var didMatch = false;
21068 var bindings = Object.keys(_keybindings).map(function (id) {
21069 return _keybindings[id];
21071 var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
21072 // so we don't strictly match on the shift key, but we prioritize
21073 // shifted keybindings first, and fallback to unshifted only if no match.
21074 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
21075 // priority match shifted keybindings first
21077 for (i = 0; i < bindings.length; i++) {
21078 binding = bindings[i];
21079 if (!binding.event.modifiers.shiftKey) continue; // no shift
21081 if (!!binding.capture !== isCapturing) continue;
21083 if (matches(d3_event, binding, true)) {
21084 binding.callback(d3_event);
21085 didMatch = true; // match a max of one binding per event
21091 if (didMatch) return; // then unshifted keybindings
21093 for (i = 0; i < bindings.length; i++) {
21094 binding = bindings[i];
21095 if (binding.event.modifiers.shiftKey) continue; // shift
21097 if (!!binding.capture !== isCapturing) continue;
21099 if (matches(d3_event, binding, false)) {
21100 binding.callback(d3_event);
21105 function matches(d3_event, binding, testShift) {
21106 var event = d3_event;
21107 var isMatch = false;
21108 var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
21110 if (event.key !== undefined) {
21111 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
21115 if (binding.event.key === undefined) {
21117 } else if (Array.isArray(binding.event.key)) {
21118 if (binding.event.key.map(function (s) {
21119 return s.toLowerCase();
21120 }).indexOf(event.key.toLowerCase()) === -1) isMatch = false;
21122 if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) isMatch = false;
21124 } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
21125 // - browser doesn't support `KeyboardEvent.key`
21126 // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
21129 if (!isMatch && tryKeyCode) {
21130 isMatch = event.keyCode === binding.event.keyCode;
21133 if (!isMatch) return false; // test modifier keys
21135 if (!(event.ctrlKey && event.altKey)) {
21136 // if both are set, assume AltGr and skip it - #4096
21137 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
21138 if (event.altKey !== binding.event.modifiers.altKey) return false;
21141 if (event.metaKey !== binding.event.modifiers.metaKey) return false;
21142 if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
21147 function capture(d3_event) {
21148 testBindings(d3_event, true);
21151 function bubble(d3_event) {
21152 var tagName = select(d3_event.target).node().tagName;
21154 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
21158 testBindings(d3_event, false);
21161 function keybinding(selection) {
21162 selection = selection || select(document);
21163 selection.on('keydown.capture.' + namespace, capture, true);
21164 selection.on('keydown.bubble.' + namespace, bubble, false);
21166 } // was: keybinding.off()
21169 keybinding.unbind = function (selection) {
21171 selection = selection || select(document);
21172 selection.on('keydown.capture.' + namespace, null);
21173 selection.on('keydown.bubble.' + namespace, null);
21177 keybinding.clear = function () {
21180 }; // Remove one or more keycode bindings.
21183 keybinding.off = function (codes, capture) {
21184 var arr = utilArrayUniq([].concat(codes));
21186 for (var i = 0; i < arr.length; i++) {
21187 var id = arr[i] + (capture ? '-capture' : '-bubble');
21188 delete _keybindings[id];
21192 }; // Add one or more keycode bindings.
21195 keybinding.on = function (codes, callback, capture) {
21196 if (typeof callback !== 'function') {
21197 return keybinding.off(codes, capture);
21200 var arr = utilArrayUniq([].concat(codes));
21202 for (var i = 0; i < arr.length; i++) {
21203 var id = arr[i] + (capture ? '-capture' : '-bubble');
21207 callback: callback,
21222 if (_keybindings[id]) {
21223 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
21226 _keybindings[id] = binding;
21227 var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
21229 for (var j = 0; j < matches.length; j++) {
21230 // Normalise matching errors
21231 if (matches[j] === '++') matches[j] = '+';
21233 if (matches[j] in utilKeybinding.modifierCodes) {
21234 var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
21235 binding.event.modifiers[prop] = true;
21237 binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
21239 if (matches[j] in utilKeybinding.keyCodes) {
21240 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
21252 * See https://github.com/keithamus/jwerty
21255 utilKeybinding.modifierCodes = {
21259 // CTRL key, on Mac: ⌃
21262 // ALT key, on Mac: ⌥ (Alt)
21266 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
21273 utilKeybinding.modifierProperties = {
21279 utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
21280 utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
21281 utilKeybinding.keys = {
21282 // Backspace key, on Mac: ⌫ (Backspace)
21284 backspace: 'Backspace',
21285 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21298 'pause-break': 'Pause',
21299 // Caps Lock key, ⇪
21302 'caps-lock': 'CapsLock',
21303 // Escape key, on Mac: ⎋, on Windows: Esc
21304 '⎋': ['Escape', 'Esc'],
21305 escape: ['Escape', 'Esc'],
21306 esc: ['Escape', 'Esc'],
21308 space: [' ', 'Spacebar'],
21309 // Page-Up key, or pgup, on Mac: ↖
21312 'page-up': 'PageUp',
21313 // Page-Down key, or pgdown, on Mac: ↘
21315 pgdown: 'PageDown',
21316 'page-down': 'PageDown',
21317 // END key, on Mac: ⇟
21320 // HOME key, on Mac: ⇞
21323 // Insert key, or ins
21326 // Delete key, on Mac: ⌦ (Delete)
21327 '⌦': ['Delete', 'Del'],
21328 del: ['Delete', 'Del'],
21329 'delete': ['Delete', 'Del'],
21330 // Left Arrow Key, or ←
21331 '←': ['ArrowLeft', 'Left'],
21332 left: ['ArrowLeft', 'Left'],
21333 'arrow-left': ['ArrowLeft', 'Left'],
21334 // Up Arrow Key, or ↑
21335 '↑': ['ArrowUp', 'Up'],
21336 up: ['ArrowUp', 'Up'],
21337 'arrow-up': ['ArrowUp', 'Up'],
21338 // Right Arrow Key, or →
21339 '→': ['ArrowRight', 'Right'],
21340 right: ['ArrowRight', 'Right'],
21341 'arrow-right': ['ArrowRight', 'Right'],
21342 // Up Arrow Key, or ↓
21343 '↓': ['ArrowDown', 'Down'],
21344 down: ['ArrowDown', 'Down'],
21345 'arrow-down': ['ArrowDown', 'Down'],
21346 // odities, stuff for backward compatibility (browsers and code):
21347 // Num-Multiply, or *
21348 '*': ['*', 'Multiply'],
21349 star: ['*', 'Multiply'],
21350 asterisk: ['*', 'Multiply'],
21351 multiply: ['*', 'Multiply'],
21354 'plus': ['+', 'Add'],
21355 // Num-Subtract, or -
21356 '-': ['-', 'Subtract'],
21357 subtract: ['-', 'Subtract'],
21358 'dash': ['-', 'Subtract'],
21365 // Period, or ., or full-stop
21368 // Slash, or /, or forward-slash
21370 'forward-slash': '/',
21371 // Tick, or `, or back-quote
21374 // Open bracket, or [
21375 'open-bracket': '[',
21376 // Back slash, or \
21377 'back-slash': '\\',
21378 // Close backet, or ]
21379 'close-bracket': ']',
21380 // Apostrophe, or Quote, or '
21421 utilKeybinding.keyCodes = {
21422 // Backspace key, on Mac: ⌫ (Backspace)
21425 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21439 // Caps Lock key, ⇪
21443 // Escape key, on Mac: ⎋, on Windows: Esc
21449 // Page-Up key, or pgup, on Mac: ↖
21453 // Page-Down key, or pgdown, on Mac: ↘
21457 // END key, on Mac: ⇟
21460 // HOME key, on Mac: ⇞
21463 // Insert key, or ins
21466 // Delete key, on Mac: ⌦ (Delete)
21470 // Left Arrow Key, or ←
21474 // Up Arrow Key, or ↑
21478 // Right Arrow Key, or →
21482 // Up Arrow Key, or ↓
21486 // odities, printing characters that come out wrong:
21489 // Num-Multiply, or *
21497 // Num-Subtract, or -
21513 // Dash / Underscore key
21515 // Period, or ., or full-stop
21519 // Slash, or /, or forward-slash
21522 'forward-slash': 191,
21523 // Tick, or `, or back-quote
21527 // Open bracket, or [
21529 'open-bracket': 219,
21530 // Back slash, or \
21533 // Close backet, or ]
21535 'close-bracket': 221,
21536 // Apostrophe, or Quote, or '
21545 while (++i$1 < 106) {
21546 utilKeybinding.keyCodes['num-' + n] = i$1;
21554 while (++i$1 < 58) {
21555 utilKeybinding.keyCodes[n] = i$1;
21563 while (++i$1 < 136) {
21564 utilKeybinding.keyCodes['f' + n] = i$1;
21571 while (++i$1 < 91) {
21572 utilKeybinding.keyCodes[String.fromCharCode(i$1).toLowerCase()] = i$1;
21575 function utilObjectOmit(obj, omitKeys) {
21576 return Object.keys(obj).reduce(function (result, key) {
21577 if (omitKeys.indexOf(key) === -1) {
21578 result[key] = obj[key]; // keep
21585 // Copies a variable number of methods from source to target.
21586 function utilRebind(target, source) {
21588 n = arguments.length,
21592 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
21596 } // Method is assumed to be a standard D3 getter-setter:
21597 // If passed with no arguments, gets the value.
21598 // If passed with arguments, sets the value and returns the target.
21600 function d3_rebind(target, source, method) {
21601 return function () {
21602 var value = method.apply(source, arguments);
21603 return value === source ? target : value;
21607 // A per-domain session mutex backed by a cookie and dead man's
21608 // switch. If the session crashes, the mutex will auto-release
21609 // after 5 seconds.
21610 // This accepts a string and returns an object that complies with utilSessionMutexType
21611 function utilSessionMutex(name) {
21616 var expires = new Date();
21617 expires.setSeconds(expires.getSeconds() + 5);
21618 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
21621 mutex.lock = function () {
21622 if (intervalID) return true;
21623 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21624 if (cookie) return false;
21626 intervalID = window.setInterval(renew, 4000);
21630 mutex.unlock = function () {
21631 if (!intervalID) return;
21632 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
21633 clearInterval(intervalID);
21637 mutex.locked = function () {
21638 return !!intervalID;
21644 function utilTiler() {
21645 var _size = [256, 256];
21647 var _tileSize = 256;
21648 var _zoomExtent = [0, 20];
21649 var _translate = [_size[0] / 2, _size[1] / 2];
21651 var _skipNullIsland = false;
21653 function clamp(num, min, max) {
21654 return Math.max(min, Math.min(num, max));
21657 function nearNullIsland(tile) {
21663 var center = Math.pow(2, z - 1);
21664 var width = Math.pow(2, z - 6);
21665 var min = center - width / 2;
21666 var max = center + width / 2 - 1;
21667 return x >= min && x <= max && y >= min && y <= max;
21674 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
21675 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
21677 var tileMax = Math.pow(2, z0) - 1;
21678 var log2ts = Math.log(_tileSize) * Math.LOG2E;
21679 var k = Math.pow(2, z - z0 + log2ts);
21680 var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
21681 var cols = range(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
21682 var rows = range(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
21685 for (var i = 0; i < rows.length; i++) {
21688 for (var j = 0; j < cols.length; j++) {
21691 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
21692 tiles.unshift([x, y, z0]); // tiles in view at beginning
21694 tiles.push([x, y, z0]); // tiles in margin at the end
21699 tiles.translate = origin;
21704 * getTiles() returns an array of tiles that cover the map view
21708 tiler.getTiles = function (projection) {
21709 var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
21710 this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
21711 var tiles = tiler();
21712 var ts = tiles.scale;
21713 return tiles.map(function (tile) {
21714 if (_skipNullIsland && nearNullIsland(tile)) {
21718 var x = tile[0] * ts - origin[0];
21719 var y = tile[1] * ts - origin[1];
21721 id: tile.toString(),
21723 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
21725 }).filter(Boolean);
21728 * getGeoJSON() returns a FeatureCollection for debugging tiles
21732 tiler.getGeoJSON = function (projection) {
21733 var features = tiler.getTiles(projection).map(function (tile) {
21742 coordinates: [tile.extent.polygon()]
21747 type: 'FeatureCollection',
21752 tiler.tileSize = function (val) {
21753 if (!arguments.length) return _tileSize;
21758 tiler.zoomExtent = function (val) {
21759 if (!arguments.length) return _zoomExtent;
21764 tiler.size = function (val) {
21765 if (!arguments.length) return _size;
21770 tiler.scale = function (val) {
21771 if (!arguments.length) return _scale;
21776 tiler.translate = function (val) {
21777 if (!arguments.length) return _translate;
21780 }; // number to extend the rows/columns beyond those covering the viewport
21783 tiler.margin = function (val) {
21784 if (!arguments.length) return _margin;
21789 tiler.skipNullIsland = function (val) {
21790 if (!arguments.length) return _skipNullIsland;
21791 _skipNullIsland = val;
21798 function utilTriggerEvent(target, type) {
21799 target.each(function () {
21800 var evt = document.createEvent('HTMLEvents');
21801 evt.initEvent(type, true, true);
21802 this.dispatchEvent(evt);
21806 var _mainLocalizer = coreLocalizer(); // singleton
21809 var _t = _mainLocalizer.t;
21810 // coreLocalizer manages language and locale parameters including translated strings
21813 function coreLocalizer() {
21814 var localizer = {};
21815 var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
21816 // * `rtl` - right-to-left or left-to-right text direction
21817 // * `pct` - the percent of strings translated; 1 = 100%, full coverage
21820 // en: { rtl: false, pct: {…} },
21821 // de: { rtl: false, pct: {…} },
21825 var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
21827 // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21828 // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21832 var _localeStrings = {}; // the current locale
21834 var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
21836 var _localeCodes = ['en-US', 'en'];
21837 var _languageCode = 'en';
21838 var _textDirection = 'ltr';
21839 var _usesMetric = false;
21840 var _languageNames = {};
21841 var _scriptNames = {}; // getters for the current locale parameters
21843 localizer.localeCode = function () {
21844 return _localeCode;
21847 localizer.localeCodes = function () {
21848 return _localeCodes;
21851 localizer.languageCode = function () {
21852 return _languageCode;
21855 localizer.textDirection = function () {
21856 return _textDirection;
21859 localizer.usesMetric = function () {
21860 return _usesMetric;
21863 localizer.languageNames = function () {
21864 return _languageNames;
21867 localizer.scriptNames = function () {
21868 return _scriptNames;
21869 }; // The client app may want to manually set the locale, regardless of the
21870 // settings provided by the browser
21873 var _preferredLocaleCodes = [];
21875 localizer.preferredLocaleCodes = function (codes) {
21876 if (!arguments.length) return _preferredLocaleCodes;
21878 if (typeof codes === 'string') {
21879 // be generous and accept delimited strings as input
21880 _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
21882 _preferredLocaleCodes = codes;
21890 localizer.ensureLoaded = function () {
21891 if (_loadPromise) return _loadPromise;
21892 return _loadPromise = Promise.all([// load the list of languages
21893 _mainFileFetcher.get('languages'), // load the list of supported locales
21894 _mainFileFetcher.get('locales')]).then(function (results) {
21895 _dataLanguages = results[0];
21896 _dataLocales = results[1];
21897 }).then(function () {
21898 var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
21899 concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
21902 _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
21904 _localeCode = _localeCodes[0]; // Will always return the index for `en` if nothing else
21906 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
21907 return _dataLocales[locale].pct === 1;
21908 }); // We only need to load locales up until we find one with full coverage
21911 var loadStringsPromises = _localeCodes.slice(0, fullCoverageIndex + 1).map(function (code) {
21912 return localizer.loadLocale(code);
21915 return Promise.all(loadStringsPromises);
21916 }).then(function () {
21917 updateForCurrentLocale();
21918 })["catch"](function (err) {
21919 return console.error(err);
21920 }); // eslint-disable-line
21921 }; // Returns the locales from `requestedLocales` supported by iD that we should use
21924 function localesToUseFrom(requestedLocales) {
21925 var supportedLocales = _dataLocales;
21928 for (var i in requestedLocales) {
21929 var locale = requestedLocales[i];
21930 if (supportedLocales[locale]) toUse.push(locale);
21932 if (locale.includes('-')) {
21933 // Full locale ('es-ES'), add fallback to the base ('es')
21934 var langPart = locale.split('-')[0];
21935 if (supportedLocales[langPart]) toUse.push(langPart);
21937 } // remove duplicates
21940 return utilArrayUniq(toUse);
21943 function updateForCurrentLocale() {
21944 if (!_localeCode) return;
21945 _languageCode = _localeCode.split('-')[0];
21946 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
21947 var hash = utilStringQs(window.location.hash);
21949 if (hash.rtl === 'true') {
21950 _textDirection = 'rtl';
21951 } else if (hash.rtl === 'false') {
21952 _textDirection = 'ltr';
21954 _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
21957 var locale = _localeCode;
21958 if (locale.toLowerCase() === 'en-us') locale = 'en';
21959 _languageNames = _localeStrings[locale].languageNames;
21960 _scriptNames = _localeStrings[locale].scriptNames;
21961 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
21964 // Returns a Promise to load the strings for the requested locale
21967 localizer.loadLocale = function (requested) {
21968 if (!_dataLocales) {
21969 return Promise.reject('loadLocale called before init');
21972 var locale = requested; // US English is the default
21974 if (locale.toLowerCase() === 'en-us') locale = 'en';
21976 if (!_dataLocales[locale]) {
21977 return Promise.reject("Unsupported locale: ".concat(requested));
21980 if (_localeStrings[locale]) {
21982 return Promise.resolve(locale);
21985 var fileMap = _mainFileFetcher.fileMap();
21986 var key = "locale_".concat(locale);
21987 fileMap[key] = "locales/".concat(locale, ".json");
21988 return _mainFileFetcher.get(key).then(function (d) {
21989 _localeStrings[locale] = d[locale];
21994 localizer.pluralRule = function (number) {
21995 return pluralRule(number, _localeCode);
21996 }; // Returns the plural rule for the given `number` with the given `localeCode`.
21997 // One of: `zero`, `one`, `two`, `few`, `many`, `other`
22000 function pluralRule(number, localeCode) {
22001 // modern browsers have this functionality built-in
22002 var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
22005 return rules.select(number);
22006 } // fallback to basic one/other, as in English
22009 if (number === 1) return 'one';
22013 * Try to find that string in `locale` or the current `_localeCode` matching
22014 * the given `stringId`. If no string can be found in the requested locale,
22015 * we'll recurse down all the `_localeCodes` until one is found.
22017 * @param {string} stringId string identifier
22018 * @param {object?} replacements token replacements and default string
22019 * @param {string?} locale locale to use (defaults to currentLocale)
22020 * @return {string?} localized string
22024 localizer.tInfo = function (stringId, replacements, locale) {
22025 locale = locale || _localeCode;
22026 var path = stringId.split('.').map(function (s) {
22027 return s.replace(/<TX_DOT>/g, '.');
22029 var stringsKey = locale; // US English is the default
22031 if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
22032 var result = _localeStrings[stringsKey];
22034 while (result !== undefined && path.length) {
22035 result = result[path.pop()];
22038 if (result !== undefined) {
22039 if (replacements) {
22040 if (_typeof(result) === 'object' && Object.keys(result).length) {
22041 // If plural forms are provided, dig one level deeper based on the
22042 // first numeric token replacement provided.
22043 var number = Object.values(replacements).find(function (value) {
22044 return typeof value === 'number';
22047 if (number !== undefined) {
22048 var rule = pluralRule(number, locale);
22050 if (result[rule]) {
22051 result = result[rule];
22053 // We're pretty sure this should be a plural but no string
22054 // could be found for the given rule. Just pick the first
22055 // string and hope it makes sense.
22056 result = Object.values(result)[0];
22061 if (typeof result === 'string') {
22062 for (var key in replacements) {
22063 var value = replacements[key];
22065 if (typeof value === 'number' && value.toLocaleString) {
22066 // format numbers for the locale
22067 value = value.toLocaleString(locale, {
22070 minimumFractionDigits: 0
22074 var token = "{".concat(key, "}");
22075 var regex = new RegExp(token, 'g');
22076 result = result.replace(regex, value);
22081 if (typeof result === 'string') {
22082 // found a localized string!
22088 } // no localized string found...
22089 // attempt to fallback to a lower-priority language
22092 var index = _localeCodes.indexOf(locale);
22094 if (index >= 0 && index < _localeCodes.length - 1) {
22095 // eventually this will be 'en' or another locale with 100% coverage
22096 var fallback = _localeCodes[index + 1];
22097 return localizer.tInfo(stringId, replacements, fallback);
22100 if (replacements && 'default' in replacements) {
22101 // Fallback to a default value if one is specified in `replacements`
22103 text: replacements["default"],
22108 var missing = "Missing ".concat(locale, " translation: ").concat(stringId);
22109 if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
22115 }; // Returns only the localized text, discarding the locale info
22118 localizer.t = function (stringId, replacements, locale) {
22119 return localizer.tInfo(stringId, replacements, locale).text;
22120 }; // Returns the localized text wrapped in an HTML element encoding the locale info
22123 localizer.t.html = function (stringId, replacements, locale) {
22124 var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
22126 return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
22129 localizer.htmlForLocalizedText = function (text, localeCode) {
22130 return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
22133 localizer.languageName = function (code, options) {
22134 if (_languageNames[code]) {
22135 // name in locale language
22137 return _languageNames[code];
22138 } // sometimes we only want the local name
22141 if (options && options.localOnly) return null;
22142 var langInfo = _dataLanguages[code];
22145 if (langInfo.nativeName) {
22146 // name in native language
22147 // e.g. "Deutsch (de)"
22148 return localizer.t('translate.language_and_code', {
22149 language: langInfo.nativeName,
22152 } else if (langInfo.base && langInfo.script) {
22153 var base = langInfo.base; // the code of the language this is based on
22155 if (_languageNames[base]) {
22156 // base language name in locale language
22157 var scriptCode = langInfo.script;
22158 var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
22160 return localizer.t('translate.language_and_code', {
22161 language: _languageNames[base],
22164 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
22165 // e.g. "српски (sr-Cyrl)"
22166 return localizer.t('translate.language_and_code', {
22167 language: _dataLanguages[base].nativeName,
22174 return code; // if not found, use the code
22180 // `presetCollection` is a wrapper around an `Array` of presets `collection`,
22181 // and decorated with some extra methods for searching and matching geometry
22184 function presetCollection(collection) {
22185 var MAXRESULTS = 50;
22188 _this.collection = collection;
22190 _this.item = function (id) {
22191 if (_memo[id]) return _memo[id];
22193 var found = _this.collection.find(function (d) {
22194 return d.id === id;
22197 if (found) _memo[id] = found;
22201 _this.index = function (id) {
22202 return _this.collection.findIndex(function (d) {
22203 return d.id === id;
22207 _this.matchGeometry = function (geometry) {
22208 return presetCollection(_this.collection.filter(function (d) {
22209 return d.matchGeometry(geometry);
22213 _this.matchAllGeometry = function (geometries) {
22214 return presetCollection(_this.collection.filter(function (d) {
22215 return d && d.matchAllGeometry(geometries);
22219 _this.matchAnyGeometry = function (geometries) {
22220 return presetCollection(_this.collection.filter(function (d) {
22221 return geometries.some(function (geom) {
22222 return d.matchGeometry(geom);
22227 _this.fallback = function (geometry) {
22229 if (id === 'vertex') id = 'point';
22230 return _this.item(id);
22233 _this.search = function (value, geometry, countryCode) {
22234 if (!value) return _this;
22235 value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
22237 function leading(a) {
22238 var index = a.indexOf(value);
22239 return index === 0 || a[index - 1] === ' ';
22240 } // match at name beginning only
22243 function leadingStrict(a) {
22244 var index = a.indexOf(value);
22245 return index === 0;
22248 function sortNames(a, b) {
22249 var aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
22250 var bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase(); // priority if search string matches preset name exactly - #4325
22252 if (value === aCompare) return -1;
22253 if (value === bCompare) return 1; // priority for higher matchScore
22255 var i = b.originalScore - a.originalScore;
22256 if (i !== 0) return i; // priority if search string appears earlier in preset name
22258 i = aCompare.indexOf(value) - bCompare.indexOf(value);
22259 if (i !== 0) return i; // priority for shorter preset names
22261 return aCompare.length - bCompare.length;
22264 var pool = _this.collection;
22267 pool = pool.filter(function (a) {
22268 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false;
22269 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false;
22274 var searchable = pool.filter(function (a) {
22275 return a.searchable !== false && a.suggestion !== true;
22277 var suggestions = pool.filter(function (a) {
22278 return a.suggestion === true;
22279 }); // matches value to preset.name
22281 var leading_name = searchable.filter(function (a) {
22282 return leading(a.name().toLowerCase());
22283 }).sort(sortNames); // matches value to preset suggestion name (original name is unhyphenated)
22285 var leading_suggestions = suggestions.filter(function (a) {
22286 return leadingStrict(a.originalName.toLowerCase());
22287 }).sort(sortNames); // matches value to preset.terms values
22289 var leading_terms = searchable.filter(function (a) {
22290 return (a.terms() || []).some(leading);
22291 }); // matches value to preset.tags values
22293 var leading_tag_values = searchable.filter(function (a) {
22294 return Object.values(a.tags || {}).filter(function (val) {
22295 return val !== '*';
22297 }); // finds close matches to value in preset.name
22299 var similar_name = searchable.map(function (a) {
22302 dist: utilEditDistance(value, a.name())
22304 }).filter(function (a) {
22305 return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
22306 }).sort(function (a, b) {
22307 return a.dist - b.dist;
22308 }).map(function (a) {
22310 }); // finds close matches to value to preset suggestion name (original name is unhyphenated)
22312 var similar_suggestions = suggestions.map(function (a) {
22315 dist: utilEditDistance(value, a.originalName.toLowerCase())
22317 }).filter(function (a) {
22318 return a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1;
22319 }).sort(function (a, b) {
22320 return a.dist - b.dist;
22321 }).map(function (a) {
22323 }); // finds close matches to value in preset.terms
22325 var similar_terms = searchable.filter(function (a) {
22326 return (a.terms() || []).some(function (b) {
22327 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
22330 var results = leading_name.concat(leading_suggestions, leading_terms, leading_tag_values, similar_name, similar_suggestions, similar_terms).slice(0, MAXRESULTS - 1);
22333 if (typeof geometry === 'string') {
22334 results.push(_this.fallback(geometry));
22336 geometry.forEach(function (geom) {
22337 return results.push(_this.fallback(geom));
22342 return presetCollection(utilArrayUniq(results));
22348 // `presetCategory` builds a `presetCollection` of member presets,
22349 // decorated with some extra methods for searching and matching geometry
22352 function presetCategory(categoryID, category, all) {
22353 var _this = Object.assign({}, category); // shallow copy
22356 _this.id = categoryID;
22357 _this.members = presetCollection(category.members.map(function (presetID) {
22358 return all.item(presetID);
22359 }).filter(Boolean));
22360 _this.geometry = _this.members.collection.reduce(function (acc, preset) {
22361 for (var i in preset.geometry) {
22362 var geometry = preset.geometry[i];
22364 if (acc.indexOf(geometry) === -1) {
22365 acc.push(geometry);
22372 _this.matchGeometry = function (geom) {
22373 return _this.geometry.indexOf(geom) >= 0;
22376 _this.matchAllGeometry = function (geometries) {
22377 return _this.members.collection.some(function (preset) {
22378 return preset.matchAllGeometry(geometries);
22382 _this.matchScore = function () {
22386 _this.name = function () {
22387 return _t("presets.categories.".concat(categoryID, ".name"), {
22388 'default': categoryID
22392 _this.nameLabel = function () {
22393 return _t.html("presets.categories.".concat(categoryID, ".name"), {
22394 'default': categoryID
22398 _this.terms = function () {
22405 // `presetField` decorates a given `field` Object
22406 // with some extra methods for searching and matching geometry
22409 function presetField(fieldID, field) {
22410 var _this = Object.assign({}, field); // shallow copy
22413 _this.id = fieldID; // for use in classes, element ids, css selectors
22415 _this.safeid = utilSafeClassName(fieldID);
22417 _this.matchGeometry = function (geom) {
22418 return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
22421 _this.matchAllGeometry = function (geometries) {
22422 return !_this.geometry || geometries.every(function (geom) {
22423 return _this.geometry.indexOf(geom) !== -1;
22427 _this.t = function (scope, options) {
22428 return _t("presets.fields.".concat(fieldID, ".").concat(scope), options);
22431 _this.t.html = function (scope, options) {
22432 return _t.html("presets.fields.".concat(fieldID, ".").concat(scope), options);
22435 _this.title = function () {
22436 return _this.overrideLabel || _this.t('label', {
22441 _this.label = function () {
22442 return _this.overrideLabel || _this.t.html('label', {
22447 var _placeholder = _this.placeholder;
22449 _this.placeholder = function () {
22450 return _this.t('placeholder', {
22451 'default': _placeholder
22455 _this.originalTerms = (_this.terms || []).join();
22457 _this.terms = function () {
22458 return _this.t('terms', {
22459 'default': _this.originalTerms
22460 }).toLowerCase().trim().split(/\s*,+\s*/);
22463 _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
22467 // `Array.prototype.lastIndexOf` method
22468 // https://tc39.github.io/ecma262/#sec-array.prototype.lastindexof
22469 _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
22470 lastIndexOf: arrayLastIndexOf
22473 // `presetPreset` decorates a given `preset` Object
22474 // with some extra methods for searching and matching geometry
22477 function presetPreset(presetID, preset, addable, allFields, allPresets) {
22478 allFields = allFields || {};
22479 allPresets = allPresets || {};
22481 var _this = Object.assign({}, preset); // shallow copy
22484 var _addable = addable || false;
22486 var _resolvedFields; // cache
22489 var _resolvedMoreFields; // cache
22492 _this.id = presetID;
22493 _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
22495 _this.originalTerms = (_this.terms || []).join();
22496 _this.originalName = _this.name || '';
22497 _this.originalScore = _this.matchScore || 1;
22498 _this.originalReference = _this.reference || {};
22499 _this.originalFields = _this.fields || [];
22500 _this.originalMoreFields = _this.moreFields || [];
22502 _this.fields = function () {
22503 return _resolvedFields || (_resolvedFields = resolve('fields'));
22506 _this.moreFields = function () {
22507 return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
22510 _this.resetFields = function () {
22511 return _resolvedFields = _resolvedMoreFields = null;
22514 _this.tags = _this.tags || {};
22515 _this.addTags = _this.addTags || _this.tags;
22516 _this.removeTags = _this.removeTags || _this.addTags;
22517 _this.geometry = _this.geometry || [];
22519 _this.matchGeometry = function (geom) {
22520 return _this.geometry.indexOf(geom) >= 0;
22523 _this.matchAllGeometry = function (geoms) {
22524 return geoms.every(_this.matchGeometry);
22527 _this.matchScore = function (entityTags) {
22528 var tags = _this.tags;
22530 var score = 0; // match on tags
22532 for (var k in tags) {
22535 if (entityTags[k] === tags[k]) {
22536 score += _this.originalScore;
22537 } else if (tags[k] === '*' && k in entityTags) {
22538 score += _this.originalScore / 2;
22542 } // boost score for additional matches in addTags - #6802
22545 var addTags = _this.addTags;
22547 for (var _k in addTags) {
22548 if (!seen[_k] && entityTags[_k] === addTags[_k]) {
22549 score += _this.originalScore;
22556 _this.t = function (scope, options) {
22557 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22558 return _t(textID, options);
22561 _this.t.html = function (scope, options) {
22562 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22563 return _t.html(textID, options);
22566 _this.name = function () {
22567 return _this.t('name', {
22568 'default': _this.originalName
22572 _this.nameLabel = function () {
22573 return _this.t.html('name', {
22574 'default': _this.originalName
22578 _this.subtitle = function () {
22579 if (_this.suggestion) {
22580 var path = presetID.split('/');
22581 path.pop(); // remove brand name
22583 return _t('presets.presets.' + path.join('/') + '.name');
22589 _this.subtitleLabel = function () {
22590 if (_this.suggestion) {
22591 var path = presetID.split('/');
22592 path.pop(); // remove brand name
22594 return _t.html('presets.presets.' + path.join('/') + '.name');
22600 _this.terms = function () {
22601 return _this.t('terms', {
22602 'default': _this.originalTerms
22603 }).toLowerCase().trim().split(/\s*,+\s*/);
22606 _this.isFallback = function () {
22607 var tagCount = Object.keys(_this.tags).length;
22608 return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
22611 _this.addable = function (val) {
22612 if (!arguments.length) return _addable;
22617 _this.reference = function () {
22618 // Lookup documentation on Wikidata...
22619 var qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
22625 } // Lookup documentation on OSM Wikibase...
22628 var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
22629 var value = _this.originalReference.value || _this.tags[key];
22631 if (value === '*') {
22643 _this.unsetTags = function (tags, geometry, skipFieldDefaults) {
22644 tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
22646 if (geometry && !skipFieldDefaults) {
22647 _this.fields().forEach(function (field) {
22648 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
22649 delete tags[field.key];
22658 _this.setTags = function (tags, geometry, skipFieldDefaults) {
22659 var addTags = _this.addTags;
22660 tags = Object.assign({}, tags); // shallow copy
22662 for (var k in addTags) {
22663 if (addTags[k] === '*') {
22666 tags[k] = addTags[k];
22668 } // Add area=yes if necessary.
22669 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
22670 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
22671 // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
22674 if (!addTags.hasOwnProperty('area')) {
22677 if (geometry === 'area') {
22678 var needsAreaTag = true;
22680 if (_this.geometry.indexOf('line') === -1) {
22681 for (var _k2 in addTags) {
22682 if (_k2 in osmAreaKeys) {
22683 needsAreaTag = false;
22689 if (needsAreaTag) {
22695 if (geometry && !skipFieldDefaults) {
22696 _this.fields().forEach(function (field) {
22697 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
22698 tags[field.key] = field["default"];
22704 }; // For a preset without fields, use the fields of the parent preset.
22705 // Replace {preset} placeholders with the fields of the specified presets.
22708 function resolve(which) {
22709 var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
22711 fieldIDs.forEach(function (fieldID) {
22712 var match = fieldID.match(/\{(.*)\}/);
22714 if (match !== null) {
22715 // a presetID wrapped in braces {}
22716 resolved = resolved.concat(inheritFields(match[1], which));
22717 } else if (allFields[fieldID]) {
22718 // a normal fieldID
22719 resolved.push(allFields[fieldID]);
22721 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
22723 }); // no fields resolved, so use the parent's if possible
22725 if (!resolved.length) {
22726 var endIndex = _this.id.lastIndexOf('/');
22728 var parentID = endIndex && _this.id.substring(0, endIndex);
22731 resolved = inheritFields(parentID, which);
22735 return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
22737 function inheritFields(presetID, which) {
22738 var parent = allPresets[presetID];
22739 if (!parent) return [];
22741 if (which === 'fields') {
22742 return parent.fields().filter(shouldInherit);
22743 } else if (which === 'moreFields') {
22744 return parent.moreFields();
22748 } // Skip `fields` for the keys which define the preset.
22749 // These are usually `typeCombo` fields like `shop=*`
22752 function shouldInherit(f) {
22753 if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
22754 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
22762 var _mainPresetIndex = presetIndex(); // singleton
22763 // `presetIndex` wraps a `presetCollection`
22764 // with methods for loading new data and returning defaults
22767 function presetIndex() {
22768 var dispatch$1 = dispatch('favoritePreset', 'recentsChange');
22769 var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
22771 var POINT = presetPreset('point', {
22774 geometry: ['point', 'vertex'],
22777 var LINE = presetPreset('line', {
22780 geometry: ['line'],
22783 var AREA = presetPreset('area', {
22788 geometry: ['area'],
22791 var RELATION = presetPreset('relation', {
22794 geometry: ['relation'],
22798 var _this = presetCollection([POINT, LINE, AREA, RELATION]);
22807 point: presetCollection([POINT]),
22808 vertex: presetCollection([POINT]),
22809 line: presetCollection([LINE]),
22810 area: presetCollection([AREA]),
22811 relation: presetCollection([RELATION])
22814 var _categories = {};
22815 var _universal = [];
22816 var _addablePresetIDs = null; // Set of preset IDs that the user can add
22820 var _favorites; // Index of presets by (geometry, tag key).
22823 var _geometryIndex = {
22833 _this.ensureLoaded = function () {
22834 if (_loadPromise) return _loadPromise;
22835 return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
22837 categories: vals[0],
22843 osmSetAreaKeys(_this.areaKeys());
22844 osmSetPointTags(_this.pointTags());
22845 osmSetVertexTags(_this.vertexTags());
22849 _this.merge = function (d) {
22852 Object.keys(d.fields).forEach(function (fieldID) {
22853 var f = d.fields[fieldID];
22857 _fields[fieldID] = presetField(fieldID, f);
22860 delete _fields[fieldID];
22867 Object.keys(d.presets).forEach(function (presetID) {
22868 var p = d.presets[presetID];
22872 var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
22874 _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
22876 // remove (but not if it's a fallback)
22877 var existing = _presets[presetID];
22879 if (existing && !existing.isFallback()) {
22880 delete _presets[presetID];
22884 } // Need to rebuild _this.collection before loading categories
22887 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Categories
22889 if (d.categories) {
22890 Object.keys(d.categories).forEach(function (categoryID) {
22891 var c = d.categories[categoryID];
22895 _categories[categoryID] = presetCategory(categoryID, c, _this);
22898 delete _categories[categoryID];
22901 } // Rebuild _this.collection after loading categories
22904 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
22907 Object.keys(d.defaults).forEach(function (geometry) {
22908 var def = d.defaults[geometry];
22910 if (Array.isArray(def)) {
22912 _defaults[geometry] = presetCollection(def.map(function (id) {
22913 return _presets[id] || _categories[id];
22914 }).filter(Boolean));
22917 delete _defaults[geometry];
22920 } // Rebuild universal fields array
22923 _universal = Object.values(_fields).filter(function (field) {
22924 return field.universal;
22925 }); // Reset all the preset fields - they'll need to be resolved again
22927 Object.values(_presets).forEach(function (preset) {
22928 return preset.resetFields();
22929 }); // Rebuild geometry index
22939 _this.collection.forEach(function (preset) {
22940 (preset.geometry || []).forEach(function (geometry) {
22941 var g = _geometryIndex[geometry];
22943 for (var key in preset.tags) {
22944 (g[key] = g[key] || []).push(preset);
22952 _this.match = function (entity, resolver) {
22953 return resolver["transient"](entity, 'presetMatch', function () {
22954 var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
22956 if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
22957 geometry = 'point';
22960 return _this.matchTags(entity.tags, geometry);
22964 _this.matchTags = function (tags, geometry) {
22965 var geometryMatches = _geometryIndex[geometry];
22970 for (var k in tags) {
22971 // If any part of an address is present, allow fallback to "Address" preset - #4353
22972 if (/^addr:/.test(k) && geometryMatches['addr:*']) {
22973 address = geometryMatches['addr:*'][0];
22976 var keyMatches = geometryMatches[k];
22977 if (!keyMatches) continue;
22979 for (var i = 0; i < keyMatches.length; i++) {
22980 var score = keyMatches[i].matchScore(tags);
22982 if (score > best) {
22984 match = keyMatches[i];
22989 if (address && (!match || match.isFallback())) {
22993 return match || _this.fallback(geometry);
22996 _this.allowsVertex = function (entity, resolver) {
22997 if (entity.type !== 'node') return false;
22998 if (Object.keys(entity.tags).length === 0) return true;
22999 return resolver["transient"](entity, 'vertexMatch', function () {
23000 // address lines allow vertices to act as standalone points
23001 if (entity.isOnAddressLine(resolver)) return true;
23002 var geometries = osmNodeGeometriesForTags(entity.tags);
23003 if (geometries.vertex) return true;
23004 if (geometries.point) return false; // allow vertices for unspecified points
23008 }; // Because of the open nature of tagging, iD will never have a complete
23009 // list of tags used in OSM, so we want it to have logic like "assume
23010 // that a closed way with an amenity tag is an area, unless the amenity
23011 // is one of these specific types". This function computes a structure
23012 // that allows testing of such conditions, based on the presets designated
23013 // as as supporting (or not supporting) the area geometry.
23015 // The returned object L is a keeplist/discardlist of tags. A closed way
23016 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
23017 // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
23018 // and the subkeys form the discardlist.
23021 _this.areaKeys = function () {
23022 // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
23023 var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
23024 var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
23026 var presets = _this.collection.filter(function (p) {
23027 return !p.suggestion && !p.replacement;
23031 presets.forEach(function (p) {
23032 var keys = p.tags && Object.keys(p.tags);
23033 var key = keys && keys.length && keys[0]; // pick the first tag
23036 if (ignore.indexOf(key) !== -1) return;
23038 if (p.geometry.indexOf('area') !== -1) {
23039 // probably an area..
23040 areaKeys[key] = areaKeys[key] || {};
23044 presets.forEach(function (p) {
23047 for (key in p.addTags) {
23048 // examine all addTags to get a better sense of what can be tagged on lines - #6800
23049 var value = p.addTags[key];
23051 if (key in areaKeys && // probably an area...
23052 p.geometry.indexOf('line') !== -1 && // but sometimes a line
23054 areaKeys[key][value] = true;
23061 _this.pointTags = function () {
23062 return _this.collection.reduce(function (pointTags, d) {
23063 // ignore name-suggestion-index, deprecated, and generic presets
23064 if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
23066 var keys = d.tags && Object.keys(d.tags);
23067 var key = keys && keys.length && keys[0]; // pick the first tag
23069 if (!key) return pointTags; // if this can be a point
23071 if (d.geometry.indexOf('point') !== -1) {
23072 pointTags[key] = pointTags[key] || {};
23073 pointTags[key][d.tags[key]] = true;
23080 _this.vertexTags = function () {
23081 return _this.collection.reduce(function (vertexTags, d) {
23082 // ignore name-suggestion-index, deprecated, and generic presets
23083 if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
23085 var keys = d.tags && Object.keys(d.tags);
23086 var key = keys && keys.length && keys[0]; // pick the first tag
23088 if (!key) return vertexTags; // if this can be a vertex
23090 if (d.geometry.indexOf('vertex') !== -1) {
23091 vertexTags[key] = vertexTags[key] || {};
23092 vertexTags[key][d.tags[key]] = true;
23099 _this.field = function (id) {
23100 return _fields[id];
23103 _this.universal = function () {
23107 _this.defaults = function (geometry, n, startWithRecents) {
23110 if (startWithRecents) {
23111 recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
23116 if (_addablePresetIDs) {
23117 defaults = Array.from(_addablePresetIDs).map(function (id) {
23118 var preset = _this.item(id);
23120 if (preset && preset.matchGeometry(geometry)) return preset;
23122 }).filter(Boolean);
23124 defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
23127 return presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
23128 }; // pass a Set of addable preset ids
23131 _this.addablePresetIDs = function (val) {
23132 if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
23134 if (Array.isArray(val)) val = new Set(val);
23135 _addablePresetIDs = val;
23137 if (_addablePresetIDs) {
23138 // reset all presets
23139 _this.collection.forEach(function (p) {
23140 // categories aren't addable
23141 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
23144 _this.collection.forEach(function (p) {
23145 if (p.addable) p.addable(true);
23152 _this.recent = function () {
23153 return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
23158 function RibbonItem(preset, source) {
23160 item.preset = preset;
23161 item.source = source;
23163 item.isFavorite = function () {
23164 return item.source === 'favorite';
23167 item.isRecent = function () {
23168 return item.source === 'recent';
23171 item.matches = function (preset) {
23172 return item.preset.id === preset.id;
23175 item.minified = function () {
23177 pID: item.preset.id
23184 function ribbonItemForMinified(d, source) {
23186 var preset = _this.item(d.pID);
23188 if (!preset) return null;
23189 return RibbonItem(preset, source);
23195 _this.getGenericRibbonItems = function () {
23196 return ['point', 'line', 'area'].map(function (id) {
23197 return RibbonItem(_this.item(id), 'generic');
23201 _this.getAddable = function () {
23202 if (!_addablePresetIDs) return [];
23203 return _addablePresetIDs.map(function (id) {
23204 var preset = _this.item(id);
23206 if (preset) return RibbonItem(preset, 'addable');
23208 }).filter(Boolean);
23211 function setRecents(items) {
23213 var minifiedItems = items.map(function (d) {
23214 return d.minified();
23216 corePreferences('preset_recents', JSON.stringify(minifiedItems));
23217 dispatch$1.call('recentsChange');
23220 _this.getRecents = function () {
23222 // fetch from local storage
23223 _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
23224 var item = ribbonItemForMinified(d, 'recent');
23225 if (item && item.preset.addable()) acc.push(item);
23233 _this.addRecent = function (preset, besidePreset, after) {
23234 var recents = _this.getRecents();
23236 var beforeItem = _this.recentMatching(besidePreset);
23238 var toIndex = recents.indexOf(beforeItem);
23239 if (after) toIndex += 1;
23240 var newItem = RibbonItem(preset, 'recent');
23241 recents.splice(toIndex, 0, newItem);
23242 setRecents(recents);
23245 _this.removeRecent = function (preset) {
23246 var item = _this.recentMatching(preset);
23249 var items = _this.getRecents();
23251 items.splice(items.indexOf(item), 1);
23256 _this.recentMatching = function (preset) {
23257 var items = _this.getRecents();
23259 for (var i in items) {
23260 if (items[i].matches(preset)) {
23268 _this.moveItem = function (items, fromIndex, toIndex) {
23269 if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
23270 items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
23274 _this.moveRecent = function (item, beforeItem) {
23275 var recents = _this.getRecents();
23277 var fromIndex = recents.indexOf(item);
23278 var toIndex = recents.indexOf(beforeItem);
23280 var items = _this.moveItem(recents, fromIndex, toIndex);
23282 if (items) setRecents(items);
23285 _this.setMostRecent = function (preset) {
23286 if (preset.searchable === false) return;
23288 var items = _this.getRecents();
23290 var item = _this.recentMatching(preset);
23293 items.splice(items.indexOf(item), 1);
23295 item = RibbonItem(preset, 'recent');
23296 } // remove the last recent (first in, first out)
23299 while (items.length >= MAXRECENTS) {
23304 items.unshift(item);
23308 function setFavorites(items) {
23309 _favorites = items;
23310 var minifiedItems = items.map(function (d) {
23311 return d.minified();
23313 corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
23315 dispatch$1.call('favoritePreset');
23318 _this.addFavorite = function (preset, besidePreset, after) {
23319 var favorites = _this.getFavorites();
23321 var beforeItem = _this.favoriteMatching(besidePreset);
23323 var toIndex = favorites.indexOf(beforeItem);
23324 if (after) toIndex += 1;
23325 var newItem = RibbonItem(preset, 'favorite');
23326 favorites.splice(toIndex, 0, newItem);
23327 setFavorites(favorites);
23330 _this.toggleFavorite = function (preset) {
23331 var favs = _this.getFavorites();
23333 var favorite = _this.favoriteMatching(preset);
23336 favs.splice(favs.indexOf(favorite), 1);
23338 // only allow 10 favorites
23339 if (favs.length === 10) {
23340 // remove the last favorite (last in, first out)
23345 favs.push(RibbonItem(preset, 'favorite'));
23348 setFavorites(favs);
23351 _this.removeFavorite = function (preset) {
23352 var item = _this.favoriteMatching(preset);
23355 var items = _this.getFavorites();
23357 items.splice(items.indexOf(item), 1);
23358 setFavorites(items);
23362 _this.getFavorites = function () {
23364 // fetch from local storage
23365 var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
23367 if (!rawFavorites) {
23369 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
23372 _favorites = rawFavorites.reduce(function (output, d) {
23373 var item = ribbonItemForMinified(d, 'favorite');
23374 if (item && item.preset.addable()) output.push(item);
23382 _this.favoriteMatching = function (preset) {
23383 var favs = _this.getFavorites();
23385 for (var index in favs) {
23386 if (favs[index].matches(preset)) {
23387 return favs[index];
23394 return utilRebind(_this, dispatch$1, 'on');
23397 function utilTagText(entity) {
23398 var obj = entity && entity.tags || {};
23399 return Object.keys(obj).map(function (k) {
23400 return k + '=' + obj[k];
23403 function utilTotalExtent(array, graph) {
23404 var extent = geoExtent();
23407 for (var i = 0; i < array.length; i++) {
23409 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
23412 extent._extend(entity.extent(graph));
23418 function utilTagDiff(oldTags, newTags) {
23420 var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
23421 keys.forEach(function (k) {
23422 var oldVal = oldTags[k];
23423 var newVal = newTags[k];
23425 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
23431 display: '- ' + k + '=' + oldVal
23435 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
23441 display: '+ ' + k + '=' + newVal
23447 function utilEntitySelector(ids) {
23448 return ids.length ? '.' + ids.join(',.') : 'nothing';
23449 } // returns an selector to select entity ids for:
23450 // - entityIDs passed in
23451 // - shallow descendant entityIDs for any of those entities that are relations
23453 function utilEntityOrMemberSelector(ids, graph) {
23454 var seen = new Set(ids);
23455 ids.forEach(collectShallowDescendants);
23456 return utilEntitySelector(Array.from(seen));
23458 function collectShallowDescendants(id) {
23459 var entity = graph.hasEntity(id);
23460 if (!entity || entity.type !== 'relation') return;
23461 entity.members.map(function (member) {
23463 }).forEach(function (id) {
23467 } // returns an selector to select entity ids for:
23468 // - entityIDs passed in
23469 // - deep descendant entityIDs for any of those entities that are relations
23471 function utilEntityOrDeepMemberSelector(ids, graph) {
23472 return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
23473 } // returns an selector to select entity ids for:
23474 // - entityIDs passed in
23475 // - deep descendant entityIDs for any of those entities that are relations
23477 function utilEntityAndDeepMemberIDs(ids, graph) {
23478 var seen = new Set();
23479 ids.forEach(collectDeepDescendants);
23480 return Array.from(seen);
23482 function collectDeepDescendants(id) {
23483 if (seen.has(id)) return;
23485 var entity = graph.hasEntity(id);
23486 if (!entity || entity.type !== 'relation') return;
23487 entity.members.map(function (member) {
23489 }).forEach(collectDeepDescendants); // recurse
23491 } // returns an selector to select entity ids for:
23492 // - deep descendant entityIDs for any of those entities that are relations
23494 function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
23495 var idsSet = new Set(ids);
23496 var seen = new Set();
23497 var returners = new Set();
23498 ids.forEach(collectDeepDescendants);
23499 return utilEntitySelector(Array.from(returners));
23501 function collectDeepDescendants(id) {
23502 if (seen.has(id)) return;
23505 if (!idsSet.has(id)) {
23509 var entity = graph.hasEntity(id);
23510 if (!entity || entity.type !== 'relation') return;
23511 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
23512 entity.members.map(function (member) {
23514 }).forEach(collectDeepDescendants); // recurse
23516 } // Adds or removes highlight styling for the specified entities
23518 function utilHighlightEntities(ids, highlighted, context) {
23519 context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
23520 } // returns an Array that is the union of:
23521 // - nodes for any nodeIDs passed in
23522 // - child nodes of any wayIDs passed in
23523 // - descendant member and child nodes of relationIDs passed in
23525 function utilGetAllNodes(ids, graph) {
23526 var seen = new Set();
23527 var nodes = new Set();
23528 ids.forEach(collectNodes);
23529 return Array.from(nodes);
23531 function collectNodes(id) {
23532 if (seen.has(id)) return;
23534 var entity = graph.hasEntity(id);
23535 if (!entity) return;
23537 if (entity.type === 'node') {
23539 } else if (entity.type === 'way') {
23540 entity.nodes.forEach(collectNodes);
23542 entity.members.map(function (member) {
23544 }).forEach(collectNodes); // recurse
23548 function utilDisplayName(entity) {
23549 var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
23550 var name = entity.tags[localizedNameKey] || entity.tags.name || '';
23551 var network = entity.tags.cycle_network || entity.tags.network;
23553 if (!name && entity.tags.ref) {
23554 name = entity.tags.ref;
23557 name = network + ' ' + name;
23563 function utilDisplayNameForPath(entity) {
23564 var name = utilDisplayName(entity);
23565 var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
23567 if (!isFirefox && name && rtlRegex.test(name)) {
23568 name = fixRTLTextForSvg(name);
23573 function utilDisplayType(id) {
23575 n: _t('inspector.node'),
23576 w: _t('inspector.way'),
23577 r: _t('inspector.relation')
23580 function utilDisplayLabel(entity, graphOrGeometry) {
23581 var displayName = utilDisplayName(entity);
23584 // use the display name if there is one
23585 return displayName;
23588 var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
23590 if (preset && preset.name()) {
23591 // use the preset name if there is a match
23592 return preset.name();
23593 } // fallback to the display type (node/way/relation)
23596 return utilDisplayType(entity.id);
23598 function utilEntityRoot(entityType) {
23604 } // Returns a single object containing the tags of all the given entities.
23607 // highway: 'service',
23608 // service: 'parking_aisle'
23612 // highway: 'service',
23613 // service: 'driveway',
23618 // highway: 'service',
23619 // service: [ 'driveway', 'parking_aisle' ],
23620 // width: [ '3', undefined ]
23623 function utilCombinedTags(entityIDs, graph) {
23625 var tagCounts = {};
23626 var allKeys = new Set();
23627 var entities = entityIDs.map(function (entityID) {
23628 return graph.hasEntity(entityID);
23629 }).filter(Boolean); // gather the aggregate keys
23631 entities.forEach(function (entity) {
23632 var keys = Object.keys(entity.tags).filter(Boolean);
23633 keys.forEach(function (key) {
23637 entities.forEach(function (entity) {
23638 allKeys.forEach(function (key) {
23639 var value = entity.tags[key]; // purposely allow `undefined`
23641 if (!tags.hasOwnProperty(key)) {
23642 // first value, set as raw
23645 if (!Array.isArray(tags[key])) {
23646 if (tags[key] !== value) {
23647 // first alternate value, replace single value with array
23648 tags[key] = [tags[key], value];
23652 if (tags[key].indexOf(value) === -1) {
23653 // subsequent alternate value, add to array
23654 tags[key].push(value);
23659 var tagHash = key + '=' + value;
23660 if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
23661 tagCounts[tagHash] += 1;
23665 for (var key in tags) {
23666 if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
23668 tags[key] = tags[key].sort(function (val1, val2) {
23669 var key = key; // capture
23671 var count2 = tagCounts[key + '=' + val2];
23672 var count1 = tagCounts[key + '=' + val1];
23674 if (count2 !== count1) {
23675 return count2 - count1;
23678 if (val2 && val1) {
23679 return val1.localeCompare(val2);
23682 return val1 ? 1 : -1;
23688 function utilStringQs(str) {
23689 var i = 0; // advance past any leading '?' or '#' characters
23691 while (i < str.length && (str[i] === '?' || str[i] === '#')) {
23695 str = str.slice(i);
23696 return str.split('&').reduce(function (obj, pair) {
23697 var parts = pair.split('=');
23699 if (parts.length === 2) {
23700 obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
23706 function utilQsString(obj, noencode) {
23707 // encode everything except special characters used in certain hash parameters:
23708 // "/" in map states, ":", ",", {" and "}" in background
23709 function softEncode(s) {
23710 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
23713 return Object.keys(obj).sort().map(function (key) {
23714 return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
23717 function utilPrefixDOMProperty(property) {
23718 var prefixes = ['webkit', 'ms', 'moz', 'o'];
23720 var n = prefixes.length;
23721 var s = document.body;
23722 if (property in s) return property;
23723 property = property.substr(0, 1).toUpperCase() + property.substr(1);
23726 if (prefixes[i] + property in s) {
23727 return prefixes[i] + property;
23733 function utilPrefixCSSProperty(property) {
23734 var prefixes = ['webkit', 'ms', 'Moz', 'O'];
23736 var n = prefixes.length;
23737 var s = document.body.style;
23739 if (property.toLowerCase() in s) {
23740 return property.toLowerCase();
23744 if (prefixes[i] + property in s) {
23745 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
23751 var transformProperty;
23752 function utilSetTransform(el, x, y, scale) {
23753 var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
23754 var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
23755 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
23756 } // Calculates Levenshtein distance between two strings
23757 // see: https://en.wikipedia.org/wiki/Levenshtein_distance
23758 // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
23760 function utilEditDistance(a, b) {
23761 a = remove$1(a.toLowerCase());
23762 b = remove$1(b.toLowerCase());
23763 if (a.length === 0) return b.length;
23764 if (b.length === 0) return a.length;
23768 for (i = 0; i <= b.length; i++) {
23772 for (j = 0; j <= a.length; j++) {
23776 for (i = 1; i <= b.length; i++) {
23777 for (j = 1; j <= a.length; j++) {
23778 if (b.charAt(i - 1) === a.charAt(j - 1)) {
23779 matrix[i][j] = matrix[i - 1][j - 1];
23781 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
23782 Math.min(matrix[i][j - 1] + 1, // insertion
23783 matrix[i - 1][j] + 1)); // deletion
23788 return matrix[b.length][a.length];
23789 } // a d3.mouse-alike which
23790 // 1. Only works on HTML elements, not SVG
23791 // 2. Does not cause style recalculation
23793 function utilFastMouse(container) {
23794 var rect = container.getBoundingClientRect();
23795 var rectLeft = rect.left;
23796 var rectTop = rect.top;
23797 var clientLeft = +container.clientLeft;
23798 var clientTop = +container.clientTop;
23799 return function (e) {
23800 return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
23803 function utilAsyncMap(inputs, func, callback) {
23804 var remaining = inputs.length;
23807 inputs.forEach(function (d, i) {
23808 func(d, function done(err, data) {
23812 if (!remaining) callback(errors, results);
23815 } // wraps an index to an interval [0..length-1]
23817 function utilWrap(index, length) {
23819 index += Math.ceil(-index / length) * length;
23822 return index % length;
23825 * a replacement for functor
23827 * @param {*} value any value
23828 * @returns {Function} a function that returns that value or the value if it's a function
23831 function utilFunctor(value) {
23832 if (typeof value === 'function') return value;
23833 return function () {
23837 function utilNoAuto(selection) {
23838 var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
23839 return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
23840 .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
23841 } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
23842 // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
23844 function utilHashcode(str) {
23847 if (str.length === 0) {
23851 for (var i = 0; i < str.length; i++) {
23852 var _char = str.charCodeAt(i);
23854 hash = (hash << 5) - hash + _char;
23855 hash = hash & hash; // Convert to 32bit integer
23859 } // Returns version of `str` with all runs of special characters replaced by `_`;
23860 // suitable for HTML ids, classes, selectors, etc.
23862 function utilSafeClassName(str) {
23863 return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
23864 } // Returns string based on `val` that is highly unlikely to collide with an id
23865 // used previously or that's present elsewhere in the document. Useful for preventing
23866 // browser-provided autofills or when embedding iD on pages with unknown elements.
23868 function utilUniqueDomId(val) {
23869 return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
23870 } // Returns the length of `str` in unicode characters. This can be less than
23871 // `String.length()` since a single unicode character can be composed of multiple
23872 // JavaScript UTF-16 code units.
23874 function utilUnicodeCharsCount(str) {
23875 // Native ES2015 implementations of `Array.from` split strings into unicode characters
23876 return Array.from(str).length;
23877 } // Returns a new string representing `str` cut from its start to `limit` length
23878 // in unicode characters. Note that this runs the risk of splitting graphemes.
23880 function utilUnicodeCharsTruncated(str, limit) {
23881 return Array.from(str).slice(0, limit).join('');
23884 function osmEntity(attrs) {
23885 // For prototypal inheritance.
23886 if (this instanceof osmEntity) return; // Create the appropriate subtype.
23888 if (attrs && attrs.type) {
23889 return osmEntity[attrs.type].apply(this, arguments);
23890 } else if (attrs && attrs.id) {
23891 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
23892 } // Initialize a generic Entity (used only in tests).
23895 return new osmEntity().initialize(arguments);
23898 osmEntity.id = function (type) {
23899 return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
23902 osmEntity.id.next = {
23909 osmEntity.id.fromOSM = function (type, id) {
23910 return type[0] + id;
23913 osmEntity.id.toOSM = function (id) {
23914 return id.slice(1);
23917 osmEntity.id.type = function (id) {
23924 }; // A function suitable for use as the second argument to d3.selection#data().
23927 osmEntity.key = function (entity) {
23928 return entity.id + 'v' + (entity.v || 0);
23931 var _deprecatedTagValuesByKey;
23933 osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
23934 if (!_deprecatedTagValuesByKey) {
23935 _deprecatedTagValuesByKey = {};
23936 dataDeprecated.forEach(function (d) {
23937 var oldKeys = Object.keys(d.old);
23939 if (oldKeys.length === 1) {
23940 var oldKey = oldKeys[0];
23941 var oldValue = d.old[oldKey];
23943 if (oldValue !== '*') {
23944 if (!_deprecatedTagValuesByKey[oldKey]) {
23945 _deprecatedTagValuesByKey[oldKey] = [oldValue];
23947 _deprecatedTagValuesByKey[oldKey].push(oldValue);
23954 return _deprecatedTagValuesByKey;
23957 osmEntity.prototype = {
23959 initialize: function initialize(sources) {
23960 for (var i = 0; i < sources.length; ++i) {
23961 var source = sources[i];
23963 for (var prop in source) {
23964 if (Object.prototype.hasOwnProperty.call(source, prop)) {
23965 if (source[prop] === undefined) {
23968 this[prop] = source[prop];
23974 if (!this.id && this.type) {
23975 this.id = osmEntity.id(this.type);
23978 if (!this.hasOwnProperty('visible')) {
23979 this.visible = true;
23984 copy: function copy(resolver, copies) {
23985 if (copies[this.id]) return copies[this.id];
23986 var copy = osmEntity(this, {
23991 copies[this.id] = copy;
23994 osmId: function osmId() {
23995 return osmEntity.id.toOSM(this.id);
23997 isNew: function isNew() {
23998 return this.osmId() < 0;
24000 update: function update(attrs) {
24001 return osmEntity(this, attrs, {
24002 v: 1 + (this.v || 0)
24005 mergeTags: function mergeTags(tags) {
24006 var merged = Object.assign({}, this.tags); // shallow copy
24008 var changed = false;
24010 for (var k in tags) {
24011 var t1 = merged[k];
24017 } else if (t1 !== t2) {
24019 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
24024 return changed ? this.update({
24028 intersects: function intersects(extent, resolver) {
24029 return this.extent(resolver).intersects(extent);
24031 hasNonGeometryTags: function hasNonGeometryTags() {
24032 return Object.keys(this.tags).some(function (k) {
24033 return k !== 'area';
24036 hasParentRelations: function hasParentRelations(resolver) {
24037 return resolver.parentRelations(this).length > 0;
24039 hasInterestingTags: function hasInterestingTags() {
24040 return Object.keys(this.tags).some(osmIsInterestingTag);
24042 hasWikidata: function hasWikidata() {
24043 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
24045 isHighwayIntersection: function isHighwayIntersection() {
24048 isDegenerate: function isDegenerate() {
24051 deprecatedTags: function deprecatedTags(dataDeprecated) {
24052 var tags = this.tags; // if there are no tags, none can be deprecated
24054 if (Object.keys(tags).length === 0) return [];
24055 var deprecated = [];
24056 dataDeprecated.forEach(function (d) {
24057 var oldKeys = Object.keys(d.old);
24060 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
24061 if (!tags[replaceKey] || d.old[replaceKey]) return false;
24062 var replaceValue = d.replace[replaceKey];
24063 if (replaceValue === '*') return false;
24064 if (replaceValue === tags[replaceKey]) return false;
24066 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
24068 if (hasExistingValues) return;
24071 var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
24072 if (!tags[oldKey]) return false;
24073 if (d.old[oldKey] === '*') return true;
24074 if (d.old[oldKey] === tags[oldKey]) return true;
24075 var vals = tags[oldKey].split(';').filter(Boolean);
24077 if (vals.length === 0) {
24079 } else if (vals.length > 1) {
24080 return vals.indexOf(d.old[oldKey]) !== -1;
24082 if (tags[oldKey] === d.old[oldKey]) {
24083 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
24084 var replaceKeys = Object.keys(d.replace);
24085 return !replaceKeys.every(function (replaceKey) {
24086 return tags[replaceKey] === d.replace[replaceKey];
24097 if (matchesDeprecatedTags) {
24098 deprecated.push(d);
24105 function osmLanes(entity) {
24106 if (entity.type !== 'way') return null;
24107 if (!entity.tags.highway) return null;
24108 var tags = entity.tags;
24109 var isOneWay = entity.isOneWay();
24110 var laneCount = getLaneCount(tags, isOneWay);
24111 var maxspeed = parseMaxspeed(tags);
24112 var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
24113 var forward = laneDirections.forward;
24114 var backward = laneDirections.backward;
24115 var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
24117 var turnLanes = {};
24118 turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
24119 turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
24120 turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
24121 var maxspeedLanes = {};
24122 maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
24123 maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
24124 maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
24126 psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
24127 psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
24128 psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
24130 busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
24131 busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
24132 busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
24133 var taxiLanes = {};
24134 taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
24135 taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
24136 taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
24138 hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
24139 hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
24140 hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
24142 hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
24143 hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
24144 hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
24145 var bicyclewayLanes = {};
24146 bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
24147 bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
24148 bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
24153 }; // map forward/backward/unspecified of each lane type to lanesObj
24155 mapToLanesObj(lanesObj, turnLanes, 'turnLane');
24156 mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
24157 mapToLanesObj(lanesObj, psvLanes, 'psv');
24158 mapToLanesObj(lanesObj, busLanes, 'bus');
24159 mapToLanesObj(lanesObj, taxiLanes, 'taxi');
24160 mapToLanesObj(lanesObj, hovLanes, 'hov');
24161 mapToLanesObj(lanesObj, hgvLanes, 'hgv');
24162 mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
24168 backward: backward,
24169 bothways: bothways,
24170 turnLanes: turnLanes,
24171 maxspeed: maxspeed,
24172 maxspeedLanes: maxspeedLanes,
24173 psvLanes: psvLanes,
24174 busLanes: busLanes,
24175 taxiLanes: taxiLanes,
24176 hovLanes: hovLanes,
24177 hgvLanes: hgvLanes,
24178 bicyclewayLanes: bicyclewayLanes
24184 function getLaneCount(tags, isOneWay) {
24188 count = parseInt(tags.lanes, 10);
24195 switch (tags.highway) {
24198 count = isOneWay ? 2 : 4;
24202 count = isOneWay ? 1 : 2;
24209 function parseMaxspeed(tags) {
24210 var maxspeed = tags.maxspeed;
24211 if (!maxspeed) return;
24212 var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
24213 if (!maxspeedRegex.test(maxspeed)) return;
24214 return parseInt(maxspeed, 10);
24217 function parseLaneDirections(tags, isOneWay, laneCount) {
24218 var forward = parseInt(tags['lanes:forward'], 10);
24219 var backward = parseInt(tags['lanes:backward'], 10);
24220 var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
24222 if (parseInt(tags.oneway, 10) === -1) {
24225 backward = laneCount;
24226 } else if (isOneWay) {
24227 forward = laneCount;
24230 } else if (isNaN(forward) && isNaN(backward)) {
24231 backward = Math.floor((laneCount - bothways) / 2);
24232 forward = laneCount - bothways - backward;
24233 } else if (isNaN(forward)) {
24234 if (backward > laneCount - bothways) {
24235 backward = laneCount - bothways;
24238 forward = laneCount - bothways - backward;
24239 } else if (isNaN(backward)) {
24240 if (forward > laneCount - bothways) {
24241 forward = laneCount - bothways;
24244 backward = laneCount - bothways - forward;
24249 backward: backward,
24254 function parseTurnLanes(tag) {
24256 var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
24257 return tag.split('|').map(function (s) {
24258 if (s === '') s = 'none';
24259 return s.split(';').map(function (d) {
24260 return validValues.indexOf(d) === -1 ? 'unknown' : d;
24265 function parseMaxspeedLanes(tag, maxspeed) {
24267 return tag.split('|').map(function (s) {
24268 if (s === 'none') return s;
24269 var m = parseInt(s, 10);
24270 if (s === '' || m === maxspeed) return null;
24271 return isNaN(m) ? 'unknown' : m;
24275 function parseMiscLanes(tag) {
24277 var validValues = ['yes', 'no', 'designated'];
24278 return tag.split('|').map(function (s) {
24279 if (s === '') s = 'no';
24280 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24284 function parseBicycleWay(tag) {
24286 var validValues = ['yes', 'no', 'designated', 'lane'];
24287 return tag.split('|').map(function (s) {
24288 if (s === '') s = 'no';
24289 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24293 function mapToLanesObj(lanesObj, data, key) {
24294 if (data.forward) data.forward.forEach(function (l, i) {
24295 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
24296 lanesObj.forward[i][key] = l;
24298 if (data.backward) data.backward.forEach(function (l, i) {
24299 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
24300 lanesObj.backward[i][key] = l;
24302 if (data.unspecified) data.unspecified.forEach(function (l, i) {
24303 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
24304 lanesObj.unspecified[i][key] = l;
24308 function osmWay() {
24309 if (!(this instanceof osmWay)) {
24310 return new osmWay().initialize(arguments);
24311 } else if (arguments.length) {
24312 this.initialize(arguments);
24315 osmEntity.way = osmWay;
24316 osmWay.prototype = Object.create(osmEntity.prototype);
24317 Object.assign(osmWay.prototype, {
24320 copy: function copy(resolver, copies) {
24321 if (copies[this.id]) return copies[this.id];
24322 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
24323 var nodes = this.nodes.map(function (id) {
24324 return resolver.entity(id).copy(resolver, copies).id;
24326 copy = copy.update({
24329 copies[this.id] = copy;
24332 extent: function extent(resolver) {
24333 return resolver["transient"](this, 'extent', function () {
24334 var extent = geoExtent();
24336 for (var i = 0; i < this.nodes.length; i++) {
24337 var node = resolver.hasEntity(this.nodes[i]);
24340 extent._extend(node.extent());
24347 first: function first() {
24348 return this.nodes[0];
24350 last: function last() {
24351 return this.nodes[this.nodes.length - 1];
24353 contains: function contains(node) {
24354 return this.nodes.indexOf(node) >= 0;
24356 affix: function affix(node) {
24357 if (this.nodes[0] === node) return 'prefix';
24358 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
24360 layer: function layer() {
24361 // explicit layer tag, clamp between -10, 10..
24362 if (isFinite(this.tags.layer)) {
24363 return Math.max(-10, Math.min(+this.tags.layer, 10));
24364 } // implied layer tag..
24367 if (this.tags.covered === 'yes') return -1;
24368 if (this.tags.location === 'overground') return 1;
24369 if (this.tags.location === 'underground') return -1;
24370 if (this.tags.location === 'underwater') return -10;
24371 if (this.tags.power === 'line') return 10;
24372 if (this.tags.power === 'minor_line') return 10;
24373 if (this.tags.aerialway) return 10;
24374 if (this.tags.bridge) return 1;
24375 if (this.tags.cutting) return -1;
24376 if (this.tags.tunnel) return -1;
24377 if (this.tags.waterway) return -1;
24378 if (this.tags.man_made === 'pipeline') return -10;
24379 if (this.tags.boundary) return -10;
24382 // the approximate width of the line based on its tags except its `width` tag
24383 impliedLineWidthMeters: function impliedLineWidthMeters() {
24384 var averageWidths = {
24386 // width is for single lane
24413 // width includes ties and rail bed, not just track gauge
24436 for (var key in averageWidths) {
24437 if (this.tags[key] && averageWidths[key][this.tags[key]]) {
24438 var width = averageWidths[key][this.tags[key]];
24440 if (key === 'highway') {
24441 var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
24442 if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
24443 return width * laneCount;
24452 isOneWay: function isOneWay() {
24453 // explicit oneway tag..
24458 'reversible': true,
24459 'alternating': true,
24464 if (values[this.tags.oneway] !== undefined) {
24465 return values[this.tags.oneway];
24466 } // implied oneway tag..
24469 for (var key in this.tags) {
24470 if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) return true;
24475 // Some identifier for tag that implies that this way is "sided",
24476 // i.e. the right side is the 'inside' (e.g. the right side of a
24477 // natural=cliff is lower).
24478 sidednessIdentifier: function sidednessIdentifier() {
24479 for (var key in this.tags) {
24480 var value = this.tags[key];
24482 if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
24483 if (osmRightSideIsInsideTags[key][value] === true) {
24486 // if the map's value is something other than a
24487 // literal true, we should use it so we can
24488 // special case some keys (e.g. natural=coastline
24489 // is handled differently to other naturals).
24490 return osmRightSideIsInsideTags[key][value];
24497 isSided: function isSided() {
24498 if (this.tags.two_sided === 'yes') {
24502 return this.sidednessIdentifier() !== null;
24504 lanes: function lanes() {
24505 return osmLanes(this);
24507 isClosed: function isClosed() {
24508 return this.nodes.length > 1 && this.first() === this.last();
24510 isConvex: function isConvex(resolver) {
24511 if (!this.isClosed() || this.isDegenerate()) return null;
24512 var nodes = utilArrayUniq(resolver.childNodes(this));
24513 var coords = nodes.map(function (n) {
24519 for (var i = 0; i < coords.length; i++) {
24520 var o = coords[(i + 1) % coords.length];
24522 var b = coords[(i + 2) % coords.length];
24523 var res = geoVecCross(a, b, o);
24524 curr = res > 0 ? 1 : res < 0 ? -1 : 0;
24528 } else if (prev && curr !== prev) {
24537 // returns an object with the tag that implies this is an area, if any
24538 tagSuggestingArea: function tagSuggestingArea() {
24539 return osmTagSuggestingArea(this.tags);
24541 isArea: function isArea() {
24542 if (this.tags.area === 'yes') return true;
24543 if (!this.isClosed() || this.tags.area === 'no') return false;
24544 return this.tagSuggestingArea() !== null;
24546 isDegenerate: function isDegenerate() {
24547 return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
24549 areAdjacent: function areAdjacent(n1, n2) {
24550 for (var i = 0; i < this.nodes.length; i++) {
24551 if (this.nodes[i] === n1) {
24552 if (this.nodes[i - 1] === n2) return true;
24553 if (this.nodes[i + 1] === n2) return true;
24559 geometry: function geometry(graph) {
24560 return graph["transient"](this, 'geometry', function () {
24561 return this.isArea() ? 'area' : 'line';
24564 // returns an array of objects representing the segments between the nodes in this way
24565 segments: function segments(graph) {
24566 function segmentExtent(graph) {
24567 var n1 = graph.hasEntity(this.nodes[0]);
24568 var n2 = graph.hasEntity(this.nodes[1]);
24569 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])]]);
24572 return graph["transient"](this, 'segments', function () {
24575 for (var i = 0; i < this.nodes.length - 1; i++) {
24577 id: this.id + '-' + i,
24580 nodes: [this.nodes[i], this.nodes[i + 1]],
24581 extent: segmentExtent
24588 // If this way is not closed, append the beginning node to the end of the nodelist to close it.
24589 close: function close() {
24590 if (this.isClosed() || !this.nodes.length) return this;
24591 var nodes = this.nodes.slice();
24592 nodes = nodes.filter(noRepeatNodes);
24593 nodes.push(nodes[0]);
24594 return this.update({
24598 // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
24599 unclose: function unclose() {
24600 if (!this.isClosed()) return this;
24601 var nodes = this.nodes.slice();
24602 var connector = this.first();
24603 var i = nodes.length - 1; // remove trailing connectors..
24605 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24606 nodes.splice(i, 1);
24607 i = nodes.length - 1;
24610 nodes = nodes.filter(noRepeatNodes);
24611 return this.update({
24615 // Adds a node (id) in front of the node which is currently at position index.
24616 // If index is undefined, the node will be added to the end of the way for linear ways,
24617 // or just before the final connecting node for circular ways.
24618 // Consecutive duplicates are eliminated including existing ones.
24619 // Circularity is always preserved when adding a node.
24620 addNode: function addNode(id, index) {
24621 var nodes = this.nodes.slice();
24622 var isClosed = this.isClosed();
24623 var max = isClosed ? nodes.length - 1 : nodes.length;
24625 if (index === undefined) {
24629 if (index < 0 || index > max) {
24630 throw new RangeError('index ' + index + ' out of range 0..' + max);
24631 } // If this is a closed way, remove all connector nodes except the first one
24632 // (there may be duplicates) and adjust index if necessary..
24636 var connector = this.first(); // leading connectors..
24640 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24641 nodes.splice(i, 1);
24642 if (index > i) index--;
24643 } // trailing connectors..
24646 i = nodes.length - 1;
24648 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24649 nodes.splice(i, 1);
24650 if (index > i) index--;
24651 i = nodes.length - 1;
24655 nodes.splice(index, 0, id);
24656 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24658 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24659 nodes.push(nodes[0]);
24662 return this.update({
24666 // Replaces the node which is currently at position index with the given node (id).
24667 // Consecutive duplicates are eliminated including existing ones.
24668 // Circularity is preserved when updating a node.
24669 updateNode: function updateNode(id, index) {
24670 var nodes = this.nodes.slice();
24671 var isClosed = this.isClosed();
24672 var max = nodes.length - 1;
24674 if (index === undefined || index < 0 || index > max) {
24675 throw new RangeError('index ' + index + ' out of range 0..' + max);
24676 } // If this is a closed way, remove all connector nodes except the first one
24677 // (there may be duplicates) and adjust index if necessary..
24681 var connector = this.first(); // leading connectors..
24685 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24686 nodes.splice(i, 1);
24687 if (index > i) index--;
24688 } // trailing connectors..
24691 i = nodes.length - 1;
24693 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24694 nodes.splice(i, 1);
24695 if (index === i) index = 0; // update leading connector instead
24697 i = nodes.length - 1;
24701 nodes.splice(index, 1, id);
24702 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24704 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24705 nodes.push(nodes[0]);
24708 return this.update({
24712 // Replaces each occurrence of node id needle with replacement.
24713 // Consecutive duplicates are eliminated including existing ones.
24714 // Circularity is preserved.
24715 replaceNode: function replaceNode(needleID, replacementID) {
24716 var nodes = this.nodes.slice();
24717 var isClosed = this.isClosed();
24719 for (var i = 0; i < nodes.length; i++) {
24720 if (nodes[i] === needleID) {
24721 nodes[i] = replacementID;
24725 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24727 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24728 nodes.push(nodes[0]);
24731 return this.update({
24735 // Removes each occurrence of node id.
24736 // Consecutive duplicates are eliminated including existing ones.
24737 // Circularity is preserved.
24738 removeNode: function removeNode(id) {
24739 var nodes = this.nodes.slice();
24740 var isClosed = this.isClosed();
24741 nodes = nodes.filter(function (node) {
24742 return node !== id;
24743 }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24745 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24746 nodes.push(nodes[0]);
24749 return this.update({
24753 asJXON: function asJXON(changeset_id) {
24756 '@id': this.osmId(),
24757 '@version': this.version || 0,
24758 nd: this.nodes.map(function (id) {
24761 ref: osmEntity.id.toOSM(id)
24765 tag: Object.keys(this.tags).map(function (k) {
24776 if (changeset_id) {
24777 r.way['@changeset'] = changeset_id;
24782 asGeoJSON: function asGeoJSON(resolver) {
24783 return resolver["transient"](this, 'GeoJSON', function () {
24784 var coordinates = resolver.childNodes(this).map(function (n) {
24788 if (this.isArea() && this.isClosed()) {
24791 coordinates: [coordinates]
24795 type: 'LineString',
24796 coordinates: coordinates
24801 area: function area(resolver) {
24802 return resolver["transient"](this, 'area', function () {
24803 var nodes = resolver.childNodes(this);
24806 coordinates: [nodes.map(function (n) {
24811 if (!this.isClosed() && nodes.length) {
24812 json.coordinates[0].push(nodes[0].loc);
24815 var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
24816 // that OpenStreetMap polygons are not hemisphere-spanning.
24818 if (area > 2 * Math.PI) {
24819 json.coordinates[0] = json.coordinates[0].reverse();
24820 area = d3_geoArea(json);
24823 return isNaN(area) ? 0 : area;
24826 }); // Filter function to eliminate consecutive duplicates.
24828 function noRepeatNodes(node, i, arr) {
24829 return i === 0 || node !== arr[i - 1];
24833 // 1. Relation tagged with `type=multipolygon` and no interesting tags.
24834 // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
24835 // 3. No members without a role.
24837 // Old multipolygons are no longer recommended but are still rendered as areas by iD.
24839 function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
24840 if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
24846 for (var memberIndex in entity.members) {
24847 var member = entity.members[memberIndex];
24849 if (!member.role || member.role === 'outer') {
24850 if (outerMember) return false;
24851 if (member.type !== 'way') return false;
24852 if (!graph.hasEntity(member.id)) return false;
24853 outerMember = graph.entity(member.id);
24855 if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
24861 return outerMember;
24862 } // For fixing up rendering of multipolygons with tags on the outer member.
24863 // https://github.com/openstreetmap/iD/issues/613
24865 function osmIsOldMultipolygonOuterMember(entity, graph) {
24866 if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) return false;
24867 var parents = graph.parentRelations(entity);
24868 if (parents.length !== 1) return false;
24869 var parent = parents[0];
24870 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24871 var members = parent.members,
24874 for (var i = 0; i < members.length; i++) {
24875 member = members[i];
24876 if (member.id === entity.id && member.role && member.role !== 'outer') return false; // Not outer member
24878 if (member.id !== entity.id && (!member.role || member.role === 'outer')) return false; // Not a simple multipolygon
24883 function osmOldMultipolygonOuterMember(entity, graph) {
24884 if (entity.type !== 'way') return false;
24885 var parents = graph.parentRelations(entity);
24886 if (parents.length !== 1) return false;
24887 var parent = parents[0];
24888 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24889 var members = parent.members,
24893 for (var i = 0; i < members.length; i++) {
24894 member = members[i];
24896 if (!member.role || member.role === 'outer') {
24897 if (outerMember) return false; // Not a simple multipolygon
24899 outerMember = member;
24903 if (!outerMember) return false;
24904 var outerEntity = graph.hasEntity(outerMember.id);
24905 if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) return false;
24906 return outerEntity;
24907 } // Join `toJoin` array into sequences of connecting ways.
24908 // Segments which share identical start/end nodes will, as much as possible,
24909 // be connected with each other.
24911 // The return value is a nested array. Each constituent array contains elements
24912 // of `toJoin` which have been determined to connect.
24914 // Each consitituent array also has a `nodes` property whose value is an
24915 // ordered array of member nodes, with appropriate order reversal and
24916 // start/end coordinate de-duplication.
24918 // Members of `toJoin` must have, at minimum, `type` and `id` properties.
24919 // Thus either an array of `osmWay`s or a relation member array may be used.
24921 // If an member is an `osmWay`, its tags and childnodes may be reversed via
24922 // `actionReverse` in the output.
24924 // The returned sequences array also has an `actions` array property, containing
24925 // any reversal actions that should be applied to the graph, should the calling
24926 // code attempt to actually join the given ways.
24928 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
24929 // false) and non-way members are ignored.
24932 function osmJoinWays(toJoin, graph) {
24933 function resolve(member) {
24934 return graph.childNodes(graph.entity(member.id));
24937 function reverse(item) {
24938 var action = actionReverse(item.id, {
24939 reverseOneway: true
24941 sequences.actions.push(action);
24942 return item instanceof osmWay ? action(graph).entity(item.id) : item;
24943 } // make a copy containing only the items to join
24946 toJoin = toJoin.filter(function (member) {
24947 return member.type === 'way' && graph.hasEntity(member.id);
24948 }); // Are the things we are joining relation members or `osmWays`?
24949 // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
24952 var joinAsMembers = true;
24954 for (i = 0; i < toJoin.length; i++) {
24955 if (toJoin[i] instanceof osmWay) {
24956 joinAsMembers = false;
24961 var sequences = [];
24962 sequences.actions = [];
24964 while (toJoin.length) {
24965 // start a new sequence
24966 var item = toJoin.shift();
24967 var currWays = [item];
24968 var currNodes = resolve(item).slice(); // add to it
24970 while (toJoin.length) {
24971 var start = currNodes[0];
24972 var end = currNodes[currNodes.length - 1];
24974 var nodes = null; // Find the next way/member to join.
24976 for (i = 0; i < toJoin.length; i++) {
24978 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
24979 // Strongly prefer to generate a forward path that preserves the order
24980 // of the members array. For multipolygons and most relations, member
24981 // order does not matter - but for routes, it does. (see #4589)
24982 // If we started this sequence backwards (i.e. next member way attaches to
24983 // the start node and not the end node), reverse the initial way before continuing.
24985 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
24986 currWays[0] = reverse(currWays[0]);
24987 currNodes.reverse();
24988 start = currNodes[0];
24989 end = currNodes[currNodes.length - 1];
24992 if (nodes[0] === end) {
24993 fn = currNodes.push; // join to end
24995 nodes = nodes.slice(1);
24997 } else if (nodes[nodes.length - 1] === end) {
24998 fn = currNodes.push; // join to end
25000 nodes = nodes.slice(0, -1).reverse();
25001 item = reverse(item);
25003 } else if (nodes[nodes.length - 1] === start) {
25004 fn = currNodes.unshift; // join to beginning
25006 nodes = nodes.slice(0, -1);
25008 } else if (nodes[0] === start) {
25009 fn = currNodes.unshift; // join to beginning
25011 nodes = nodes.slice(1).reverse();
25012 item = reverse(item);
25020 // couldn't find a joinable way/member
25024 fn.apply(currWays, [item]);
25025 fn.apply(currNodes, nodes);
25026 toJoin.splice(i, 1);
25029 currWays.nodes = currNodes;
25030 sequences.push(currWays);
25036 function actionAddMember(relationId, member, memberIndex, insertPair) {
25037 return function action(graph) {
25038 var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
25040 var isPTv2 = /stop|platform/.test(member.role);
25042 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
25043 // Try to perform sensible inserts based on how the ways join together
25044 graph = addWayMember(relation, graph);
25046 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25047 // Stops and Platforms for PTv2 should be ordered first.
25048 // hack: We do not currently have the ability to place them in the exactly correct order.
25049 if (isPTv2 && isNaN(memberIndex)) {
25053 graph = graph.replace(relation.addMember(member, memberIndex));
25057 }; // Add a way member into the relation "wherever it makes sense".
25058 // In this situation we were not supplied a memberIndex.
25060 function addWayMember(relation, graph) {
25061 var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
25063 var PTv2members = [];
25066 for (i = 0; i < relation.members.length; i++) {
25067 var m = relation.members[i];
25069 if (/stop|platform/.test(m.role)) {
25070 PTv2members.push(m);
25076 relation = relation.update({
25081 // We're adding a member that must stay paired with an existing member.
25082 // (This feature is used by `actionSplit`)
25084 // This is tricky because the members may exist multiple times in the
25085 // member list, and with different A-B/B-A ordering and different roles.
25086 // (e.g. a bus route that loops out and back - #4589).
25088 // Replace the existing member with a temporary way,
25089 // so that `osmJoinWays` can treat the pair like a single way.
25092 nodes: insertPair.nodes
25094 graph = graph.replace(tempWay);
25100 var tempRelation = relation.replaceMember({
25101 id: insertPair.originalID
25102 }, tempMember, true);
25103 groups = utilArrayGroupBy(tempRelation.members, 'type');
25104 groups.way = groups.way || [];
25106 // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
25107 groups = utilArrayGroupBy(relation.members, 'type');
25108 groups.way = groups.way || [];
25109 groups.way.push(member);
25112 members = withIndex(groups.way);
25113 var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
25114 // But will contain only the completed (downloaded) members
25116 for (i = 0; i < joined.length; i++) {
25117 var segment = joined[i];
25118 var nodes = segment.nodes.slice();
25119 var startIndex = segment[0].index; // j = array index in `members` where this segment starts
25121 for (j = 0; j < members.length; j++) {
25122 if (members[j].index === startIndex) {
25125 } // k = each member in segment
25128 for (k = 0; k < segment.length; k++) {
25130 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
25132 if (tempWay && item.id === tempWay.id) {
25133 if (nodes[0].id === insertPair.nodes[0]) {
25135 id: insertPair.originalID,
25139 id: insertPair.insertedID,
25145 id: insertPair.insertedID,
25149 id: insertPair.originalID,
25154 } // reorder `members` if necessary
25158 if (j + k >= members.length || item.index !== members[j + k].index) {
25159 moveMember(members, item.index, j + k);
25163 nodes.splice(0, way.nodes.length - 1);
25168 graph = graph.remove(tempWay);
25169 } // Final pass: skip dead items, split pairs, remove index properties
25172 var wayMembers = [];
25174 for (i = 0; i < members.length; i++) {
25176 if (item.index === -1) continue;
25179 wayMembers.push(item.pair[0]);
25180 wayMembers.push(item.pair[1]);
25182 wayMembers.push(utilObjectOmit(item, ['index']));
25184 } // Put stops and platforms first, then nodes, ways, relations
25185 // This is recommended for Public Transport v2 routes:
25186 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25189 var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
25190 return graph.replace(relation.update({
25191 members: newMembers
25192 })); // `moveMember()` changes the `members` array in place by splicing
25193 // the item with `.index = findIndex` to where it belongs,
25194 // and marking the old position as "dead" with `.index = -1`
25198 // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
25202 // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
25203 // members 0 1 2 3 x 5 4 6 7 8 9 moved
25207 // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
25208 // members 0 1 2 3 x 5 4 7 6 x 8 9 moved
25212 // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
25215 function moveMember(arr, findIndex, toIndex) {
25218 for (i = 0; i < arr.length; i++) {
25219 if (arr[i].index === findIndex) {
25224 var item = Object.assign({}, arr[i]); // shallow copy
25226 arr[i].index = -1; // mark as dead
25228 item.index = toIndex;
25229 arr.splice(toIndex, 0, item);
25230 } // This is the same as `Relation.indexedMembers`,
25231 // Except we don't want to index all the members, only the ways
25234 function withIndex(arr) {
25235 var result = new Array(arr.length);
25237 for (var i = 0; i < arr.length; i++) {
25238 result[i] = Object.assign({}, arr[i]); // shallow copy
25240 result[i].index = i;
25248 function actionAddMidpoint(midpoint, node) {
25249 return function (graph) {
25250 graph = graph.replace(node.move(midpoint.loc));
25251 var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
25252 parents.forEach(function (way) {
25253 for (var i = 0; i < way.nodes.length - 1; i++) {
25254 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
25255 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
25256 // turning them into self-intersections.
25266 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
25267 function actionAddVertex(wayId, nodeId, index) {
25268 return function (graph) {
25269 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
25273 function actionChangeMember(relationId, member, memberIndex) {
25274 return function (graph) {
25275 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
25279 function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
25280 return function action(graph) {
25281 var entity = graph.entity(entityID);
25282 var geometry = entity.geometry(graph);
25283 var tags = entity.tags;
25284 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry);
25285 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
25286 return graph.replace(entity.update({
25292 function actionChangeTags(entityId, tags) {
25293 return function (graph) {
25294 var entity = graph.entity(entityId);
25295 return graph.replace(entity.update({
25301 function osmNode() {
25302 if (!(this instanceof osmNode)) {
25303 return new osmNode().initialize(arguments);
25304 } else if (arguments.length) {
25305 this.initialize(arguments);
25308 osmEntity.node = osmNode;
25309 osmNode.prototype = Object.create(osmEntity.prototype);
25310 Object.assign(osmNode.prototype, {
25313 extent: function extent() {
25314 return new geoExtent(this.loc);
25316 geometry: function geometry(graph) {
25317 return graph["transient"](this, 'geometry', function () {
25318 return graph.isPoi(this) ? 'point' : 'vertex';
25321 move: function move(loc) {
25322 return this.update({
25326 isDegenerate: function isDegenerate() {
25327 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);
25329 // Inspect tags and geometry to determine which direction(s) this node/vertex points
25330 directions: function directions(resolver, projection) {
25332 var i; // which tag to use?
25334 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
25335 // all-way stop tag on a highway intersection
25338 // generic direction tag
25339 val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
25341 var re = /:direction$/i;
25342 var keys = Object.keys(this.tags);
25344 for (i = 0; i < keys.length; i++) {
25345 if (re.test(keys[i])) {
25346 val = this.tags[keys[i]].toLowerCase();
25352 if (val === '') return [];
25356 northnortheast: 22,
25364 eastsoutheast: 112,
25368 southsoutheast: 157,
25372 southsouthwest: 202,
25376 westsouthwest: 247,
25380 westnorthwest: 292,
25384 northnorthwest: 337,
25387 var values = val.split(';');
25389 values.forEach(function (v) {
25390 // swap cardinal for numeric directions
25391 if (cardinal[v] !== undefined) {
25393 } // numeric direction - just add to results
25396 if (v !== '' && !isNaN(+v)) {
25399 } // string direction - inspect parent ways
25402 var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
25403 var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
25404 if (!lookForward && !lookBackward) return;
25406 resolver.parentWays(this).forEach(function (parent) {
25407 var nodes = parent.nodes;
25409 for (i = 0; i < nodes.length; i++) {
25410 if (nodes[i] === this.id) {
25411 // match current entity
25412 if (lookForward && i > 0) {
25413 nodeIds[nodes[i - 1]] = true; // look back to prev node
25416 if (lookBackward && i < nodes.length - 1) {
25417 nodeIds[nodes[i + 1]] = true; // look ahead to next node
25422 Object.keys(nodeIds).forEach(function (nodeId) {
25423 // +90 because geoAngle returns angle from X axis, not Y (north)
25424 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
25427 return utilArrayUniq(results);
25429 isEndpoint: function isEndpoint(resolver) {
25430 return resolver["transient"](this, 'isEndpoint', function () {
25432 return resolver.parentWays(this).filter(function (parent) {
25433 return !parent.isClosed() && !!parent.affix(id);
25437 isConnected: function isConnected(resolver) {
25438 return resolver["transient"](this, 'isConnected', function () {
25439 var parents = resolver.parentWays(this);
25441 if (parents.length > 1) {
25442 // vertex is connected to multiple parent ways
25443 for (var i in parents) {
25444 if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
25446 } else if (parents.length === 1) {
25447 var way = parents[0];
25448 var nodes = way.nodes.slice();
25450 if (way.isClosed()) {
25452 } // ignore connecting node if closed
25453 // return true if vertex appears multiple times (way is self intersecting)
25456 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
25462 parentIntersectionWays: function parentIntersectionWays(resolver) {
25463 return resolver["transient"](this, 'parentIntersectionWays', function () {
25464 return resolver.parentWays(this).filter(function (parent) {
25465 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
25469 isIntersection: function isIntersection(resolver) {
25470 return this.parentIntersectionWays(resolver).length > 1;
25472 isHighwayIntersection: function isHighwayIntersection(resolver) {
25473 return resolver["transient"](this, 'isHighwayIntersection', function () {
25474 return resolver.parentWays(this).filter(function (parent) {
25475 return parent.tags.highway && parent.geometry(resolver) === 'line';
25479 isOnAddressLine: function isOnAddressLine(resolver) {
25480 return resolver["transient"](this, 'isOnAddressLine', function () {
25481 return resolver.parentWays(this).filter(function (parent) {
25482 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
25486 asJXON: function asJXON(changeset_id) {
25489 '@id': this.osmId(),
25490 '@lon': this.loc[0],
25491 '@lat': this.loc[1],
25492 '@version': this.version || 0,
25493 tag: Object.keys(this.tags).map(function (k) {
25503 if (changeset_id) r.node['@changeset'] = changeset_id;
25506 asGeoJSON: function asGeoJSON() {
25509 coordinates: this.loc
25514 function actionCircularize(wayId, projection, maxAngle) {
25515 maxAngle = (maxAngle || 20) * Math.PI / 180;
25517 var action = function action(graph, t) {
25518 if (t === null || !isFinite(t)) t = 1;
25519 t = Math.min(Math.max(+t, 0), 1);
25520 var way = graph.entity(wayId);
25521 var origNodes = {};
25522 graph.childNodes(way).forEach(function (node) {
25523 if (!origNodes[node.id]) origNodes[node.id] = node;
25526 if (!way.isConvex(graph)) {
25527 graph = action.makeConvex(graph);
25530 var nodes = utilArrayUniq(graph.childNodes(way));
25531 var keyNodes = nodes.filter(function (n) {
25532 return graph.parentWays(n).length !== 1;
25534 var points = nodes.map(function (n) {
25535 return projection(n.loc);
25537 var keyPoints = keyNodes.map(function (n) {
25538 return projection(n.loc);
25540 var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
25541 var radius = d3_median(points, function (p) {
25542 return geoVecLength(centroid, p);
25544 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25545 var ids, i, j, k; // we need at least two key nodes for the algorithm to work
25547 if (!keyNodes.length) {
25548 keyNodes = [nodes[0]];
25549 keyPoints = [points[0]];
25552 if (keyNodes.length === 1) {
25553 var index = nodes.indexOf(keyNodes[0]);
25554 var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
25555 keyNodes.push(nodes[oppositeIndex]);
25556 keyPoints.push(points[oppositeIndex]);
25557 } // key points and nodes are those connected to the ways,
25558 // they are projected onto the circle, in between nodes are moved
25559 // to constant intervals between key nodes, extra in between nodes are
25560 // added if necessary.
25563 for (i = 0; i < keyPoints.length; i++) {
25564 var nextKeyNodeIndex = (i + 1) % keyNodes.length;
25565 var startNode = keyNodes[i];
25566 var endNode = keyNodes[nextKeyNodeIndex];
25567 var startNodeIndex = nodes.indexOf(startNode);
25568 var endNodeIndex = nodes.indexOf(endNode);
25569 var numberNewPoints = -1;
25570 var indexRange = endNodeIndex - startNodeIndex;
25571 var nearNodes = {};
25572 var inBetweenNodes = [];
25573 var startAngle, endAngle, totalAngle, eachAngle;
25574 var angle, loc, node, origNode;
25576 if (indexRange < 0) {
25577 indexRange += nodes.length;
25578 } // position this key node
25581 var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
25582 keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
25583 loc = projection.invert(keyPoints[i]);
25584 node = keyNodes[i];
25585 origNode = origNodes[node.id];
25586 node = node.move(geoVecInterp(origNode.loc, loc, t));
25587 graph = graph.replace(node); // figure out the between delta angle we want to match to
25589 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
25590 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
25591 totalAngle = endAngle - startAngle; // detects looping around -pi/pi
25593 if (totalAngle * sign > 0) {
25594 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
25599 eachAngle = totalAngle / (indexRange + numberNewPoints);
25600 } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
25603 for (j = 1; j < indexRange; j++) {
25604 angle = startAngle + j * eachAngle;
25605 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
25606 node = nodes[(j + startNodeIndex) % nodes.length];
25607 origNode = origNodes[node.id];
25608 nearNodes[node.id] = angle;
25609 node = node.move(geoVecInterp(origNode.loc, loc, t));
25610 graph = graph.replace(node);
25611 } // add new in between nodes if necessary
25614 for (j = 0; j < numberNewPoints; j++) {
25615 angle = startAngle + (indexRange + j) * eachAngle;
25616 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
25618 var min = Infinity;
25620 for (var nodeId in nearNodes) {
25621 var nearAngle = nearNodes[nodeId];
25622 var dist = Math.abs(nearAngle - angle);
25626 origNode = origNodes[nodeId];
25631 loc: geoVecInterp(origNode.loc, loc, t)
25633 graph = graph.replace(node);
25634 nodes.splice(endNodeIndex + j, 0, node);
25635 inBetweenNodes.push(node.id);
25636 } // Check for other ways that share these keyNodes..
25637 // If keyNodes are adjacent in both ways,
25638 // we can add inBetweenNodes to that shared way too..
25641 if (indexRange === 1 && inBetweenNodes.length) {
25642 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
25643 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
25644 var wayDirection1 = endIndex1 - startIndex1;
25646 if (wayDirection1 < -1) {
25650 var parentWays = graph.parentWays(keyNodes[i]);
25652 for (j = 0; j < parentWays.length; j++) {
25653 var sharedWay = parentWays[j];
25654 if (sharedWay === way) continue;
25656 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
25657 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
25658 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
25659 var wayDirection2 = endIndex2 - startIndex2;
25660 var insertAt = endIndex2;
25662 if (wayDirection2 < -1) {
25666 if (wayDirection1 !== wayDirection2) {
25667 inBetweenNodes.reverse();
25668 insertAt = startIndex2;
25671 for (k = 0; k < inBetweenNodes.length; k++) {
25672 sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
25675 graph = graph.replace(sharedWay);
25679 } // update the way to have all the new nodes
25682 ids = nodes.map(function (n) {
25689 graph = graph.replace(way);
25693 action.makeConvex = function (graph) {
25694 var way = graph.entity(wayId);
25695 var nodes = utilArrayUniq(graph.childNodes(way));
25696 var points = nodes.map(function (n) {
25697 return projection(n.loc);
25699 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25700 var hull = d3_polygonHull(points);
25701 var i, j; // D3 convex hulls go counterclockwise..
25708 for (i = 0; i < hull.length - 1; i++) {
25709 var startIndex = points.indexOf(hull[i]);
25710 var endIndex = points.indexOf(hull[i + 1]);
25711 var indexRange = endIndex - startIndex;
25713 if (indexRange < 0) {
25714 indexRange += nodes.length;
25715 } // move interior nodes to the surface of the convex hull..
25718 for (j = 1; j < indexRange; j++) {
25719 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
25720 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
25721 graph = graph.replace(node);
25728 action.disabled = function (graph) {
25729 if (!graph.entity(wayId).isClosed()) {
25730 return 'not_closed';
25731 } //disable when already circular
25734 var way = graph.entity(wayId);
25735 var nodes = utilArrayUniq(graph.childNodes(way));
25736 var points = nodes.map(function (n) {
25737 return projection(n.loc);
25739 var hull = d3_polygonHull(points);
25740 var epsilonAngle = Math.PI / 180;
25742 if (hull.length !== points.length || hull.length < 3) {
25746 var centroid = d3_polygonCentroid(points);
25747 var radius = geoVecLengthSquare(centroid, points[0]);
25748 var i, actualPoint; // compare distances between centroid and points
25750 for (i = 0; i < hull.length; i++) {
25751 actualPoint = hull[i];
25752 var actualDist = geoVecLengthSquare(actualPoint, centroid);
25753 var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
25755 if (diff > 0.05 * radius) {
25758 } //check if central angles are smaller than maxAngle
25761 for (i = 0; i < hull.length; i++) {
25762 actualPoint = hull[i];
25763 var nextPoint = hull[(i + 1) % hull.length];
25764 var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
25765 var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
25766 var angle = endAngle - startAngle;
25772 if (angle > Math.PI) {
25773 angle = 2 * Math.PI - angle;
25776 if (angle > maxAngle + epsilonAngle) {
25781 return 'already_circular';
25784 action.transitionable = true;
25788 function actionDeleteWay(wayID) {
25789 function canDeleteNode(node, graph) {
25790 // don't delete nodes still attached to ways or relations
25791 if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
25792 var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
25794 if (geometries.point) return false; // delete if this node only be a vertex
25796 if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
25797 // so only delete if there are no interesting tags
25799 return !node.hasInterestingTags();
25802 var action = function action(graph) {
25803 var way = graph.entity(wayID);
25804 graph.parentRelations(way).forEach(function (parent) {
25805 parent = parent.removeMembersWithID(wayID);
25806 graph = graph.replace(parent);
25808 if (parent.isDegenerate()) {
25809 graph = actionDeleteRelation(parent.id)(graph);
25812 new Set(way.nodes).forEach(function (nodeID) {
25813 graph = graph.replace(way.removeNode(nodeID));
25814 var node = graph.entity(nodeID);
25816 if (canDeleteNode(node, graph)) {
25817 graph = graph.remove(node);
25820 return graph.remove(way);
25826 function actionDeleteMultiple(ids) {
25828 way: actionDeleteWay,
25829 node: actionDeleteNode,
25830 relation: actionDeleteRelation
25833 var action = function action(graph) {
25834 ids.forEach(function (id) {
25835 if (graph.hasEntity(id)) {
25836 // It may have been deleted already.
25837 graph = actions[graph.entity(id).type](id)(graph);
25846 function actionDeleteRelation(relationID, allowUntaggedMembers) {
25847 function canDeleteEntity(entity, graph) {
25848 return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
25851 var action = function action(graph) {
25852 var relation = graph.entity(relationID);
25853 graph.parentRelations(relation).forEach(function (parent) {
25854 parent = parent.removeMembersWithID(relationID);
25855 graph = graph.replace(parent);
25857 if (parent.isDegenerate()) {
25858 graph = actionDeleteRelation(parent.id)(graph);
25861 var memberIDs = utilArrayUniq(relation.members.map(function (m) {
25864 memberIDs.forEach(function (memberID) {
25865 graph = graph.replace(relation.removeMembersWithID(memberID));
25866 var entity = graph.entity(memberID);
25868 if (canDeleteEntity(entity, graph)) {
25869 graph = actionDeleteMultiple([memberID])(graph);
25872 return graph.remove(relation);
25878 function actionDeleteNode(nodeId) {
25879 var action = function action(graph) {
25880 var node = graph.entity(nodeId);
25881 graph.parentWays(node).forEach(function (parent) {
25882 parent = parent.removeNode(nodeId);
25883 graph = graph.replace(parent);
25885 if (parent.isDegenerate()) {
25886 graph = actionDeleteWay(parent.id)(graph);
25889 graph.parentRelations(node).forEach(function (parent) {
25890 parent = parent.removeMembersWithID(nodeId);
25891 graph = graph.replace(parent);
25893 if (parent.isDegenerate()) {
25894 graph = actionDeleteRelation(parent.id)(graph);
25897 return graph.remove(node);
25904 // First choose a node to be the survivor, with preference given
25905 // to an existing (not new) node.
25907 // Tags and relation memberships of of non-surviving nodes are merged
25908 // to the survivor.
25910 // This is the inverse of `iD.actionDisconnect`.
25913 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
25914 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
25917 function actionConnect(nodeIDs) {
25918 var action = function action(graph) {
25922 var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
25924 for (i = 0; i < nodeIDs.length; i++) {
25925 survivor = graph.entity(nodeIDs[i]);
25926 if (survivor.version) break; // found one
25927 } // Replace all non-surviving nodes with the survivor and merge tags.
25930 for (i = 0; i < nodeIDs.length; i++) {
25931 node = graph.entity(nodeIDs[i]);
25932 if (node.id === survivor.id) continue;
25933 parents = graph.parentWays(node);
25935 for (j = 0; j < parents.length; j++) {
25936 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
25939 parents = graph.parentRelations(node);
25941 for (j = 0; j < parents.length; j++) {
25942 graph = graph.replace(parents[j].replaceMember(node, survivor));
25945 survivor = survivor.mergeTags(node.tags);
25946 graph = actionDeleteNode(node.id)(graph);
25949 graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
25951 parents = graph.parentWays(survivor);
25953 for (i = 0; i < parents.length; i++) {
25954 if (parents[i].isDegenerate()) {
25955 graph = actionDeleteWay(parents[i].id)(graph);
25962 action.disabled = function (graph) {
25964 var restrictionIDs = [];
25967 var relations, relation, role;
25968 var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
25970 for (i = 0; i < nodeIDs.length; i++) {
25971 survivor = graph.entity(nodeIDs[i]);
25972 if (survivor.version) break; // found one
25973 } // 1. disable if the nodes being connected have conflicting relation roles
25976 for (i = 0; i < nodeIDs.length; i++) {
25977 node = graph.entity(nodeIDs[i]);
25978 relations = graph.parentRelations(node);
25980 for (j = 0; j < relations.length; j++) {
25981 relation = relations[j];
25982 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
25984 if (relation.hasFromViaTo()) {
25985 restrictionIDs.push(relation.id);
25988 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
25991 seen[relation.id] = role;
25994 } // gather restrictions for parent ways
25997 for (i = 0; i < nodeIDs.length; i++) {
25998 node = graph.entity(nodeIDs[i]);
25999 var parents = graph.parentWays(node);
26001 for (j = 0; j < parents.length; j++) {
26002 var parent = parents[j];
26003 relations = graph.parentRelations(parent);
26005 for (k = 0; k < relations.length; k++) {
26006 relation = relations[k];
26008 if (relation.hasFromViaTo()) {
26009 restrictionIDs.push(relation.id);
26013 } // test restrictions
26016 restrictionIDs = utilArrayUniq(restrictionIDs);
26018 for (i = 0; i < restrictionIDs.length; i++) {
26019 relation = graph.entity(restrictionIDs[i]);
26020 if (!relation.isComplete(graph)) continue;
26021 var memberWays = relation.members.filter(function (m) {
26022 return m.type === 'way';
26023 }).map(function (m) {
26024 return graph.entity(m.id);
26026 memberWays = utilArrayUniq(memberWays);
26027 var f = relation.memberByRole('from');
26028 var t = relation.memberByRole('to');
26029 var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
26030 // (a key node is a node at the junction of ways)
26040 for (j = 0; j < relation.members.length; j++) {
26041 collectNodes(relation.members[j], nodes);
26044 nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
26045 nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
26046 var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
26047 nodes.from = nodes.from.filter(filter);
26048 nodes.via = nodes.via.filter(filter);
26049 nodes.to = nodes.to.filter(filter);
26050 var connectFrom = false;
26051 var connectVia = false;
26052 var connectTo = false;
26053 var connectKeyFrom = false;
26054 var connectKeyTo = false;
26056 for (j = 0; j < nodeIDs.length; j++) {
26057 var n = nodeIDs[j];
26059 if (nodes.from.indexOf(n) !== -1) {
26060 connectFrom = true;
26063 if (nodes.via.indexOf(n) !== -1) {
26067 if (nodes.to.indexOf(n) !== -1) {
26071 if (nodes.keyfrom.indexOf(n) !== -1) {
26072 connectKeyFrom = true;
26075 if (nodes.keyto.indexOf(n) !== -1) {
26076 connectKeyTo = true;
26080 if (connectFrom && connectTo && !isUturn) {
26081 return 'restriction';
26084 if (connectFrom && connectVia) {
26085 return 'restriction';
26088 if (connectTo && connectVia) {
26089 return 'restriction';
26090 } // connecting to a key node -
26091 // if both nodes are on a member way (i.e. part of the turn restriction),
26092 // the connecting node must be adjacent to the key node.
26095 if (connectKeyFrom || connectKeyTo) {
26096 if (nodeIDs.length !== 2) {
26097 return 'restriction';
26103 for (j = 0; j < memberWays.length; j++) {
26104 way = memberWays[j];
26106 if (way.contains(nodeIDs[0])) {
26110 if (way.contains(nodeIDs[1])) {
26116 // both nodes are part of the restriction
26119 for (j = 0; j < memberWays.length; j++) {
26120 way = memberWays[j];
26122 if (way.areAdjacent(n0, n1)) {
26129 return 'restriction';
26132 } // 2b. disable if nodes being connected will destroy a member way in a restriction
26133 // (to test, make a copy and try actually connecting the nodes)
26136 for (j = 0; j < memberWays.length; j++) {
26137 way = memberWays[j].update({}); // make copy
26139 for (k = 0; k < nodeIDs.length; k++) {
26140 if (nodeIDs[k] === survivor.id) continue;
26142 if (way.areAdjacent(nodeIDs[k], survivor.id)) {
26143 way = way.removeNode(nodeIDs[k]);
26145 way = way.replaceNode(nodeIDs[k], survivor.id);
26149 if (way.isDegenerate()) {
26150 return 'restriction';
26155 return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
26157 function hasDuplicates(n, i, arr) {
26158 return arr.indexOf(n) !== arr.lastIndexOf(n);
26161 function keyNodeFilter(froms, tos) {
26162 return function (n) {
26163 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
26167 function collectNodes(member, collection) {
26168 var entity = graph.hasEntity(member.id);
26169 if (!entity) return;
26170 var role = member.role || '';
26172 if (!collection[role]) {
26173 collection[role] = [];
26176 if (member.type === 'node') {
26177 collection[role].push(member.id);
26179 if (role === 'via') {
26180 collection.keyfrom.push(member.id);
26181 collection.keyto.push(member.id);
26183 } else if (member.type === 'way') {
26184 collection[role].push.apply(collection[role], entity.nodes);
26186 if (role === 'from' || role === 'via') {
26187 collection.keyfrom.push(entity.first());
26188 collection.keyfrom.push(entity.last());
26191 if (role === 'to' || role === 'via') {
26192 collection.keyto.push(entity.first());
26193 collection.keyto.push(entity.last());
26202 function actionCopyEntities(ids, fromGraph) {
26205 var action = function action(graph) {
26206 ids.forEach(function (id) {
26207 fromGraph.entity(id).copy(fromGraph, _copies);
26210 for (var id in _copies) {
26211 graph = graph.replace(_copies[id]);
26217 action.copies = function () {
26224 function actionDeleteMember(relationId, memberIndex) {
26225 return function (graph) {
26226 var relation = graph.entity(relationId).removeMember(memberIndex);
26227 graph = graph.replace(relation);
26228 if (relation.isDegenerate()) graph = actionDeleteRelation(relation.id)(graph);
26233 function actionDiscardTags(difference, discardTags) {
26234 discardTags = discardTags || {};
26235 return function (graph) {
26236 difference.modified().forEach(checkTags);
26237 difference.created().forEach(checkTags);
26240 function checkTags(entity) {
26241 var keys = Object.keys(entity.tags);
26242 var didDiscard = false;
26245 for (var i = 0; i < keys.length; i++) {
26248 if (discardTags[k] || !entity.tags[k]) {
26251 tags[k] = entity.tags[k];
26256 graph = graph.replace(entity.update({
26265 // Optionally, disconnect only the given ways.
26267 // For testing convenience, accepts an ID to assign to the (first) new node.
26268 // Normally, this will be undefined and the way will automatically
26269 // be assigned a new ID.
26271 // This is the inverse of `iD.actionConnect`.
26274 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
26275 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
26278 function actionDisconnect(nodeId, newNodeId) {
26281 var action = function action(graph) {
26282 var node = graph.entity(nodeId);
26283 var connections = action.connections(graph);
26284 connections.forEach(function (connection) {
26285 var way = graph.entity(connection.wayID);
26286 var newNode = osmNode({
26291 graph = graph.replace(newNode);
26293 if (connection.index === 0 && way.isArea()) {
26294 // replace shared node with shared node..
26295 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
26296 } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
26297 // replace closing node with new new node..
26298 graph = graph.replace(way.unclose().addNode(newNode.id));
26300 // replace shared node with multiple new nodes..
26301 graph = graph.replace(way.updateNode(newNode.id, connection.index));
26307 action.connections = function (graph) {
26308 var candidates = [];
26309 var keeping = false;
26310 var parentWays = graph.parentWays(graph.entity(nodeId));
26313 for (var i = 0; i < parentWays.length; i++) {
26314 way = parentWays[i];
26316 if (wayIds && wayIds.indexOf(way.id) === -1) {
26321 if (way.isArea() && way.nodes[0] === nodeId) {
26327 for (var j = 0; j < way.nodes.length; j++) {
26328 waynode = way.nodes[j];
26330 if (waynode === nodeId) {
26331 if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
26344 return keeping ? candidates : candidates.slice(1);
26347 action.disabled = function (graph) {
26348 var connections = action.connections(graph);
26349 if (connections.length === 0) return 'not_connected';
26350 var parentWays = graph.parentWays(graph.entity(nodeId));
26351 var seenRelationIds = {};
26352 var sharedRelation;
26353 parentWays.forEach(function (way) {
26354 var relations = graph.parentRelations(way);
26355 relations.forEach(function (relation) {
26356 if (relation.id in seenRelationIds) {
26358 if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
26359 sharedRelation = relation;
26362 sharedRelation = relation;
26365 seenRelationIds[relation.id] = way.id;
26369 if (sharedRelation) return 'relation';
26372 action.limitWays = function (val) {
26373 if (!arguments.length) return wayIds;
26381 var geojsonRewind = rewind;
26383 function rewind(gj, outer) {
26384 var type = gj && gj.type,
26387 if (type === 'FeatureCollection') {
26388 for (i = 0; i < gj.features.length; i++) {
26389 rewind(gj.features[i], outer);
26391 } else if (type === 'GeometryCollection') {
26392 for (i = 0; i < gj.geometries.length; i++) {
26393 rewind(gj.geometries[i], outer);
26395 } else if (type === 'Feature') {
26396 rewind(gj.geometry, outer);
26397 } else if (type === 'Polygon') {
26398 rewindRings(gj.coordinates, outer);
26399 } else if (type === 'MultiPolygon') {
26400 for (i = 0; i < gj.coordinates.length; i++) {
26401 rewindRings(gj.coordinates[i], outer);
26408 function rewindRings(rings, outer) {
26409 if (rings.length === 0) return;
26410 rewindRing(rings[0], outer);
26412 for (var i = 1; i < rings.length; i++) {
26413 rewindRing(rings[i], !outer);
26417 function rewindRing(ring, dir) {
26420 for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
26421 area += (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
26424 if (area >= 0 !== !!dir) ring.reverse();
26427 function actionExtract(entityID) {
26428 var extractedNodeID;
26430 var action = function action(graph) {
26431 var entity = graph.entity(entityID);
26433 if (entity.type === 'node') {
26434 return extractFromNode(entity, graph);
26437 return extractFromWayOrRelation(entity, graph);
26440 function extractFromNode(node, graph) {
26441 extractedNodeID = node.id; // Create a new node to replace the one we will detach
26443 var replacement = osmNode({
26446 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
26448 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
26449 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
26450 }, graph); // Process any relations too
26452 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
26453 return accGraph.replace(parentRel.replaceMember(node, replacement));
26457 function extractFromWayOrRelation(entity, graph) {
26458 var fromGeometry = entity.geometry(graph);
26459 var keysToCopyAndRetain = ['source', 'wheelchair'];
26460 var keysToRetain = ['area'];
26461 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
26463 var extractedLoc = d3_geoCentroid(geojsonRewind(Object.assign({}, entity.asGeoJSON(graph)), true));
26465 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
26466 extractedLoc = entity.extent(graph).center();
26469 var indoorAreaValues = {
26476 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
26477 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
26478 var entityTags = Object.assign({}, entity.tags); // shallow copy
26480 var pointTags = {};
26482 for (var key in entityTags) {
26483 if (entity.type === 'relation' && key === 'type') {
26487 if (keysToRetain.indexOf(key) !== -1) {
26492 // don't transfer building-related tags
26493 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
26494 } // leave `indoor` tag on the area
26497 if (isIndoorArea && key === 'indoor') {
26499 } // copy the tag from the entity to the point
26502 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
26504 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
26506 } else if (isIndoorArea && key === 'level') {
26507 // leave `level` on both features
26509 } // remove the tag from the entity
26512 delete entityTags[key];
26515 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
26516 // ensure that areas keep area geometry
26517 entityTags.area = 'yes';
26520 var replacement = osmNode({
26524 graph = graph.replace(replacement);
26525 extractedNodeID = replacement.id;
26526 return graph.replace(entity.update({
26531 action.getExtractedNodeID = function () {
26532 return extractedNodeID;
26539 // This is the inverse of `iD.actionSplit`.
26542 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
26543 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
26546 function actionJoin(ids) {
26547 function groupEntitiesByGeometry(graph) {
26548 var entities = ids.map(function (id) {
26549 return graph.entity(id);
26551 return Object.assign({
26553 }, utilArrayGroupBy(entities, function (entity) {
26554 return entity.geometry(graph);
26558 var action = function action(graph) {
26559 var ways = ids.map(graph.entity, graph);
26560 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
26561 // sort them first so they establish the overall order - #6033
26563 ways.sort(function (a, b) {
26564 var aSided = a.isSided();
26565 var bSided = b.isSided();
26566 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
26567 }); // Prefer to keep an existing way.
26569 for (var i = 0; i < ways.length; i++) {
26570 if (!ways[i].isNew()) {
26571 survivorID = ways[i].id;
26576 var sequences = osmJoinWays(ways, graph);
26577 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
26578 // `joined.actions` property will contain any actions we need to apply.
26580 graph = sequences.actions.reduce(function (g, action) {
26583 var survivor = graph.entity(survivorID);
26584 survivor = survivor.update({
26585 nodes: joined.nodes.map(function (n) {
26589 graph = graph.replace(survivor);
26590 joined.forEach(function (way) {
26591 if (way.id === survivorID) return;
26592 graph.parentRelations(way).forEach(function (parent) {
26593 graph = graph.replace(parent.replaceMember(way, survivor));
26595 survivor = survivor.mergeTags(way.tags);
26596 graph = graph.replace(survivor);
26597 graph = actionDeleteWay(way.id)(graph);
26598 }); // Finds if the join created a single-member multipolygon,
26599 // and if so turns it into a basic area instead
26601 function checkForSimpleMultipolygon() {
26602 if (!survivor.isClosed()) return;
26603 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
26604 // find multipolygons where the survivor is the only member
26605 return multipolygon.members.length === 1;
26606 }); // skip if this is the single member of multiple multipolygons
26608 if (multipolygons.length !== 1) return;
26609 var multipolygon = multipolygons[0];
26611 for (var key in survivor.tags) {
26612 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
26613 multipolygon.tags[key] !== survivor.tags[key]) return;
26616 survivor = survivor.mergeTags(multipolygon.tags);
26617 graph = graph.replace(survivor);
26618 graph = actionDeleteRelation(multipolygon.id, true
26619 /* allow untagged members */
26621 var tags = Object.assign({}, survivor.tags);
26623 if (survivor.geometry(graph) !== 'area') {
26624 // ensure the feature persists as an area
26628 delete tags.type; // remove type=multipolygon
26630 survivor = survivor.update({
26633 graph = graph.replace(survivor);
26636 checkForSimpleMultipolygon();
26638 }; // Returns the number of nodes the resultant way is expected to have
26641 action.resultingWayNodesLength = function (graph) {
26642 return ids.reduce(function (count, id) {
26643 return count + graph.entity(id).nodes.length;
26644 }, 0) - ids.length - 1;
26647 action.disabled = function (graph) {
26648 var geometries = groupEntitiesByGeometry(graph);
26650 if (ids.length < 2 || ids.length !== geometries.line.length) {
26651 return 'not_eligible';
26654 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
26656 if (joined.length > 1) {
26657 return 'not_adjacent';
26658 } // Loop through all combinations of path-pairs
26659 // to check potential intersections between all pairs
26662 for (var i = 0; i < ids.length - 1; i++) {
26663 for (var j = i + 1; j < ids.length; j++) {
26664 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
26667 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
26670 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
26671 // each other/the line, as opposed to crossing it
26673 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
26674 return n.loc.toString();
26675 }), intersections.map(function (n) {
26676 return n.toString();
26679 if (common.length !== intersections.length) {
26680 return 'paths_intersect';
26685 var nodeIds = joined[0].nodes.map(function (n) {
26690 var conflicting = false;
26691 joined[0].forEach(function (way) {
26692 var parents = graph.parentRelations(way);
26693 parents.forEach(function (parent) {
26694 if (parent.isRestriction() && parent.members.some(function (m) {
26695 return nodeIds.indexOf(m.id) >= 0;
26701 for (var k in way.tags) {
26702 if (!(k in tags)) {
26703 tags[k] = way.tags[k];
26704 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
26705 conflicting = true;
26711 return 'restriction';
26715 return 'conflicting_tags';
26722 function actionMerge(ids) {
26723 function groupEntitiesByGeometry(graph) {
26724 var entities = ids.map(function (id) {
26725 return graph.entity(id);
26727 return Object.assign({
26732 }, utilArrayGroupBy(entities, function (entity) {
26733 return entity.geometry(graph);
26737 var action = function action(graph) {
26738 var geometries = groupEntitiesByGeometry(graph);
26739 var target = geometries.area[0] || geometries.line[0];
26740 var points = geometries.point;
26741 points.forEach(function (point) {
26742 target = target.mergeTags(point.tags);
26743 graph = graph.replace(target);
26744 graph.parentRelations(point).forEach(function (parent) {
26745 graph = graph.replace(parent.replaceMember(point, target));
26747 var nodes = utilArrayUniq(graph.childNodes(target));
26748 var removeNode = point;
26750 for (var i = 0; i < nodes.length; i++) {
26751 var node = nodes[i];
26753 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
26755 } // Found an uninteresting child node on the target way.
26756 // Move orig point into its place to preserve point's history. #3683
26759 graph = graph.replace(point.update({
26763 target = target.replaceNode(node.id, point.id);
26764 graph = graph.replace(target);
26769 graph = graph.remove(removeNode);
26772 if (target.tags.area === 'yes') {
26773 var tags = Object.assign({}, target.tags); // shallow copy
26777 if (osmTagSuggestingArea(tags)) {
26778 // remove the `area` tag if area geometry is now implied - #3851
26779 target = target.update({
26782 graph = graph.replace(target);
26789 action.disabled = function (graph) {
26790 var geometries = groupEntitiesByGeometry(graph);
26792 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
26793 return 'not_eligible';
26801 // 1. move all the nodes to a common location
26802 // 2. `actionConnect` them
26804 function actionMergeNodes(nodeIDs, loc) {
26805 // If there is a single "interesting" node, use that as the location.
26806 // Otherwise return the average location of all the nodes.
26807 function chooseLoc(graph) {
26808 if (!nodeIDs.length) return null;
26810 var interestingCount = 0;
26811 var interestingLoc;
26813 for (var i = 0; i < nodeIDs.length; i++) {
26814 var node = graph.entity(nodeIDs[i]);
26816 if (node.hasInterestingTags()) {
26817 interestingLoc = ++interestingCount === 1 ? node.loc : null;
26820 sum = geoVecAdd(sum, node.loc);
26823 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
26826 var action = function action(graph) {
26827 if (nodeIDs.length < 2) return graph;
26831 toLoc = chooseLoc(graph);
26834 for (var i = 0; i < nodeIDs.length; i++) {
26835 var node = graph.entity(nodeIDs[i]);
26837 if (node.loc !== toLoc) {
26838 graph = graph.replace(node.move(toLoc));
26842 return actionConnect(nodeIDs)(graph);
26845 action.disabled = function (graph) {
26846 if (nodeIDs.length < 2) return 'not_eligible';
26848 for (var i = 0; i < nodeIDs.length; i++) {
26849 var entity = graph.entity(nodeIDs[i]);
26850 if (entity.type !== 'node') return 'not_eligible';
26853 return actionConnect(nodeIDs).disabled(graph);
26859 function osmChangeset() {
26860 if (!(this instanceof osmChangeset)) {
26861 return new osmChangeset().initialize(arguments);
26862 } else if (arguments.length) {
26863 this.initialize(arguments);
26866 osmEntity.changeset = osmChangeset;
26867 osmChangeset.prototype = Object.create(osmEntity.prototype);
26868 Object.assign(osmChangeset.prototype, {
26870 extent: function extent() {
26871 return new geoExtent();
26873 geometry: function geometry() {
26874 return 'changeset';
26876 asJXON: function asJXON() {
26880 tag: Object.keys(this.tags).map(function (k) {
26892 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26893 // XML. Returns a string.
26894 osmChangeJXON: function osmChangeJXON(changes) {
26895 var changeset_id = this.id;
26897 function nest(x, order) {
26900 for (var i = 0; i < x.length; i++) {
26901 var tagName = Object.keys(x[i])[0];
26902 if (!groups[tagName]) groups[tagName] = [];
26903 groups[tagName].push(x[i][tagName]);
26907 order.forEach(function (o) {
26908 if (groups[o]) ordered[o] = groups[o];
26911 } // sort relations in a changeset by dependencies
26914 function sort(changes) {
26915 // find a referenced relation in the current changeset
26916 function resolve(item) {
26917 return relations.find(function (relation) {
26918 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
26920 } // a new item is an item that has not been already processed
26923 function isNew(item) {
26924 return !sorted[item['@id']] && !processing.find(function (proc) {
26925 return proc['@id'] === item['@id'];
26929 var processing = [];
26931 var relations = changes.relation;
26932 if (!relations) return changes;
26934 for (var i = 0; i < relations.length; i++) {
26935 var relation = relations[i]; // skip relation if already sorted
26937 if (!sorted[relation['@id']]) {
26938 processing.push(relation);
26941 while (processing.length > 0) {
26942 var next = processing[0],
26943 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
26945 if (deps.length === 0) {
26946 sorted[next['@id']] = next;
26947 processing.shift();
26949 processing = deps.concat(processing);
26954 changes.relation = Object.values(sorted);
26958 function rep(entity) {
26959 return entity.asJXON(changeset_id);
26965 '@generator': 'iD',
26966 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
26967 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26968 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
26974 asGeoJSON: function asGeoJSON() {
26979 function osmNote() {
26980 if (!(this instanceof osmNote)) {
26981 return new osmNote().initialize(arguments);
26982 } else if (arguments.length) {
26983 this.initialize(arguments);
26987 osmNote.id = function () {
26988 return osmNote.id.next--;
26991 osmNote.id.next = -1;
26992 Object.assign(osmNote.prototype, {
26994 initialize: function initialize(sources) {
26995 for (var i = 0; i < sources.length; ++i) {
26996 var source = sources[i];
26998 for (var prop in source) {
26999 if (Object.prototype.hasOwnProperty.call(source, prop)) {
27000 if (source[prop] === undefined) {
27003 this[prop] = source[prop];
27010 this.id = osmNote.id().toString();
27015 extent: function extent() {
27016 return new geoExtent(this.loc);
27018 update: function update(attrs) {
27019 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
27021 isNew: function isNew() {
27022 return this.id < 0;
27024 move: function move(loc) {
27025 return this.update({
27031 function osmRelation() {
27032 if (!(this instanceof osmRelation)) {
27033 return new osmRelation().initialize(arguments);
27034 } else if (arguments.length) {
27035 this.initialize(arguments);
27038 osmEntity.relation = osmRelation;
27039 osmRelation.prototype = Object.create(osmEntity.prototype);
27041 osmRelation.creationOrder = function (a, b) {
27042 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
27043 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
27044 if (aId < 0 || bId < 0) return aId - bId;
27048 Object.assign(osmRelation.prototype, {
27051 copy: function copy(resolver, copies) {
27052 if (copies[this.id]) return copies[this.id];
27053 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
27054 var members = this.members.map(function (member) {
27055 return Object.assign({}, member, {
27056 id: resolver.entity(member.id).copy(resolver, copies).id
27059 copy = copy.update({
27062 copies[this.id] = copy;
27065 extent: function extent(resolver, memo) {
27066 return resolver["transient"](this, 'extent', function () {
27067 if (memo && memo[this.id]) return geoExtent();
27069 memo[this.id] = true;
27070 var extent = geoExtent();
27072 for (var i = 0; i < this.members.length; i++) {
27073 var member = resolver.hasEntity(this.members[i].id);
27076 extent._extend(member.extent(resolver, memo));
27083 geometry: function geometry(graph) {
27084 return graph["transient"](this, 'geometry', function () {
27085 return this.isMultipolygon() ? 'area' : 'relation';
27088 isDegenerate: function isDegenerate() {
27089 return this.members.length === 0;
27091 // Return an array of members, each extended with an 'index' property whose value
27092 // is the member index.
27093 indexedMembers: function indexedMembers() {
27094 var result = new Array(this.members.length);
27096 for (var i = 0; i < this.members.length; i++) {
27097 result[i] = Object.assign({}, this.members[i], {
27104 // Return the first member with the given role. A copy of the member object
27105 // is returned, extended with an 'index' property whose value is the member index.
27106 memberByRole: function memberByRole(role) {
27107 for (var i = 0; i < this.members.length; i++) {
27108 if (this.members[i].role === role) {
27109 return Object.assign({}, this.members[i], {
27115 // Same as memberByRole, but returns all members with the given role
27116 membersByRole: function membersByRole(role) {
27119 for (var i = 0; i < this.members.length; i++) {
27120 if (this.members[i].role === role) {
27121 result.push(Object.assign({}, this.members[i], {
27129 // Return the first member with the given id. A copy of the member object
27130 // is returned, extended with an 'index' property whose value is the member index.
27131 memberById: function memberById(id) {
27132 for (var i = 0; i < this.members.length; i++) {
27133 if (this.members[i].id === id) {
27134 return Object.assign({}, this.members[i], {
27140 // Return the first member with the given id and role. A copy of the member object
27141 // is returned, extended with an 'index' property whose value is the member index.
27142 memberByIdAndRole: function memberByIdAndRole(id, role) {
27143 for (var i = 0; i < this.members.length; i++) {
27144 if (this.members[i].id === id && this.members[i].role === role) {
27145 return Object.assign({}, this.members[i], {
27151 addMember: function addMember(member, index) {
27152 var members = this.members.slice();
27153 members.splice(index === undefined ? members.length : index, 0, member);
27154 return this.update({
27158 updateMember: function updateMember(member, index) {
27159 var members = this.members.slice();
27160 members.splice(index, 1, Object.assign({}, members[index], member));
27161 return this.update({
27165 removeMember: function removeMember(index) {
27166 var members = this.members.slice();
27167 members.splice(index, 1);
27168 return this.update({
27172 removeMembersWithID: function removeMembersWithID(id) {
27173 var members = this.members.filter(function (m) {
27174 return m.id !== id;
27176 return this.update({
27180 moveMember: function moveMember(fromIndex, toIndex) {
27181 var members = this.members.slice();
27182 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
27183 return this.update({
27187 // Wherever a member appears with id `needle.id`, replace it with a member
27188 // with id `replacement.id`, type `replacement.type`, and the original role,
27189 // By default, adding a duplicate member (by id and role) is prevented.
27190 // Return an updated relation.
27191 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
27192 if (!this.memberById(needle.id)) return this;
27195 for (var i = 0; i < this.members.length; i++) {
27196 var member = this.members[i];
27198 if (member.id !== needle.id) {
27199 members.push(member);
27200 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
27202 id: replacement.id,
27203 type: replacement.type,
27209 return this.update({
27213 asJXON: function asJXON(changeset_id) {
27216 '@id': this.osmId(),
27217 '@version': this.version || 0,
27218 member: this.members.map(function (member) {
27223 ref: osmEntity.id.toOSM(member.id)
27227 tag: Object.keys(this.tags).map(function (k) {
27238 if (changeset_id) {
27239 r.relation['@changeset'] = changeset_id;
27244 asGeoJSON: function asGeoJSON(resolver) {
27245 return resolver["transient"](this, 'GeoJSON', function () {
27246 if (this.isMultipolygon()) {
27248 type: 'MultiPolygon',
27249 coordinates: this.multipolygon(resolver)
27253 type: 'FeatureCollection',
27254 properties: this.tags,
27255 features: this.members.map(function (member) {
27256 return Object.assign({
27258 }, resolver.entity(member.id).asGeoJSON(resolver));
27264 area: function area(resolver) {
27265 return resolver["transient"](this, 'area', function () {
27266 return d3_geoArea(this.asGeoJSON(resolver));
27269 isMultipolygon: function isMultipolygon() {
27270 return this.tags.type === 'multipolygon';
27272 isComplete: function isComplete(resolver) {
27273 for (var i = 0; i < this.members.length; i++) {
27274 if (!resolver.hasEntity(this.members[i].id)) {
27281 hasFromViaTo: function hasFromViaTo() {
27282 return this.members.some(function (m) {
27283 return m.role === 'from';
27284 }) && this.members.some(function (m) {
27285 return m.role === 'via';
27286 }) && this.members.some(function (m) {
27287 return m.role === 'to';
27290 isRestriction: function isRestriction() {
27291 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27293 isValidRestriction: function isValidRestriction() {
27294 if (!this.isRestriction()) return false;
27295 var froms = this.members.filter(function (m) {
27296 return m.role === 'from';
27298 var vias = this.members.filter(function (m) {
27299 return m.role === 'via';
27301 var tos = this.members.filter(function (m) {
27302 return m.role === 'to';
27304 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
27305 if (froms.some(function (m) {
27306 return m.type !== 'way';
27308 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
27309 if (tos.some(function (m) {
27310 return m.type !== 'way';
27312 if (vias.length === 0) return false;
27313 if (vias.length > 1 && vias.some(function (m) {
27314 return m.type !== 'way';
27318 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27319 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27321 // This corresponds to the structure needed for rendering a multipolygon path using a
27322 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27324 // In the case of invalid geometries, this function will still return a result which
27325 // includes the nodes of all way members, but some Nds may be unclosed and some inner
27326 // rings not matched with the intended outer ring.
27328 multipolygon: function multipolygon(resolver) {
27329 var outers = this.members.filter(function (m) {
27330 return 'outer' === (m.role || 'outer');
27332 var inners = this.members.filter(function (m) {
27333 return 'inner' === m.role;
27335 outers = osmJoinWays(outers, resolver);
27336 inners = osmJoinWays(inners, resolver);
27338 var sequenceToLineString = function sequenceToLineString(sequence) {
27339 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
27340 // close unclosed parts to ensure correct area rendering - #2945
27341 sequence.nodes.push(sequence.nodes[0]);
27344 return sequence.nodes.map(function (node) {
27349 outers = outers.map(sequenceToLineString);
27350 inners = inners.map(sequenceToLineString);
27351 var result = outers.map(function (o) {
27352 // Heuristic for detecting counterclockwise winding order. Assumes
27353 // that OpenStreetMap polygons are not hemisphere-spanning.
27354 return [d3_geoArea({
27357 }) > 2 * Math.PI ? o.reverse() : o];
27360 function findOuter(inner) {
27363 for (o = 0; o < outers.length; o++) {
27365 if (geoPolygonContainsPolygon(outer, inner)) return o;
27368 for (o = 0; o < outers.length; o++) {
27370 if (geoPolygonIntersectsPolygon(outer, inner, false)) return o;
27374 for (var i = 0; i < inners.length; i++) {
27375 var inner = inners[i];
27379 coordinates: [inner]
27380 }) < 2 * Math.PI) {
27381 inner = inner.reverse();
27384 var o = findOuter(inners[i]);
27386 if (o !== undefined) {
27387 result[o].push(inners[i]);
27389 result.push([inners[i]]); // Invalid geometry
27397 var QAItem = /*#__PURE__*/function () {
27398 function QAItem(loc, service, itemType, id, props) {
27399 _classCallCheck(this, QAItem);
27401 // Store required properties
27403 this.service = service.title;
27404 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
27406 this.id = id ? id : "".concat(QAItem.id());
27407 this.update(props); // Some QA services have marker icons to differentiate issues
27409 if (service && typeof service.getIcon === 'function') {
27410 this.icon = service.getIcon(itemType);
27414 _createClass(QAItem, [{
27416 value: function update(props) {
27419 // You can't override this initial information
27420 var loc = this.loc,
27421 service = this.service,
27422 itemType = this.itemType,
27424 Object.keys(props).forEach(function (prop) {
27425 return _this[prop] = props[prop];
27428 this.service = service;
27429 this.itemType = itemType;
27432 } // Generic handling for newly created QAItems
27436 value: function id() {
27437 return this.nextId--;
27443 QAItem.nextId = -1;
27446 // Optionally, split only the given ways, if multiple ways share
27449 // This is the inverse of `iD.actionJoin`.
27451 // For testing convenience, accepts an ID to assign to the new way.
27452 // Normally, this will be undefined and the way will automatically
27453 // be assigned a new ID.
27456 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
27459 function actionSplit(nodeIds, newWayIds) {
27460 // accept single ID for backwards-compatiblity
27461 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
27463 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
27466 var _keepHistoryOn = 'longest'; // 'longest', 'first'
27467 // The IDs of the ways actually created by running this action
27469 var _createdWayIDs = [];
27471 function dist(graph, nA, nB) {
27472 var locA = graph.entity(nA).loc;
27473 var locB = graph.entity(nB).loc;
27474 var epsilon = 1e-6;
27475 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
27476 } // If the way is closed, we need to search for a partner node
27477 // to split the way at.
27479 // The following looks for a node that is both far away from
27480 // the initial node in terms of way segment length and nearby
27481 // in terms of beeline-distance. This assures that areas get
27482 // split on the most "natural" points (independent of the number
27484 // For example: bone-shaped areas get split across their waist
27485 // line, circles across the diameter.
27488 function splitArea(nodes, idxA, graph) {
27489 var lengths = new Array(nodes.length);
27495 function wrap(index) {
27496 return utilWrap(index, nodes.length);
27497 } // calculate lengths
27502 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
27503 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
27504 lengths[i] = length;
27509 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
27510 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
27512 if (length < lengths[i]) {
27513 lengths[i] = length;
27515 } // determine best opposite node to split
27518 for (i = 0; i < nodes.length; i++) {
27519 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
27530 function totalLengthBetweenNodes(graph, nodes) {
27531 var totalLength = 0;
27533 for (var i = 0; i < nodes.length - 1; i++) {
27534 totalLength += dist(graph, nodes[i], nodes[i + 1]);
27537 return totalLength;
27540 function split(graph, nodeId, wayA, newWayId) {
27541 var wayB = osmWay({
27544 }); // `wayB` is the NEW way
27546 var origNodes = wayA.nodes.slice();
27549 var isArea = wayA.isArea();
27550 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
27552 if (wayA.isClosed()) {
27553 var nodes = wayA.nodes.slice(0, -1);
27554 var idxA = nodes.indexOf(nodeId);
27555 var idxB = splitArea(nodes, idxA, graph);
27558 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
27559 nodesB = nodes.slice(idxB, idxA + 1);
27561 nodesA = nodes.slice(idxA, idxB + 1);
27562 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
27565 var idx = wayA.nodes.indexOf(nodeId, 1);
27566 nodesA = wayA.nodes.slice(0, idx + 1);
27567 nodesB = wayA.nodes.slice(idx);
27570 var lengthA = totalLengthBetweenNodes(graph, nodesA);
27571 var lengthB = totalLengthBetweenNodes(graph, nodesB);
27573 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
27574 // keep the history on the longer way, regardless of the node count
27575 wayA = wayA.update({
27578 wayB = wayB.update({
27581 var temp = lengthA;
27585 wayA = wayA.update({
27588 wayB = wayB.update({
27593 if (wayA.tags.step_count) {
27594 // divide up the the step count proportionally between the two ways
27595 var stepCount = parseFloat(wayA.tags.step_count);
27597 if (stepCount && // ensure a number
27598 isFinite(stepCount) && // ensure positive
27599 stepCount > 0 && // ensure integer
27600 Math.round(stepCount) === stepCount) {
27601 var tagsA = Object.assign({}, wayA.tags);
27602 var tagsB = Object.assign({}, wayB.tags);
27603 var ratioA = lengthA / (lengthA + lengthB);
27604 var countA = Math.round(stepCount * ratioA);
27605 tagsA.step_count = countA.toString();
27606 tagsB.step_count = (stepCount - countA).toString();
27607 wayA = wayA.update({
27610 wayB = wayB.update({
27616 graph = graph.replace(wayA);
27617 graph = graph.replace(wayB);
27618 graph.parentRelations(wayA).forEach(function (relation) {
27619 var member; // Turn restrictions - make sure:
27620 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
27621 // (whichever one is connected to the VIA node/ways)
27622 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
27624 if (relation.hasFromViaTo()) {
27625 var f = relation.memberByRole('from');
27626 var v = relation.membersByRole('via');
27627 var t = relation.memberByRole('to');
27628 var i; // 1. split a FROM/TO
27630 if (f.id === wayA.id || t.id === wayA.id) {
27633 if (v.length === 1 && v[0].type === 'node') {
27635 keepB = wayB.contains(v[0].id);
27637 // check via way(s)
27638 for (i = 0; i < v.length; i++) {
27639 if (v[i].type === 'way') {
27640 var wayVia = graph.hasEntity(v[i].id);
27642 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
27651 relation = relation.replaceMember(wayA, wayB);
27652 graph = graph.replace(relation);
27653 } // 2. split a VIA
27656 for (i = 0; i < v.length; i++) {
27657 if (v[i].type === 'way' && v[i].id === wayA.id) {
27663 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
27667 } // All other relations (Routes, Multipolygons, etc):
27668 // 1. Both `wayA` and `wayB` remain in the relation
27669 // 2. But must be inserted as a pair (see `actionAddMember` for details)
27672 if (relation === isOuter) {
27673 graph = graph.replace(relation.mergeTags(wayA.tags));
27674 graph = graph.replace(wayA.update({
27677 graph = graph.replace(wayB.update({
27685 role: relation.memberById(wayA.id).role
27688 originalID: wayA.id,
27689 insertedID: wayB.id,
27692 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
27696 if (!isOuter && isArea) {
27697 var multipolygon = osmRelation({
27698 tags: Object.assign({}, wayA.tags, {
27699 type: 'multipolygon'
27711 graph = graph.replace(multipolygon);
27712 graph = graph.replace(wayA.update({
27715 graph = graph.replace(wayB.update({
27720 _createdWayIDs.push(wayB.id);
27725 var action = function action(graph) {
27726 _createdWayIDs = [];
27727 var newWayIndex = 0;
27729 for (var i = 0; i < nodeIds.length; i++) {
27730 var nodeId = nodeIds[i];
27731 var candidates = action.waysForNode(nodeId, graph);
27733 for (var j = 0; j < candidates.length; j++) {
27734 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
27742 action.getCreatedWayIDs = function () {
27743 return _createdWayIDs;
27746 action.waysForNode = function (nodeId, graph) {
27747 var node = graph.entity(nodeId);
27748 var splittableParents = graph.parentWays(node).filter(isSplittable);
27751 // If the ways to split aren't specified, only split the lines.
27752 // If there are no lines to split, split the areas.
27753 var hasLine = splittableParents.some(function (parent) {
27754 return parent.geometry(graph) === 'line';
27758 return splittableParents.filter(function (parent) {
27759 return parent.geometry(graph) === 'line';
27764 return splittableParents;
27766 function isSplittable(parent) {
27767 // If the ways to split are specified, ignore everything else.
27768 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
27770 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
27772 for (var i = 1; i < parent.nodes.length - 1; i++) {
27773 if (parent.nodes[i] === nodeId) return true;
27780 action.ways = function (graph) {
27781 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
27782 return action.waysForNode(nodeId, graph);
27786 action.disabled = function (graph) {
27787 for (var i = 0; i < nodeIds.length; i++) {
27788 var nodeId = nodeIds[i];
27789 var candidates = action.waysForNode(nodeId, graph);
27791 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
27792 return 'not_eligible';
27797 action.limitWays = function (val) {
27798 if (!arguments.length) return _wayIDs;
27803 action.keepHistoryOn = function (val) {
27804 if (!arguments.length) return _keepHistoryOn;
27805 _keepHistoryOn = val;
27812 function coreGraph(other, mutable) {
27813 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
27815 if (other instanceof coreGraph) {
27816 var base = other.base();
27817 this.entities = Object.assign(Object.create(base.entities), other.entities);
27818 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
27819 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
27821 this.entities = Object.create({});
27822 this._parentWays = Object.create({});
27823 this._parentRels = Object.create({});
27824 this.rebase(other || [], [this]);
27827 this.transients = {};
27828 this._childNodes = {};
27829 this.frozen = !mutable;
27831 coreGraph.prototype = {
27832 hasEntity: function hasEntity(id) {
27833 return this.entities[id];
27835 entity: function entity(id) {
27836 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
27839 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
27843 throw new Error('entity ' + id + ' not found');
27848 geometry: function geometry(id) {
27849 return this.entity(id).geometry(this);
27851 "transient": function transient(entity, key, fn) {
27852 var id = entity.id;
27853 var transients = this.transients[id] || (this.transients[id] = {});
27855 if (transients[key] !== undefined) {
27856 return transients[key];
27859 transients[key] = fn.call(entity);
27860 return transients[key];
27862 parentWays: function parentWays(entity) {
27863 var parents = this._parentWays[entity.id];
27867 parents.forEach(function (id) {
27868 result.push(this.entity(id));
27874 isPoi: function isPoi(entity) {
27875 var parents = this._parentWays[entity.id];
27876 return !parents || parents.size === 0;
27878 isShared: function isShared(entity) {
27879 var parents = this._parentWays[entity.id];
27880 return parents && parents.size > 1;
27882 parentRelations: function parentRelations(entity) {
27883 var parents = this._parentRels[entity.id];
27887 parents.forEach(function (id) {
27888 result.push(this.entity(id));
27894 parentMultipolygons: function parentMultipolygons(entity) {
27895 return this.parentRelations(entity).filter(function (relation) {
27896 return relation.isMultipolygon();
27899 childNodes: function childNodes(entity) {
27900 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27901 if (!entity.nodes) return [];
27904 for (var i = 0; i < entity.nodes.length; i++) {
27905 nodes[i] = this.entity(entity.nodes[i]);
27907 this._childNodes[entity.id] = nodes;
27908 return this._childNodes[entity.id];
27910 base: function base() {
27912 'entities': Object.getPrototypeOf(this.entities),
27913 'parentWays': Object.getPrototypeOf(this._parentWays),
27914 'parentRels': Object.getPrototypeOf(this._parentRels)
27917 // Unlike other graph methods, rebase mutates in place. This is because it
27918 // is used only during the history operation that merges newly downloaded
27919 // data into each state. To external consumers, it should appear as if the
27920 // graph always contained the newly downloaded data.
27921 rebase: function rebase(entities, stack, force) {
27922 var base = this.base();
27925 for (i = 0; i < entities.length; i++) {
27926 var entity = entities[i];
27927 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
27929 base.entities[entity.id] = entity;
27931 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
27934 if (entity.type === 'way') {
27935 for (j = 0; j < entity.nodes.length; j++) {
27936 id = entity.nodes[j];
27938 for (k = 1; k < stack.length; k++) {
27939 var ents = stack[k].entities;
27941 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27949 for (i = 0; i < stack.length; i++) {
27950 stack[i]._updateRebased();
27953 _updateRebased: function _updateRebased() {
27954 var base = this.base();
27955 Object.keys(this._parentWays).forEach(function (child) {
27956 if (base.parentWays[child]) {
27957 base.parentWays[child].forEach(function (id) {
27958 if (!this.entities.hasOwnProperty(id)) {
27959 this._parentWays[child].add(id);
27964 Object.keys(this._parentRels).forEach(function (child) {
27965 if (base.parentRels[child]) {
27966 base.parentRels[child].forEach(function (id) {
27967 if (!this.entities.hasOwnProperty(id)) {
27968 this._parentRels[child].add(id);
27973 this.transients = {}; // this._childNodes is not updated, under the assumption that
27974 // ways are always downloaded with their child nodes.
27976 // Updates calculated properties (parentWays, parentRels) for the specified change
27977 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
27978 parentWays = parentWays || this._parentWays;
27979 parentRels = parentRels || this._parentRels;
27980 var type = entity && entity.type || oldentity && oldentity.type;
27981 var removed, added, i;
27983 if (type === 'way') {
27984 // Update parentWays
27985 if (oldentity && entity) {
27986 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
27987 added = utilArrayDifference(entity.nodes, oldentity.nodes);
27988 } else if (oldentity) {
27989 removed = oldentity.nodes;
27991 } else if (entity) {
27993 added = entity.nodes;
27996 for (i = 0; i < removed.length; i++) {
27997 // make a copy of prototype property, store as own property, and update..
27998 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
27999 parentWays[removed[i]]["delete"](oldentity.id);
28002 for (i = 0; i < added.length; i++) {
28003 // make a copy of prototype property, store as own property, and update..
28004 parentWays[added[i]] = new Set(parentWays[added[i]]);
28005 parentWays[added[i]].add(entity.id);
28007 } else if (type === 'relation') {
28008 // Update parentRels
28009 // diff only on the IDs since the same entity can be a member multiple times with different roles
28010 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
28013 var entityMemberIDs = entity ? entity.members.map(function (m) {
28017 if (oldentity && entity) {
28018 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
28019 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
28020 } else if (oldentity) {
28021 removed = oldentityMemberIDs;
28023 } else if (entity) {
28025 added = entityMemberIDs;
28028 for (i = 0; i < removed.length; i++) {
28029 // make a copy of prototype property, store as own property, and update..
28030 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
28031 parentRels[removed[i]]["delete"](oldentity.id);
28034 for (i = 0; i < added.length; i++) {
28035 // make a copy of prototype property, store as own property, and update..
28036 parentRels[added[i]] = new Set(parentRels[added[i]]);
28037 parentRels[added[i]].add(entity.id);
28041 replace: function replace(entity) {
28042 if (this.entities[entity.id] === entity) return this;
28043 return this.update(function () {
28044 this._updateCalculated(this.entities[entity.id], entity);
28046 this.entities[entity.id] = entity;
28049 remove: function remove(entity) {
28050 return this.update(function () {
28051 this._updateCalculated(entity, undefined);
28053 this.entities[entity.id] = undefined;
28056 revert: function revert(id) {
28057 var baseEntity = this.base().entities[id];
28058 var headEntity = this.entities[id];
28059 if (headEntity === baseEntity) return this;
28060 return this.update(function () {
28061 this._updateCalculated(headEntity, baseEntity);
28063 delete this.entities[id];
28066 update: function update() {
28067 var graph = this.frozen ? coreGraph(this, true) : this;
28069 for (var i = 0; i < arguments.length; i++) {
28070 arguments[i].call(graph, graph);
28073 if (this.frozen) graph.frozen = true;
28076 // Obliterates any existing entities
28077 load: function load(entities) {
28078 var base = this.base();
28079 this.entities = Object.create(base.entities);
28081 for (var i in entities) {
28082 this.entities[i] = entities[i];
28084 this._updateCalculated(base.entities[i], this.entities[i]);
28091 function osmTurn(turn) {
28092 if (!(this instanceof osmTurn)) {
28093 return new osmTurn(turn);
28096 Object.assign(this, turn);
28098 function osmIntersection(graph, startVertexId, maxDistance) {
28099 maxDistance = maxDistance || 30; // in meters
28101 var vgraph = coreGraph(); // virtual graph
28105 function memberOfRestriction(entity) {
28106 return graph.parentRelations(entity).some(function (r) {
28107 return r.isRestriction();
28111 function isRoad(way) {
28112 if (way.isArea() || way.isDegenerate()) return false;
28115 'motorway_link': true,
28117 'trunk_link': true,
28119 'primary_link': true,
28121 'secondary_link': true,
28123 'tertiary_link': true,
28124 'residential': true,
28125 'unclassified': true,
28126 'living_street': true,
28131 return roads[way.tags.highway];
28134 var startNode = graph.entity(startVertexId);
28135 var checkVertices = [startNode];
28138 var vertexIds = [];
28146 var parent; // `actions` will store whatever actions must be performed to satisfy
28147 // preconditions for adding a turn restriction to this intersection.
28148 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
28149 // - Reverse oneways so that they are drawn in the forward direction
28150 // - Split ways on key vertices
28152 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
28153 // for more key vertices and ways to include in the intersection..
28155 while (checkVertices.length) {
28156 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
28158 checkWays = graph.parentWays(vertex);
28159 var hasWays = false;
28161 for (i = 0; i < checkWays.length; i++) {
28162 way = checkWays[i];
28163 if (!isRoad(way) && !memberOfRestriction(way)) continue;
28164 ways.push(way); // it's a road, or it's already in a turn restriction
28166 hasWays = true; // check the way's children for more key vertices
28168 nodes = utilArrayUniq(graph.childNodes(way));
28170 for (j = 0; j < nodes.length; j++) {
28172 if (node === vertex) continue; // same thing
28174 if (vertices.indexOf(node) !== -1) continue; // seen it already
28176 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
28177 // a key vertex will have parents that are also roads
28179 var hasParents = false;
28180 parents = graph.parentWays(node);
28182 for (k = 0; k < parents.length; k++) {
28183 parent = parents[k];
28184 if (parent === way) continue; // same thing
28186 if (ways.indexOf(parent) !== -1) continue; // seen it already
28188 if (!isRoad(parent)) continue; // not a road
28195 checkVertices.push(node);
28201 vertices.push(vertex);
28205 vertices = utilArrayUniq(vertices);
28206 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
28207 // Everything done after this step should act on the virtual graph
28208 // Any actions that must be performed later to the main graph go in `actions` array
28210 ways.forEach(function (way) {
28211 graph.childNodes(way).forEach(function (node) {
28212 vgraph = vgraph.replace(node);
28214 vgraph = vgraph.replace(way);
28215 graph.parentRelations(way).forEach(function (relation) {
28216 if (relation.isRestriction()) {
28217 if (relation.isValidRestriction(graph)) {
28218 vgraph = vgraph.replace(relation);
28219 } else if (relation.isComplete(graph)) {
28220 actions.push(actionDeleteRelation(relation.id));
28224 }); // STEP 3: Force all oneways to be drawn in the forward direction
28226 ways.forEach(function (w) {
28227 var way = vgraph.entity(w.id);
28229 if (way.tags.oneway === '-1') {
28230 var action = actionReverse(way.id, {
28231 reverseOneway: true
28233 actions.push(action);
28234 vgraph = action(vgraph);
28236 }); // STEP 4: Split ways on key vertices
28238 var origCount = osmEntity.id.next.way;
28239 vertices.forEach(function (v) {
28240 // This is an odd way to do it, but we need to find all the ways that
28241 // will be split here, then split them one at a time to ensure that these
28242 // actions can be replayed on the main graph exactly in the same order.
28243 // (It is unintuitive, but the order of ways returned from graph.parentWays()
28244 // is arbitrary, depending on how the main graph and vgraph were built)
28245 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
28247 if (!splitAll.disabled(vgraph)) {
28248 splitAll.ways(vgraph).forEach(function (way) {
28249 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
28250 actions.push(splitOne);
28251 vgraph = splitOne(vgraph);
28254 }); // In here is where we should also split the intersection at nearby junction.
28255 // for https://github.com/mapbox/iD-internal/issues/31
28256 // nearbyVertices.forEach(function(v) {
28258 // Reasons why we reset the way id count here:
28259 // 1. Continuity with way ids created by the splits so that we can replay
28260 // these actions later if the user decides to create a turn restriction
28261 // 2. Avoids churning way ids just by hovering over a vertex
28262 // and displaying the turn restriction editor
28264 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
28266 vertexIds = vertices.map(function (v) {
28271 vertexIds.forEach(function (id) {
28272 var vertex = vgraph.entity(id);
28273 var parents = vgraph.parentWays(vertex);
28274 vertices.push(vertex);
28275 ways = ways.concat(parents);
28277 vertices = utilArrayUniq(vertices);
28278 ways = utilArrayUniq(ways);
28279 vertexIds = vertices.map(function (v) {
28282 wayIds = ways.map(function (w) {
28284 }); // STEP 6: Update the ways with some metadata that will be useful for
28285 // walking the intersection graph later and rendering turn arrows.
28287 function withMetadata(way, vertexIds) {
28288 var __oneWay = way.isOneWay(); // which affixes are key vertices?
28291 var __first = vertexIds.indexOf(way.first()) !== -1;
28293 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
28296 var __via = __first && __last;
28298 var __from = __first && !__oneWay || __last;
28300 var __to = __first || __last && !__oneWay;
28302 return way.update({
28313 wayIds.forEach(function (id) {
28314 var way = withMetadata(vgraph.entity(id), vertexIds);
28315 vgraph = vgraph.replace(way);
28317 }); // STEP 7: Simplify - This is an iterative process where we:
28318 // 1. Find trivial vertices with only 2 parents
28319 // 2. trim off the leaf way from those vertices and remove from vgraph
28322 var removeWayIds = [];
28323 var removeVertexIds = [];
28327 checkVertices = vertexIds.slice();
28329 for (i = 0; i < checkVertices.length; i++) {
28330 var vertexId = checkVertices[i];
28331 vertex = vgraph.hasEntity(vertexId);
28334 if (vertexIds.indexOf(vertexId) !== -1) {
28335 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28338 removeVertexIds.push(vertexId);
28342 parents = vgraph.parentWays(vertex);
28344 if (parents.length < 3) {
28345 if (vertexIds.indexOf(vertexId) !== -1) {
28346 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28350 if (parents.length === 2) {
28351 // vertex with 2 parents is trivial
28352 var a = parents[0];
28353 var b = parents[1];
28354 var aIsLeaf = a && !a.__via;
28355 var bIsLeaf = b && !b.__via;
28356 var leaf, survivor;
28358 if (aIsLeaf && !bIsLeaf) {
28361 } else if (!aIsLeaf && bIsLeaf) {
28366 if (leaf && survivor) {
28367 survivor = withMetadata(survivor, vertexIds); // update survivor way
28369 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
28371 removeWayIds.push(leaf.id);
28376 parents = vgraph.parentWays(vertex);
28378 if (parents.length < 2) {
28379 // vertex is no longer a key vertex
28380 if (vertexIds.indexOf(vertexId) !== -1) {
28381 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28384 removeVertexIds.push(vertexId);
28388 if (parents.length < 1) {
28389 // vertex is no longer attached to anything
28390 vgraph = vgraph.remove(vertex);
28393 } while (keepGoing);
28395 vertices = vertices.filter(function (vertex) {
28396 return removeVertexIds.indexOf(vertex.id) === -1;
28397 }).map(function (vertex) {
28398 return vgraph.entity(vertex.id);
28400 ways = ways.filter(function (way) {
28401 return removeWayIds.indexOf(way.id) === -1;
28402 }).map(function (way) {
28403 return vgraph.entity(way.id);
28404 }); // OK! Here is our intersection..
28406 var intersection = {
28409 vertices: vertices,
28411 }; // Get all the valid turns through this intersection given a starting way id.
28412 // This operates on the virtual graph for everything.
28414 // Basically, walk through all possible paths from starting way,
28415 // honoring the existing turn restrictions as we go (watch out for loops!)
28417 // For each path found, generate and return a `osmTurn` datastructure.
28420 intersection.turns = function (fromWayId, maxViaWay) {
28421 if (!fromWayId) return [];
28422 if (!maxViaWay) maxViaWay = 0;
28423 var vgraph = intersection.graph;
28424 var keyVertexIds = intersection.vertices.map(function (v) {
28427 var start = vgraph.entity(fromWayId);
28428 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
28429 // maxViaWay=1 from-*-via-*-to (1 via max)
28430 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
28432 var maxPathLength = maxViaWay * 2 + 3;
28435 return turns; // traverse the intersection graph and find all the valid paths
28437 function step(entity, currPath, currRestrictions, matchedRestriction) {
28438 currPath = (currPath || []).slice(); // shallow copy
28440 if (currPath.length >= maxPathLength) return;
28441 currPath.push(entity.id);
28442 currRestrictions = (currRestrictions || []).slice(); // shallow copy
28446 if (entity.type === 'node') {
28447 var parents = vgraph.parentWays(entity);
28448 var nextWays = []; // which ways can we step into?
28450 for (i = 0; i < parents.length; i++) {
28451 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
28453 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
28455 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
28457 var restrict = null;
28459 for (j = 0; j < currRestrictions.length; j++) {
28460 var restriction = currRestrictions[j];
28461 var f = restriction.memberByRole('from');
28462 var v = restriction.membersByRole('via');
28463 var t = restriction.memberByRole('to');
28464 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
28466 var matchesFrom = f.id === fromWayId;
28467 var matchesViaTo = false;
28468 var isAlongOnlyPath = false;
28470 if (t.id === way.id) {
28472 if (v.length === 1 && v[0].type === 'node') {
28474 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
28476 // match all VIA ways
28479 for (k = 2; k < currPath.length; k += 2) {
28480 // k = 2 skips FROM
28481 pathVias.push(currPath[k]); // (path goes way-node-way...)
28484 var restrictionVias = [];
28486 for (k = 0; k < v.length; k++) {
28487 if (v[k].type === 'way') {
28488 restrictionVias.push(v[k].id);
28492 var diff = utilArrayDifference(pathVias, restrictionVias);
28493 matchesViaTo = !diff.length;
28495 } else if (isOnly) {
28496 for (k = 0; k < v.length; k++) {
28497 // way doesn't match TO, but is one of the via ways along the path of an "only"
28498 if (v[k].type === 'way' && v[k].id === way.id) {
28499 isAlongOnlyPath = true;
28505 if (matchesViaTo) {
28508 id: restriction.id,
28509 direct: matchesFrom,
28516 id: restriction.id,
28517 direct: matchesFrom,
28524 // indirect - caused by a different nearby restriction
28525 if (isAlongOnlyPath) {
28527 id: restriction.id,
28533 } else if (isOnly) {
28535 id: restriction.id,
28542 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
28545 if (restrict && restrict.direct) break;
28554 nextWays.forEach(function (nextWay) {
28555 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
28558 // entity.type === 'way'
28559 if (currPath.length >= 3) {
28560 // this is a "complete" path..
28561 var turnPath = currPath.slice(); // shallow copy
28562 // an indirect restriction - only include the partial path (starting at FROM)
28564 if (matchedRestriction && matchedRestriction.direct === false) {
28565 for (i = 0; i < turnPath.length; i++) {
28566 if (turnPath[i] === matchedRestriction.from) {
28567 turnPath = turnPath.slice(i);
28573 var turn = pathToTurn(turnPath);
28576 if (matchedRestriction) {
28577 turn.restrictionID = matchedRestriction.id;
28578 turn.no = matchedRestriction.no;
28579 turn.only = matchedRestriction.only;
28580 turn.direct = matchedRestriction.direct;
28583 turns.push(osmTurn(turn));
28586 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
28589 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
28590 // which nodes can we step into?
28592 var n1 = vgraph.entity(entity.first());
28593 var n2 = vgraph.entity(entity.last());
28594 var dist = geoSphericalDistance(n1.loc, n2.loc);
28595 var nextNodes = [];
28597 if (currPath.length > 1) {
28598 if (dist > maxDistance) return; // the next node is too far
28600 if (!entity.__via) return; // this way is a leaf / can't be a via
28603 if (!entity.__oneWay && // bidirectional..
28604 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
28605 currPath.indexOf(n1.id) === -1) {
28606 // haven't seen it yet..
28607 nextNodes.push(n1); // can advance to first node
28610 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
28611 currPath.indexOf(n2.id) === -1) {
28612 // haven't seen it yet..
28613 nextNodes.push(n2); // can advance to last node
28616 nextNodes.forEach(function (nextNode) {
28617 // gather restrictions FROM this way
28618 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
28619 if (!r.isRestriction()) return false;
28620 var f = r.memberByRole('from');
28621 if (!f || f.id !== entity.id) return false;
28622 var isOnly = /^only_/.test(r.tags.restriction);
28623 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
28625 var isOnlyVia = false;
28626 var v = r.membersByRole('via');
28628 if (v.length === 1 && v[0].type === 'node') {
28630 isOnlyVia = v[0].id === nextNode.id;
28633 for (var i = 0; i < v.length; i++) {
28634 if (v[i].type !== 'way') continue;
28635 var viaWay = vgraph.entity(v[i].id);
28637 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
28646 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
28649 } // assumes path is alternating way-node-way of odd length
28652 function pathToTurn(path) {
28653 if (path.length < 3) return;
28654 var fromWayId, fromNodeId, fromVertexId;
28655 var toWayId, toNodeId, toVertexId;
28656 var viaWayIds, viaNodeId, isUturn;
28657 fromWayId = path[0];
28658 toWayId = path[path.length - 1];
28660 if (path.length === 3 && fromWayId === toWayId) {
28662 var way = vgraph.entity(fromWayId);
28663 if (way.__oneWay) return null;
28665 viaNodeId = fromVertexId = toVertexId = path[1];
28666 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
28669 fromVertexId = path[1];
28670 fromNodeId = adjacentNode(fromWayId, fromVertexId);
28671 toVertexId = path[path.length - 2];
28672 toNodeId = adjacentNode(toWayId, toVertexId);
28674 if (path.length === 3) {
28675 viaNodeId = path[1];
28677 viaWayIds = path.filter(function (entityId) {
28678 return entityId[0] === 'w';
28680 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
28685 key: path.join('_'),
28690 vertex: fromVertexId
28704 function adjacentNode(wayId, affixId) {
28705 var nodes = vgraph.entity(wayId).nodes;
28706 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
28711 return intersection;
28713 function osmInferRestriction(graph, turn, projection) {
28714 var fromWay = graph.entity(turn.from.way);
28715 var fromNode = graph.entity(turn.from.node);
28716 var fromVertex = graph.entity(turn.from.vertex);
28717 var toWay = graph.entity(turn.to.way);
28718 var toNode = graph.entity(turn.to.node);
28719 var toVertex = graph.entity(turn.to.vertex);
28720 var fromOneWay = fromWay.tags.oneway === 'yes';
28721 var toOneWay = toWay.tags.oneway === 'yes';
28722 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
28724 while (angle < 0) {
28728 if (fromNode === toNode) return 'no_u_turn';
28729 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
28731 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)
28733 if (angle < 158) return 'no_right_turn';
28734 if (angle > 202) return 'no_left_turn';
28735 return 'no_straight_on';
28738 function actionMergePolygon(ids, newRelationId) {
28739 function groupEntities(graph) {
28740 var entities = ids.map(function (id) {
28741 return graph.entity(id);
28743 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
28744 if (entity.type === 'way' && entity.isClosed()) {
28745 return 'closedWay';
28746 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
28747 return 'multipolygon';
28752 return Object.assign({
28756 }, geometryGroups);
28759 var action = function action(graph) {
28760 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
28762 // Each element is itself an array of objects with an id property, and has a
28763 // locs property which is an array of the locations forming the polygon.
28765 var polygons = entities.multipolygon.reduce(function (polygons, m) {
28766 return polygons.concat(osmJoinWays(m.members, graph));
28767 }, []).concat(entities.closedWay.map(function (d) {
28771 member.nodes = graph.childNodes(d);
28773 })); // contained is an array of arrays of boolean values,
28774 // where contained[j][k] is true iff the jth way is
28775 // contained by the kth way.
28777 var contained = polygons.map(function (w, i) {
28778 return polygons.map(function (d, n) {
28779 if (i === n) return null;
28780 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
28782 }), w.nodes.map(function (n) {
28786 }); // Sort all polygons as either outer or inner ways
28791 while (polygons.length) {
28792 extractUncontained(polygons);
28793 polygons = polygons.filter(isContained);
28794 contained = contained.filter(isContained).map(filterContained);
28797 function isContained(d, i) {
28798 return contained[i].some(function (val) {
28803 function filterContained(d) {
28804 return d.filter(isContained);
28807 function extractUncontained(polygons) {
28808 polygons.forEach(function (d, i) {
28809 if (!isContained(d, i)) {
28810 d.forEach(function (member) {
28814 role: outer ? 'outer' : 'inner'
28820 } // Move all tags to one relation
28823 var relation = entities.multipolygon[0] || osmRelation({
28826 type: 'multipolygon'
28829 entities.multipolygon.slice(1).forEach(function (m) {
28830 relation = relation.mergeTags(m.tags);
28831 graph = graph.remove(m);
28833 entities.closedWay.forEach(function (way) {
28834 function isThisOuter(m) {
28835 return m.id === way.id && m.role !== 'inner';
28838 if (members.some(isThisOuter)) {
28839 relation = relation.mergeTags(way.tags);
28840 graph = graph.replace(way.update({
28845 return graph.replace(relation.update({
28847 tags: utilObjectOmit(relation.tags, ['area'])
28851 action.disabled = function (graph) {
28852 var entities = groupEntities(graph);
28854 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
28855 return 'not_eligible';
28858 if (!entities.multipolygon.every(function (r) {
28859 return r.isComplete(graph);
28861 return 'incomplete_relation';
28864 if (!entities.multipolygon.length) {
28865 var sharedMultipolygons = [];
28866 entities.closedWay.forEach(function (way, i) {
28868 sharedMultipolygons = graph.parentMultipolygons(way);
28870 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
28873 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
28874 return relation.members.length === entities.closedWay.length;
28877 if (sharedMultipolygons.length) {
28878 // don't create a new multipolygon if it'd be redundant
28879 return 'not_eligible';
28881 } else if (entities.closedWay.some(function (way) {
28882 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
28884 // don't add a way to a multipolygon again if it's already a member
28885 return 'not_eligible';
28892 var UNSUPPORTED_Y$3 = regexpStickyHelpers.UNSUPPORTED_Y;
28894 // `RegExp.prototype.flags` getter
28895 // https://tc39.github.io/ecma262/#sec-get-regexp.prototype.flags
28896 if (descriptors && (/./g.flags != 'g' || UNSUPPORTED_Y$3)) {
28897 objectDefineProperty.f(RegExp.prototype, 'flags', {
28898 configurable: true,
28903 var fastDeepEqual = function equal(a, b) {
28904 if (a === b) return true;
28906 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
28907 if (a.constructor !== b.constructor) return false;
28908 var length, i, keys;
28910 if (Array.isArray(a)) {
28912 if (length != b.length) return false;
28914 for (i = length; i-- !== 0;) {
28915 if (!equal(a[i], b[i])) return false;
28921 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
28922 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
28923 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
28924 keys = Object.keys(a);
28925 length = keys.length;
28926 if (length !== Object.keys(b).length) return false;
28928 for (i = length; i-- !== 0;) {
28929 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
28932 for (i = length; i-- !== 0;) {
28934 if (!equal(a[key], b[key])) return false;
28938 } // true if both NaN, false otherwise
28941 return a !== a && b !== b;
28944 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
28945 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
28946 // http://www.cs.dartmouth.edu/~doug/
28947 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
28949 // Expects two arrays, finds longest common sequence
28951 function LCS(buffer1, buffer2) {
28952 var equivalenceClasses = {};
28954 for (var j = 0; j < buffer2.length; j++) {
28955 var item = buffer2[j];
28957 if (equivalenceClasses[item]) {
28958 equivalenceClasses[item].push(j);
28960 equivalenceClasses[item] = [j];
28969 var candidates = [NULLRESULT];
28971 for (var i = 0; i < buffer1.length; i++) {
28972 var _item = buffer1[i];
28973 var buffer2indices = equivalenceClasses[_item] || [];
28975 var c = candidates[0];
28977 for (var jx = 0; jx < buffer2indices.length; jx++) {
28978 var _j = buffer2indices[jx];
28981 for (s = r; s < candidates.length; s++) {
28982 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
28987 if (s < candidates.length) {
28988 var newCandidate = {
28991 chain: candidates[s]
28994 if (r === candidates.length) {
28995 candidates.push(c);
29003 if (r === candidates.length) {
29004 break; // no point in examining further (j)s
29010 } // At this point, we know the LCS: it's in the reverse of the
29011 // linked-list through .chain of candidates[candidates.length - 1].
29014 return candidates[candidates.length - 1];
29015 } // We apply the LCS to build a 'comm'-style picture of the
29016 // offsets and lengths of mismatched chunks in the input
29017 // buffers. This is used by diff3MergeRegions.
29020 function diffIndices(buffer1, buffer2) {
29021 var lcs = LCS(buffer1, buffer2);
29023 var tail1 = buffer1.length;
29024 var tail2 = buffer2.length;
29026 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
29027 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
29028 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
29029 tail1 = candidate.buffer1index;
29030 tail2 = candidate.buffer2index;
29032 if (mismatchLength1 || mismatchLength2) {
29034 buffer1: [tail1 + 1, mismatchLength1],
29035 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
29036 buffer2: [tail2 + 1, mismatchLength2],
29037 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
29044 } // We apply the LCS to build a JSON representation of a
29045 // independently derived from O, returns a fairly complicated
29046 // internal representation of merge decisions it's taken. The
29047 // interested reader may wish to consult
29049 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
29050 // 'A Formal Investigation of ' In Arvind and Prasad,
29051 // editors, Foundations of Software Technology and Theoretical
29052 // Computer Science (FSTTCS), December 2007.
29054 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
29058 function diff3MergeRegions(a, o, b) {
29059 // "hunks" are array subsets where `a` or `b` are different from `o`
29060 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
29063 function addHunk(h, ab) {
29066 oStart: h.buffer1[0],
29067 oLength: h.buffer1[1],
29068 // length of o to remove
29069 abStart: h.buffer2[0],
29070 abLength: h.buffer2[1] // length of a/b to insert
29071 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
29076 diffIndices(o, a).forEach(function (item) {
29077 return addHunk(item, 'a');
29079 diffIndices(o, b).forEach(function (item) {
29080 return addHunk(item, 'b');
29082 hunks.sort(function (x, y) {
29083 return x.oStart - y.oStart;
29086 var currOffset = 0;
29088 function advanceTo(endOffset) {
29089 if (endOffset > currOffset) {
29093 bufferStart: currOffset,
29094 bufferLength: endOffset - currOffset,
29095 bufferContent: o.slice(currOffset, endOffset)
29097 currOffset = endOffset;
29101 while (hunks.length) {
29102 var hunk = hunks.shift();
29103 var regionStart = hunk.oStart;
29104 var regionEnd = hunk.oStart + hunk.oLength;
29105 var regionHunks = [hunk];
29106 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
29108 while (hunks.length) {
29109 var nextHunk = hunks[0];
29110 var nextHunkStart = nextHunk.oStart;
29111 if (nextHunkStart > regionEnd) break; // no overlap
29113 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
29114 regionHunks.push(hunks.shift());
29117 if (regionHunks.length === 1) {
29118 // Only one hunk touches this region, meaning that there is no conflict here.
29119 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
29120 if (hunk.abLength > 0) {
29121 var buffer = hunk.ab === 'a' ? a : b;
29125 bufferStart: hunk.abStart,
29126 bufferLength: hunk.abLength,
29127 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
29131 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
29132 // Effectively merge all the `a` hunks into one giant hunk, then do the
29133 // same for the `b` hunks; then, correct for skew in the regions of `o`
29134 // that each side changed, and report appropriate spans for the three sides.
29136 a: [a.length, -1, o.length, -1],
29137 b: [b.length, -1, o.length, -1]
29140 while (regionHunks.length) {
29141 hunk = regionHunks.shift();
29142 var oStart = hunk.oStart;
29143 var oEnd = oStart + hunk.oLength;
29144 var abStart = hunk.abStart;
29145 var abEnd = abStart + hunk.abLength;
29146 var _b = bounds[hunk.ab];
29147 _b[0] = Math.min(abStart, _b[0]);
29148 _b[1] = Math.max(abEnd, _b[1]);
29149 _b[2] = Math.min(oStart, _b[2]);
29150 _b[3] = Math.max(oEnd, _b[3]);
29153 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
29154 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
29155 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
29156 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
29160 aLength: aEnd - aStart,
29161 aContent: a.slice(aStart, aEnd),
29162 oStart: regionStart,
29163 oLength: regionEnd - regionStart,
29164 oContent: o.slice(regionStart, regionEnd),
29166 bLength: bEnd - bStart,
29167 bContent: b.slice(bStart, bEnd)
29169 results.push(result);
29172 currOffset = regionEnd;
29175 advanceTo(o.length);
29177 } // Applies the output of diff3MergeRegions to actually
29178 // construct the merged buffer; the returned result alternates
29179 // between 'ok' and 'conflict' blocks.
29180 // A "false conflict" is where `a` and `b` both change the same from `o`
29183 function diff3Merge(a, o, b, options) {
29185 excludeFalseConflicts: true,
29186 stringSeparator: /\s+/
29188 options = Object.assign(defaults, options);
29189 var aString = typeof a === 'string';
29190 var oString = typeof o === 'string';
29191 var bString = typeof b === 'string';
29192 if (aString) a = a.split(options.stringSeparator);
29193 if (oString) o = o.split(options.stringSeparator);
29194 if (bString) b = b.split(options.stringSeparator);
29196 var regions = diff3MergeRegions(a, o, b);
29199 function flushOk() {
29200 if (okBuffer.length) {
29209 function isFalseConflict(a, b) {
29210 if (a.length !== b.length) return false;
29212 for (var i = 0; i < a.length; i++) {
29213 if (a[i] !== b[i]) return false;
29219 regions.forEach(function (region) {
29220 if (region.stable) {
29223 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
29225 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
29228 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
29233 a: region.aContent,
29234 aIndex: region.aStart,
29235 o: region.oContent,
29236 oIndex: region.oStart,
29237 b: region.bContent,
29238 bIndex: region.bStart
29248 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
29249 discardTags = discardTags || {};
29250 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
29252 var _conflicts = [];
29255 return typeof formatUser === 'function' ? formatUser(d) : d;
29258 function mergeLocation(remote, target) {
29259 function pointEqual(a, b) {
29260 var epsilon = 1e-6;
29261 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
29264 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
29268 if (_option === 'force_remote') {
29269 return target.update({
29274 _conflicts.push(_t('merge_remote_changes.conflict.location', {
29275 user: user(remote.user)
29281 function mergeNodes(base, remote, target) {
29282 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
29286 if (_option === 'force_remote') {
29287 return target.update({
29288 nodes: remote.nodes
29292 var ccount = _conflicts.length;
29293 var o = base.nodes || [];
29294 var a = target.nodes || [];
29295 var b = remote.nodes || [];
29297 var hunks = diff3Merge(a, o, b, {
29298 excludeFalseConflicts: true
29301 for (var i = 0; i < hunks.length; i++) {
29302 var hunk = hunks[i];
29305 nodes.push.apply(nodes, hunk.ok);
29307 // for all conflicts, we can assume c.a !== c.b
29308 // because `diff3Merge` called with `true` option to exclude false conflicts..
29309 var c = hunk.conflict;
29311 if (fastDeepEqual(c.o, c.a)) {
29312 // only changed remotely
29313 nodes.push.apply(nodes, c.b);
29314 } else if (fastDeepEqual(c.o, c.b)) {
29315 // only changed locally
29316 nodes.push.apply(nodes, c.a);
29318 // changed both locally and remotely
29319 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
29320 user: user(remote.user)
29328 return _conflicts.length === ccount ? target.update({
29333 function mergeChildren(targetWay, children, updates, graph) {
29334 function isUsed(node, targetWay) {
29335 var hasInterestingParent = graph.parentWays(node).some(function (way) {
29336 return way.id !== targetWay.id;
29338 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
29341 var ccount = _conflicts.length;
29343 for (var i = 0; i < children.length; i++) {
29344 var id = children[i];
29345 var node = graph.hasEntity(id); // remove unused childNodes..
29347 if (targetWay.nodes.indexOf(id) === -1) {
29348 if (node && !isUsed(node, targetWay)) {
29349 updates.removeIds.push(id);
29353 } // restore used childNodes..
29356 var local = localGraph.hasEntity(id);
29357 var remote = remoteGraph.hasEntity(id);
29360 if (_option === 'force_remote' && remote && remote.visible) {
29361 updates.replacements.push(remote);
29362 } else if (_option === 'force_local' && local) {
29363 target = osmEntity(local);
29366 target = target.update({
29367 version: remote.version
29371 updates.replacements.push(target);
29372 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
29373 target = osmEntity(local, {
29374 version: remote.version
29377 if (remote.visible) {
29378 target = mergeLocation(remote, target);
29380 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29381 user: user(remote.user)
29385 if (_conflicts.length !== ccount) break;
29386 updates.replacements.push(target);
29393 function updateChildren(updates, graph) {
29394 for (var i = 0; i < updates.replacements.length; i++) {
29395 graph = graph.replace(updates.replacements[i]);
29398 if (updates.removeIds.length) {
29399 graph = actionDeleteMultiple(updates.removeIds)(graph);
29405 function mergeMembers(remote, target) {
29406 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
29410 if (_option === 'force_remote') {
29411 return target.update({
29412 members: remote.members
29416 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
29417 user: user(remote.user)
29423 function mergeTags(base, remote, target) {
29424 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
29428 if (_option === 'force_remote') {
29429 return target.update({
29434 var ccount = _conflicts.length;
29435 var o = base.tags || {};
29436 var a = target.tags || {};
29437 var b = remote.tags || {};
29438 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
29439 return !discardTags[k];
29441 var tags = Object.assign({}, a); // shallow copy
29443 var changed = false;
29445 for (var i = 0; i < keys.length; i++) {
29448 if (o[k] !== b[k] && a[k] !== b[k]) {
29449 // changed remotely..
29450 if (o[k] !== a[k]) {
29451 // changed locally..
29452 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
29456 user: user(remote.user)
29459 // unchanged locally, accept remote change..
29460 if (b.hasOwnProperty(k)) {
29471 return changed && _conflicts.length === ccount ? target.update({
29474 } // `graph.base()` is the common ancestor of the two graphs.
29475 // `localGraph` contains user's edits up to saving
29476 // `remoteGraph` contains remote edits to modified nodes
29477 // `graph` must be a descendent of `localGraph` and may include
29478 // some conflict resolution actions performed on it.
29480 // --- ... --- `localGraph` -- ... -- `graph`
29482 // `graph.base()` --- ... --- `remoteGraph`
29486 var action = function action(graph) {
29491 var base = graph.base().entities[id];
29492 var local = localGraph.entity(id);
29493 var remote = remoteGraph.entity(id);
29494 var target = osmEntity(local, {
29495 version: remote.version
29496 }); // delete/undelete
29498 if (!remote.visible) {
29499 if (_option === 'force_remote') {
29500 return actionDeleteMultiple([id])(graph);
29501 } else if (_option === 'force_local') {
29502 if (target.type === 'way') {
29503 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
29504 graph = updateChildren(updates, graph);
29507 return graph.replace(target);
29509 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29510 user: user(remote.user)
29513 return graph; // do nothing
29518 if (target.type === 'node') {
29519 target = mergeLocation(remote, target);
29520 } else if (target.type === 'way') {
29521 // pull in any child nodes that may not be present locally..
29522 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
29523 target = mergeNodes(base, remote, target);
29524 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
29525 } else if (target.type === 'relation') {
29526 target = mergeMembers(remote, target);
29529 target = mergeTags(base, remote, target);
29531 if (!_conflicts.length) {
29532 graph = updateChildren(updates, graph).replace(target);
29538 action.withOption = function (opt) {
29543 action.conflicts = function () {
29550 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
29552 function actionMove(moveIDs, tryDelta, projection, cache) {
29553 var _delta = tryDelta;
29555 function setupCache(graph) {
29556 function canMove(nodeID) {
29557 // Allow movement of any node that is in the selectedIDs list..
29558 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
29560 var parents = graph.parentWays(graph.entity(nodeID));
29561 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
29563 var parentsMoving = parents.every(function (way) {
29564 return cache.moving[way.id];
29566 if (!parentsMoving) delete cache.moving[nodeID];
29567 return parentsMoving;
29570 function cacheEntities(ids) {
29571 for (var i = 0; i < ids.length; i++) {
29573 if (cache.moving[id]) continue;
29574 cache.moving[id] = true;
29575 var entity = graph.hasEntity(id);
29576 if (!entity) continue;
29578 if (entity.type === 'node') {
29579 cache.nodes.push(id);
29580 cache.startLoc[id] = entity.loc;
29581 } else if (entity.type === 'way') {
29582 cache.ways.push(id);
29583 cacheEntities(entity.nodes);
29585 cacheEntities(entity.members.map(function (member) {
29592 function cacheIntersections(ids) {
29593 function isEndpoint(way, id) {
29594 return !way.isClosed() && !!way.affix(id);
29597 for (var i = 0; i < ids.length; i++) {
29598 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
29600 var childNodes = graph.childNodes(graph.entity(id));
29602 for (var j = 0; j < childNodes.length; j++) {
29603 var node = childNodes[j];
29604 var parents = graph.parentWays(node);
29605 if (parents.length !== 2) continue;
29606 var moved = graph.entity(id);
29607 var unmoved = null;
29609 for (var k = 0; k < parents.length; k++) {
29610 var way = parents[k];
29612 if (!cache.moving[way.id]) {
29618 if (!unmoved) continue; // exclude ways that are overly connected..
29620 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
29621 if (moved.isArea() || unmoved.isArea()) continue;
29622 cache.intersections.push({
29625 unmovedId: unmoved.id,
29626 movedIsEP: isEndpoint(moved, node.id),
29627 unmovedIsEP: isEndpoint(unmoved, node.id)
29639 cache.intersections = [];
29640 cache.replacedVertex = {};
29641 cache.startLoc = {};
29644 cacheEntities(moveIDs);
29645 cacheIntersections(cache.ways);
29646 cache.nodes = cache.nodes.filter(canMove);
29649 } // Place a vertex where the moved vertex used to be, to preserve way shape..
29658 // * node '*' added to preserve shape
29660 // / b ---- e way `b,e` moved here:
29667 function replaceMovedVertex(nodeId, wayId, graph, delta) {
29668 var way = graph.entity(wayId);
29669 var moved = graph.entity(nodeId);
29670 var movedIndex = way.nodes.indexOf(nodeId);
29671 var len, prevIndex, nextIndex;
29673 if (way.isClosed()) {
29674 len = way.nodes.length - 1;
29675 prevIndex = (movedIndex + len - 1) % len;
29676 nextIndex = (movedIndex + len + 1) % len;
29678 len = way.nodes.length;
29679 prevIndex = movedIndex - 1;
29680 nextIndex = movedIndex + 1;
29683 var prev = graph.hasEntity(way.nodes[prevIndex]);
29684 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
29686 if (!prev || !next) return graph;
29687 var key = wayId + '_' + nodeId;
29688 var orig = cache.replacedVertex[key];
29692 cache.replacedVertex[key] = orig;
29693 cache.startLoc[orig.id] = cache.startLoc[nodeId];
29699 start = projection(cache.startLoc[nodeId]);
29700 end = projection.invert(geoVecAdd(start, delta));
29702 end = cache.startLoc[nodeId];
29705 orig = orig.move(end);
29706 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..
29708 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
29710 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
29711 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
29712 var d1 = geoPathLength(p1);
29713 var d2 = geoPathLength(p2);
29714 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
29716 if (way.isClosed() && insertAt === 0) insertAt = len;
29717 way = way.addNode(orig.id, insertAt);
29718 return graph.replace(orig).replace(way);
29719 } // Remove duplicate vertex that might have been added by
29720 // replaceMovedVertex. This is done after the unzorro checks.
29723 function removeDuplicateVertices(wayId, graph) {
29724 var way = graph.entity(wayId);
29725 var epsilon = 1e-6;
29728 function isInteresting(node, graph) {
29729 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
29732 for (var i = 0; i < way.nodes.length; i++) {
29733 curr = graph.entity(way.nodes[i]);
29735 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
29736 if (!isInteresting(prev, graph)) {
29737 way = way.removeNode(prev.id);
29738 graph = graph.replace(way).remove(prev);
29739 } else if (!isInteresting(curr, graph)) {
29740 way = way.removeNode(curr.id);
29741 graph = graph.replace(way).remove(curr);
29749 } // Reorder nodes around intersections that have moved..
29751 // Start: way1.nodes: b,e (moving)
29752 // a - b - c ----- d way2.nodes: a,b,c,d (static)
29754 // e isEP1: true, isEP2, false
29756 // way1 `b,e` moved here:
29757 // a ----- c = b - d
29761 // reorder nodes way1.nodes: b,e
29762 // a ----- c - b - d way2.nodes: a,c,b,d
29768 function unZorroIntersection(intersection, graph) {
29769 var vertex = graph.entity(intersection.nodeId);
29770 var way1 = graph.entity(intersection.movedId);
29771 var way2 = graph.entity(intersection.unmovedId);
29772 var isEP1 = intersection.movedIsEP;
29773 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
29775 if (isEP1 && isEP2) return graph;
29776 var nodes1 = graph.childNodes(way1).filter(function (n) {
29777 return n !== vertex;
29779 var nodes2 = graph.childNodes(way2).filter(function (n) {
29780 return n !== vertex;
29782 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
29783 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
29784 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
29785 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
29786 var loc; // snap vertex to nearest edge (or some point between them)..
29788 if (!isEP1 && !isEP2) {
29789 var epsilon = 1e-6,
29792 for (var i = 0; i < maxIter; i++) {
29793 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
29794 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
29795 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
29796 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
29798 } else if (!isEP1) {
29804 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
29806 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
29807 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
29808 graph = graph.replace(way1);
29811 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
29812 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
29813 graph = graph.replace(way2);
29819 function cleanupIntersections(graph) {
29820 for (var i = 0; i < cache.intersections.length; i++) {
29821 var obj = cache.intersections[i];
29822 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
29823 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
29824 graph = unZorroIntersection(obj, graph);
29825 graph = removeDuplicateVertices(obj.movedId, graph);
29826 graph = removeDuplicateVertices(obj.unmovedId, graph);
29830 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
29833 function limitDelta(graph) {
29834 function moveNode(loc) {
29835 return geoVecAdd(projection(loc), _delta);
29838 for (var i = 0; i < cache.intersections.length; i++) {
29839 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
29841 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
29843 if (!obj.movedIsEP) continue;
29844 var node = graph.entity(obj.nodeId);
29845 var start = projection(node.loc);
29846 var end = geoVecAdd(start, _delta);
29847 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
29848 var movedPath = movedNodes.map(function (n) {
29849 return moveNode(n.loc);
29851 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
29852 var unmovedPath = unmovedNodes.map(function (n) {
29853 return projection(n.loc);
29855 var hits = geoPathIntersections(movedPath, unmovedPath);
29857 for (var j = 0; i < hits.length; i++) {
29858 if (geoVecEqual(hits[j], end)) continue;
29859 var edge = geoChooseEdge(unmovedNodes, end, projection);
29860 _delta = geoVecSubtract(projection(edge.loc), start);
29865 var action = function action(graph) {
29866 if (_delta[0] === 0 && _delta[1] === 0) return graph;
29869 if (cache.intersections.length) {
29873 for (var i = 0; i < cache.nodes.length; i++) {
29874 var node = graph.entity(cache.nodes[i]);
29875 var start = projection(node.loc);
29876 var end = geoVecAdd(start, _delta);
29877 graph = graph.replace(node.move(projection.invert(end)));
29880 if (cache.intersections.length) {
29881 graph = cleanupIntersections(graph);
29887 action.delta = function () {
29894 function actionMoveMember(relationId, fromIndex, toIndex) {
29895 return function (graph) {
29896 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
29900 function actionMoveNode(nodeID, toLoc) {
29901 var action = function action(graph, t) {
29902 if (t === null || !isFinite(t)) t = 1;
29903 t = Math.min(Math.max(+t, 0), 1);
29904 var node = graph.entity(nodeID);
29905 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
29908 action.transitionable = true;
29912 function actionNoop() {
29913 return function (graph) {
29918 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
29919 var epsilon = ep || 1e-4;
29920 var threshold = degThresh || 13; // degrees within right or straight to alter
29921 // We test normalized dot products so we can compare as cos(angle)
29923 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
29924 var upperThreshold = Math.cos(threshold * Math.PI / 180);
29926 var action = function action(graph, t) {
29927 if (t === null || !isFinite(t)) t = 1;
29928 t = Math.min(Math.max(+t, 0), 1);
29929 var way = graph.entity(wayID);
29930 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
29932 if (way.tags.nonsquare) {
29933 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
29935 delete tags.nonsquare;
29941 graph = graph.replace(way);
29942 var isClosed = way.isClosed();
29943 var nodes = graph.childNodes(way).slice(); // shallow copy
29945 if (isClosed) nodes.pop();
29947 if (vertexID !== undefined) {
29948 nodes = nodeSubset(nodes, vertexID, isClosed);
29949 if (nodes.length !== 3) return graph;
29950 } // note: all geometry functions here use the unclosed node/point/coord list
29953 var nodeCount = {};
29959 var node, point, loc, score, motions, i, j;
29961 for (i = 0; i < nodes.length; i++) {
29963 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
29966 coord: projection(node.loc)
29970 if (points.length === 3) {
29971 // move only one vertex for right triangle
29972 for (i = 0; i < 1000; i++) {
29973 motions = points.map(calcMotion);
29974 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
29975 score = corner.dotp;
29977 if (score < epsilon) {
29982 node = graph.entity(nodes[corner.i].id);
29983 loc = projection.invert(points[corner.i].coord);
29984 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29986 var straights = [];
29987 var simplified = []; // Remove points from nearly straight sections..
29988 // This produces a simplified shape to orthogonalize
29990 for (i = 0; i < points.length; i++) {
29994 if (isClosed || i > 0 && i < points.length - 1) {
29995 var a = points[(i - 1 + points.length) % points.length];
29996 var b = points[(i + 1) % points.length];
29997 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
30000 if (dotp > upperThreshold) {
30001 straights.push(point);
30003 simplified.push(point);
30005 } // Orthogonalize the simplified shape
30008 var bestPoints = clonePoints(simplified);
30009 var originalPoints = clonePoints(simplified);
30012 for (i = 0; i < 1000; i++) {
30013 motions = simplified.map(calcMotion);
30015 for (j = 0; j < motions.length; j++) {
30016 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
30019 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
30021 if (newScore < score) {
30022 bestPoints = clonePoints(simplified);
30026 if (score < epsilon) {
30031 var bestCoords = bestPoints.map(function (p) {
30034 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
30036 for (i = 0; i < bestPoints.length; i++) {
30037 point = bestPoints[i];
30039 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
30040 node = graph.entity(point.id);
30041 loc = projection.invert(point.coord);
30042 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30044 } // move the nodes along straight segments
30047 for (i = 0; i < straights.length; i++) {
30048 point = straights[i];
30049 if (nodeCount[point.id] > 1) continue; // skip self-intersections
30051 node = graph.entity(point.id);
30053 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
30054 // remove uninteresting points..
30055 graph = actionDeleteNode(node.id)(graph);
30057 // move interesting points to the nearest edge..
30058 var choice = geoVecProject(point.coord, bestCoords);
30061 loc = projection.invert(choice.target);
30062 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30070 function clonePoints(array) {
30071 return array.map(function (p) {
30074 coord: [p.coord[0], p.coord[1]]
30079 function calcMotion(point, i, array) {
30080 // don't try to move the endpoints of a non-closed way.
30081 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)
30083 if (nodeCount[array[i].id] > 1) return [0, 0];
30084 var a = array[(i - 1 + array.length) % array.length].coord;
30085 var origin = point.coord;
30086 var b = array[(i + 1) % array.length].coord;
30087 var p = geoVecSubtract(a, origin);
30088 var q = geoVecSubtract(b, origin);
30089 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
30090 p = geoVecNormalize(p);
30091 q = geoVecNormalize(q);
30092 var dotp = p[0] * q[0] + p[1] * q[1];
30093 var val = Math.abs(dotp);
30095 if (val < lowerThreshold) {
30096 // nearly orthogonal
30099 var vec = geoVecNormalize(geoVecAdd(p, q));
30100 return geoVecScale(vec, 0.1 * dotp * scale);
30103 return [0, 0]; // do nothing
30105 }; // if we are only orthogonalizing one vertex,
30106 // get that vertex and the previous and next
30109 function nodeSubset(nodes, vertexID, isClosed) {
30110 var first = isClosed ? 0 : 1;
30111 var last = isClosed ? nodes.length : nodes.length - 1;
30113 for (var i = first; i < last; i++) {
30114 if (nodes[i].id === vertexID) {
30115 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
30122 action.disabled = function (graph) {
30123 var way = graph.entity(wayID);
30124 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
30126 graph = graph.replace(way);
30127 var isClosed = way.isClosed();
30128 var nodes = graph.childNodes(way).slice(); // shallow copy
30130 if (isClosed) nodes.pop();
30131 var allowStraightAngles = false;
30133 if (vertexID !== undefined) {
30134 allowStraightAngles = true;
30135 nodes = nodeSubset(nodes, vertexID, isClosed);
30136 if (nodes.length !== 3) return 'end_vertex';
30139 var coords = nodes.map(function (n) {
30140 return projection(n.loc);
30142 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
30144 if (score === null) {
30145 return 'not_squarish';
30146 } else if (score === 0) {
30147 return 'square_enough';
30153 action.transitionable = true;
30158 // `turn` must be an `osmTurn` object
30159 // see osm/intersection.js, pathToTurn()
30161 // This specifies a restriction of type `restriction` when traveling from
30162 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
30163 // (The action does not check that these entities form a valid intersection.)
30165 // From, to, and via ways should be split before calling this action.
30166 // (old versions of the code would split the ways here, but we no longer do it)
30168 // For testing convenience, accepts a restrictionID to assign to the new
30169 // relation. Normally, this will be undefined and the relation will
30170 // automatically be assigned a new ID.
30173 function actionRestrictTurn(turn, restrictionType, restrictionID) {
30174 return function (graph) {
30175 var fromWay = graph.entity(turn.from.way);
30176 var toWay = graph.entity(turn.to.way);
30177 var viaNode = turn.via.node && graph.entity(turn.via.node);
30178 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
30179 return graph.entity(id);
30194 } else if (viaWays) {
30195 viaWays.forEach(function (viaWay) {
30209 return graph.replace(osmRelation({
30212 type: 'restriction',
30213 restriction: restrictionType
30220 function actionRevert(id) {
30221 var action = function action(graph) {
30222 var entity = graph.hasEntity(id),
30223 base = graph.base().entities[id];
30225 if (entity && !base) {
30226 // entity will be removed..
30227 if (entity.type === 'node') {
30228 graph.parentWays(entity).forEach(function (parent) {
30229 parent = parent.removeNode(id);
30230 graph = graph.replace(parent);
30232 if (parent.isDegenerate()) {
30233 graph = actionDeleteWay(parent.id)(graph);
30238 graph.parentRelations(entity).forEach(function (parent) {
30239 parent = parent.removeMembersWithID(id);
30240 graph = graph.replace(parent);
30242 if (parent.isDegenerate()) {
30243 graph = actionDeleteRelation(parent.id)(graph);
30248 return graph.revert(id);
30254 function actionRotate(rotateIds, pivot, angle, projection) {
30255 var action = function action(graph) {
30256 return graph.update(function (graph) {
30257 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
30258 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
30259 graph = graph.replace(node.move(projection.invert(point)));
30267 function actionScale(ids, pivotLoc, scaleFactor, projection) {
30268 return function (graph) {
30269 return graph.update(function (graph) {
30271 utilGetAllNodes(ids, graph).forEach(function (node) {
30272 point = projection(node.loc);
30273 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
30274 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
30275 graph = graph.replace(node.move(projection.invert(point)));
30281 /* Align nodes along their common axis */
30283 function actionStraightenNodes(nodeIDs, projection) {
30284 function positionAlongWay(a, o, b) {
30285 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30286 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
30289 function getEndpoints(points) {
30290 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30291 // The shape's surrounding rectangle has 2 axes of symmetry.
30292 // Snap points to the long axis
30294 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30295 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30296 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30297 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30298 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30307 var action = function action(graph, t) {
30308 if (t === null || !isFinite(t)) t = 1;
30309 t = Math.min(Math.max(+t, 0), 1);
30310 var nodes = nodeIDs.map(function (id) {
30311 return graph.entity(id);
30313 var points = nodes.map(function (n) {
30314 return projection(n.loc);
30316 var endpoints = getEndpoints(points);
30317 var startPoint = endpoints[0];
30318 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
30320 for (var i = 0; i < points.length; i++) {
30321 var node = nodes[i];
30322 var point = points[i];
30323 var u = positionAlongWay(point, startPoint, endPoint);
30324 var point2 = geoVecInterp(startPoint, endPoint, u);
30325 var loc2 = projection.invert(point2);
30326 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30332 action.disabled = function (graph) {
30333 var nodes = nodeIDs.map(function (id) {
30334 return graph.entity(id);
30336 var points = nodes.map(function (n) {
30337 return projection(n.loc);
30339 var endpoints = getEndpoints(points);
30340 var startPoint = endpoints[0];
30341 var endPoint = endpoints[1];
30342 var maxDistance = 0;
30344 for (var i = 0; i < points.length; i++) {
30345 var point = points[i];
30346 var u = positionAlongWay(point, startPoint, endPoint);
30347 var p = geoVecInterp(startPoint, endPoint, u);
30348 var dist = geoVecLength(p, point);
30350 if (!isNaN(dist) && dist > maxDistance) {
30351 maxDistance = dist;
30355 if (maxDistance < 0.0001) {
30356 return 'straight_enough';
30360 action.transitionable = true;
30365 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
30368 function actionStraightenWay(selectedIDs, projection) {
30369 function positionAlongWay(a, o, b) {
30370 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30371 } // Return all selected ways as a continuous, ordered array of nodes
30374 function allNodes(graph) {
30376 var startNodes = [];
30378 var remainingWays = [];
30379 var selectedWays = selectedIDs.filter(function (w) {
30380 return graph.entity(w).type === 'way';
30382 var selectedNodes = selectedIDs.filter(function (n) {
30383 return graph.entity(n).type === 'node';
30386 for (var i = 0; i < selectedWays.length; i++) {
30387 var way = graph.entity(selectedWays[i]);
30388 nodes = way.nodes.slice(0);
30389 remainingWays.push(nodes);
30390 startNodes.push(nodes[0]);
30391 endNodes.push(nodes[nodes.length - 1]);
30392 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
30393 // and need to be removed so currNode difference calculation below works)
30394 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
30397 startNodes = startNodes.filter(function (n) {
30398 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
30400 endNodes = endNodes.filter(function (n) {
30401 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
30402 }); // Choose the initial endpoint to start from
30404 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
30406 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
30408 var getNextWay = function getNextWay(currNode, remainingWays) {
30409 return remainingWays.filter(function (way) {
30410 return way[0] === currNode || way[way.length - 1] === currNode;
30412 }; // Add nodes to end of nodes array, until all ways are added
30415 while (remainingWays.length) {
30416 nextWay = getNextWay(currNode, remainingWays);
30417 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
30419 if (nextWay[0] !== currNode) {
30423 nodes = nodes.concat(nextWay);
30424 currNode = nodes[nodes.length - 1];
30425 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
30428 if (selectedNodes.length === 2) {
30429 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
30430 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
30431 var sortedStartEnd = [startNodeIdx, endNodeIdx];
30432 sortedStartEnd.sort(function (a, b) {
30435 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
30438 return nodes.map(function (n) {
30439 return graph.entity(n);
30443 function shouldKeepNode(node, graph) {
30444 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
30447 var action = function action(graph, t) {
30448 if (t === null || !isFinite(t)) t = 1;
30449 t = Math.min(Math.max(+t, 0), 1);
30450 var nodes = allNodes(graph);
30451 var points = nodes.map(function (n) {
30452 return projection(n.loc);
30454 var startPoint = points[0];
30455 var endPoint = points[points.length - 1];
30459 for (i = 1; i < points.length - 1; i++) {
30460 var node = nodes[i];
30461 var point = points[i];
30463 if (t < 1 || shouldKeepNode(node, graph)) {
30464 var u = positionAlongWay(point, startPoint, endPoint);
30465 var p = geoVecInterp(startPoint, endPoint, u);
30466 var loc2 = projection.invert(p);
30467 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30470 if (toDelete.indexOf(node) === -1) {
30471 toDelete.push(node);
30476 for (i = 0; i < toDelete.length; i++) {
30477 graph = actionDeleteNode(toDelete[i].id)(graph);
30483 action.disabled = function (graph) {
30484 // check way isn't too bendy
30485 var nodes = allNodes(graph);
30486 var points = nodes.map(function (n) {
30487 return projection(n.loc);
30489 var startPoint = points[0];
30490 var endPoint = points[points.length - 1];
30491 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
30494 if (threshold === 0) {
30495 return 'too_bendy';
30498 var maxDistance = 0;
30500 for (i = 1; i < points.length - 1; i++) {
30501 var point = points[i];
30502 var u = positionAlongWay(point, startPoint, endPoint);
30503 var p = geoVecInterp(startPoint, endPoint, u);
30504 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
30506 if (isNaN(dist) || dist > threshold) {
30507 return 'too_bendy';
30508 } else if (dist > maxDistance) {
30509 maxDistance = dist;
30513 var keepingAllNodes = nodes.every(function (node, i) {
30514 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
30517 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
30519 return 'straight_enough';
30523 action.transitionable = true;
30528 // `turn` must be an `osmTurn` object with a `restrictionID` property.
30529 // see osm/intersection.js, pathToTurn()
30532 function actionUnrestrictTurn(turn) {
30533 return function (graph) {
30534 return actionDeleteRelation(turn.restrictionID)(graph);
30538 /* Reflect the given area around its axis of symmetry */
30540 function actionReflect(reflectIds, projection) {
30541 var _useLongAxis = true;
30543 var action = function action(graph, t) {
30544 if (t === null || !isFinite(t)) t = 1;
30545 t = Math.min(Math.max(+t, 0), 1);
30546 var nodes = utilGetAllNodes(reflectIds, graph);
30547 var points = nodes.map(function (n) {
30548 return projection(n.loc);
30550 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30551 // The shape's surrounding rectangle has 2 axes of symmetry.
30552 // Reflect across the longer axis by default.
30554 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30555 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30556 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30557 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30559 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30561 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
30567 } // reflect c across pq
30568 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
30571 var dx = q[0] - p[0];
30572 var dy = q[1] - p[1];
30573 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
30574 var b = 2 * dx * dy / (dx * dx + dy * dy);
30576 for (var i = 0; i < nodes.length; i++) {
30577 var node = nodes[i];
30578 var c = projection(node.loc);
30579 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]];
30580 var loc2 = projection.invert(c2);
30581 node = node.move(geoVecInterp(node.loc, loc2, t));
30582 graph = graph.replace(node);
30588 action.useLongAxis = function (val) {
30589 if (!arguments.length) return _useLongAxis;
30590 _useLongAxis = val;
30594 action.transitionable = true;
30598 function actionUpgradeTags(entityId, oldTags, replaceTags) {
30599 return function (graph) {
30600 var entity = graph.entity(entityId);
30601 var tags = Object.assign({}, entity.tags); // shallow copy
30606 for (var oldTagKey in oldTags) {
30607 if (!(oldTagKey in tags)) continue; // wildcard match
30609 if (oldTags[oldTagKey] === '*') {
30610 // note the value since we might need to transfer it
30611 transferValue = tags[oldTagKey];
30612 delete tags[oldTagKey]; // exact match
30613 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
30614 delete tags[oldTagKey]; // match is within semicolon-delimited values
30616 var vals = tags[oldTagKey].split(';').filter(Boolean);
30617 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
30619 if (vals.length === 1 || oldIndex === -1) {
30620 delete tags[oldTagKey];
30622 if (replaceTags && replaceTags[oldTagKey]) {
30623 // replacing a value within a semicolon-delimited value, note the index
30624 semiIndex = oldIndex;
30627 vals.splice(oldIndex, 1);
30628 tags[oldTagKey] = vals.join(';');
30634 for (var replaceKey in replaceTags) {
30635 var replaceValue = replaceTags[replaceKey];
30637 if (replaceValue === '*') {
30638 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
30639 // allow any pre-existing value except `no` (troll tag)
30642 // otherwise assume `yes` is okay
30643 tags[replaceKey] = 'yes';
30645 } else if (replaceValue === '$1') {
30646 tags[replaceKey] = transferValue;
30648 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
30649 // don't override preexisting values
30650 var existingVals = tags[replaceKey].split(';').filter(Boolean);
30652 if (existingVals.indexOf(replaceValue) === -1) {
30653 existingVals.splice(semiIndex, 0, replaceValue);
30654 tags[replaceKey] = existingVals.join(';');
30657 tags[replaceKey] = replaceValue;
30663 return graph.replace(entity.update({
30669 function behaviorEdit(context) {
30670 function behavior() {
30671 context.map().minzoom(context.minEditableZoom());
30674 behavior.off = function () {
30675 context.map().minzoom(0);
30682 The hover behavior adds the `.hover` class on pointerover to all elements to which
30683 the identical datum is bound, and removes it on pointerout.
30685 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
30686 representation may consist of several elements scattered throughout the DOM hierarchy.
30687 Only one of these elements can have the :hover pseudo-class, but all of them will
30688 have the .hover class.
30691 function behaviorHover(context) {
30692 var dispatch$1 = dispatch('hover');
30694 var _selection = select(null);
30696 var _newNodeId = null;
30697 var _initialNodeID = null;
30703 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
30705 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
30707 function keydown(d3_event) {
30708 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30709 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
30711 _selection.classed('hover-disabled', true);
30713 dispatch$1.call('hover', this, null);
30717 function keyup(d3_event) {
30718 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30719 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
30721 _selection.classed('hover-disabled', false);
30723 dispatch$1.call('hover', this, _targets);
30727 function behavior(selection) {
30728 _selection = selection;
30731 if (_initialNodeID) {
30732 _newNodeId = _initialNodeID;
30733 _initialNodeID = null;
30738 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
30739 .on(_pointerPrefix + 'down.hover', pointerover);
30741 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
30743 function eventTarget(d3_event) {
30744 var datum = d3_event.target && d3_event.target.__data__;
30745 if (_typeof(datum) !== 'object') return null;
30747 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
30748 return datum.properties.entity;
30754 function pointerover(d3_event) {
30755 // ignore mouse hovers with buttons pressed unless dragging
30756 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
30757 var target = eventTarget(d3_event);
30759 if (target && _targets.indexOf(target) === -1) {
30760 _targets.push(target);
30762 updateHover(d3_event, _targets);
30766 function pointerout(d3_event) {
30767 var target = eventTarget(d3_event);
30769 var index = _targets.indexOf(target);
30771 if (index !== -1) {
30772 _targets.splice(index);
30774 updateHover(d3_event, _targets);
30778 function allowsVertex(d) {
30779 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30782 function modeAllowsHover(target) {
30783 var mode = context.mode();
30785 if (mode.id === 'add-point') {
30786 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
30792 function updateHover(d3_event, targets) {
30793 _selection.selectAll('.hover').classed('hover', false);
30795 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30797 var mode = context.mode();
30799 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
30800 var node = targets.find(function (target) {
30801 return target instanceof osmEntity && target.type === 'node';
30803 _newNodeId = node && node.id;
30806 targets = targets.filter(function (datum) {
30807 if (datum instanceof osmEntity) {
30808 // If drawing a way, don't hover on a node that was just placed. #3974
30809 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
30816 for (var i in targets) {
30817 var datum = targets[i]; // What are we hovering over?
30819 if (datum.__featurehash__) {
30820 // hovering custom data
30821 selector += ', .data' + datum.__featurehash__;
30822 } else if (datum instanceof QAItem) {
30823 selector += ', .' + datum.service + '.itemId-' + datum.id;
30824 } else if (datum instanceof osmNote) {
30825 selector += ', .note-' + datum.id;
30826 } else if (datum instanceof osmEntity) {
30827 selector += ', .' + datum.id;
30829 if (datum.type === 'relation') {
30830 for (var j in datum.members) {
30831 selector += ', .' + datum.members[j].id;
30837 var suppressed = _altDisables && d3_event && d3_event.altKey;
30839 if (selector.trim().length) {
30840 // remove the first comma
30841 selector = selector.slice(1);
30843 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
30846 dispatch$1.call('hover', this, !suppressed && targets);
30850 behavior.off = function (selection) {
30851 selection.selectAll('.hover').classed('hover', false);
30852 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30853 selection.classed('hover-disabled', false);
30854 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
30855 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
30858 behavior.altDisables = function (val) {
30859 if (!arguments.length) return _altDisables;
30860 _altDisables = val;
30864 behavior.ignoreVertex = function (val) {
30865 if (!arguments.length) return _ignoreVertex;
30866 _ignoreVertex = val;
30870 behavior.initialNodeID = function (nodeId) {
30871 _initialNodeID = nodeId;
30875 return utilRebind(behavior, dispatch$1, 'on');
30878 var _disableSpace = false;
30879 var _lastSpace = null;
30880 function behaviorDraw(context) {
30881 var dispatch$1 = dispatch('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
30882 var keybinding = utilKeybinding('draw');
30884 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
30886 var _edit = behaviorEdit(context);
30888 var _closeTolerance = 4;
30889 var _tolerance = 12;
30890 var _mouseLeave = false;
30891 var _lastMouse = null;
30893 var _lastPointerUpEvent;
30895 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
30898 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
30899 // - `mode/drag_node.js` `datum()`
30902 function datum(d3_event) {
30903 var mode = context.mode();
30904 var isNote = mode && mode.id.indexOf('note') !== -1;
30905 if (d3_event.altKey || isNote) return {};
30908 if (d3_event.type === 'keydown') {
30909 element = _lastMouse && _lastMouse.target;
30911 element = d3_event.target;
30912 } // When drawing, snap only to touch targets..
30913 // (this excludes area fills and active drawing elements)
30916 var d = element.__data__;
30917 return d && d.properties && d.properties.target ? d : {};
30920 function pointerdown(d3_event) {
30921 if (_downPointer) return;
30922 var pointerLocGetter = utilFastMouse(this);
30924 id: d3_event.pointerId || 'mouse',
30925 pointerLocGetter: pointerLocGetter,
30926 downTime: +new Date(),
30927 downLoc: pointerLocGetter(d3_event)
30929 dispatch$1.call('down', this, d3_event, datum(d3_event));
30932 function pointerup(d3_event) {
30933 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
30934 var downPointer = _downPointer;
30935 _downPointer = null;
30936 _lastPointerUpEvent = d3_event;
30937 if (downPointer.isCancelled) return;
30938 var t2 = +new Date();
30939 var p2 = downPointer.pointerLocGetter(d3_event);
30940 var dist = geoVecLength(downPointer.downLoc, p2);
30942 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
30943 // Prevent a quick second click
30944 select(window).on('click.draw-block', function () {
30945 d3_event.stopPropagation();
30947 context.map().dblclickZoomEnable(false);
30948 window.setTimeout(function () {
30949 context.map().dblclickZoomEnable(true);
30950 select(window).on('click.draw-block', null);
30952 click(d3_event, p2);
30956 function pointermove(d3_event) {
30957 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
30958 var p2 = _downPointer.pointerLocGetter(d3_event);
30960 var dist = geoVecLength(_downPointer.downLoc, p2);
30962 if (dist >= _closeTolerance) {
30963 _downPointer.isCancelled = true;
30964 dispatch$1.call('downcancel', this);
30968 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
30969 // events immediately after non-mouse pointerup events; detect and ignore them.
30971 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
30972 _lastMouse = d3_event;
30973 dispatch$1.call('move', this, d3_event, datum(d3_event));
30976 function pointercancel(d3_event) {
30977 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
30978 if (!_downPointer.isCancelled) {
30979 dispatch$1.call('downcancel', this);
30982 _downPointer = null;
30986 function mouseenter() {
30987 _mouseLeave = false;
30990 function mouseleave() {
30991 _mouseLeave = true;
30994 function allowsVertex(d) {
30995 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30997 // - `mode/drag_node.js` `doMove()`
30998 // - `behavior/draw.js` `click()`
30999 // - `behavior/draw_way.js` `move()`
31002 function click(d3_event, loc) {
31003 var d = datum(d3_event);
31004 var target = d && d.properties && d.properties.entity;
31005 var mode = context.mode();
31007 if (target && target.type === 'node' && allowsVertex(target)) {
31009 dispatch$1.call('clickNode', this, target, d);
31011 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
31013 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
31016 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
31017 dispatch$1.call('clickWay', this, choice.loc, edge, d);
31020 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
31021 var locLatLng = context.projection.invert(loc);
31022 dispatch$1.call('click', this, locLatLng, d);
31024 } // treat a spacebar press like a click
31027 function space(d3_event) {
31028 d3_event.preventDefault();
31029 d3_event.stopPropagation();
31030 var currSpace = context.map().mouse();
31032 if (_disableSpace && _lastSpace) {
31033 var dist = geoVecLength(_lastSpace, currSpace);
31035 if (dist > _tolerance) {
31036 _disableSpace = false;
31040 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
31042 _lastSpace = currSpace;
31043 _disableSpace = true;
31044 select(window).on('keyup.space-block', function () {
31045 d3_event.preventDefault();
31046 d3_event.stopPropagation();
31047 _disableSpace = false;
31048 select(window).on('keyup.space-block', null);
31049 }); // get the current mouse position
31051 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
31052 context.projection(context.map().center());
31053 click(d3_event, loc);
31056 function backspace(d3_event) {
31057 d3_event.preventDefault();
31058 dispatch$1.call('undo');
31061 function del(d3_event) {
31062 d3_event.preventDefault();
31063 dispatch$1.call('cancel');
31066 function ret(d3_event) {
31067 d3_event.preventDefault();
31068 dispatch$1.call('finish');
31071 function behavior(selection) {
31072 context.install(_hover);
31073 context.install(_edit);
31074 _downPointer = null;
31075 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
31076 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
31077 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
31078 select(document).call(keybinding);
31082 behavior.off = function (selection) {
31083 context.ui().sidebar.hover.cancel();
31084 context.uninstall(_hover);
31085 context.uninstall(_edit);
31086 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
31087 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
31089 select(document).call(keybinding.unbind);
31092 behavior.hover = function () {
31096 return utilRebind(behavior, dispatch$1, 'on');
31099 function initRange(domain, range) {
31100 switch (arguments.length) {
31105 this.range(domain);
31109 this.range(range).domain(domain);
31116 function constants(x) {
31117 return function () {
31122 function number$1(x) {
31127 function identity$3(x) {
31131 function normalize$1(a, b) {
31132 return (b -= a = +a) ? function (x) {
31133 return (x - a) / b;
31134 } : constants(isNaN(b) ? NaN : 0.5);
31137 function clamper(a, b) {
31139 if (a > b) t = a, a = b, b = t;
31140 return function (x) {
31141 return Math.max(a, Math.min(b, x));
31143 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
31144 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
31147 function bimap(domain, range, interpolate) {
31148 var d0 = domain[0],
31152 if (d1 < d0) d0 = normalize$1(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize$1(d0, d1), r0 = interpolate(r0, r1);
31153 return function (x) {
31158 function polymap(domain, range, interpolate) {
31159 var j = Math.min(domain.length, range.length) - 1,
31162 i = -1; // Reverse descending domains.
31164 if (domain[j] < domain[0]) {
31165 domain = domain.slice().reverse();
31166 range = range.slice().reverse();
31170 d[i] = normalize$1(domain[i], domain[i + 1]);
31171 r[i] = interpolate(range[i], range[i + 1]);
31174 return function (x) {
31175 var i = bisectRight(domain, x, 1, j) - 1;
31176 return r[i](d[i](x));
31180 function copy(source, target) {
31181 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
31183 function transformer$1() {
31186 interpolate$1 = interpolate,
31190 clamp = identity$3,
31195 function rescale() {
31196 var n = Math.min(domain.length, range.length);
31197 if (clamp !== identity$3) clamp = clamper(domain[0], domain[n - 1]);
31198 piecewise = n > 2 ? polymap : bimap;
31199 output = input = null;
31203 function scale(x) {
31204 return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
31207 scale.invert = function (y) {
31208 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
31211 scale.domain = function (_) {
31212 return arguments.length ? (domain = Array.from(_, number$1), rescale()) : domain.slice();
31215 scale.range = function (_) {
31216 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
31219 scale.rangeRound = function (_) {
31220 return range = Array.from(_), interpolate$1 = interpolateRound, rescale();
31223 scale.clamp = function (_) {
31224 return arguments.length ? (clamp = _ ? true : identity$3, rescale()) : clamp !== identity$3;
31227 scale.interpolate = function (_) {
31228 return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
31231 scale.unknown = function (_) {
31232 return arguments.length ? (unknown = _, scale) : unknown;
31235 return function (t, u) {
31236 transform = t, untransform = u;
31240 function continuous() {
31241 return transformer$1()(identity$3, identity$3);
31244 function formatDecimal (x) {
31245 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
31246 } // Computes the decimal coefficient and exponent of the specified number x with
31247 // significant digits p, where x is positive and p is in [1, 21] or undefined.
31248 // For example, formatDecimalParts(1.23) returns ["123", 0].
31250 function formatDecimalParts(x, p) {
31251 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
31254 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
31255 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
31257 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
31260 function exponent (x) {
31261 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
31264 function formatGroup (grouping, thousands) {
31265 return function (value, width) {
31266 var i = value.length,
31272 while (i > 0 && g > 0) {
31273 if (length + g + 1 > width) g = Math.max(1, width - length);
31274 t.push(value.substring(i -= g, i + g));
31275 if ((length += g + 1) > width) break;
31276 g = grouping[j = (j + 1) % grouping.length];
31279 return t.reverse().join(thousands);
31283 function formatNumerals (numerals) {
31284 return function (value) {
31285 return value.replace(/[0-9]/g, function (i) {
31286 return numerals[+i];
31291 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
31292 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
31293 function formatSpecifier(specifier) {
31294 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
31296 return new FormatSpecifier({
31304 precision: match[8] && match[8].slice(1),
31309 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
31311 function FormatSpecifier(specifier) {
31312 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
31313 this.align = specifier.align === undefined ? ">" : specifier.align + "";
31314 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
31315 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
31316 this.zero = !!specifier.zero;
31317 this.width = specifier.width === undefined ? undefined : +specifier.width;
31318 this.comma = !!specifier.comma;
31319 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
31320 this.trim = !!specifier.trim;
31321 this.type = specifier.type === undefined ? "" : specifier.type + "";
31324 FormatSpecifier.prototype.toString = function () {
31325 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;
31328 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
31329 function formatTrim (s) {
31330 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
31337 if (i0 === 0) i0 = i;
31342 if (!+s[i]) break out;
31343 if (i0 > 0) i0 = 0;
31348 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
31351 // `thisNumberValue` abstract operation
31352 // https://tc39.github.io/ecma262/#sec-thisnumbervalue
31353 var thisNumberValue = function (value) {
31354 if (typeof value != 'number' && classofRaw(value) != 'Number') {
31355 throw TypeError('Incorrect invocation');
31360 // `String.prototype.repeat` method implementation
31361 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
31362 var stringRepeat = ''.repeat || function repeat(count) {
31363 var str = String(requireObjectCoercible(this));
31365 var n = toInteger(count);
31366 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
31367 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
31371 var nativeToFixed = 1.0.toFixed;
31372 var floor$6 = Math.floor;
31374 var pow$2 = function (x, n, acc) {
31375 return n === 0 ? acc : n % 2 === 1 ? pow$2(x, n - 1, acc * x) : pow$2(x * x, n / 2, acc);
31378 var log$2 = function (x) {
31381 while (x2 >= 4096) {
31391 var FORCED$c = nativeToFixed && (
31392 0.00008.toFixed(3) !== '0.000' ||
31393 0.9.toFixed(0) !== '1' ||
31394 1.255.toFixed(2) !== '1.25' ||
31395 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
31396 ) || !fails(function () {
31397 // V8 ~ Android 4.3-
31398 nativeToFixed.call({});
31401 // `Number.prototype.toFixed` method
31402 // https://tc39.github.io/ecma262/#sec-number.prototype.tofixed
31403 _export({ target: 'Number', proto: true, forced: FORCED$c }, {
31404 // eslint-disable-next-line max-statements
31405 toFixed: function toFixed(fractionDigits) {
31406 var number = thisNumberValue(this);
31407 var fractDigits = toInteger(fractionDigits);
31408 var data = [0, 0, 0, 0, 0, 0];
31413 var multiply = function (n, c) {
31416 while (++index < 6) {
31417 c2 += n * data[index];
31418 data[index] = c2 % 1e7;
31419 c2 = floor$6(c2 / 1e7);
31423 var divide = function (n) {
31426 while (--index >= 0) {
31428 data[index] = floor$6(c / n);
31433 var dataToString = function () {
31436 while (--index >= 0) {
31437 if (s !== '' || index === 0 || data[index] !== 0) {
31438 var t = String(data[index]);
31439 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
31444 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
31445 // eslint-disable-next-line no-self-compare
31446 if (number != number) return 'NaN';
31447 if (number <= -1e21 || number >= 1e21) return String(number);
31452 if (number > 1e-21) {
31453 e = log$2(number * pow$2(2, 69, 1)) - 69;
31454 z = e < 0 ? number * pow$2(2, -e, 1) : number / pow$2(2, e, 1);
31455 z *= 0x10000000000000;
31464 multiply(pow$2(10, j, 1), 0);
31473 result = dataToString();
31476 multiply(1 << -e, 0);
31477 result = dataToString() + stringRepeat.call('0', fractDigits);
31480 if (fractDigits > 0) {
31482 result = sign + (k <= fractDigits
31483 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
31484 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
31486 result = sign + result;
31491 var nativeToPrecision = 1.0.toPrecision;
31493 var FORCED$d = fails(function () {
31495 return nativeToPrecision.call(1, undefined) !== '1';
31496 }) || !fails(function () {
31497 // V8 ~ Android 4.3-
31498 nativeToPrecision.call({});
31501 // `Number.prototype.toPrecision` method
31502 // https://tc39.github.io/ecma262/#sec-number.prototype.toprecision
31503 _export({ target: 'Number', proto: true, forced: FORCED$d }, {
31504 toPrecision: function toPrecision(precision) {
31505 return precision === undefined
31506 ? nativeToPrecision.call(thisNumberValue(this))
31507 : nativeToPrecision.call(thisNumberValue(this), precision);
31511 var prefixExponent;
31512 function formatPrefixAuto (x, p) {
31513 var d = formatDecimalParts(x, p);
31514 if (!d) return x + "";
31515 var coefficient = d[0],
31517 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
31518 n = coefficient.length;
31519 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!
31522 function formatRounded (x, p) {
31523 var d = formatDecimalParts(x, p);
31524 if (!d) return x + "";
31525 var coefficient = d[0],
31527 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");
31530 var formatTypes = {
31531 "%": function _(x, p) {
31532 return (x * 100).toFixed(p);
31534 "b": function b(x) {
31535 return Math.round(x).toString(2);
31537 "c": function c(x) {
31540 "d": formatDecimal,
31541 "e": function e(x, p) {
31542 return x.toExponential(p);
31544 "f": function f(x, p) {
31545 return x.toFixed(p);
31547 "g": function g(x, p) {
31548 return x.toPrecision(p);
31550 "o": function o(x) {
31551 return Math.round(x).toString(8);
31553 "p": function p(x, _p) {
31554 return formatRounded(x * 100, _p);
31556 "r": formatRounded,
31557 "s": formatPrefixAuto,
31558 "X": function X(x) {
31559 return Math.round(x).toString(16).toUpperCase();
31561 "x": function x(_x) {
31562 return Math.round(_x).toString(16);
31566 function identity$4 (x) {
31570 var map = Array.prototype.map,
31571 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
31572 function formatLocale (locale) {
31573 var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
31574 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
31575 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
31576 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
31577 numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map.call(locale.numerals, String)),
31578 percent = locale.percent === undefined ? "%" : locale.percent + "",
31579 minus = locale.minus === undefined ? "−" : locale.minus + "",
31580 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
31582 function newFormat(specifier) {
31583 specifier = formatSpecifier(specifier);
31584 var fill = specifier.fill,
31585 align = specifier.align,
31586 sign = specifier.sign,
31587 symbol = specifier.symbol,
31588 zero = specifier.zero,
31589 width = specifier.width,
31590 comma = specifier.comma,
31591 precision = specifier.precision,
31592 trim = specifier.trim,
31593 type = specifier.type; // The "n" type is an alias for ",g".
31595 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
31596 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
31598 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
31599 // For SI-prefix, the suffix is lazily computed.
31601 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
31602 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
31603 // Is this an integer type?
31604 // Can this type generate exponential notation?
31606 var formatType = formatTypes[type],
31607 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
31608 // or clamp the specified precision to the supported range.
31609 // For significant precision, it must be in [1, 21].
31610 // For fixed precision, it must be in [0, 20].
31612 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
31614 function format(value) {
31615 var valuePrefix = prefix,
31616 valueSuffix = suffix,
31621 if (type === "c") {
31622 valueSuffix = formatType(value) + valueSuffix;
31625 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
31627 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
31629 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
31631 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
31633 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
31635 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
31636 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
31637 // grouped, and fractional or exponential “suffix” part that is not.
31640 i = -1, n = value.length;
31643 if (c = value.charCodeAt(i), 48 > c || c > 57) {
31644 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
31645 value = value.slice(0, i);
31650 } // If the fill character is not "0", grouping is applied before padding.
31653 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
31655 var length = valuePrefix.length + value.length + valueSuffix.length,
31656 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
31658 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
31662 value = valuePrefix + value + valueSuffix + padding;
31666 value = valuePrefix + padding + value + valueSuffix;
31670 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
31674 value = padding + valuePrefix + value + valueSuffix;
31678 return numerals(value);
31681 format.toString = function () {
31682 return specifier + "";
31688 function formatPrefix(specifier, value) {
31689 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
31690 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
31691 k = Math.pow(10, -e),
31692 prefix = prefixes[8 + e / 3];
31693 return function (value) {
31694 return f(k * value) + prefix;
31700 formatPrefix: formatPrefix
31710 currency: ["$", ""]
31712 function defaultLocale(definition) {
31713 locale = formatLocale(definition);
31714 format = locale.format;
31715 formatPrefix = locale.formatPrefix;
31719 function precisionFixed (step) {
31720 return Math.max(0, -exponent(Math.abs(step)));
31723 function precisionPrefix (step, value) {
31724 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
31727 function precisionRound (step, max) {
31728 step = Math.abs(step), max = Math.abs(max) - step;
31729 return Math.max(0, exponent(max) - exponent(step)) + 1;
31732 function tickFormat(start, stop, count, specifier) {
31733 var step = tickStep(start, stop, count),
31735 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
31737 switch (specifier.type) {
31740 var value = Math.max(Math.abs(start), Math.abs(stop));
31741 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
31742 return formatPrefix(specifier, value);
31751 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
31758 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
31763 return format(specifier);
31766 function linearish(scale) {
31767 var domain = scale.domain;
31769 scale.ticks = function (count) {
31771 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
31774 scale.tickFormat = function (count, specifier) {
31776 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
31779 scale.nice = function (count) {
31780 if (count == null) count = 10;
31783 var i1 = d.length - 1;
31790 if (stop < start) {
31791 step = start, start = stop, stop = step;
31792 step = i0, i0 = i1, i1 = step;
31795 while (maxIter-- > 0) {
31796 step = tickIncrement(start, stop, count);
31798 if (step === prestep) {
31802 } else if (step > 0) {
31803 start = Math.floor(start / step) * step;
31804 stop = Math.ceil(stop / step) * step;
31805 } else if (step < 0) {
31806 start = Math.ceil(start * step) / step;
31807 stop = Math.floor(stop * step) / step;
31820 function linear$2() {
31821 var scale = continuous();
31823 scale.copy = function () {
31824 return copy(scale, linear$2());
31827 initRange.apply(scale, arguments);
31828 return linearish(scale);
31831 var nativeExpm1 = Math.expm1;
31832 var exp$1 = Math.exp;
31834 // `Math.expm1` method implementation
31835 // https://tc39.github.io/ecma262/#sec-math.expm1
31836 var mathExpm1 = (!nativeExpm1
31838 || nativeExpm1(10) > 22025.465794806719 || nativeExpm1(10) < 22025.4657948067165168
31840 || nativeExpm1(-2e-17) != -2e-17
31841 ) ? function expm1(x) {
31842 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
31845 function quantize() {
31853 function scale(x) {
31854 return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
31857 function rescale() {
31859 domain = new Array(n);
31862 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
31868 scale.domain = function (_) {
31871 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
31874 scale.range = function (_) {
31875 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
31878 scale.invertExtent = function (y) {
31879 var i = range.indexOf(y);
31880 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
31883 scale.unknown = function (_) {
31884 return arguments.length ? (unknown = _, scale) : scale;
31887 scale.thresholds = function () {
31888 return domain.slice();
31891 scale.copy = function () {
31892 return quantize().domain([x0, x1]).range(range).unknown(unknown);
31895 return initRange.apply(linearish(scale), arguments);
31898 // https://github.com/tc39/proposal-string-pad-start-end
31903 var ceil$1 = Math.ceil;
31905 // `String.prototype.{ padStart, padEnd }` methods implementation
31906 var createMethod$6 = function (IS_END) {
31907 return function ($this, maxLength, fillString) {
31908 var S = String(requireObjectCoercible($this));
31909 var stringLength = S.length;
31910 var fillStr = fillString === undefined ? ' ' : String(fillString);
31911 var intMaxLength = toLength(maxLength);
31912 var fillLen, stringFiller;
31913 if (intMaxLength <= stringLength || fillStr == '') return S;
31914 fillLen = intMaxLength - stringLength;
31915 stringFiller = stringRepeat.call(fillStr, ceil$1(fillLen / fillStr.length));
31916 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
31917 return IS_END ? S + stringFiller : stringFiller + S;
31922 // `String.prototype.padStart` method
31923 // https://tc39.github.io/ecma262/#sec-string.prototype.padstart
31924 start: createMethod$6(false),
31925 // `String.prototype.padEnd` method
31926 // https://tc39.github.io/ecma262/#sec-string.prototype.padend
31927 end: createMethod$6(true)
31930 var padStart = stringPad.start;
31932 var abs$3 = Math.abs;
31933 var DatePrototype$1 = Date.prototype;
31934 var getTime$1 = DatePrototype$1.getTime;
31935 var nativeDateToISOString = DatePrototype$1.toISOString;
31937 // `Date.prototype.toISOString` method implementation
31938 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31939 // PhantomJS / old WebKit fails here:
31940 var dateToIsoString = (fails(function () {
31941 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
31942 }) || !fails(function () {
31943 nativeDateToISOString.call(new Date(NaN));
31944 })) ? function toISOString() {
31945 if (!isFinite(getTime$1.call(this))) throw RangeError('Invalid time value');
31947 var year = date.getUTCFullYear();
31948 var milliseconds = date.getUTCMilliseconds();
31949 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
31950 return sign + padStart(abs$3(year), sign ? 6 : 4, 0) +
31951 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
31952 '-' + padStart(date.getUTCDate(), 2, 0) +
31953 'T' + padStart(date.getUTCHours(), 2, 0) +
31954 ':' + padStart(date.getUTCMinutes(), 2, 0) +
31955 ':' + padStart(date.getUTCSeconds(), 2, 0) +
31956 '.' + padStart(milliseconds, 3, 0) +
31958 } : nativeDateToISOString;
31960 // `Date.prototype.toISOString` method
31961 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31962 // PhantomJS / old WebKit has a broken implementations
31963 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
31964 toISOString: dateToIsoString
31967 function behaviorBreathe() {
31968 var duration = 800;
31970 var selector = '.selected.shadow, .selected .shadow';
31972 var _selected = select(null);
31980 function ratchetyInterpolator(a, b, steps, units) {
31983 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
31984 return function (t) {
31985 return String(sample(t)) + (units || '');
31989 function reset(selection) {
31990 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
31993 function setAnimationParams(transition, fromTo) {
31994 var toFrom = fromTo === 'from' ? 'to' : 'from';
31995 transition.styleTween('stroke-opacity', function (d) {
31996 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
31997 }).styleTween('stroke-width', function (d) {
31998 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
31999 }).styleTween('fill-opacity', function (d) {
32000 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
32001 }).styleTween('r', function (d) {
32002 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
32006 function calcAnimationParams(selection) {
32007 selection.call(reset).each(function (d) {
32008 var s = select(this);
32009 var tag = s.node().tagName;
32015 var width; // determine base opacity and width
32017 if (tag === 'circle') {
32018 opacity = parseFloat(s.style('fill-opacity') || 0.5);
32019 width = parseFloat(s.style('r') || 15.5);
32021 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
32022 width = parseFloat(s.style('stroke-width') || 10);
32023 } // calculate from/to interpolation params..
32027 p.from.opacity = opacity * 0.6;
32028 p.to.opacity = opacity * 1.25;
32029 p.from.width = width * 0.7;
32030 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
32035 function run(surface, fromTo) {
32036 var toFrom = fromTo === 'from' ? 'to' : 'from';
32037 var currSelected = surface.selectAll(selector);
32038 var currClassed = surface.attr('class');
32040 if (_done || currSelected.empty()) {
32041 _selected.call(reset);
32043 _selected = select(null);
32047 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
32048 _selected.call(reset);
32050 _classed = currClassed;
32051 _selected = currSelected.call(calcAnimationParams);
32054 var didCallNextRun = false;
32056 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
32057 // `end` event is called for each selected element, but we want
32058 // it to run only once
32059 if (!didCallNextRun) {
32060 surface.call(run, toFrom);
32061 didCallNextRun = true;
32062 } // if entity was deselected, remove breathe styling
32065 if (!select(this).classed('selected')) {
32066 reset(select(this));
32071 function behavior(surface) {
32073 _timer = timer(function () {
32074 // wait for elements to actually become selected
32075 if (surface.selectAll(selector).empty()) {
32079 surface.call(run, 'from');
32087 behavior.restartIfNeeded = function (surface) {
32088 if (_selected.empty()) {
32089 surface.call(run, 'from');
32097 behavior.off = function () {
32104 _selected.interrupt().call(reset);
32110 /* Creates a keybinding behavior for an operation */
32111 function behaviorOperation(context) {
32114 function keypress(d3_event) {
32115 // prevent operations during low zoom selection
32116 if (!context.map().withinEditableZoom()) return;
32117 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
32118 d3_event.preventDefault();
32120 var disabled = _operation.disabled();
32123 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
32125 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
32126 if (_operation.point) _operation.point(null);
32132 function behavior() {
32133 if (_operation && _operation.available()) {
32134 context.keybinding().on(_operation.keys, keypress);
32140 behavior.off = function () {
32141 context.keybinding().off(_operation.keys);
32144 behavior.which = function (_) {
32145 if (!arguments.length) return _operation;
32153 function operationCircularize(context, selectedIDs) {
32156 var _actions = selectedIDs.map(getAction).filter(Boolean);
32158 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32160 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32164 function getAction(entityID) {
32165 var entity = context.entity(entityID);
32166 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
32169 _extent = entity.extent(context.graph());
32171 _extent = _extent.extend(entity.extent(context.graph()));
32174 return actionCircularize(entityID, context.projection);
32177 var operation = function operation() {
32178 if (!_actions.length) return;
32180 var combinedAction = function combinedAction(graph, t) {
32181 _actions.forEach(function (action) {
32182 if (!action.disabled(graph)) {
32183 graph = action(graph, t);
32190 combinedAction.transitionable = true;
32191 context.perform(combinedAction, operation.annotation());
32192 window.setTimeout(function () {
32193 context.validator().validate();
32194 }, 300); // after any transition
32197 operation.available = function () {
32198 return _actions.length && selectedIDs.length === _actions.length;
32199 }; // don't cache this because the visible extent could change
32202 operation.disabled = function () {
32203 if (!_actions.length) return '';
32205 var actionDisableds = _actions.map(function (action) {
32206 return action.disabled(context.graph());
32207 }).filter(Boolean);
32209 if (actionDisableds.length === _actions.length) {
32210 // none of the features can be circularized
32211 if (new Set(actionDisableds).size > 1) {
32212 return 'multiple_blockers';
32215 return actionDisableds[0];
32216 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
32217 return 'too_large';
32218 } else if (someMissing()) {
32219 return 'not_downloaded';
32220 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32221 return 'connected_to_hidden';
32226 function someMissing() {
32227 if (context.inIntro()) return false;
32228 var osm = context.connection();
32231 var missing = _coords.filter(function (loc) {
32232 return !osm.isDataLoaded(loc);
32235 if (missing.length) {
32236 missing.forEach(function (loc) {
32237 context.loadTileAtLoc(loc);
32247 operation.tooltip = function () {
32248 var disable = operation.disabled();
32249 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
32252 operation.annotation = function () {
32253 return _t('operations.circularize.annotation.feature', {
32258 operation.id = 'circularize';
32259 operation.keys = [_t('operations.circularize.key')];
32260 operation.title = _t('operations.circularize.title');
32261 operation.behavior = behaviorOperation(context).which(operation);
32265 // For example, ⌘Z -> Ctrl+Z
32267 var uiCmd = function uiCmd(code) {
32268 var detected = utilDetect();
32270 if (detected.os === 'mac') {
32274 if (detected.os === 'win') {
32275 if (code === '⌘⇧Z') return 'Ctrl+Y';
32287 for (var i = 0; i < code.length; i++) {
32288 if (code[i] in replacements) {
32289 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
32296 }; // return a display-focused string for a given keyboard code
32298 uiCmd.display = function (code) {
32299 if (code.length !== 1) return code;
32300 var detected = utilDetect();
32301 var mac = detected.os === 'mac';
32302 var replacements = {
32303 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
32304 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
32305 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
32306 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
32307 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
32308 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
32309 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
32310 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
32311 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
32312 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
32313 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
32314 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
32315 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
32317 return replacements[code] || code;
32320 function operationDelete(context, selectedIDs) {
32321 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32322 var action = actionDeleteMultiple(selectedIDs);
32323 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32324 var coords = nodes.map(function (n) {
32327 var extent = utilTotalExtent(selectedIDs, context.graph());
32329 var operation = function operation() {
32330 var nextSelectedID;
32331 var nextSelectedLoc;
32333 if (selectedIDs.length === 1) {
32334 var id = selectedIDs[0];
32335 var entity = context.entity(id);
32336 var geometry = entity.geometry(context.graph());
32337 var parents = context.graph().parentWays(entity);
32338 var parent = parents[0]; // Select the next closest node in the way.
32340 if (geometry === 'vertex') {
32341 var nodes = parent.nodes;
32342 var i = nodes.indexOf(id);
32346 } else if (i === nodes.length - 1) {
32349 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
32350 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
32351 i = a < b ? i - 1 : i + 1;
32354 nextSelectedID = nodes[i];
32355 nextSelectedLoc = context.entity(nextSelectedID).loc;
32359 context.perform(action, operation.annotation());
32360 context.validator().validate();
32362 if (nextSelectedID && nextSelectedLoc) {
32363 if (context.hasEntity(nextSelectedID)) {
32364 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
32366 context.map().centerEase(nextSelectedLoc);
32367 context.enter(modeBrowse(context));
32370 context.enter(modeBrowse(context));
32374 operation.available = function () {
32378 operation.disabled = function () {
32379 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32380 return 'too_large';
32381 } else if (someMissing()) {
32382 return 'not_downloaded';
32383 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32384 return 'connected_to_hidden';
32385 } else if (selectedIDs.some(protectedMember)) {
32386 return 'part_of_relation';
32387 } else if (selectedIDs.some(incompleteRelation)) {
32388 return 'incomplete_relation';
32389 } else if (selectedIDs.some(hasWikidataTag)) {
32390 return 'has_wikidata_tag';
32395 function someMissing() {
32396 if (context.inIntro()) return false;
32397 var osm = context.connection();
32400 var missing = coords.filter(function (loc) {
32401 return !osm.isDataLoaded(loc);
32404 if (missing.length) {
32405 missing.forEach(function (loc) {
32406 context.loadTileAtLoc(loc);
32415 function hasWikidataTag(id) {
32416 var entity = context.entity(id);
32417 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
32420 function incompleteRelation(id) {
32421 var entity = context.entity(id);
32422 return entity.type === 'relation' && !entity.isComplete(context.graph());
32425 function protectedMember(id) {
32426 var entity = context.entity(id);
32427 if (entity.type !== 'way') return false;
32428 var parents = context.graph().parentRelations(entity);
32430 for (var i = 0; i < parents.length; i++) {
32431 var parent = parents[i];
32432 var type = parent.tags.type;
32433 var role = parent.memberById(id).role || 'outer';
32435 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
32444 operation.tooltip = function () {
32445 var disable = operation.disabled();
32446 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
32449 operation.annotation = function () {
32450 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
32451 n: selectedIDs.length
32455 operation.id = 'delete';
32456 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
32457 operation.title = _t('operations.delete.title');
32458 operation.behavior = behaviorOperation(context).which(operation);
32462 function operationOrthogonalize(context, selectedIDs) {
32467 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
32469 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32471 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32475 function chooseAction(entityID) {
32476 var entity = context.entity(entityID);
32477 var geometry = entity.geometry(context.graph());
32480 _extent = entity.extent(context.graph());
32482 _extent = _extent.extend(entity.extent(context.graph()));
32483 } // square a line/area
32486 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
32487 if (_type && _type !== 'feature') return null;
32489 return actionOrthogonalize(entityID, context.projection); // square a single vertex
32490 } else if (geometry === 'vertex') {
32491 if (_type && _type !== 'corner') return null;
32493 var graph = context.graph();
32494 var parents = graph.parentWays(entity);
32496 if (parents.length === 1) {
32497 var way = parents[0];
32499 if (way.nodes.indexOf(entityID) !== -1) {
32500 return actionOrthogonalize(way.id, context.projection, entityID);
32508 var operation = function operation() {
32509 if (!_actions.length) return;
32511 var combinedAction = function combinedAction(graph, t) {
32512 _actions.forEach(function (action) {
32513 if (!action.disabled(graph)) {
32514 graph = action(graph, t);
32521 combinedAction.transitionable = true;
32522 context.perform(combinedAction, operation.annotation());
32523 window.setTimeout(function () {
32524 context.validator().validate();
32525 }, 300); // after any transition
32528 operation.available = function () {
32529 return _actions.length && selectedIDs.length === _actions.length;
32530 }; // don't cache this because the visible extent could change
32533 operation.disabled = function () {
32534 if (!_actions.length) return '';
32536 var actionDisableds = _actions.map(function (action) {
32537 return action.disabled(context.graph());
32538 }).filter(Boolean);
32540 if (actionDisableds.length === _actions.length) {
32541 // none of the features can be squared
32542 if (new Set(actionDisableds).size > 1) {
32543 return 'multiple_blockers';
32546 return actionDisableds[0];
32547 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
32548 return 'too_large';
32549 } else if (someMissing()) {
32550 return 'not_downloaded';
32551 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32552 return 'connected_to_hidden';
32557 function someMissing() {
32558 if (context.inIntro()) return false;
32559 var osm = context.connection();
32562 var missing = _coords.filter(function (loc) {
32563 return !osm.isDataLoaded(loc);
32566 if (missing.length) {
32567 missing.forEach(function (loc) {
32568 context.loadTileAtLoc(loc);
32578 operation.tooltip = function () {
32579 var disable = operation.disabled();
32580 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
32583 operation.annotation = function () {
32584 return _t('operations.orthogonalize.annotation.' + _type, {
32589 operation.id = 'orthogonalize';
32590 operation.keys = [_t('operations.orthogonalize.key')];
32591 operation.title = _t('operations.orthogonalize.title');
32592 operation.behavior = behaviorOperation(context).which(operation);
32596 function operationReflectShort(context, selectedIDs) {
32597 return operationReflect(context, selectedIDs, 'short');
32599 function operationReflectLong(context, selectedIDs) {
32600 return operationReflect(context, selectedIDs, 'long');
32602 function operationReflect(context, selectedIDs, axis) {
32603 axis = axis || 'long';
32604 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32605 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32606 var coords = nodes.map(function (n) {
32609 var extent = utilTotalExtent(selectedIDs, context.graph());
32611 var operation = function operation() {
32612 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
32613 context.perform(action, operation.annotation());
32614 window.setTimeout(function () {
32615 context.validator().validate();
32616 }, 300); // after any transition
32619 operation.available = function () {
32620 return nodes.length >= 3;
32621 }; // don't cache this because the visible extent could change
32624 operation.disabled = function () {
32625 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32626 return 'too_large';
32627 } else if (someMissing()) {
32628 return 'not_downloaded';
32629 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32630 return 'connected_to_hidden';
32631 } else if (selectedIDs.some(incompleteRelation)) {
32632 return 'incomplete_relation';
32637 function someMissing() {
32638 if (context.inIntro()) return false;
32639 var osm = context.connection();
32642 var missing = coords.filter(function (loc) {
32643 return !osm.isDataLoaded(loc);
32646 if (missing.length) {
32647 missing.forEach(function (loc) {
32648 context.loadTileAtLoc(loc);
32657 function incompleteRelation(id) {
32658 var entity = context.entity(id);
32659 return entity.type === 'relation' && !entity.isComplete(context.graph());
32663 operation.tooltip = function () {
32664 var disable = operation.disabled();
32665 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
32668 operation.annotation = function () {
32669 return _t('operations.reflect.annotation.' + axis + '.feature', {
32670 n: selectedIDs.length
32674 operation.id = 'reflect-' + axis;
32675 operation.keys = [_t('operations.reflect.key.' + axis)];
32676 operation.title = _t('operations.reflect.title.' + axis);
32677 operation.behavior = behaviorOperation(context).which(operation);
32681 function operationMove(context, selectedIDs) {
32682 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32683 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32684 var coords = nodes.map(function (n) {
32687 var extent = utilTotalExtent(selectedIDs, context.graph());
32689 var operation = function operation() {
32690 context.enter(modeMove(context, selectedIDs));
32693 operation.available = function () {
32694 return selectedIDs.length > 1 || context.entity(selectedIDs[0]).type !== 'node';
32697 operation.disabled = function () {
32698 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32699 return 'too_large';
32700 } else if (someMissing()) {
32701 return 'not_downloaded';
32702 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32703 return 'connected_to_hidden';
32704 } else if (selectedIDs.some(incompleteRelation)) {
32705 return 'incomplete_relation';
32710 function someMissing() {
32711 if (context.inIntro()) return false;
32712 var osm = context.connection();
32715 var missing = coords.filter(function (loc) {
32716 return !osm.isDataLoaded(loc);
32719 if (missing.length) {
32720 missing.forEach(function (loc) {
32721 context.loadTileAtLoc(loc);
32730 function incompleteRelation(id) {
32731 var entity = context.entity(id);
32732 return entity.type === 'relation' && !entity.isComplete(context.graph());
32736 operation.tooltip = function () {
32737 var disable = operation.disabled();
32738 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
32741 operation.annotation = function () {
32742 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
32743 n: selectedIDs.length
32747 operation.id = 'move';
32748 operation.keys = [_t('operations.move.key')];
32749 operation.title = _t('operations.move.title');
32750 operation.behavior = behaviorOperation(context).which(operation);
32751 operation.mouseOnly = true;
32755 function modeRotate(context, entityIDs) {
32760 var keybinding = utilKeybinding('rotate');
32761 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];
32762 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
32763 n: entityIDs.length
32770 var _prevTransform;
32774 function doRotate() {
32777 if (context.graph() !== _prevGraph) {
32778 fn = context.perform;
32780 fn = context.replace;
32781 } // projection changed, recalculate _pivot
32784 var projection = context.projection;
32785 var currTransform = projection.transform();
32787 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
32788 var nodes = utilGetAllNodes(entityIDs, context.graph());
32789 var points = nodes.map(function (n) {
32790 return projection(n.loc);
32792 _pivot = getPivot(points);
32793 _prevAngle = undefined;
32796 var currMouse = context.map().mouse();
32797 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
32798 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
32799 var delta = currAngle - _prevAngle;
32800 fn(actionRotate(entityIDs, _pivot, delta, projection));
32801 _prevTransform = currTransform;
32802 _prevAngle = currAngle;
32803 _prevGraph = context.graph();
32806 function getPivot(points) {
32809 if (points.length === 1) {
32810 _pivot = points[0];
32811 } else if (points.length === 2) {
32812 _pivot = geoVecInterp(points[0], points[1], 0.5);
32814 var polygonHull = d3_polygonHull(points);
32816 if (polygonHull.length === 2) {
32817 _pivot = geoVecInterp(points[0], points[1], 0.5);
32819 _pivot = d3_polygonCentroid(d3_polygonHull(points));
32826 function finish(d3_event) {
32827 d3_event.stopPropagation();
32828 context.replace(actionNoop(), annotation);
32829 context.enter(modeSelect(context, entityIDs));
32832 function cancel() {
32834 context.enter(modeSelect(context, entityIDs));
32837 function undone() {
32838 context.enter(modeBrowse(context));
32841 mode.enter = function () {
32842 context.features().forceVisible(entityIDs);
32843 behaviors.forEach(context.install);
32844 context.surface().on('mousemove.rotate', doRotate).on('click.rotate', finish);
32845 context.history().on('undone.rotate', undone);
32846 keybinding.on('⎋', cancel).on('↩', finish);
32847 select(document).call(keybinding);
32850 mode.exit = function () {
32851 behaviors.forEach(context.uninstall);
32852 context.surface().on('mousemove.rotate', null).on('click.rotate', null);
32853 context.history().on('undone.rotate', null);
32854 select(document).call(keybinding.unbind);
32855 context.features().forceVisible([]);
32858 mode.selectedIDs = function () {
32859 if (!arguments.length) return entityIDs; // no assign
32867 function operationRotate(context, selectedIDs) {
32868 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32869 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32870 var coords = nodes.map(function (n) {
32873 var extent = utilTotalExtent(selectedIDs, context.graph());
32875 var operation = function operation() {
32876 context.enter(modeRotate(context, selectedIDs));
32879 operation.available = function () {
32880 return nodes.length >= 2;
32883 operation.disabled = function () {
32884 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32885 return 'too_large';
32886 } else if (someMissing()) {
32887 return 'not_downloaded';
32888 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32889 return 'connected_to_hidden';
32890 } else if (selectedIDs.some(incompleteRelation)) {
32891 return 'incomplete_relation';
32896 function someMissing() {
32897 if (context.inIntro()) return false;
32898 var osm = context.connection();
32901 var missing = coords.filter(function (loc) {
32902 return !osm.isDataLoaded(loc);
32905 if (missing.length) {
32906 missing.forEach(function (loc) {
32907 context.loadTileAtLoc(loc);
32916 function incompleteRelation(id) {
32917 var entity = context.entity(id);
32918 return entity.type === 'relation' && !entity.isComplete(context.graph());
32922 operation.tooltip = function () {
32923 var disable = operation.disabled();
32924 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
32927 operation.annotation = function () {
32928 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
32929 n: selectedIDs.length
32933 operation.id = 'rotate';
32934 operation.keys = [_t('operations.rotate.key')];
32935 operation.title = _t('operations.rotate.title');
32936 operation.behavior = behaviorOperation(context).which(operation);
32937 operation.mouseOnly = true;
32941 function modeMove(context, entityIDs, baseGraph) {
32946 var keybinding = utilKeybinding('move');
32947 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];
32948 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
32949 n: entityIDs.length
32958 var _nudgeInterval;
32960 function doMove(nudge) {
32961 nudge = nudge || [0, 0];
32964 if (_prevGraph !== context.graph()) {
32966 _origin = context.map().mouseCoordinates();
32967 fn = context.perform;
32969 fn = context.overwrite;
32972 var currMouse = context.map().mouse();
32973 var origMouse = context.projection(_origin);
32974 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
32975 fn(actionMove(entityIDs, delta, context.projection, _cache));
32976 _prevGraph = context.graph();
32979 function startNudge(nudge) {
32980 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
32981 _nudgeInterval = window.setInterval(function () {
32982 context.map().pan(nudge);
32987 function stopNudge() {
32988 if (_nudgeInterval) {
32989 window.clearInterval(_nudgeInterval);
32990 _nudgeInterval = null;
32996 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
33005 function finish(d3_event) {
33006 d3_event.stopPropagation();
33007 context.replace(actionNoop(), annotation);
33008 context.enter(modeSelect(context, entityIDs));
33012 function cancel() {
33014 while (context.graph() !== baseGraph) {
33018 context.enter(modeBrowse(context));
33021 context.enter(modeSelect(context, entityIDs));
33027 function undone() {
33028 context.enter(modeBrowse(context));
33031 mode.enter = function () {
33032 _origin = context.map().mouseCoordinates();
33035 context.features().forceVisible(entityIDs);
33036 behaviors.forEach(context.install);
33037 context.surface().on('mousemove.move', move).on('click.move', finish);
33038 context.history().on('undone.move', undone);
33039 keybinding.on('⎋', cancel).on('↩', finish);
33040 select(document).call(keybinding);
33043 mode.exit = function () {
33045 behaviors.forEach(function (behavior) {
33046 context.uninstall(behavior);
33048 context.surface().on('mousemove.move', null).on('click.move', null);
33049 context.history().on('undone.move', null);
33050 select(document).call(keybinding.unbind);
33051 context.features().forceVisible([]);
33054 mode.selectedIDs = function () {
33055 if (!arguments.length) return entityIDs; // no assign
33063 function behaviorPaste(context) {
33064 function doPaste(d3_event) {
33065 // prevent paste during low zoom selection
33066 if (!context.map().withinEditableZoom()) return;
33067 d3_event.preventDefault();
33068 var baseGraph = context.graph();
33069 var mouse = context.map().mouse();
33070 var projection = context.projection;
33071 var viewport = geoExtent(projection.clipExtent()).polygon();
33072 if (!geoPointInPolygon(mouse, viewport)) return;
33073 var oldIDs = context.copyIDs();
33074 if (!oldIDs.length) return;
33075 var extent = geoExtent();
33076 var oldGraph = context.copyGraph();
33078 var action = actionCopyEntities(oldIDs, oldGraph);
33079 context.perform(action);
33080 var copies = action.copies();
33081 var originals = new Set();
33082 Object.values(copies).forEach(function (entity) {
33083 originals.add(entity.id);
33086 for (var id in copies) {
33087 var oldEntity = oldGraph.entity(id);
33088 var newEntity = copies[id];
33090 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
33093 var parents = context.graph().parentWays(newEntity);
33094 var parentCopied = parents.some(function (parent) {
33095 return originals.has(parent.id);
33098 if (!parentCopied) {
33099 newIDs.push(newEntity.id);
33101 } // Put pasted objects where mouse pointer is..
33104 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
33105 var delta = geoVecSubtract(mouse, copyPoint);
33106 context.perform(actionMove(newIDs, delta, projection));
33107 context.enter(modeMove(context, newIDs, baseGraph));
33110 function behavior() {
33111 context.keybinding().on(uiCmd('⌘V'), doPaste);
33115 behavior.off = function () {
33116 context.keybinding().off(uiCmd('⌘V'));
33122 // `String.prototype.repeat` method
33123 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
33124 _export({ target: 'String', proto: true }, {
33125 repeat: stringRepeat
33129 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
33131 * The `origin` function is expected to return an [x, y] tuple rather than an
33133 * The events are `start`, `move`, and `end`.
33134 (https://github.com/mbostock/d3/issues/563)
33135 * The `start` event is not dispatched until the first cursor movement occurs.
33136 (https://github.com/mbostock/d3/pull/368)
33137 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
33138 than `x`, `y`, `dx`, and `dy` properties.
33139 * The `end` event is not dispatched if no movement occurs.
33140 * An `off` function is available that unbinds the drag's internal event handlers.
33143 function behaviorDrag() {
33144 var dispatch$1 = dispatch('start', 'move', 'end'); // see also behaviorSelect
33146 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
33148 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
33150 var _origin = null;
33151 var _selector = '';
33159 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
33162 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
33164 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
33166 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
33167 var selection$1 = selection();
33168 var select = selection$1.style(d3_event_userSelectProperty);
33169 selection$1.style(d3_event_userSelectProperty, 'none');
33170 return function () {
33171 selection$1.style(d3_event_userSelectProperty, select);
33175 function pointerdown(d3_event) {
33176 if (_pointerId) return;
33177 _pointerId = d3_event.pointerId || 'mouse';
33178 _targetNode = this; // only force reflow once per drag
33180 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
33182 var startOrigin = pointerLocGetter(d3_event);
33183 var started = false;
33184 var selectEnable = d3_event_userSelectSuppress();
33185 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
33188 offset = _origin.call(_targetNode, _targetEntity);
33189 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
33194 d3_event.stopPropagation();
33196 function pointermove(d3_event) {
33197 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33198 var p = pointerLocGetter(d3_event);
33201 var dist = geoVecLength(startOrigin, p);
33202 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
33204 if (dist < tolerance) return;
33206 dispatch$1.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
33207 // a midpoint will convert the target to a node.
33210 d3_event.stopPropagation();
33211 d3_event.preventDefault();
33212 var dx = p[0] - startOrigin[0];
33213 var dy = p[1] - startOrigin[1];
33214 dispatch$1.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
33218 function pointerup(d3_event) {
33219 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33223 dispatch$1.call('end', this, d3_event, _targetEntity);
33224 d3_event.preventDefault();
33227 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33232 function behavior(selection) {
33233 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
33234 var delegate = pointerdown;
33237 delegate = function delegate(d3_event) {
33239 var target = d3_event.target;
33241 for (; target && target !== root; target = target.parentNode) {
33242 var datum = target.__data__;
33243 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
33245 if (_targetEntity && target[matchesSelector](_selector)) {
33246 return pointerdown.call(target, d3_event);
33252 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
33255 behavior.off = function (selection) {
33256 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
33259 behavior.selector = function (_) {
33260 if (!arguments.length) return _selector;
33265 behavior.origin = function (_) {
33266 if (!arguments.length) return _origin;
33271 behavior.cancel = function () {
33272 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33276 behavior.targetNode = function (_) {
33277 if (!arguments.length) return _targetNode;
33282 behavior.targetEntity = function (_) {
33283 if (!arguments.length) return _targetEntity;
33288 behavior.surface = function (_) {
33289 if (!arguments.length) return _surface;
33294 return utilRebind(behavior, dispatch$1, 'on');
33297 function modeDragNode(context) {
33302 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
33303 var edit = behaviorEdit(context);
33305 var _nudgeInterval;
33307 var _restoreSelectedIDs = [];
33308 var _wasMidpoint = false;
33309 var _isCancelled = false;
33317 function startNudge(d3_event, entity, nudge) {
33318 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
33319 _nudgeInterval = window.setInterval(function () {
33320 context.map().pan(nudge);
33321 doMove(d3_event, entity, nudge);
33325 function stopNudge() {
33326 if (_nudgeInterval) {
33327 window.clearInterval(_nudgeInterval);
33328 _nudgeInterval = null;
33332 function moveAnnotation(entity) {
33333 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
33336 function connectAnnotation(nodeEntity, targetEntity) {
33337 var nodeGeometry = nodeEntity.geometry(context.graph());
33338 var targetGeometry = targetEntity.geometry(context.graph());
33340 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
33341 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
33342 var targetParentWayIDs = context.graph().parentWays(targetEntity);
33343 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
33345 if (sharedParentWays.length !== 0) {
33346 // if the nodes are next to each other, they are merged
33347 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
33348 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
33351 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
33355 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
33358 function shouldSnapToNode(target) {
33359 if (!_activeEntity) return false;
33360 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
33363 function origin(entity) {
33364 return context.projection(entity.loc);
33367 function keydown(d3_event) {
33368 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33369 if (context.surface().classed('nope')) {
33370 context.surface().classed('nope-suppressed', true);
33373 context.surface().classed('nope', false).classed('nope-disabled', true);
33377 function keyup(d3_event) {
33378 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33379 if (context.surface().classed('nope-suppressed')) {
33380 context.surface().classed('nope', true);
33383 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
33387 function start(d3_event, entity) {
33388 _wasMidpoint = entity.type === 'midpoint';
33389 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
33390 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
33392 if (_isCancelled) {
33394 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
33397 return drag.cancel();
33400 if (_wasMidpoint) {
33401 var midpoint = entity;
33402 entity = osmNode();
33403 context.perform(actionAddMidpoint(midpoint, entity));
33404 entity = context.entity(entity.id); // get post-action entity
33406 var vertex = context.surface().selectAll('.' + entity.id);
33407 drag.targetNode(vertex.node()).targetEntity(entity);
33409 context.perform(actionNoop());
33412 _activeEntity = entity;
33413 _startLoc = entity.loc;
33414 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
33415 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
33416 context.enter(mode);
33418 // - `behavior/draw.js` `datum()`
33421 function datum(d3_event) {
33422 if (!d3_event || d3_event.altKey) {
33425 // When dragging, snap only to touch targets..
33426 // (this excludes area fills and active drawing elements)
33427 var d = d3_event.target.__data__;
33428 return d && d.properties && d.properties.target ? d : {};
33432 function doMove(d3_event, entity, nudge) {
33433 nudge = nudge || [0, 0];
33434 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
33435 var currMouse = geoVecSubtract(currPoint, nudge);
33436 var loc = context.projection.invert(currMouse);
33439 if (!_nudgeInterval) {
33440 // If not nudging at the edge of the viewport, try to snap..
33442 // - `mode/drag_node.js` `doMove()`
33443 // - `behavior/draw.js` `click()`
33444 // - `behavior/draw_way.js` `move()`
33445 var d = datum(d3_event);
33446 target = d && d.properties && d.properties.entity;
33447 var targetLoc = target && target.loc;
33448 var targetNodes = d && d.properties && d.properties.nodes;
33451 // snap to node/vertex - a point target with `.loc`
33452 if (shouldSnapToNode(target)) {
33455 } else if (targetNodes) {
33456 // snap to way - a line target with `.nodes`
33457 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
33465 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
33467 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
33470 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
33471 } // Check if this drag causes the geometry to break..
33475 isInvalid = hasInvalidGeometry(entity, context.graph());
33478 var nope = context.surface().classed('nope');
33480 if (isInvalid === 'relation' || isInvalid === 'restriction') {
33482 // about to nope - show hint
33483 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
33484 relation: _mainPresetIndex.item('type/restriction').name()
33487 } else if (isInvalid) {
33488 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
33489 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
33492 // about to un-nope, remove hint
33493 context.ui().flash.duration(1).label('')();
33497 var nopeDisabled = context.surface().classed('nope-disabled');
33499 if (nopeDisabled) {
33500 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
33502 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
33506 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
33509 function hasRelationConflict(entity, target, edge, graph) {
33510 var testGraph = graph.update(); // copy
33511 // if snapping to way - add midpoint there and consider that the target..
33514 var midpoint = osmNode();
33515 var action = actionAddMidpoint({
33517 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
33519 testGraph = action(testGraph);
33521 } // can we connect to it?
33524 var ids = [entity.id, target.id];
33525 return actionConnect(ids).disabled(testGraph);
33528 function hasInvalidGeometry(entity, graph) {
33529 var parents = graph.parentWays(entity);
33532 for (i = 0; i < parents.length; i++) {
33533 var parent = parents[i];
33535 var activeIndex = null; // which multipolygon ring contains node being dragged
33536 // test any parent multipolygons for valid geometry
33538 var relations = graph.parentRelations(parent);
33540 for (j = 0; j < relations.length; j++) {
33541 if (!relations[j].isMultipolygon()) continue;
33542 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
33544 for (k = 0; k < rings.length; k++) {
33545 nodes = rings[k].nodes;
33547 if (nodes.find(function (n) {
33548 return n.id === entity.id;
33552 if (geoHasSelfIntersections(nodes, entity.id)) {
33553 return 'multipolygonMember';
33557 rings[k].coords = nodes.map(function (n) {
33560 } // test active ring for intersections with other rings in the multipolygon
33563 for (k = 0; k < rings.length; k++) {
33564 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
33566 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
33567 return 'multipolygonRing';
33570 } // If we still haven't tested this node's parent way for self-intersections.
33571 // (because it's not a member of a multipolygon), test it now.
33574 if (activeIndex === null) {
33575 nodes = parent.nodes.map(function (nodeID) {
33576 return graph.entity(nodeID);
33579 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
33580 return parent.geometry(graph);
33588 function move(d3_event, entity, point) {
33589 if (_isCancelled) return;
33590 d3_event.stopPropagation();
33591 context.surface().classed('nope-disabled', d3_event.altKey);
33592 _lastLoc = context.projection.invert(point);
33593 doMove(d3_event, entity);
33594 var nudge = geoViewportEdge(point, context.map().dimensions());
33597 startNudge(d3_event, entity, nudge);
33603 function end(d3_event, entity) {
33604 if (_isCancelled) return;
33605 var wasPoint = entity.geometry(context.graph()) === 'point';
33606 var d = datum(d3_event);
33607 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
33608 var target = d && d.properties && d.properties.entity; // entity to snap to
33612 context.perform(_actionBounceBack(entity.id, _startLoc));
33613 } else if (target && target.type === 'way') {
33614 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
33615 context.replace(actionAddMidpoint({
33617 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
33618 }, entity), connectAnnotation(entity, target));
33619 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
33620 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
33621 } else if (_wasMidpoint) {
33622 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
33624 context.replace(actionNoop(), moveAnnotation(entity));
33628 context.enter(modeSelect(context, [entity.id]));
33630 var reselection = _restoreSelectedIDs.filter(function (id) {
33631 return context.graph().hasEntity(id);
33634 if (reselection.length) {
33635 context.enter(modeSelect(context, reselection));
33637 context.enter(modeBrowse(context));
33642 function _actionBounceBack(nodeID, toLoc) {
33643 var moveNode = actionMoveNode(nodeID, toLoc);
33645 var action = function action(graph, t) {
33646 // last time through, pop off the bounceback perform.
33647 // it will then overwrite the initial perform with a moveNode that does nothing
33648 if (t === 1) context.pop();
33649 return moveNode(graph, t);
33652 action.transitionable = true;
33656 function cancel() {
33658 context.enter(modeBrowse(context));
33661 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);
33663 mode.enter = function () {
33664 context.install(hover);
33665 context.install(edit);
33666 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
33667 context.history().on('undone.drag-node', cancel);
33670 mode.exit = function () {
33671 context.ui().sidebar.hover.cancel();
33672 context.uninstall(hover);
33673 context.uninstall(edit);
33674 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
33675 context.history().on('undone.drag-node', null);
33676 _activeEntity = null;
33677 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
33681 mode.selectedIDs = function () {
33682 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
33687 mode.activeID = function () {
33688 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
33693 mode.restoreSelectedIDs = function (_) {
33694 if (!arguments.length) return _restoreSelectedIDs;
33695 _restoreSelectedIDs = _;
33699 mode.behavior = drag;
33703 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
33704 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
33705 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
33708 // `Promise.prototype.finally` method
33709 // https://tc39.github.io/ecma262/#sec-promise.prototype.finally
33710 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
33711 'finally': function (onFinally) {
33712 var C = speciesConstructor(this, getBuiltIn('Promise'));
33713 var isFunction = typeof onFinally == 'function';
33715 isFunction ? function (x) {
33716 return promiseResolve(C, onFinally()).then(function () { return x; });
33718 isFunction ? function (e) {
33719 return promiseResolve(C, onFinally()).then(function () { throw e; });
33725 // patch native Promise.prototype for native async functions
33726 if ( typeof nativePromiseConstructor == 'function' && !nativePromiseConstructor.prototype['finally']) {
33727 redefine(nativePromiseConstructor.prototype, 'finally', getBuiltIn('Promise').prototype['finally']);
33731 fixRegexpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybeCallNative) {
33733 // `String.prototype.search` method
33734 // https://tc39.github.io/ecma262/#sec-string.prototype.search
33735 function search(regexp) {
33736 var O = requireObjectCoercible(this);
33737 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
33738 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
33740 // `RegExp.prototype[@@search]` method
33741 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@search
33742 function (regexp) {
33743 var res = maybeCallNative(nativeSearch, regexp, this);
33744 if (res.done) return res.value;
33746 var rx = anObject(regexp);
33747 var S = String(this);
33749 var previousLastIndex = rx.lastIndex;
33750 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
33751 var result = regexpExecAbstract(rx, S);
33752 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
33753 return result === null ? -1 : result.index;
33758 function quickselect$1(arr, k, left, right, compare) {
33759 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
33762 function quickselectStep(arr, k, left, right, compare) {
33763 while (right > left) {
33764 if (right - left > 600) {
33765 var n = right - left + 1;
33766 var m = k - left + 1;
33767 var z = Math.log(n);
33768 var s = 0.5 * Math.exp(2 * z / 3);
33769 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
33770 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
33771 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
33772 quickselectStep(arr, k, newLeft, newRight, compare);
33778 swap$1(arr, left, k);
33779 if (compare(arr[right], t) > 0) swap$1(arr, left, right);
33786 while (compare(arr[i], t) < 0) {
33790 while (compare(arr[j], t) > 0) {
33795 if (compare(arr[left], t) === 0) swap$1(arr, left, j);else {
33797 swap$1(arr, j, right);
33799 if (j <= k) left = j + 1;
33800 if (k <= j) right = j - 1;
33804 function swap$1(arr, i, j) {
33810 function defaultCompare(a, b) {
33811 return a < b ? -1 : a > b ? 1 : 0;
33814 var RBush = /*#__PURE__*/function () {
33816 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
33818 _classCallCheck(this, RBush);
33820 // max entries in a node is 9 by default; min node fill is 40% for best performance
33821 this._maxEntries = Math.max(4, maxEntries);
33822 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
33826 _createClass(RBush, [{
33828 value: function all() {
33829 return this._all(this.data, []);
33833 value: function search(bbox) {
33834 var node = this.data;
33836 if (!intersects(bbox, node)) return result;
33837 var toBBox = this.toBBox;
33838 var nodesToSearch = [];
33841 for (var i = 0; i < node.children.length; i++) {
33842 var child = node.children[i];
33843 var childBBox = node.leaf ? toBBox(child) : child;
33845 if (intersects(bbox, childBBox)) {
33846 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
33850 node = nodesToSearch.pop();
33857 value: function collides(bbox) {
33858 var node = this.data;
33859 if (!intersects(bbox, node)) return false;
33860 var nodesToSearch = [];
33863 for (var i = 0; i < node.children.length; i++) {
33864 var child = node.children[i];
33865 var childBBox = node.leaf ? this.toBBox(child) : child;
33867 if (intersects(bbox, childBBox)) {
33868 if (node.leaf || contains(bbox, childBBox)) return true;
33869 nodesToSearch.push(child);
33873 node = nodesToSearch.pop();
33880 value: function load(data) {
33881 if (!(data && data.length)) return this;
33883 if (data.length < this._minEntries) {
33884 for (var i = 0; i < data.length; i++) {
33885 this.insert(data[i]);
33889 } // recursively build the tree with the given data from scratch using OMT algorithm
33892 var node = this._build(data.slice(), 0, data.length - 1, 0);
33894 if (!this.data.children.length) {
33895 // save as is if tree is empty
33897 } else if (this.data.height === node.height) {
33898 // split root if trees have the same height
33899 this._splitRoot(this.data, node);
33901 if (this.data.height < node.height) {
33902 // swap trees if inserted one is bigger
33903 var tmpNode = this.data;
33906 } // insert the small tree into the large tree at appropriate level
33909 this._insert(node, this.data.height - node.height - 1, true);
33916 value: function insert(item) {
33917 if (item) this._insert(item, this.data.height - 1);
33922 value: function clear() {
33923 this.data = createNode([]);
33928 value: function remove(item, equalsFn) {
33929 if (!item) return this;
33930 var node = this.data;
33931 var bbox = this.toBBox(item);
33934 var i, parent, goingUp; // depth-first iterative tree traversal
33936 while (node || path.length) {
33940 parent = path[path.length - 1];
33946 // check current node
33947 var index = findItem(item, node.children, equalsFn);
33949 if (index !== -1) {
33950 // item found, remove the item and condense tree upwards
33951 node.children.splice(index, 1);
33954 this._condense(path);
33960 if (!goingUp && !node.leaf && contains(node, bbox)) {
33966 node = node.children[0];
33967 } else if (parent) {
33970 node = parent.children[i];
33972 } else node = null; // nothing found
33980 value: function toBBox(item) {
33984 key: "compareMinX",
33985 value: function compareMinX(a, b) {
33986 return a.minX - b.minX;
33989 key: "compareMinY",
33990 value: function compareMinY(a, b) {
33991 return a.minY - b.minY;
33995 value: function toJSON() {
34000 value: function fromJSON(data) {
34006 value: function _all(node, result) {
34007 var nodesToSearch = [];
34010 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
34011 node = nodesToSearch.pop();
34018 value: function _build(items, left, right, height) {
34019 var N = right - left + 1;
34020 var M = this._maxEntries;
34024 // reached leaf level; return leaf
34025 node = createNode(items.slice(left, right + 1));
34026 calcBBox(node, this.toBBox);
34031 // target height of the bulk-loaded tree
34032 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
34034 M = Math.ceil(N / Math.pow(M, height - 1));
34037 node = createNode([]);
34039 node.height = height; // split the items into M mostly square tiles
34041 var N2 = Math.ceil(N / M);
34042 var N1 = N2 * Math.ceil(Math.sqrt(M));
34043 multiSelect(items, left, right, N1, this.compareMinX);
34045 for (var i = left; i <= right; i += N1) {
34046 var right2 = Math.min(i + N1 - 1, right);
34047 multiSelect(items, i, right2, N2, this.compareMinY);
34049 for (var j = i; j <= right2; j += N2) {
34050 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
34052 node.children.push(this._build(items, j, right3, height - 1));
34056 calcBBox(node, this.toBBox);
34060 key: "_chooseSubtree",
34061 value: function _chooseSubtree(bbox, node, level, path) {
34064 if (node.leaf || path.length - 1 === level) break;
34065 var minArea = Infinity;
34066 var minEnlargement = Infinity;
34067 var targetNode = void 0;
34069 for (var i = 0; i < node.children.length; i++) {
34070 var child = node.children[i];
34071 var area = bboxArea(child);
34072 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
34074 if (enlargement < minEnlargement) {
34075 minEnlargement = enlargement;
34076 minArea = area < minArea ? area : minArea;
34077 targetNode = child;
34078 } else if (enlargement === minEnlargement) {
34079 // otherwise choose one with the smallest area
34080 if (area < minArea) {
34082 targetNode = child;
34087 node = targetNode || node.children[0];
34094 value: function _insert(item, level, isNode) {
34095 var bbox = isNode ? item : this.toBBox(item);
34096 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
34098 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
34101 node.children.push(item);
34102 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
34104 while (level >= 0) {
34105 if (insertPath[level].children.length > this._maxEntries) {
34106 this._split(insertPath, level);
34110 } // adjust bboxes along the insertion path
34113 this._adjustParentBBoxes(bbox, insertPath, level);
34114 } // split overflowed node into two
34118 value: function _split(insertPath, level) {
34119 var node = insertPath[level];
34120 var M = node.children.length;
34121 var m = this._minEntries;
34123 this._chooseSplitAxis(node, m, M);
34125 var splitIndex = this._chooseSplitIndex(node, m, M);
34127 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
34128 newNode.height = node.height;
34129 newNode.leaf = node.leaf;
34130 calcBBox(node, this.toBBox);
34131 calcBBox(newNode, this.toBBox);
34132 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
34136 value: function _splitRoot(node, newNode) {
34138 this.data = createNode([node, newNode]);
34139 this.data.height = node.height + 1;
34140 this.data.leaf = false;
34141 calcBBox(this.data, this.toBBox);
34144 key: "_chooseSplitIndex",
34145 value: function _chooseSplitIndex(node, m, M) {
34147 var minOverlap = Infinity;
34148 var minArea = Infinity;
34150 for (var i = m; i <= M - m; i++) {
34151 var bbox1 = distBBox(node, 0, i, this.toBBox);
34152 var bbox2 = distBBox(node, i, M, this.toBBox);
34153 var overlap = intersectionArea(bbox1, bbox2);
34154 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
34156 if (overlap < minOverlap) {
34157 minOverlap = overlap;
34159 minArea = area < minArea ? area : minArea;
34160 } else if (overlap === minOverlap) {
34161 // otherwise choose distribution with minimum area
34162 if (area < minArea) {
34169 return index || M - m;
34170 } // sorts node children by the best axis for split
34173 key: "_chooseSplitAxis",
34174 value: function _chooseSplitAxis(node, m, M) {
34175 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
34176 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
34178 var xMargin = this._allDistMargin(node, m, M, compareMinX);
34180 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
34181 // otherwise it's already sorted by minY
34184 if (xMargin < yMargin) node.children.sort(compareMinX);
34185 } // total margin of all possible split distributions where each node is at least m full
34188 key: "_allDistMargin",
34189 value: function _allDistMargin(node, m, M, compare) {
34190 node.children.sort(compare);
34191 var toBBox = this.toBBox;
34192 var leftBBox = distBBox(node, 0, m, toBBox);
34193 var rightBBox = distBBox(node, M - m, M, toBBox);
34194 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
34196 for (var i = m; i < M - m; i++) {
34197 var child = node.children[i];
34198 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
34199 margin += bboxMargin(leftBBox);
34202 for (var _i = M - m - 1; _i >= m; _i--) {
34203 var _child = node.children[_i];
34204 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
34205 margin += bboxMargin(rightBBox);
34211 key: "_adjustParentBBoxes",
34212 value: function _adjustParentBBoxes(bbox, path, level) {
34213 // adjust bboxes along the given tree path
34214 for (var i = level; i >= 0; i--) {
34215 extend$1(path[i], bbox);
34220 value: function _condense(path) {
34221 // go through the path, removing empty nodes and updating bboxes
34222 for (var i = path.length - 1, siblings; i >= 0; i--) {
34223 if (path[i].children.length === 0) {
34225 siblings = path[i - 1].children;
34226 siblings.splice(siblings.indexOf(path[i]), 1);
34227 } else this.clear();
34228 } else calcBBox(path[i], this.toBBox);
34236 function findItem(item, items, equalsFn) {
34237 if (!equalsFn) return items.indexOf(item);
34239 for (var i = 0; i < items.length; i++) {
34240 if (equalsFn(item, items[i])) return i;
34244 } // calculate node's bbox from bboxes of its children
34247 function calcBBox(node, toBBox) {
34248 distBBox(node, 0, node.children.length, toBBox, node);
34249 } // min bounding rectangle of node children from k to p-1
34252 function distBBox(node, k, p, toBBox, destNode) {
34253 if (!destNode) destNode = createNode(null);
34254 destNode.minX = Infinity;
34255 destNode.minY = Infinity;
34256 destNode.maxX = -Infinity;
34257 destNode.maxY = -Infinity;
34259 for (var i = k; i < p; i++) {
34260 var child = node.children[i];
34261 extend$1(destNode, node.leaf ? toBBox(child) : child);
34267 function extend$1(a, b) {
34268 a.minX = Math.min(a.minX, b.minX);
34269 a.minY = Math.min(a.minY, b.minY);
34270 a.maxX = Math.max(a.maxX, b.maxX);
34271 a.maxY = Math.max(a.maxY, b.maxY);
34275 function compareNodeMinX(a, b) {
34276 return a.minX - b.minX;
34279 function compareNodeMinY(a, b) {
34280 return a.minY - b.minY;
34283 function bboxArea(a) {
34284 return (a.maxX - a.minX) * (a.maxY - a.minY);
34287 function bboxMargin(a) {
34288 return a.maxX - a.minX + (a.maxY - a.minY);
34291 function enlargedArea(a, b) {
34292 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));
34295 function intersectionArea(a, b) {
34296 var minX = Math.max(a.minX, b.minX);
34297 var minY = Math.max(a.minY, b.minY);
34298 var maxX = Math.min(a.maxX, b.maxX);
34299 var maxY = Math.min(a.maxY, b.maxY);
34300 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
34303 function contains(a, b) {
34304 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
34307 function intersects(a, b) {
34308 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
34311 function createNode(children) {
34313 children: children,
34321 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
34322 // combines selection algorithm with binary divide & conquer approach
34325 function multiSelect(arr, left, right, n, compare) {
34326 var stack = [left, right];
34328 while (stack.length) {
34329 right = stack.pop();
34330 left = stack.pop();
34331 if (right - left <= n) continue;
34332 var mid = left + Math.ceil((right - left) / n / 2) * n;
34333 quickselect$1(arr, mid, left, right, compare);
34334 stack.push(left, mid, mid, right);
34338 var tiler = utilTiler();
34339 var dispatch$1 = dispatch('loaded');
34340 var _tileZoom = 14;
34341 var _krUrlRoot = 'https://www.keepright.at';
34344 localizeStrings: {}
34345 }; // This gets reassigned if reset
34349 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
34350 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];
34352 function abortRequest(controller) {
34354 controller.abort();
34358 function abortUnwantedRequests(cache, tiles) {
34359 Object.keys(cache.inflightTile).forEach(function (k) {
34360 var wanted = tiles.find(function (tile) {
34361 return k === tile.id;
34365 abortRequest(cache.inflightTile[k]);
34366 delete cache.inflightTile[k];
34371 function encodeIssueRtree(d) {
34379 } // Replace or remove QAItem from rtree
34382 function updateRtree(item, replace) {
34383 _cache.rtree.remove(item, function (a, b) {
34384 return a.data.id === b.data.id;
34388 _cache.rtree.insert(item);
34392 function tokenReplacements(d) {
34393 if (!(d instanceof QAItem)) return;
34394 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
34395 var replacements = {};
34396 var issueTemplate = _krData.errorTypes[d.whichType];
34398 if (!issueTemplate) {
34399 /* eslint-disable no-console */
34400 console.log('No Template: ', d.whichType);
34401 console.log(' ', d.description);
34402 /* eslint-enable no-console */
34405 } // some descriptions are just fixed text
34408 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
34410 var errorRegex = new RegExp(issueTemplate.regex, 'i');
34411 var errorMatch = errorRegex.exec(d.description);
34414 /* eslint-disable no-console */
34415 console.log('Unmatched: ', d.whichType);
34416 console.log(' ', d.description);
34417 console.log(' ', errorRegex);
34418 /* eslint-enable no-console */
34423 for (var i = 1; i < errorMatch.length; i++) {
34425 var capture = errorMatch[i];
34426 var idType = void 0;
34427 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
34429 if (idType && capture) {
34430 // link IDs if present in the capture
34431 capture = parseError(capture, idType);
34432 } else if (htmlRegex.test(capture)) {
34433 // escape any html in non-IDs
34434 capture = '\\' + capture + '\\';
34436 var compare = capture.toLowerCase();
34438 if (_krData.localizeStrings[compare]) {
34439 // some replacement strings can be localized
34440 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34444 replacements['var' + i] = capture;
34447 return replacements;
34450 function parseError(capture, idType) {
34451 var compare = capture.toLowerCase();
34453 if (_krData.localizeStrings[compare]) {
34454 // some replacement strings can be localized
34455 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34459 // link a string like "this node"
34461 capture = linkErrorObject(capture);
34465 capture = linkURL(capture);
34467 // link an entity ID
34472 capture = linkEntity(idType + capture);
34474 // some errors have more complex ID lists/variance
34477 capture = parse20(capture);
34481 capture = parse211(capture);
34485 capture = parse231(capture);
34489 capture = parse294(capture);
34493 capture = parse370(capture);
34499 function linkErrorObject(d) {
34500 return "<a class=\"error_object_link\">".concat(d, "</a>");
34503 function linkEntity(d) {
34504 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34507 function linkURL(d) {
34508 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
34509 } // arbitrary node list of form: #ID, #ID, #ID...
34512 function parse211(capture) {
34514 var items = capture.split(', ');
34515 items.forEach(function (item) {
34516 // ID has # at the front
34517 var id = linkEntity('n' + item.slice(1));
34520 return newList.join(', ');
34521 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
34524 function parse231(capture) {
34525 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
34527 var items = capture.split('),');
34528 items.forEach(function (item) {
34529 var match = item.match(/\#(\d+)\((.+)\)?/);
34531 if (match !== null && match.length > 2) {
34532 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
34537 return newList.join(', ');
34538 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
34541 function parse294(capture) {
34543 var items = capture.split(',');
34544 items.forEach(function (item) {
34545 // item of form "from/to node/relation #ID"
34546 item = item.split(' '); // to/from role is more clear in quotes
34548 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
34550 var idType = item[1].slice(0, 1); // ID has # at the front
34552 var id = item[2].slice(1);
34553 id = linkEntity(idType + id);
34554 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
34556 return newList.join(', ');
34557 } // may or may not include the string "(including the name 'name')"
34560 function parse370(capture) {
34561 if (!capture) return '';
34562 var match = capture.match(/\(including the name (\'.+\')\)/);
34564 if (match && match.length) {
34565 return _t('QA.keepRight.errorTypes.370.including_the_name', {
34571 } // arbitrary node list of form: #ID,#ID,#ID...
34574 function parse20(capture) {
34576 var items = capture.split(',');
34577 items.forEach(function (item) {
34578 // ID has # at the front
34579 var id = linkEntity('n' + item.slice(1));
34582 return newList.join(', ');
34586 var serviceKeepRight = {
34587 title: 'keepRight',
34588 init: function init() {
34589 _mainFileFetcher.get('keepRight').then(function (d) {
34590 return _krData = d;
34597 this.event = utilRebind(this, dispatch$1, 'on');
34599 reset: function reset() {
34601 Object.values(_cache.inflightTile).forEach(abortRequest);
34613 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
34614 loadIssues: function loadIssues(projection) {
34620 }; // determine the needed tiles to cover the view
34622 var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
34624 abortUnwantedRequests(_cache, tiles); // issue new requests..
34626 tiles.forEach(function (tile) {
34627 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
34629 var _tile$extent$rectangl = tile.extent.rectangle(),
34630 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34631 left = _tile$extent$rectangl2[0],
34632 top = _tile$extent$rectangl2[1],
34633 right = _tile$extent$rectangl2[2],
34634 bottom = _tile$extent$rectangl2[3];
34636 var params = Object.assign({}, options, {
34642 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
34643 var controller = new AbortController();
34644 _cache.inflightTile[tile.id] = controller;
34646 signal: controller.signal
34647 }).then(function (data) {
34648 delete _cache.inflightTile[tile.id];
34649 _cache.loadedTile[tile.id] = true;
34651 if (!data || !data.features || !data.features.length) {
34652 throw new Error('No Data');
34655 data.features.forEach(function (feature) {
34656 var _feature$properties = feature.properties,
34657 itemType = _feature$properties.error_type,
34658 id = _feature$properties.error_id,
34659 _feature$properties$c = _feature$properties.comment,
34660 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
34661 objectId = _feature$properties.object_id,
34662 objectType = _feature$properties.object_type,
34663 schema = _feature$properties.schema,
34664 title = _feature$properties.title;
34665 var loc = feature.geometry.coordinates,
34666 _feature$properties$d = feature.properties.description,
34667 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
34668 // Error 191 = "highway-highway"
34669 // Error 190 = "intersections without junctions" (parent)
34671 var issueTemplate = _krData.errorTypes[itemType];
34672 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
34674 var whichType = issueTemplate ? itemType : parentIssueType;
34675 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
34676 // This is done to make them easier to linkify and translate.
34678 switch (whichType) {
34680 description = "This feature has a FIXME tag: ".concat(description);
34685 description = description.replace('A turn-', 'This turn-');
34693 description = "This turn-restriction~".concat(description);
34697 description = 'This highway is missing a maxspeed tag';
34703 description = "This feature~".concat(description);
34705 } // move markers slightly so it doesn't obscure the geometry,
34706 // then move markers away from other coincident markers
34709 var coincident = false;
34712 // first time, move marker up. after that, move marker right.
34713 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
34714 loc = geoVecAdd(loc, delta);
34715 var bbox = geoExtent(loc).bbox();
34716 coincident = _cache.rtree.search(bbox).length;
34717 } while (coincident);
34719 var d = new QAItem(loc, _this, itemType, id, {
34721 description: description,
34722 whichType: whichType,
34723 parentIssueType: parentIssueType,
34724 severity: whichTemplate.severity || 'error',
34725 objectId: objectId,
34726 objectType: objectType,
34730 d.replacements = tokenReplacements(d);
34731 _cache.data[id] = d;
34733 _cache.rtree.insert(encodeIssueRtree(d));
34735 dispatch$1.call('loaded');
34736 })["catch"](function () {
34737 delete _cache.inflightTile[tile.id];
34738 _cache.loadedTile[tile.id] = true;
34742 postUpdate: function postUpdate(d, callback) {
34745 if (_cache.inflightPost[d.id]) {
34747 message: 'Error update already inflight',
34758 params.st = d.newStatus;
34761 if (d.newComment !== undefined) {
34762 params.co = d.newComment;
34763 } // NOTE: This throws a CORS err, but it seems successful.
34764 // We don't care too much about the response, so this is fine.
34767 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
34768 var controller = new AbortController();
34769 _cache.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
34770 // (worst case scenario the request truly fails and issue will show up if iD restarts)
34773 signal: controller.signal
34774 })["finally"](function () {
34775 delete _cache.inflightPost[d.id];
34777 if (d.newStatus === 'ignore') {
34778 // ignore permanently (false positive)
34779 _this2.removeItem(d);
34780 } else if (d.newStatus === 'ignore_t') {
34781 // ignore temporarily (error fixed)
34782 _this2.removeItem(d);
34784 _cache.closed["".concat(d.schema, ":").concat(d.id)] = true;
34786 d = _this2.replaceItem(d.update({
34787 comment: d.newComment,
34788 newComment: undefined,
34789 newState: undefined
34793 if (callback) callback(null, d);
34796 // Get all cached QAItems covering the viewport
34797 getItems: function getItems(projection) {
34798 var viewport = projection.clipExtent();
34799 var min = [viewport[0][0], viewport[1][1]];
34800 var max = [viewport[1][0], viewport[0][1]];
34801 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34802 return _cache.rtree.search(bbox).map(function (d) {
34806 // Get a QAItem from cache
34807 // NOTE: Don't change method name until UI v3 is merged
34808 getError: function getError(id) {
34809 return _cache.data[id];
34811 // Replace a single QAItem in the cache
34812 replaceItem: function replaceItem(item) {
34813 if (!(item instanceof QAItem) || !item.id) return;
34814 _cache.data[item.id] = item;
34815 updateRtree(encodeIssueRtree(item), true); // true = replace
34819 // Remove a single QAItem from the cache
34820 removeItem: function removeItem(item) {
34821 if (!(item instanceof QAItem) || !item.id) return;
34822 delete _cache.data[item.id];
34823 updateRtree(encodeIssueRtree(item), false); // false = remove
34825 issueURL: function issueURL(item) {
34826 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
34828 // Get an array of issues closed during this session.
34829 // Used to populate `closed:keepright` changeset tag
34830 getClosedIDs: function getClosedIDs() {
34831 return Object.keys(_cache.closed).sort();
34835 var tiler$1 = utilTiler();
34836 var dispatch$2 = dispatch('loaded');
34837 var _tileZoom$1 = 14;
34838 var _impOsmUrls = {
34839 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
34840 mr: 'https://grab.community.improve-osm.org/missingGeoService',
34841 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
34843 var _impOsmData = {
34845 }; // This gets reassigned if reset
34849 function abortRequest$1(i) {
34850 Object.values(i).forEach(function (controller) {
34852 controller.abort();
34857 function abortUnwantedRequests$1(cache, tiles) {
34858 Object.keys(cache.inflightTile).forEach(function (k) {
34859 var wanted = tiles.find(function (tile) {
34860 return k === tile.id;
34864 abortRequest$1(cache.inflightTile[k]);
34865 delete cache.inflightTile[k];
34870 function encodeIssueRtree$1(d) {
34878 } // Replace or remove QAItem from rtree
34881 function updateRtree$1(item, replace) {
34882 _cache$1.rtree.remove(item, function (a, b) {
34883 return a.data.id === b.data.id;
34887 _cache$1.rtree.insert(item);
34891 function linkErrorObject(d) {
34892 return "<a class=\"error_object_link\">".concat(d, "</a>");
34895 function linkEntity(d) {
34896 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34899 function pointAverage(points) {
34900 if (points.length) {
34901 var sum = points.reduce(function (acc, point) {
34902 return geoVecAdd(acc, [point.lon, point.lat]);
34904 return geoVecScale(sum, 1 / points.length);
34910 function relativeBearing(p1, p2) {
34911 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
34914 angle += 2 * Math.PI;
34915 } // Return degrees
34918 return angle * 180 / Math.PI;
34919 } // Assuming range [0,360)
34922 function cardinalDirection(bearing) {
34923 var dir = 45 * Math.round(bearing / 45);
34935 return _t("QA.improveOSM.directions.".concat(compass[dir]));
34936 } // Errors shouldn't obscure each other
34939 function preventCoincident(loc, bumpUp) {
34940 var coincident = false;
34943 // first time, move marker up. after that, move marker right.
34944 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
34945 loc = geoVecAdd(loc, delta);
34946 var bbox = geoExtent(loc).bbox();
34947 coincident = _cache$1.rtree.search(bbox).length;
34948 } while (coincident);
34953 var serviceImproveOSM = {
34954 title: 'improveOSM',
34955 init: function init() {
34956 _mainFileFetcher.get('qa_data').then(function (d) {
34957 return _impOsmData = d.improveOSM;
34964 this.event = utilRebind(this, dispatch$2, 'on');
34966 reset: function reset() {
34968 Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
34980 loadIssues: function loadIssues(projection) {
34986 zoom: '19' // Use a high zoom so that clusters aren't returned
34988 }; // determine the needed tiles to cover the view
34990 var tiles = tiler$1.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
34992 abortUnwantedRequests$1(_cache$1, tiles); // issue new requests..
34994 tiles.forEach(function (tile) {
34995 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
34997 var _tile$extent$rectangl = tile.extent.rectangle(),
34998 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34999 east = _tile$extent$rectangl2[0],
35000 north = _tile$extent$rectangl2[1],
35001 west = _tile$extent$rectangl2[2],
35002 south = _tile$extent$rectangl2[3];
35004 var params = Object.assign({}, options, {
35009 }); // 3 separate requests to store for each tile
35012 Object.keys(_impOsmUrls).forEach(function (k) {
35013 // We exclude WATER from missing geometry as it doesn't seem useful
35014 // We use most confident one-way and turn restrictions only, still have false positives
35015 var kParams = Object.assign({}, params, k === 'mr' ? {
35016 type: 'PARKING,ROAD,BOTH,PATH'
35018 confidenceLevel: 'C1'
35020 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
35021 var controller = new AbortController();
35022 requests[k] = controller;
35024 signal: controller.signal
35025 }).then(function (data) {
35026 delete _cache$1.inflightTile[tile.id][k];
35028 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35029 delete _cache$1.inflightTile[tile.id];
35030 _cache$1.loadedTile[tile.id] = true;
35031 } // Road segments at high zoom == oneways
35034 if (data.roadSegments) {
35035 data.roadSegments.forEach(function (feature) {
35036 // Position error at the approximate middle of the segment
35037 var points = feature.points,
35038 wayId = feature.wayId,
35039 fromNodeId = feature.fromNodeId,
35040 toNodeId = feature.toNodeId;
35041 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
35042 var mid = points.length / 2;
35043 var loc; // Even number of points, find midpoint of the middle two
35044 // Odd number of points, use position of very middle point
35046 if (mid % 1 === 0) {
35047 loc = pointAverage([points[mid - 1], points[mid]]);
35049 mid = points[Math.floor(mid)];
35050 loc = [mid.lon, mid.lat];
35051 } // One-ways can land on same segment in opposite direction
35054 loc = preventCoincident(loc, false);
35055 var d = new QAItem(loc, _this, k, itemId, {
35057 // used as a category
35059 // used to post changes
35061 fromNodeId: fromNodeId,
35066 }); // Variables used in the description
35069 percentage: feature.percentOfTrips,
35070 num_trips: feature.numberOfTrips,
35071 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
35072 from_node: linkEntity('n' + feature.fromNodeId),
35073 to_node: linkEntity('n' + feature.toNodeId)
35075 _cache$1.data[d.id] = d;
35077 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35079 } // Tiles at high zoom == missing roads
35083 data.tiles.forEach(function (feature) {
35084 var type = feature.type,
35087 numberOfTrips = feature.numberOfTrips;
35088 var geoType = type.toLowerCase();
35089 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
35090 // Missing geometry could happen to land on another error
35092 var loc = pointAverage(feature.points);
35093 loc = preventCoincident(loc, false);
35094 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
35102 num_trips: numberOfTrips,
35103 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
35104 }; // -1 trips indicates data came from a 3rd party
35106 if (numberOfTrips === -1) {
35107 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
35110 _cache$1.data[d.id] = d;
35112 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35114 } // Entities at high zoom == turn restrictions
35117 if (data.entities) {
35118 data.entities.forEach(function (feature) {
35119 var point = feature.point,
35121 segments = feature.segments,
35122 numberOfPasses = feature.numberOfPasses,
35123 turnType = feature.turnType;
35124 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
35125 // We also want to bump the error up so node is accessible
35127 var loc = preventCoincident([point.lon, point.lat], true); // Elements are presented in a strange way
35129 var ids = id.split(',');
35130 var from_way = ids[0];
35131 var via_node = ids[3];
35132 var to_way = ids[2].split(':')[1];
35133 var d = new QAItem(loc, _this, k, itemId, {
35136 objectId: via_node,
35138 }); // Travel direction along from_way clarifies the turn restriction
35140 var _segments$0$points = _slicedToArray(segments[0].points, 2),
35141 p1 = _segments$0$points[0],
35142 p2 = _segments$0$points[1];
35144 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
35147 num_passed: numberOfPasses,
35148 num_trips: segments[0].numberOfTrips,
35149 turn_restriction: turnType.toLowerCase(),
35150 from_way: linkEntity('w' + from_way),
35151 to_way: linkEntity('w' + to_way),
35152 travel_direction: dir_of_travel,
35153 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
35155 _cache$1.data[d.id] = d;
35157 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35159 dispatch$2.call('loaded');
35162 })["catch"](function () {
35163 delete _cache$1.inflightTile[tile.id][k];
35165 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35166 delete _cache$1.inflightTile[tile.id];
35167 _cache$1.loadedTile[tile.id] = true;
35171 _cache$1.inflightTile[tile.id] = requests;
35174 getComments: function getComments(item) {
35177 // If comments already retrieved no need to do so again
35178 if (item.comments) {
35179 return Promise.resolve(item);
35182 var key = item.issueKey;
35185 if (key === 'ow') {
35186 qParams = item.identifier;
35187 } else if (key === 'mr') {
35188 qParams.tileX = item.identifier.x;
35189 qParams.tileY = item.identifier.y;
35190 } else if (key === 'tr') {
35191 qParams.targetId = item.identifier;
35194 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
35196 var cacheComments = function cacheComments(data) {
35197 // Assign directly for immediate use afterwards
35198 // comments are served newest to oldest
35199 item.comments = data.comments ? data.comments.reverse() : [];
35201 _this2.replaceItem(item);
35204 return d3_json(url).then(cacheComments).then(function () {
35208 postUpdate: function postUpdate(d, callback) {
35209 if (!serviceOsm.authenticated()) {
35210 // Username required in payload
35212 message: 'Not Authenticated',
35217 if (_cache$1.inflightPost[d.id]) {
35219 message: 'Error update already inflight',
35222 } // Payload can only be sent once username is established
35225 serviceOsm.userDetails(sendPayload.bind(this));
35227 function sendPayload(err, user) {
35231 return callback(err, d);
35234 var key = d.issueKey;
35235 var url = "".concat(_impOsmUrls[key], "/comment");
35237 username: user.display_name,
35238 targetIds: [d.identifier]
35242 payload.status = d.newStatus;
35243 payload.text = 'status changed';
35244 } // Comment take place of default text
35247 if (d.newComment) {
35248 payload.text = d.newComment;
35251 var controller = new AbortController();
35252 _cache$1.inflightPost[d.id] = controller;
35255 signal: controller.signal,
35256 body: JSON.stringify(payload)
35258 d3_json(url, options).then(function () {
35259 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
35261 if (!d.newStatus) {
35262 var now = new Date();
35263 var comments = d.comments ? d.comments : [];
35265 username: payload.username,
35266 text: payload.text,
35267 timestamp: now.getTime() / 1000
35270 _this3.replaceItem(d.update({
35271 comments: comments,
35272 newComment: undefined
35275 _this3.removeItem(d);
35277 if (d.newStatus === 'SOLVED') {
35278 // Keep track of the number of issues closed per type to tag the changeset
35279 if (!(d.issueKey in _cache$1.closed)) {
35280 _cache$1.closed[d.issueKey] = 0;
35283 _cache$1.closed[d.issueKey] += 1;
35287 if (callback) callback(null, d);
35288 })["catch"](function (err) {
35289 delete _cache$1.inflightPost[d.id];
35290 if (callback) callback(err.message);
35294 // Get all cached QAItems covering the viewport
35295 getItems: function getItems(projection) {
35296 var viewport = projection.clipExtent();
35297 var min = [viewport[0][0], viewport[1][1]];
35298 var max = [viewport[1][0], viewport[0][1]];
35299 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35300 return _cache$1.rtree.search(bbox).map(function (d) {
35304 // Get a QAItem from cache
35305 // NOTE: Don't change method name until UI v3 is merged
35306 getError: function getError(id) {
35307 return _cache$1.data[id];
35309 // get the name of the icon to display for this item
35310 getIcon: function getIcon(itemType) {
35311 return _impOsmData.icons[itemType];
35313 // Replace a single QAItem in the cache
35314 replaceItem: function replaceItem(issue) {
35315 if (!(issue instanceof QAItem) || !issue.id) return;
35316 _cache$1.data[issue.id] = issue;
35317 updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
35321 // Remove a single QAItem from the cache
35322 removeItem: function removeItem(issue) {
35323 if (!(issue instanceof QAItem) || !issue.id) return;
35324 delete _cache$1.data[issue.id];
35325 updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
35327 // Used to populate `closed:improveosm:*` changeset tags
35328 getClosedCounts: function getClosedCounts() {
35329 return _cache$1.closed;
35335 // B.2.3.2.1 CreateHTML(string, tag, attribute, value)
35336 // https://tc39.github.io/ecma262/#sec-createhtml
35337 var createHtml = function (string, tag, attribute, value) {
35338 var S = String(requireObjectCoercible(string));
35339 var p1 = '<' + tag;
35340 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
35341 return p1 + '>' + S + '</' + tag + '>';
35344 // check the existence of a method, lowercase
35345 // of a tag and escaping quotes in arguments
35346 var stringHtmlForced = function (METHOD_NAME) {
35347 return fails(function () {
35348 var test = ''[METHOD_NAME]('"');
35349 return test !== test.toLowerCase() || test.split('"').length > 3;
35353 // `String.prototype.link` method
35354 // https://tc39.github.io/ecma262/#sec-string.prototype.link
35355 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
35356 link: function link(url) {
35357 return createHtml(this, 'a', 'href', url);
35361 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
35368 var nativeEndsWith = ''.endsWith;
35369 var min$8 = Math.min;
35371 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
35372 // https://github.com/zloirock/core-js/pull/702
35373 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
35374 var descriptor = getOwnPropertyDescriptor$4(String.prototype, 'endsWith');
35375 return descriptor && !descriptor.writable;
35378 // `String.prototype.endsWith` method
35379 // https://tc39.github.io/ecma262/#sec-string.prototype.endswith
35380 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
35381 endsWith: function endsWith(searchString /* , endPosition = @length */) {
35382 var that = String(requireObjectCoercible(this));
35383 notARegexp(searchString);
35384 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
35385 var len = toLength(that.length);
35386 var end = endPosition === undefined ? len : min$8(toLength(endPosition), len);
35387 var search = String(searchString);
35388 return nativeEndsWith
35389 ? nativeEndsWith.call(that, search, end)
35390 : that.slice(end - search.length, end) === search;
35394 var getOwnPropertyDescriptor$5 = objectGetOwnPropertyDescriptor.f;
35401 var nativeStartsWith = ''.startsWith;
35402 var min$9 = Math.min;
35404 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
35405 // https://github.com/zloirock/core-js/pull/702
35406 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
35407 var descriptor = getOwnPropertyDescriptor$5(String.prototype, 'startsWith');
35408 return descriptor && !descriptor.writable;
35411 // `String.prototype.startsWith` method
35412 // https://tc39.github.io/ecma262/#sec-string.prototype.startswith
35413 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
35414 startsWith: function startsWith(searchString /* , position = 0 */) {
35415 var that = String(requireObjectCoercible(this));
35416 notARegexp(searchString);
35417 var index = toLength(min$9(arguments.length > 1 ? arguments[1] : undefined, that.length));
35418 var search = String(searchString);
35419 return nativeStartsWith
35420 ? nativeStartsWith.call(that, search, index)
35421 : that.slice(index, index + search.length) === search;
35425 var $trimEnd = stringTrim.end;
35428 var FORCED$e = stringTrimForced('trimEnd');
35430 var trimEnd = FORCED$e ? function trimEnd() {
35431 return $trimEnd(this);
35434 // `String.prototype.{ trimEnd, trimRight }` methods
35435 // https://github.com/tc39/ecmascript-string-left-right-trim
35436 _export({ target: 'String', proto: true, forced: FORCED$e }, {
35441 var defaults = createCommonjsModule(function (module) {
35442 function getDefaults() {
35450 langPrefix: 'language-',
35458 smartypants: false,
35465 function changeDefaults(newDefaults) {
35466 module.exports.defaults = newDefaults;
35470 defaults: getDefaults(),
35471 getDefaults: getDefaults,
35472 changeDefaults: changeDefaults
35479 var escapeTest = /[&<>"']/;
35480 var escapeReplace = /[&<>"']/g;
35481 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
35482 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
35483 var escapeReplacements = {
35491 var getEscapeReplacement = function getEscapeReplacement(ch) {
35492 return escapeReplacements[ch];
35495 function escape$1(html, encode) {
35497 if (escapeTest.test(html)) {
35498 return html.replace(escapeReplace, getEscapeReplacement);
35501 if (escapeTestNoEncode.test(html)) {
35502 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
35509 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
35511 function unescape$1(html) {
35512 // explicitly match decimal, hex, and named HTML entities
35513 return html.replace(unescapeTest, function (_, n) {
35514 n = n.toLowerCase();
35515 if (n === 'colon') return ':';
35517 if (n.charAt(0) === '#') {
35518 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
35525 var caret = /(^|[^\[])\^/g;
35527 function edit(regex, opt) {
35528 regex = regex.source || regex;
35531 replace: function replace(name, val) {
35532 val = val.source || val;
35533 val = val.replace(caret, '$1');
35534 regex = regex.replace(name, val);
35537 getRegex: function getRegex() {
35538 return new RegExp(regex, opt);
35544 var nonWordAndColonTest = /[^\w:]/g;
35545 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
35547 function cleanUrl(sanitize, base, href) {
35552 prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase();
35557 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
35562 if (base && !originIndependentUrl.test(href)) {
35563 href = resolveUrl(base, href);
35567 href = encodeURI(href).replace(/%25/g, '%');
35576 var justDomain = /^[^:]+:\/*[^/]*$/;
35577 var protocol = /^([^:]+:)[\s\S]*$/;
35578 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
35580 function resolveUrl(base, href) {
35581 if (!baseUrls[' ' + base]) {
35582 // we can ignore everything in base after the last slash of its path component,
35583 // but we might need to add _that_
35584 // https://tools.ietf.org/html/rfc3986#section-3
35585 if (justDomain.test(base)) {
35586 baseUrls[' ' + base] = base + '/';
35588 baseUrls[' ' + base] = rtrim$1(base, '/', true);
35592 base = baseUrls[' ' + base];
35593 var relativeBase = base.indexOf(':') === -1;
35595 if (href.substring(0, 2) === '//') {
35596 if (relativeBase) {
35600 return base.replace(protocol, '$1') + href;
35601 } else if (href.charAt(0) === '/') {
35602 if (relativeBase) {
35606 return base.replace(domain, '$1') + href;
35608 return base + href;
35613 exec: function noopTest() {}
35616 function merge$1(obj) {
35621 for (; i < arguments.length; i++) {
35622 target = arguments[i];
35624 for (key in target) {
35625 if (Object.prototype.hasOwnProperty.call(target, key)) {
35626 obj[key] = target[key];
35634 function splitCells(tableRow, count) {
35635 // ensure that every cell-delimiting pipe has a space
35636 // before it to distinguish it from an escaped pipe
35637 var row = tableRow.replace(/\|/g, function (match, offset, str) {
35638 var escaped = false,
35641 while (--curr >= 0 && str[curr] === '\\') {
35642 escaped = !escaped;
35646 // odd number of slashes means | is escaped
35647 // so we leave it alone
35650 // add space before unescaped |
35654 cells = row.split(/ \|/);
35657 if (cells.length > count) {
35658 cells.splice(count);
35660 while (cells.length < count) {
35665 for (; i < cells.length; i++) {
35666 // leading or trailing whitespace is ignored per the gfm spec
35667 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
35671 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
35672 // /c*$/ is vulnerable to REDOS.
35673 // invert: Remove suffix of non-c chars instead. Default falsey.
35676 function rtrim$1(str, c, invert) {
35677 var l = str.length;
35681 } // Length of suffix matching the invert condition.
35684 var suffLen = 0; // Step left until we fail to match the invert condition.
35686 while (suffLen < l) {
35687 var currChar = str.charAt(l - suffLen - 1);
35689 if (currChar === c && !invert) {
35691 } else if (currChar !== c && invert) {
35698 return str.substr(0, l - suffLen);
35701 function findClosingBracket(str, b) {
35702 if (str.indexOf(b[1]) === -1) {
35706 var l = str.length;
35710 for (; i < l; i++) {
35711 if (str[i] === '\\') {
35713 } else if (str[i] === b[0]) {
35715 } else if (str[i] === b[1]) {
35727 function checkSanitizeDeprecation(opt) {
35728 if (opt && opt.sanitize && !opt.silent) {
35729 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');
35731 } // copied from https://stackoverflow.com/a/5450113/806777
35734 function repeatString(pattern, count) {
35741 while (count > 1) {
35747 pattern += pattern;
35750 return result + pattern;
35755 unescape: unescape$1,
35757 cleanUrl: cleanUrl,
35758 resolveUrl: resolveUrl,
35759 noopTest: noopTest,
35761 splitCells: splitCells,
35763 findClosingBracket: findClosingBracket,
35764 checkSanitizeDeprecation: checkSanitizeDeprecation,
35765 repeatString: repeatString
35768 var defaults$1 = defaults.defaults;
35769 var rtrim$2 = helpers.rtrim,
35770 splitCells$1 = helpers.splitCells,
35771 _escape = helpers.escape,
35772 findClosingBracket$1 = helpers.findClosingBracket;
35774 function outputLink(cap, link, raw) {
35775 var href = link.href;
35776 var title = link.title ? _escape(link.title) : null;
35777 var text = cap[1].replace(/\\([\[\]])/g, '$1');
35779 if (cap[0].charAt(0) !== '!') {
35793 text: _escape(text)
35798 function indentCodeCompensation(raw, text) {
35799 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
35801 if (matchIndentToCode === null) {
35805 var indentToCode = matchIndentToCode[1];
35806 return text.split('\n').map(function (node) {
35807 var matchIndentInNode = node.match(/^\s+/);
35809 if (matchIndentInNode === null) {
35813 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
35814 indentInNode = _matchIndentInNode[0];
35816 if (indentInNode.length >= indentToCode.length) {
35817 return node.slice(indentToCode.length);
35828 var Tokenizer_1 = /*#__PURE__*/function () {
35829 function Tokenizer(options) {
35830 _classCallCheck(this, Tokenizer);
35832 this.options = options || defaults$1;
35835 _createClass(Tokenizer, [{
35837 value: function space(src) {
35838 var cap = this.rules.block.newline.exec(src);
35841 if (cap[0].length > 1) {
35855 value: function code(src, tokens) {
35856 var cap = this.rules.block.code.exec(src);
35859 var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
35861 if (lastToken && lastToken.type === 'paragraph') {
35864 text: cap[0].trimRight()
35868 var text = cap[0].replace(/^ {4}/gm, '');
35872 codeBlockStyle: 'indented',
35873 text: !this.options.pedantic ? rtrim$2(text, '\n') : text
35879 value: function fences(src) {
35880 var cap = this.rules.block.fences.exec(src);
35884 var text = indentCodeCompensation(raw, cap[3] || '');
35888 lang: cap[2] ? cap[2].trim() : cap[2],
35895 value: function heading(src) {
35896 var cap = this.rules.block.heading.exec(src);
35902 depth: cap[1].length,
35909 value: function nptable(src) {
35910 var cap = this.rules.block.nptable.exec(src);
35915 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
35916 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
35917 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
35921 if (item.header.length === item.align.length) {
35922 var l = item.align.length;
35925 for (i = 0; i < l; i++) {
35926 if (/^ *-+: *$/.test(item.align[i])) {
35927 item.align[i] = 'right';
35928 } else if (/^ *:-+: *$/.test(item.align[i])) {
35929 item.align[i] = 'center';
35930 } else if (/^ *:-+ *$/.test(item.align[i])) {
35931 item.align[i] = 'left';
35933 item.align[i] = null;
35937 l = item.cells.length;
35939 for (i = 0; i < l; i++) {
35940 item.cells[i] = splitCells$1(item.cells[i], item.header.length);
35949 value: function hr(src) {
35950 var cap = this.rules.block.hr.exec(src);
35961 value: function blockquote(src) {
35962 var cap = this.rules.block.blockquote.exec(src);
35965 var text = cap[0].replace(/^ *> ?/gm, '');
35967 type: 'blockquote',
35975 value: function list(src) {
35976 var cap = this.rules.block.list.exec(src);
35981 var isordered = bull.length > 1;
35985 ordered: isordered,
35986 start: isordered ? +bull.slice(0, -1) : '',
35989 }; // Get each top-level item.
35991 var itemMatch = cap[0].match(this.rules.block.item);
36001 var l = itemMatch.length;
36002 bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
36004 for (var i = 0; i < l; i++) {
36005 item = itemMatch[i];
36006 raw = item; // Determine whether the next list item belongs here.
36007 // Backpedal if it does not belong in this list.
36010 bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
36012 if (bnext[1].length > bcurr[0].length || bnext[1].length > 3) {
36014 itemMatch.splice(i, 2, itemMatch[i] + '\n' + itemMatch[i + 1]);
36019 if ( // different bullet style
36020 !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
36021 addBack = itemMatch.slice(i + 1).join('\n');
36022 list.raw = list.raw.substring(0, list.raw.length - addBack.length);
36028 } // Remove the list item's bullet
36029 // so it is seen as the next token.
36032 space = item.length;
36033 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
36034 // list item contains. Hacky.
36036 if (~item.indexOf('\n ')) {
36037 space -= item.length;
36038 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
36039 } // Determine whether item is loose or not.
36040 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
36041 // for discount behavior.
36044 loose = next || /\n\n(?!\s*$)/.test(item);
36047 next = item.charAt(item.length - 1) === '\n';
36048 if (!loose) loose = next;
36053 } // Check for task list items
36056 istask = /^\[[ xX]\] /.test(item);
36057 ischecked = undefined;
36060 ischecked = item[1] !== ' ';
36061 item = item.replace(/^\[[ xX]\] +/, '');
36068 checked: ischecked,
36079 value: function html(src) {
36080 var cap = this.rules.block.html.exec(src);
36084 type: this.options.sanitize ? 'paragraph' : 'html',
36086 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
36087 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36093 value: function def(src) {
36094 var cap = this.rules.block.def.exec(src);
36097 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
36098 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
36109 value: function table(src) {
36110 var cap = this.rules.block.table.exec(src);
36115 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
36116 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
36117 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
36120 if (item.header.length === item.align.length) {
36122 var l = item.align.length;
36125 for (i = 0; i < l; i++) {
36126 if (/^ *-+: *$/.test(item.align[i])) {
36127 item.align[i] = 'right';
36128 } else if (/^ *:-+: *$/.test(item.align[i])) {
36129 item.align[i] = 'center';
36130 } else if (/^ *:-+ *$/.test(item.align[i])) {
36131 item.align[i] = 'left';
36133 item.align[i] = null;
36137 l = item.cells.length;
36139 for (i = 0; i < l; i++) {
36140 item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
36149 value: function lheading(src) {
36150 var cap = this.rules.block.lheading.exec(src);
36156 depth: cap[2].charAt(0) === '=' ? 1 : 2,
36163 value: function paragraph(src) {
36164 var cap = this.rules.block.paragraph.exec(src);
36170 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
36176 value: function text(src, tokens) {
36177 var cap = this.rules.block.text.exec(src);
36180 var lastToken = tokens[tokens.length - 1];
36182 if (lastToken && lastToken.type === 'text') {
36198 value: function escape(src) {
36199 var cap = this.rules.inline.escape.exec(src);
36205 text: _escape(cap[1])
36211 value: function tag(src, inLink, inRawBlock) {
36212 var cap = this.rules.inline.tag.exec(src);
36215 if (!inLink && /^<a /i.test(cap[0])) {
36217 } else if (inLink && /^<\/a>/i.test(cap[0])) {
36221 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36223 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36224 inRawBlock = false;
36228 type: this.options.sanitize ? 'text' : 'html',
36231 inRawBlock: inRawBlock,
36232 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36238 value: function link(src) {
36239 var cap = this.rules.inline.link.exec(src);
36242 var lastParenIndex = findClosingBracket$1(cap[2], '()');
36244 if (lastParenIndex > -1) {
36245 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
36246 var linkLen = start + cap[1].length + lastParenIndex;
36247 cap[2] = cap[2].substring(0, lastParenIndex);
36248 cap[0] = cap[0].substring(0, linkLen).trim();
36255 if (this.options.pedantic) {
36256 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
36265 title = cap[3] ? cap[3].slice(1, -1) : '';
36268 href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
36269 var token = outputLink(cap, {
36270 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
36271 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
36278 value: function reflink(src, links) {
36281 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
36282 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
36283 link = links[link.toLowerCase()];
36285 if (!link || !link.href) {
36286 var text = cap[0].charAt(0);
36294 var token = outputLink(cap, link, cap[0]);
36300 value: function strong(src, maskedSrc) {
36301 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36302 var match = this.rules.inline.strong.start.exec(src);
36304 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36305 maskedSrc = maskedSrc.slice(-1 * src.length);
36306 var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd;
36307 endReg.lastIndex = 0;
36310 while ((match = endReg.exec(maskedSrc)) != null) {
36311 cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3));
36316 raw: src.slice(0, cap[0].length),
36317 text: src.slice(2, cap[0].length - 2)
36325 value: function em(src, maskedSrc) {
36326 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36327 var match = this.rules.inline.em.start.exec(src);
36329 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36330 maskedSrc = maskedSrc.slice(-1 * src.length);
36331 var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd;
36332 endReg.lastIndex = 0;
36335 while ((match = endReg.exec(maskedSrc)) != null) {
36336 cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2));
36341 raw: src.slice(0, cap[0].length),
36342 text: src.slice(1, cap[0].length - 1)
36350 value: function codespan(src) {
36351 var cap = this.rules.inline.code.exec(src);
36354 var text = cap[2].replace(/\n/g, ' ');
36355 var hasNonSpaceChars = /[^ ]/.test(text);
36356 var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
36358 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
36359 text = text.substring(1, text.length - 1);
36362 text = _escape(text, true);
36372 value: function br(src) {
36373 var cap = this.rules.inline.br.exec(src);
36384 value: function del(src) {
36385 var cap = this.rules.inline.del.exec(src);
36397 value: function autolink(src, mangle) {
36398 var cap = this.rules.inline.autolink.exec(src);
36403 if (cap[2] === '@') {
36404 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
36405 href = 'mailto:' + text;
36407 text = _escape(cap[1]);
36426 value: function url(src, mangle) {
36429 if (cap = this.rules.inline.url.exec(src)) {
36432 if (cap[2] === '@') {
36433 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
36434 href = 'mailto:' + text;
36436 // do extended autolink path validation
36440 prevCapZero = cap[0];
36441 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
36442 } while (prevCapZero !== cap[0]);
36444 text = _escape(cap[0]);
36446 if (cap[1] === 'www.') {
36447 href = 'http://' + text;
36468 value: function inlineText(src, inRawBlock, smartypants) {
36469 var cap = this.rules.inline.text.exec(src);
36475 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
36477 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
36492 var noopTest$1 = helpers.noopTest,
36493 edit$1 = helpers.edit,
36494 merge$2 = helpers.merge;
36496 * Block-Level Grammar
36501 code: /^( {4}[^\n]+\n*)+/,
36502 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
36503 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
36504 heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
36505 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
36506 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
36507 html: '^ {0,3}(?:' // optional indentation
36508 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
36509 + '|comment[^\\n]*(\\n+|$)' // (2)
36510 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
36511 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
36512 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
36513 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
36514 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
36515 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
36517 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
36518 nptable: noopTest$1,
36520 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
36521 // regex template, placeholders will be replaced according to different paragraph
36522 // interruption rules of commonmark and the original markdown spec:
36523 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
36526 block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
36527 block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
36528 block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
36529 block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
36530 block.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
36531 block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
36532 block.listItemStart = edit$1(/^( *)(bull)/).replace('bull', block.bullet).getRegex();
36533 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();
36534 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';
36535 block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
36536 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();
36537 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
36538 .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
36539 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
36541 block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex();
36543 * Normal Block Grammar
36546 block.normal = merge$2({}, block);
36548 * GFM Block Grammar
36551 block.gfm = merge$2({}, block.normal, {
36552 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
36553 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
36554 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
36556 table: '^ *\\|(.+)\\n' // Header
36557 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
36558 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
36561 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
36562 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36564 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
36565 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36568 * Pedantic grammar (original John Gruber's loose markdown specification)
36571 block.pedantic = merge$2({}, block.normal, {
36572 html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
36573 + '|<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(),
36574 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
36575 heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
36576 fences: noopTest$1,
36577 // fences not supported
36578 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()
36581 * Inline-Level Grammar
36585 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
36586 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
36588 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
36589 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
36590 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
36591 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
36592 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
36594 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
36595 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
36596 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
36597 reflinkSearch: 'reflink|nolink(?!\\()',
36599 start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,
36600 // (1) returns if starts w/ punctuation
36601 middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,
36602 endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36603 // last char can't be punct, or final * must also be followed by punct (or endline)
36604 endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36608 start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,
36609 // (1) returns if starts w/ punctuation
36610 middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,
36611 endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36612 // last char can't be punct, or final * must also be followed by punct (or endline)
36613 endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36616 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
36617 br: /^( {2,}|\\)\n(?!\s*$)/,
36619 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n)))/,
36620 punctuation: /^([\s*punctuation])/
36621 }; // list of punctuation marks from common mark spec
36622 // without * and _ to workaround cases with double emphasis
36624 inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
36625 inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
36627 inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>';
36628 inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*';
36629 inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex();
36630 inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex();
36631 inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36632 inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36633 inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36634 inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex();
36635 inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36636 inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36637 inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36638 inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex();
36639 inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex();
36640 inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
36641 inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
36642 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])?)+(?![-_])/;
36643 inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex();
36644 inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
36645 inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex();
36646 inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
36647 inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
36648 inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
36649 inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex();
36650 inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex();
36651 inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex();
36653 * Normal Inline Grammar
36656 inline.normal = merge$2({}, inline);
36658 * Pedantic Inline Grammar
36661 inline.pedantic = merge$2({}, inline.normal, {
36664 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
36665 endAst: /\*\*(?!\*)/g,
36670 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
36671 endAst: /\*(?!\*)/g,
36674 link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(),
36675 reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex()
36678 * GFM Inline Grammar
36681 inline.gfm = merge$2({}, inline.normal, {
36682 escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
36683 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
36684 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
36685 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
36686 del: /^~+(?=\S)([\s\S]*?\S)~+/,
36687 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
36689 inline.gfm.url = edit$1(inline.gfm.url, 'i').replace('email', inline.gfm._extended_email).getRegex();
36691 * GFM + Line Breaks Inline Grammar
36694 inline.breaks = merge$2({}, inline.gfm, {
36695 br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
36696 text: edit$1(inline.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
36703 var defaults$2 = defaults.defaults;
36704 var block$1 = rules.block,
36705 inline$1 = rules.inline;
36706 var repeatString$1 = helpers.repeatString;
36708 * smartypants text replacement
36711 function smartypants(text) {
36712 return text // em-dashes
36713 .replace(/---/g, "\u2014") // en-dashes
36714 .replace(/--/g, "\u2013") // opening singles
36715 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
36716 .replace(/'/g, "\u2019") // opening doubles
36717 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
36718 .replace(/"/g, "\u201D") // ellipses
36719 .replace(/\.{3}/g, "\u2026");
36722 * mangle email addresses
36726 function mangle(text) {
36730 var l = text.length;
36732 for (i = 0; i < l; i++) {
36733 ch = text.charCodeAt(i);
36735 if (Math.random() > 0.5) {
36736 ch = 'x' + ch.toString(16);
36739 out += '&#' + ch + ';';
36749 var Lexer_1 = /*#__PURE__*/function () {
36750 function Lexer(options) {
36751 _classCallCheck(this, Lexer);
36754 this.tokens.links = Object.create(null);
36755 this.options = options || defaults$2;
36756 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
36757 this.tokenizer = this.options.tokenizer;
36758 this.tokenizer.options = this.options;
36760 block: block$1.normal,
36761 inline: inline$1.normal
36764 if (this.options.pedantic) {
36765 rules.block = block$1.pedantic;
36766 rules.inline = inline$1.pedantic;
36767 } else if (this.options.gfm) {
36768 rules.block = block$1.gfm;
36770 if (this.options.breaks) {
36771 rules.inline = inline$1.breaks;
36773 rules.inline = inline$1.gfm;
36777 this.tokenizer.rules = rules;
36784 _createClass(Lexer, [{
36790 value: function lex(src) {
36791 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
36792 this.blockTokens(src, this.tokens, true);
36793 this.inline(this.tokens);
36794 return this.tokens;
36801 key: "blockTokens",
36802 value: function blockTokens(src) {
36803 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36804 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
36805 src = src.replace(/^ +$/gm, '');
36806 var token, i, l, lastToken;
36810 if (token = this.tokenizer.space(src)) {
36811 src = src.substring(token.raw.length);
36814 tokens.push(token);
36821 if (token = this.tokenizer.code(src, tokens)) {
36822 src = src.substring(token.raw.length);
36825 tokens.push(token);
36827 lastToken = tokens[tokens.length - 1];
36828 lastToken.raw += '\n' + token.raw;
36829 lastToken.text += '\n' + token.text;
36836 if (token = this.tokenizer.fences(src)) {
36837 src = src.substring(token.raw.length);
36838 tokens.push(token);
36843 if (token = this.tokenizer.heading(src)) {
36844 src = src.substring(token.raw.length);
36845 tokens.push(token);
36847 } // table no leading pipe (gfm)
36850 if (token = this.tokenizer.nptable(src)) {
36851 src = src.substring(token.raw.length);
36852 tokens.push(token);
36857 if (token = this.tokenizer.hr(src)) {
36858 src = src.substring(token.raw.length);
36859 tokens.push(token);
36864 if (token = this.tokenizer.blockquote(src)) {
36865 src = src.substring(token.raw.length);
36866 token.tokens = this.blockTokens(token.text, [], top);
36867 tokens.push(token);
36872 if (token = this.tokenizer.list(src)) {
36873 src = src.substring(token.raw.length);
36874 l = token.items.length;
36876 for (i = 0; i < l; i++) {
36877 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
36880 tokens.push(token);
36885 if (token = this.tokenizer.html(src)) {
36886 src = src.substring(token.raw.length);
36887 tokens.push(token);
36892 if (top && (token = this.tokenizer.def(src))) {
36893 src = src.substring(token.raw.length);
36895 if (!this.tokens.links[token.tag]) {
36896 this.tokens.links[token.tag] = {
36906 if (token = this.tokenizer.table(src)) {
36907 src = src.substring(token.raw.length);
36908 tokens.push(token);
36913 if (token = this.tokenizer.lheading(src)) {
36914 src = src.substring(token.raw.length);
36915 tokens.push(token);
36917 } // top-level paragraph
36920 if (top && (token = this.tokenizer.paragraph(src))) {
36921 src = src.substring(token.raw.length);
36922 tokens.push(token);
36927 if (token = this.tokenizer.text(src, tokens)) {
36928 src = src.substring(token.raw.length);
36931 tokens.push(token);
36933 lastToken = tokens[tokens.length - 1];
36934 lastToken.raw += '\n' + token.raw;
36935 lastToken.text += '\n' + token.text;
36942 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
36944 if (this.options.silent) {
36945 console.error(errMsg);
36948 throw new Error(errMsg);
36957 value: function inline(tokens) {
36958 var i, j, k, l2, row, token;
36959 var l = tokens.length;
36961 for (i = 0; i < l; i++) {
36964 switch (token.type) {
36970 this.inlineTokens(token.text, token.tokens);
36981 l2 = token.header.length;
36983 for (j = 0; j < l2; j++) {
36984 token.tokens.header[j] = [];
36985 this.inlineTokens(token.header[j], token.tokens.header[j]);
36989 l2 = token.cells.length;
36991 for (j = 0; j < l2; j++) {
36992 row = token.cells[j];
36993 token.tokens.cells[j] = [];
36995 for (k = 0; k < row.length; k++) {
36996 token.tokens.cells[j][k] = [];
36997 this.inlineTokens(row[k], token.tokens.cells[j][k]);
37006 this.inline(token.tokens);
37012 l2 = token.items.length;
37014 for (j = 0; j < l2; j++) {
37015 this.inline(token.items[j].tokens);
37030 key: "inlineTokens",
37031 value: function inlineTokens(src) {
37032 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
37033 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
37034 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
37035 var prevChar = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';
37036 var token; // String with links masked to avoid interference with em and strong
37038 var maskedSrc = src;
37039 var match; // Mask out reflinks
37041 if (this.tokens.links) {
37042 var links = Object.keys(this.tokens.links);
37044 if (links.length > 0) {
37045 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
37046 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
37047 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
37051 } // Mask out other blocks
37054 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
37055 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
37060 if (token = this.tokenizer.escape(src)) {
37061 src = src.substring(token.raw.length);
37062 tokens.push(token);
37067 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
37068 src = src.substring(token.raw.length);
37069 inLink = token.inLink;
37070 inRawBlock = token.inRawBlock;
37071 tokens.push(token);
37076 if (token = this.tokenizer.link(src)) {
37077 src = src.substring(token.raw.length);
37079 if (token.type === 'link') {
37080 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37083 tokens.push(token);
37085 } // reflink, nolink
37088 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
37089 src = src.substring(token.raw.length);
37091 if (token.type === 'link') {
37092 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37095 tokens.push(token);
37100 if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) {
37101 src = src.substring(token.raw.length);
37102 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37103 tokens.push(token);
37108 if (token = this.tokenizer.em(src, maskedSrc, prevChar)) {
37109 src = src.substring(token.raw.length);
37110 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37111 tokens.push(token);
37116 if (token = this.tokenizer.codespan(src)) {
37117 src = src.substring(token.raw.length);
37118 tokens.push(token);
37123 if (token = this.tokenizer.br(src)) {
37124 src = src.substring(token.raw.length);
37125 tokens.push(token);
37130 if (token = this.tokenizer.del(src)) {
37131 src = src.substring(token.raw.length);
37132 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37133 tokens.push(token);
37138 if (token = this.tokenizer.autolink(src, mangle)) {
37139 src = src.substring(token.raw.length);
37140 tokens.push(token);
37145 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
37146 src = src.substring(token.raw.length);
37147 tokens.push(token);
37152 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
37153 src = src.substring(token.raw.length);
37154 prevChar = token.raw.slice(-1);
37155 tokens.push(token);
37160 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
37162 if (this.options.silent) {
37163 console.error(errMsg);
37166 throw new Error(errMsg);
37177 * Static Lex Method
37179 value: function lex(src, options) {
37180 var lexer = new Lexer(options);
37181 return lexer.lex(src);
37184 * Static Lex Inline Method
37189 value: function lexInline(src, options) {
37190 var lexer = new Lexer(options);
37191 return lexer.inlineTokens(src);
37195 get: function get() {
37206 var defaults$3 = defaults.defaults;
37207 var cleanUrl$1 = helpers.cleanUrl,
37208 escape$2 = helpers.escape;
37213 var Renderer_1 = /*#__PURE__*/function () {
37214 function Renderer(options) {
37215 _classCallCheck(this, Renderer);
37217 this.options = options || defaults$3;
37220 _createClass(Renderer, [{
37222 value: function code(_code, infostring, escaped) {
37223 var lang = (infostring || '').match(/\S*/)[0];
37225 if (this.options.highlight) {
37226 var out = this.options.highlight(_code, lang);
37228 if (out != null && out !== _code) {
37235 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37238 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37242 value: function blockquote(quote) {
37243 return '<blockquote>\n' + quote + '</blockquote>\n';
37247 value: function html(_html) {
37252 value: function heading(text, level, raw, slugger) {
37253 if (this.options.headerIds) {
37254 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
37258 return '<h' + level + '>' + text + '</h' + level + '>\n';
37262 value: function hr() {
37263 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
37267 value: function list(body, ordered, start) {
37268 var type = ordered ? 'ol' : 'ul',
37269 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
37270 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
37274 value: function listitem(text) {
37275 return '<li>' + text + '</li>\n';
37279 value: function checkbox(checked) {
37280 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
37284 value: function paragraph(text) {
37285 return '<p>' + text + '</p>\n';
37289 value: function table(header, body) {
37290 if (body) body = '<tbody>' + body + '</tbody>';
37291 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
37295 value: function tablerow(content) {
37296 return '<tr>\n' + content + '</tr>\n';
37300 value: function tablecell(content, flags) {
37301 var type = flags.header ? 'th' : 'td';
37302 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
37303 return tag + content + '</' + type + '>\n';
37304 } // span level renderer
37308 value: function strong(text) {
37309 return '<strong>' + text + '</strong>';
37313 value: function em(text) {
37314 return '<em>' + text + '</em>';
37318 value: function codespan(text) {
37319 return '<code>' + text + '</code>';
37323 value: function br() {
37324 return this.options.xhtml ? '<br/>' : '<br>';
37328 value: function del(text) {
37329 return '<del>' + text + '</del>';
37333 value: function link(href, title, text) {
37334 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37336 if (href === null) {
37340 var out = '<a href="' + escape$2(href) + '"';
37343 out += ' title="' + title + '"';
37346 out += '>' + text + '</a>';
37351 value: function image(href, title, text) {
37352 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37354 if (href === null) {
37358 var out = '<img src="' + href + '" alt="' + text + '"';
37361 out += ' title="' + title + '"';
37364 out += this.options.xhtml ? '/>' : '>';
37369 value: function text(_text) {
37379 * returns only the textual part of the token
37381 var TextRenderer_1 = /*#__PURE__*/function () {
37382 function TextRenderer() {
37383 _classCallCheck(this, TextRenderer);
37386 _createClass(TextRenderer, [{
37388 // no need for block level renderers
37389 value: function strong(text) {
37394 value: function em(text) {
37399 value: function codespan(text) {
37404 value: function del(text) {
37409 value: function html(text) {
37414 value: function text(_text) {
37419 value: function link(href, title, text) {
37424 value: function image(href, title, text) {
37429 value: function br() {
37434 return TextRenderer;
37438 * Slugger generates header id
37440 var Slugger_1 = /*#__PURE__*/function () {
37441 function Slugger() {
37442 _classCallCheck(this, Slugger);
37447 _createClass(Slugger, [{
37449 value: function serialize(value) {
37450 return value.toLowerCase().trim() // remove html tags
37451 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
37452 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
37455 * Finds the next safe (unique) slug to use
37459 key: "getNextSafeSlug",
37460 value: function getNextSafeSlug(originalSlug, isDryRun) {
37461 var slug = originalSlug;
37462 var occurenceAccumulator = 0;
37464 if (this.seen.hasOwnProperty(slug)) {
37465 occurenceAccumulator = this.seen[originalSlug];
37468 occurenceAccumulator++;
37469 slug = originalSlug + '-' + occurenceAccumulator;
37470 } while (this.seen.hasOwnProperty(slug));
37474 this.seen[originalSlug] = occurenceAccumulator;
37475 this.seen[slug] = 0;
37481 * Convert string to unique id
37482 * @param {object} options
37483 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
37488 value: function slug(value) {
37489 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
37490 var slug = this.serialize(value);
37491 return this.getNextSafeSlug(slug, options.dryrun);
37498 var defaults$4 = defaults.defaults;
37499 var unescape$2 = helpers.unescape;
37501 * Parsing & Compiling
37504 var Parser_1 = /*#__PURE__*/function () {
37505 function Parser(options) {
37506 _classCallCheck(this, Parser);
37508 this.options = options || defaults$4;
37509 this.options.renderer = this.options.renderer || new Renderer_1();
37510 this.renderer = this.options.renderer;
37511 this.renderer.options = this.options;
37512 this.textRenderer = new TextRenderer_1();
37513 this.slugger = new Slugger_1();
37516 * Static Parse Method
37520 _createClass(Parser, [{
37526 value: function parse(tokens) {
37527 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
37547 var l = tokens.length;
37549 for (i = 0; i < l; i++) {
37552 switch (token.type) {
37560 out += this.renderer.hr();
37566 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$2(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
37572 out += this.renderer.code(token.text, token.lang, token.escaped);
37578 header = ''; // header
37581 l2 = token.header.length;
37583 for (j = 0; j < l2; j++) {
37584 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
37586 align: token.align[j]
37590 header += this.renderer.tablerow(cell);
37592 l2 = token.cells.length;
37594 for (j = 0; j < l2; j++) {
37595 row = token.tokens.cells[j];
37599 for (k = 0; k < l3; k++) {
37600 cell += this.renderer.tablecell(this.parseInline(row[k]), {
37602 align: token.align[k]
37606 body += this.renderer.tablerow(cell);
37609 out += this.renderer.table(header, body);
37615 body = this.parse(token.tokens);
37616 out += this.renderer.blockquote(body);
37622 ordered = token.ordered;
37623 start = token.start;
37624 loose = token.loose;
37625 l2 = token.items.length;
37628 for (j = 0; j < l2; j++) {
37629 item = token.items[j];
37630 checked = item.checked;
37635 checkbox = this.renderer.checkbox(checked);
37638 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
37639 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
37641 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
37642 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
37645 item.tokens.unshift({
37651 itemBody += checkbox;
37655 itemBody += this.parse(item.tokens, loose);
37656 body += this.renderer.listitem(itemBody, task, checked);
37659 out += this.renderer.list(body, ordered, start);
37665 // TODO parse inline content if parameter markdown=1
37666 out += this.renderer.html(token.text);
37672 out += this.renderer.paragraph(this.parseInline(token.tokens));
37678 body = token.tokens ? this.parseInline(token.tokens) : token.text;
37680 while (i + 1 < l && tokens[i + 1].type === 'text') {
37681 token = tokens[++i];
37682 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
37685 out += top ? this.renderer.paragraph(body) : body;
37691 var errMsg = 'Token with "' + token.type + '" type was not found.';
37693 if (this.options.silent) {
37694 console.error(errMsg);
37697 throw new Error(errMsg);
37706 * Parse Inline Tokens
37710 key: "parseInline",
37711 value: function parseInline(tokens, renderer) {
37712 renderer = renderer || this.renderer;
37716 var l = tokens.length;
37718 for (i = 0; i < l; i++) {
37721 switch (token.type) {
37724 out += renderer.text(token.text);
37730 out += renderer.html(token.text);
37736 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
37742 out += renderer.image(token.href, token.title, token.text);
37748 out += renderer.strong(this.parseInline(token.tokens, renderer));
37754 out += renderer.em(this.parseInline(token.tokens, renderer));
37760 out += renderer.codespan(token.text);
37766 out += renderer.br();
37772 out += renderer.del(this.parseInline(token.tokens, renderer));
37778 out += renderer.text(token.text);
37784 var errMsg = 'Token with "' + token.type + '" type was not found.';
37786 if (this.options.silent) {
37787 console.error(errMsg);
37790 throw new Error(errMsg);
37800 value: function parse(tokens, options) {
37801 var parser = new Parser(options);
37802 return parser.parse(tokens);
37805 * Static Parse Inline Method
37809 key: "parseInline",
37810 value: function parseInline(tokens, options) {
37811 var parser = new Parser(options);
37812 return parser.parseInline(tokens);
37819 var merge$3 = helpers.merge,
37820 checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation,
37821 escape$3 = helpers.escape;
37822 var getDefaults = defaults.getDefaults,
37823 changeDefaults = defaults.changeDefaults,
37824 defaults$5 = defaults.defaults;
37829 function marked(src, opt, callback) {
37830 // throw error in case of non string input
37831 if (typeof src === 'undefined' || src === null) {
37832 throw new Error('marked(): input parameter is undefined or null');
37835 if (typeof src !== 'string') {
37836 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
37839 if (typeof opt === 'function') {
37844 opt = merge$3({}, marked.defaults, opt || {});
37845 checkSanitizeDeprecation$1(opt);
37848 var highlight = opt.highlight;
37852 tokens = Lexer_1.lex(src, opt);
37854 return callback(e);
37857 var done = function done(err) {
37862 out = Parser_1.parse(tokens, opt);
37868 opt.highlight = highlight;
37869 return err ? callback(err) : callback(null, out);
37872 if (!highlight || highlight.length < 3) {
37876 delete opt.highlight;
37877 if (!tokens.length) return done();
37879 marked.walkTokens(tokens, function (token) {
37880 if (token.type === 'code') {
37882 setTimeout(function () {
37883 highlight(token.text, token.lang, function (err, code) {
37888 if (code != null && code !== token.text) {
37890 token.escaped = true;
37895 if (pending === 0) {
37903 if (pending === 0) {
37911 var _tokens = Lexer_1.lex(src, opt);
37913 if (opt.walkTokens) {
37914 marked.walkTokens(_tokens, opt.walkTokens);
37917 return Parser_1.parse(_tokens, opt);
37919 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
37922 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
37933 marked.options = marked.setOptions = function (opt) {
37934 merge$3(marked.defaults, opt);
37935 changeDefaults(marked.defaults);
37939 marked.getDefaults = getDefaults;
37940 marked.defaults = defaults$5;
37945 marked.use = function (extension) {
37946 var opts = merge$3({}, extension);
37948 if (extension.renderer) {
37950 var renderer = marked.defaults.renderer || new Renderer_1();
37952 var _loop = function _loop(prop) {
37953 var prevRenderer = renderer[prop];
37955 renderer[prop] = function () {
37956 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
37957 args[_key] = arguments[_key];
37960 var ret = extension.renderer[prop].apply(renderer, args);
37962 if (ret === false) {
37963 ret = prevRenderer.apply(renderer, args);
37970 for (var prop in extension.renderer) {
37974 opts.renderer = renderer;
37978 if (extension.tokenizer) {
37980 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
37982 var _loop2 = function _loop2(prop) {
37983 var prevTokenizer = tokenizer[prop];
37985 tokenizer[prop] = function () {
37986 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
37987 args[_key2] = arguments[_key2];
37990 var ret = extension.tokenizer[prop].apply(tokenizer, args);
37992 if (ret === false) {
37993 ret = prevTokenizer.apply(tokenizer, args);
38000 for (var prop in extension.tokenizer) {
38004 opts.tokenizer = tokenizer;
38008 if (extension.walkTokens) {
38009 var walkTokens = marked.defaults.walkTokens;
38011 opts.walkTokens = function (token) {
38012 extension.walkTokens(token);
38020 marked.setOptions(opts);
38023 * Run callback for every token
38027 marked.walkTokens = function (tokens, callback) {
38028 var _iterator = _createForOfIteratorHelper(tokens),
38032 for (_iterator.s(); !(_step = _iterator.n()).done;) {
38033 var token = _step.value;
38036 switch (token.type) {
38039 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
38043 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
38044 var cell = _step2.value;
38045 marked.walkTokens(cell, callback);
38053 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
38057 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
38058 var row = _step3.value;
38060 var _iterator4 = _createForOfIteratorHelper(row),
38064 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
38065 var _cell = _step4.value;
38066 marked.walkTokens(_cell, callback);
38085 marked.walkTokens(token.items, callback);
38091 if (token.tokens) {
38092 marked.walkTokens(token.tokens, callback);
38108 marked.parseInline = function (src, opt) {
38109 // throw error in case of non string input
38110 if (typeof src === 'undefined' || src === null) {
38111 throw new Error('marked.parseInline(): input parameter is undefined or null');
38114 if (typeof src !== 'string') {
38115 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
38118 opt = merge$3({}, marked.defaults, opt || {});
38119 checkSanitizeDeprecation$1(opt);
38122 var tokens = Lexer_1.lexInline(src, opt);
38124 if (opt.walkTokens) {
38125 marked.walkTokens(tokens, opt.walkTokens);
38128 return Parser_1.parseInline(tokens, opt);
38130 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
38133 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
38144 marked.Parser = Parser_1;
38145 marked.parser = Parser_1.parse;
38146 marked.Renderer = Renderer_1;
38147 marked.TextRenderer = TextRenderer_1;
38148 marked.Lexer = Lexer_1;
38149 marked.lexer = Lexer_1.lex;
38150 marked.Tokenizer = Tokenizer_1;
38151 marked.Slugger = Slugger_1;
38152 marked.parse = marked;
38153 var marked_1 = marked;
38155 var tiler$2 = utilTiler();
38156 var dispatch$3 = dispatch('loaded');
38157 var _tileZoom$2 = 14;
38158 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
38159 var _osmoseData = {
38162 }; // This gets reassigned if reset
38166 function abortRequest$2(controller) {
38168 controller.abort();
38172 function abortUnwantedRequests$2(cache, tiles) {
38173 Object.keys(cache.inflightTile).forEach(function (k) {
38174 var wanted = tiles.find(function (tile) {
38175 return k === tile.id;
38179 abortRequest$2(cache.inflightTile[k]);
38180 delete cache.inflightTile[k];
38185 function encodeIssueRtree$2(d) {
38193 } // Replace or remove QAItem from rtree
38196 function updateRtree$2(item, replace) {
38197 _cache$2.rtree.remove(item, function (a, b) {
38198 return a.data.id === b.data.id;
38202 _cache$2.rtree.insert(item);
38204 } // Issues shouldn't obscure each other
38207 function preventCoincident$1(loc) {
38208 var coincident = false;
38211 // first time, move marker up. after that, move marker right.
38212 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
38213 loc = geoVecAdd(loc, delta);
38214 var bbox = geoExtent(loc).bbox();
38215 coincident = _cache$2.rtree.search(bbox).length;
38216 } while (coincident);
38221 var serviceOsmose = {
38223 init: function init() {
38224 _mainFileFetcher.get('qa_data').then(function (d) {
38225 _osmoseData = d.osmose;
38226 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
38227 return s.split('-')[0];
38228 }).reduce(function (unique, item) {
38229 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
38237 this.event = utilRebind(this, dispatch$3, 'on');
38239 reset: function reset() {
38244 Object.values(_cache$2.inflightTile).forEach(abortRequest$2); // Strings and colors are static and should not be re-populated
38246 _strings = _cache$2.strings;
38247 _colors = _cache$2.colors;
38256 rtree: new RBush(),
38261 loadIssues: function loadIssues(projection) {
38265 // Tiles return a maximum # of issues
38266 // So we want to filter our request for only types iD supports
38267 item: _osmoseData.items
38268 }; // determine the needed tiles to cover the view
38270 var tiles = tiler$2.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
38272 abortUnwantedRequests$2(_cache$2, tiles); // issue new requests..
38274 tiles.forEach(function (tile) {
38275 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
38277 var _tile$xyz = _slicedToArray(tile.xyz, 3),
38282 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
38283 var controller = new AbortController();
38284 _cache$2.inflightTile[tile.id] = controller;
38286 signal: controller.signal
38287 }).then(function (data) {
38288 delete _cache$2.inflightTile[tile.id];
38289 _cache$2.loadedTile[tile.id] = true;
38291 if (data.features) {
38292 data.features.forEach(function (issue) {
38293 var _issue$properties = issue.properties,
38294 item = _issue$properties.item,
38295 cl = _issue$properties["class"],
38296 id = _issue$properties.uuid;
38297 /* Osmose issues are uniquely identified by a unique
38298 `item` and `class` combination (both integer values) */
38300 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
38302 if (itemType in _osmoseData.icons) {
38303 var loc = issue.geometry.coordinates; // lon, lat
38305 loc = preventCoincident$1(loc);
38306 var d = new QAItem(loc, _this, itemType, id, {
38308 }); // Setting elems here prevents UI detail requests
38310 if (item === 8300 || item === 8360) {
38314 _cache$2.data[d.id] = d;
38316 _cache$2.rtree.insert(encodeIssueRtree$2(d));
38321 dispatch$3.call('loaded');
38322 })["catch"](function () {
38323 delete _cache$2.inflightTile[tile.id];
38324 _cache$2.loadedTile[tile.id] = true;
38328 loadIssueDetail: function loadIssueDetail(issue) {
38331 // Issue details only need to be fetched once
38332 if (issue.elems !== undefined) {
38333 return Promise.resolve(issue);
38336 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
38338 var cacheDetails = function cacheDetails(data) {
38339 // Associated elements used for highlighting
38340 // Assign directly for immediate use in the callback
38341 issue.elems = data.elems.map(function (e) {
38342 return e.type.substring(0, 1) + e.id;
38343 }); // Some issues have instance specific detail in a subtitle
38345 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
38347 _this2.replaceItem(issue);
38350 return d3_json(url).then(cacheDetails).then(function () {
38354 loadStrings: function loadStrings() {
38355 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
38356 var items = Object.keys(_osmoseData.icons);
38358 if (locale in _cache$2.strings && Object.keys(_cache$2.strings[locale]).length === items.length) {
38359 return Promise.resolve(_cache$2.strings[locale]);
38360 } // May be partially populated already if some requests were successful
38363 if (!(locale in _cache$2.strings)) {
38364 _cache$2.strings[locale] = {};
38365 } // Only need to cache strings for supported issue types
38366 // Using multiple individual item + class requests to reduce fetched data size
38369 var allRequests = items.map(function (itemType) {
38370 // No need to request data we already have
38371 if (itemType in _cache$2.strings[locale]) return null;
38373 var cacheData = function cacheData(data) {
38374 // Bunch of nested single value arrays of objects
38375 var _data$categories = _slicedToArray(data.categories, 1),
38376 _data$categories$ = _data$categories[0],
38377 cat = _data$categories$ === void 0 ? {
38379 } : _data$categories$;
38381 var _cat$items = _slicedToArray(cat.items, 1),
38382 _cat$items$ = _cat$items[0],
38383 item = _cat$items$ === void 0 ? {
38387 var _item$class = _slicedToArray(item["class"], 1),
38388 _item$class$ = _item$class[0],
38389 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
38393 /* eslint-disable no-console */
38394 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
38395 /* eslint-enable no-console */
38398 } // Cache served item colors to automatically style issue markers later
38401 var itemInt = item.item,
38402 color = item.color;
38404 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
38405 _cache$2.colors[itemInt] = color;
38406 } // Value of root key will be null if no string exists
38407 // If string exists, value is an object with key 'auto' for string
38410 var title = cl.title,
38411 detail = cl.detail,
38413 trap = cl.trap; // Osmose titles shouldn't contain markdown
38415 var issueStrings = {};
38416 if (title) issueStrings.title = title.auto;
38417 if (detail) issueStrings.detail = marked_1(detail.auto);
38418 if (trap) issueStrings.trap = marked_1(trap.auto);
38419 if (fix) issueStrings.fix = marked_1(fix.auto);
38420 _cache$2.strings[locale][itemType] = issueStrings;
38423 var _itemType$split = itemType.split('-'),
38424 _itemType$split2 = _slicedToArray(_itemType$split, 2),
38425 item = _itemType$split2[0],
38426 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
38429 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
38430 return d3_json(url).then(cacheData);
38431 }).filter(Boolean);
38432 return Promise.all(allRequests).then(function () {
38433 return _cache$2.strings[locale];
38436 getStrings: function getStrings(itemType) {
38437 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
38438 // No need to fallback to English, Osmose API handles this for us
38439 return locale in _cache$2.strings ? _cache$2.strings[locale][itemType] : {};
38441 getColor: function getColor(itemType) {
38442 return itemType in _cache$2.colors ? _cache$2.colors[itemType] : '#FFFFFF';
38444 postUpdate: function postUpdate(issue, callback) {
38447 if (_cache$2.inflightPost[issue.id]) {
38449 message: 'Issue update already inflight',
38452 } // UI sets the status to either 'done' or 'false'
38455 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
38456 var controller = new AbortController();
38458 var after = function after() {
38459 delete _cache$2.inflightPost[issue.id];
38461 _this3.removeItem(issue);
38463 if (issue.newStatus === 'done') {
38464 // Keep track of the number of issues closed per `item` to tag the changeset
38465 if (!(issue.item in _cache$2.closed)) {
38466 _cache$2.closed[issue.item] = 0;
38469 _cache$2.closed[issue.item] += 1;
38472 if (callback) callback(null, issue);
38475 _cache$2.inflightPost[issue.id] = controller;
38477 signal: controller.signal
38478 }).then(after)["catch"](function (err) {
38479 delete _cache$2.inflightPost[issue.id];
38480 if (callback) callback(err.message);
38483 // Get all cached QAItems covering the viewport
38484 getItems: function getItems(projection) {
38485 var viewport = projection.clipExtent();
38486 var min = [viewport[0][0], viewport[1][1]];
38487 var max = [viewport[1][0], viewport[0][1]];
38488 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38489 return _cache$2.rtree.search(bbox).map(function (d) {
38493 // Get a QAItem from cache
38494 // NOTE: Don't change method name until UI v3 is merged
38495 getError: function getError(id) {
38496 return _cache$2.data[id];
38498 // get the name of the icon to display for this item
38499 getIcon: function getIcon(itemType) {
38500 return _osmoseData.icons[itemType];
38502 // Replace a single QAItem in the cache
38503 replaceItem: function replaceItem(item) {
38504 if (!(item instanceof QAItem) || !item.id) return;
38505 _cache$2.data[item.id] = item;
38506 updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
38510 // Remove a single QAItem from the cache
38511 removeItem: function removeItem(item) {
38512 if (!(item instanceof QAItem) || !item.id) return;
38513 delete _cache$2.data[item.id];
38514 updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
38516 // Used to populate `closed:osmose:*` changeset tags
38517 getClosedCounts: function getClosedCounts() {
38518 return _cache$2.closed;
38520 itemURL: function itemURL(item) {
38521 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
38525 var apibase = 'https://a.mapillary.com/v3/';
38526 var viewercss = 'mapillary-js/mapillary.min.css';
38527 var viewerjs = 'mapillary-js/mapillary.min.js';
38528 var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
38529 var mapFeatureConfig = {
38530 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(',')
38532 var maxResults = 1000;
38534 var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
38535 var dispatch$4 = dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'nodeChanged');
38536 var _mlyFallback = false;
38542 var _mlyActiveImage;
38544 var _mlySelectedImageKey;
38548 var _mlyViewerFilter = ['all'];
38550 var _loadViewerPromise;
38552 var _mlyHighlightedDetection;
38554 var _mlyShowFeatureDetections = false;
38555 var _mlyShowSignDetections = false;
38557 function abortRequest$3(controller) {
38558 controller.abort();
38561 function loadTiles(which, url, projection) {
38562 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
38563 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
38565 var cache = _mlyCache[which];
38566 Object.keys(cache.inflight).forEach(function (k) {
38567 var wanted = tiles.find(function (tile) {
38568 return k.indexOf(tile.id + ',') === 0;
38572 abortRequest$3(cache.inflight[k]);
38573 delete cache.inflight[k];
38576 tiles.forEach(function (tile) {
38577 loadNextTilePage(which, currZoom, url, tile);
38581 function loadNextTilePage(which, currZoom, url, tile) {
38582 var cache = _mlyCache[which];
38583 var rect = tile.extent.rectangle();
38584 var maxPages = maxPageAtZoom(currZoom);
38585 var nextPage = cache.nextPage[tile.id] || 0;
38586 var nextURL = cache.nextURL[tile.id] || url + utilQsString({
38587 per_page: maxResults,
38589 client_id: clientId,
38590 bbox: [rect[0], rect[1], rect[2], rect[3]].join(',')
38592 if (nextPage > maxPages) return;
38593 var id = tile.id + ',' + String(nextPage);
38594 if (cache.loaded[id] || cache.inflight[id]) return;
38595 var controller = new AbortController();
38596 cache.inflight[id] = controller;
38599 signal: controller.signal,
38601 'Content-Type': 'application/json'
38604 fetch(nextURL, options).then(function (response) {
38605 if (!response.ok) {
38606 throw new Error(response.status + ' ' + response.statusText);
38609 var linkHeader = response.headers.get('Link');
38612 var pagination = parsePagination(linkHeader);
38614 if (pagination.next) {
38615 cache.nextURL[tile.id] = pagination.next;
38619 return response.json();
38620 }).then(function (data) {
38621 cache.loaded[id] = true;
38622 delete cache.inflight[id];
38624 if (!data || !data.features || !data.features.length) {
38625 throw new Error('No Data');
38628 var features = data.features.map(function (feature) {
38629 var loc = feature.geometry.coordinates;
38630 var d; // An image (shown as a green dot on the map) is a single street photo with extra
38631 // information such as location, camera angle (CA), camera model, and so on.
38632 // Each image feature is a GeoJSON Point
38634 if (which === 'images') {
38637 key: feature.properties.key,
38638 ca: feature.properties.ca,
38639 captured_at: feature.properties.captured_at,
38640 captured_by: feature.properties.username,
38641 pano: feature.properties.pano
38643 cache.forImageKey[d.key] = d; // cache imageKey -> image
38644 // Mapillary organizes images as sequences. A sequence of images are continuously captured
38645 // by a user at a give time. Sequences are shown on the map as green lines.
38646 // Each sequence feature is a GeoJSON LineString
38647 } else if (which === 'sequences') {
38648 var sequenceKey = feature.properties.key;
38649 cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString
38651 feature.properties.coordinateProperties.image_keys.forEach(function (imageKey) {
38652 cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey
38654 return false; // because no `d` data worth loading into an rbush
38655 // A map feature is a real world object that can be shown on a map. It could be any object
38656 // recognized from images, manually added in images, or added on the map.
38657 // Each map feature is a GeoJSON Point (located where the feature is)
38658 } else if (which === 'map_features' || which === 'points') {
38661 key: feature.properties.key,
38662 value: feature.properties.value,
38663 detections: feature.properties.detections,
38664 direction: feature.properties.direction,
38665 accuracy: feature.properties.accuracy,
38666 first_seen_at: feature.properties.first_seen_at,
38667 last_seen_at: feature.properties.last_seen_at
38678 }).filter(Boolean);
38680 if (cache.rtree && features) {
38681 cache.rtree.load(features);
38684 if (data.features.length === maxResults) {
38685 // more pages to load
38686 cache.nextPage[tile.id] = nextPage + 1;
38687 loadNextTilePage(which, currZoom, url, tile);
38689 cache.nextPage[tile.id] = Infinity; // no more pages to load
38692 if (which === 'images' || which === 'sequences') {
38693 dispatch$4.call('loadedImages');
38694 } else if (which === 'map_features') {
38695 dispatch$4.call('loadedSigns');
38696 } else if (which === 'points') {
38697 dispatch$4.call('loadedMapFeatures');
38699 })["catch"](function () {
38700 cache.loaded[id] = true;
38701 delete cache.inflight[id];
38705 function loadData(which, url) {
38706 var cache = _mlyCache[which];
38710 'Content-Type': 'application/json'
38713 var nextUrl = url + '&client_id=' + clientId;
38714 return fetch(nextUrl, options).then(function (response) {
38715 if (!response.ok) {
38716 throw new Error(response.status + ' ' + response.statusText);
38719 return response.json();
38720 }).then(function (data) {
38721 if (!data || !data.features || !data.features.length) {
38722 throw new Error('No Data');
38725 data.features.forEach(function (feature) {
38728 if (which === 'image_detections') {
38730 key: feature.properties.key,
38731 image_key: feature.properties.image_key,
38732 value: feature.properties.value,
38733 shape: feature.properties.shape
38736 if (!cache.forImageKey[d.image_key]) {
38737 cache.forImageKey[d.image_key] = [];
38740 cache.forImageKey[d.image_key].push(d);
38746 function maxPageAtZoom(z) {
38747 if (z < 15) return 2;
38748 if (z === 15) return 5;
38749 if (z === 16) return 10;
38750 if (z === 17) return 20;
38751 if (z === 18) return 40;
38752 if (z > 18) return 80;
38753 } // extract links to pages of API results
38756 function parsePagination(links) {
38757 return links.split(',').map(function (rel) {
38758 var elements = rel.split(';');
38760 if (elements.length === 2) {
38761 return [/<(.+)>/.exec(elements[0])[1], /rel="(.+)"/.exec(elements[1])[1]];
38765 }).reduce(function (pagination, val) {
38766 pagination[val[1]] = val[0];
38769 } // partition viewport into higher zoom tiles
38772 function partitionViewport(projection) {
38773 var z = geoScaleToZoom(projection.scale());
38774 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
38776 var tiler = utilTiler().zoomExtent([z2, z2]);
38777 return tiler.getTiles(projection).map(function (tile) {
38778 return tile.extent;
38780 } // no more than `limit` results per partition.
38783 function searchLimited(limit, projection, rtree) {
38784 limit = limit || 5;
38785 return partitionViewport(projection).reduce(function (result, extent) {
38786 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
38789 return found.length ? result.concat(found) : result;
38793 var serviceMapillary = {
38794 init: function init() {
38799 this.event = utilRebind(this, dispatch$4, 'on');
38801 reset: function reset() {
38803 Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
38804 Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
38805 Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
38806 Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
38807 Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
38816 rtree: new RBush(),
38819 image_detections: {
38845 rtree: new RBush(),
38850 _mlySelectedImageKey = null;
38851 _mlyActiveImage = null;
38854 images: function images(projection) {
38856 return searchLimited(limit, projection, _mlyCache.images.rtree);
38858 signs: function signs(projection) {
38860 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
38862 mapFeatures: function mapFeatures(projection) {
38864 return searchLimited(limit, projection, _mlyCache.points.rtree);
38866 cachedImage: function cachedImage(imageKey) {
38867 return _mlyCache.images.forImageKey[imageKey];
38869 sequences: function sequences(projection) {
38870 var viewport = projection.clipExtent();
38871 var min = [viewport[0][0], viewport[1][1]];
38872 var max = [viewport[1][0], viewport[0][1]];
38873 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38874 var sequenceKeys = {}; // all sequences for images in viewport
38876 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
38877 var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
38880 sequenceKeys[sequenceKey] = true;
38882 }); // Return lineStrings for the sequences
38885 return Object.keys(sequenceKeys).map(function (sequenceKey) {
38886 return _mlyCache.sequences.lineString[sequenceKey];
38889 signsSupported: function signsSupported() {
38892 loadImages: function loadImages(projection) {
38893 loadTiles('images', apibase + 'images?sort_by=key&', projection);
38894 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
38896 loadSigns: function loadSigns(projection) {
38897 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
38899 loadMapFeatures: function loadMapFeatures(projection) {
38900 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
38902 ensureViewerLoaded: function ensureViewerLoaded(context) {
38903 if (_loadViewerPromise) return _loadViewerPromise; // add mly-wrapper
38905 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
38906 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
38908 _loadViewerPromise = new Promise(function (resolve, reject) {
38909 var loadedCount = 0;
38911 function loaded() {
38912 loadedCount += 1; // wait until both files are loaded
38914 if (loadedCount === 2) resolve();
38917 var head = select('head'); // load mapillary-viewercss
38919 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 () {
38921 }); // load mapillary-viewerjs
38923 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 () {
38926 })["catch"](function () {
38927 _loadViewerPromise = null;
38928 }).then(function () {
38929 that.initViewer(context);
38931 return _loadViewerPromise;
38933 loadSignResources: function loadSignResources(context) {
38934 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
38935 /* don't override colors */
38939 loadObjectResources: function loadObjectResources(context) {
38940 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
38941 /* don't override colors */
38945 resetTags: function resetTags() {
38946 if (_mlyViewer && !_mlyFallback) {
38947 _mlyViewer.getComponent('tag').removeAll(); // remove previous detections
38951 showFeatureDetections: function showFeatureDetections(value) {
38952 _mlyShowFeatureDetections = value;
38954 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38958 showSignDetections: function showSignDetections(value) {
38959 _mlyShowSignDetections = value;
38961 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38965 filterViewer: function filterViewer(context) {
38966 var showsPano = context.photos().showsPanoramic();
38967 var showsFlat = context.photos().showsFlat();
38968 var fromDate = context.photos().fromDate();
38969 var toDate = context.photos().toDate();
38970 var usernames = context.photos().usernames();
38971 var filter = ['all'];
38972 if (!showsPano) filter.push(['==', 'pano', false]);
38973 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
38974 if (usernames && usernames.length) filter.push(['==', 'username', usernames[0]]);
38977 var fromTimestamp = new Date(fromDate).getTime();
38978 filter.push(['>=', 'capturedAt', fromTimestamp]);
38982 var toTimestamp = new Date(toDate).getTime();
38983 filter.push(['>=', 'capturedAt', toTimestamp]);
38987 _mlyViewer.setFilter(filter);
38990 _mlyViewerFilter = filter;
38993 showViewer: function showViewer(context) {
38994 var wrap = context.container().select('.photoviewer').classed('hide', false);
38995 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
38997 if (isHidden && _mlyViewer) {
38998 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
38999 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
39001 _mlyViewer.resize();
39006 hideViewer: function hideViewer(context) {
39007 _mlyActiveImage = null;
39008 _mlySelectedImageKey = null;
39010 if (!_mlyFallback && _mlyViewer) {
39011 _mlyViewer.getComponent('sequence').stop();
39014 var viewer = context.container().select('.photoviewer');
39015 if (!viewer.empty()) viewer.datum(null);
39016 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
39017 this.updateUrlImage(null);
39018 dispatch$4.call('nodeChanged');
39019 return this.setStyles(context, null, true);
39021 parsePagination: parsePagination,
39022 updateUrlImage: function updateUrlImage(imageKey) {
39023 if (!window.mocha) {
39024 var hash = utilStringQs(window.location.hash);
39027 hash.photo = 'mapillary/' + imageKey;
39032 window.location.replace('#' + utilQsString(hash, true));
39035 highlightDetection: function highlightDetection(detection) {
39037 _mlyHighlightedDetection = detection.detection_key;
39042 initViewer: function initViewer(context) {
39044 if (!window.Mapillary) return;
39046 baseImageSize: 320,
39052 }; // Disable components requiring WebGL support
39054 if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
39055 _mlyFallback = true;
39066 navigation: true // fallback
39071 _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
39073 _mlyViewer.on('nodechanged', nodeChanged);
39075 _mlyViewer.on('bearingchanged', bearingChanged);
39077 if (_mlyViewerFilter) {
39078 _mlyViewer.setFilter(_mlyViewerFilter);
39079 } // Register viewer resize handler
39082 context.ui().photoviewer.on('resize.mapillary', function () {
39083 if (_mlyViewer) _mlyViewer.resize();
39084 }); // nodeChanged: called after the viewer has changed images and is ready.
39086 // There is some logic here to batch up clicks into a _mlyClicks array
39087 // because the user might click on a lot of markers quickly and nodechanged
39088 // may be called out of order asynchronously.
39090 // Clicks are added to the array in `selectedImage` and removed here.
39093 function nodeChanged(node) {
39095 var clicks = _mlyClicks;
39096 var index = clicks.indexOf(node.key);
39097 var selectedKey = _mlySelectedImageKey;
39098 that.setActiveImage(node);
39101 // `nodechanged` initiated from clicking on a marker..
39102 clicks.splice(index, 1); // remove the click
39103 // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
39104 // one more time to update the detections and attribution..
39106 if (node.key === selectedKey) {
39107 that.selectImage(context, _mlySelectedImageKey, true);
39110 // `nodechanged` initiated from the Mapillary viewer controls..
39111 var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
39112 context.map().centerEase(loc);
39113 that.selectImage(context, node.key, true);
39116 dispatch$4.call('nodeChanged');
39119 function bearingChanged(e) {
39120 dispatch$4.call('bearingChanged', undefined, e);
39123 // Pass in the image key string as `imageKey`.
39124 // This allows images to be selected from places that dont have access
39125 // to the full image datum (like the street signs layer or the js viewer)
39126 selectImage: function selectImage(context, imageKey, fromViewer) {
39127 _mlySelectedImageKey = imageKey;
39128 this.updateUrlImage(imageKey);
39129 var d = _mlyCache.images.forImageKey[imageKey];
39130 var viewer = context.container().select('.photoviewer');
39131 if (!viewer.empty()) viewer.datum(d);
39132 imageKey = d && d.key || imageKey;
39134 if (!fromViewer && imageKey) {
39135 _mlyClicks.push(imageKey);
39138 this.setStyles(context, null, true);
39140 if (_mlyShowFeatureDetections) {
39141 this.updateDetections(imageKey, apibase + 'image_detections?layers=points&values=' + mapFeatureConfig.values + '&image_keys=' + imageKey);
39144 if (_mlyShowSignDetections) {
39145 this.updateDetections(imageKey, apibase + 'image_detections?layers=trafficsigns&image_keys=' + imageKey);
39148 if (_mlyViewer && imageKey) {
39149 _mlyViewer.moveToKey(imageKey)["catch"](function (e) {
39150 console.error('mly3', e);
39151 }); // eslint-disable-line no-console
39157 getActiveImage: function getActiveImage() {
39158 return _mlyActiveImage;
39160 getSelectedImageKey: function getSelectedImageKey() {
39161 return _mlySelectedImageKey;
39163 getSequenceKeyForImageKey: function getSequenceKeyForImageKey(imageKey) {
39164 return _mlyCache.sequences.forImageKey[imageKey];
39166 setActiveImage: function setActiveImage(node) {
39168 _mlyActiveImage = {
39169 ca: node.originalCA,
39171 loc: [node.originalLatLon.lon, node.originalLatLon.lat],
39175 _mlyActiveImage = null;
39178 // Updates the currently highlighted sequence and selected bubble.
39179 // Reset is only necessary when interacting with the viewport because
39180 // this implicitly changes the currently selected bubble/sequence
39181 setStyles: function setStyles(context, hovered, reset) {
39183 // reset all layers
39184 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false);
39185 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
39188 var hoveredImageKey = hovered && hovered.key;
39189 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
39190 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
39191 var hoveredImageKeys = hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys || [];
39192 var selectedImageKey = _mlySelectedImageKey;
39193 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
39194 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
39195 var selectedImageKeys = selectedLineString && selectedLineString.properties.coordinateProperties.image_keys || []; // highlight sibling viewfields on either the selected or the hovered sequences
39197 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
39198 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
39199 return highlightedImageKeys.indexOf(d.key) !== -1;
39200 }).classed('hovered', function (d) {
39201 return d.key === hoveredImageKey;
39203 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
39204 return d.properties.key === hoveredSequenceKey;
39205 }).classed('currentView', function (d) {
39206 return d.properties.key === selectedSequenceKey;
39207 }); // update viewfields if needed
39209 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
39211 function viewfieldPath() {
39212 var d = this.parentNode.__data__;
39214 if (d.pano && d.key !== selectedImageKey) {
39215 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
39217 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
39223 updateDetections: function updateDetections(imageKey, url) {
39224 if (!_mlyViewer || _mlyFallback) return;
39225 if (!imageKey) return;
39227 if (!_mlyCache.image_detections.forImageKey[imageKey]) {
39228 loadData('image_detections', url).then(function () {
39229 showDetections(_mlyCache.image_detections.forImageKey[imageKey] || []);
39232 showDetections(_mlyCache.image_detections.forImageKey[imageKey]);
39235 function showDetections(detections) {
39236 detections.forEach(function (data) {
39237 var tag = makeTag(data);
39240 var tagComponent = _mlyViewer.getComponent('tag');
39242 tagComponent.add([tag]);
39247 function makeTag(data) {
39248 var valueParts = data.value.split('--');
39249 if (!valueParts.length) return;
39252 var color = 0xffffff;
39254 if (_mlyHighlightedDetection === data.key) {
39256 text = valueParts[1];
39258 if (text === 'flat' || text === 'discrete' || text === 'sign') {
39259 text = valueParts[2];
39262 text = text.replace(/-/g, ' ');
39263 text = text.charAt(0).toUpperCase() + text.slice(1);
39264 _mlyHighlightedDetection = null;
39267 if (data.shape.type === 'Polygon') {
39268 var polygonGeometry = new Mapillary.TagComponent.PolygonGeometry(data.shape.coordinates[0]);
39269 tag = new Mapillary.TagComponent.OutlineTag(data.key, polygonGeometry, {
39277 } else if (data.shape.type === 'Point') {
39278 var pointGeometry = new Mapillary.TagComponent.PointGeometry(data.shape.coordinates[0]);
39279 tag = new Mapillary.TagComponent.SpotTag(data.key, pointGeometry, {
39289 cache: function cache() {
39294 function validationIssue(attrs) {
39295 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
39297 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
39299 this.severity = attrs.severity; // required - 'warning' or 'error'
39301 this.message = attrs.message; // required - function returning localized string
39303 this.reference = attrs.reference; // optional - function(selection) to render reference information
39305 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
39307 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
39309 this.data = attrs.data; // optional - object containing extra data for the fixes
39311 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
39313 this.hash = attrs.hash; // optional - string to further differentiate the issue
39315 this.id = generateID.apply(this); // generated - see below
39317 this.autoFix = null; // generated - if autofix exists, will be set below
39318 // A unique, deterministic string hash.
39319 // Issues with identical id values are considered identical.
39321 function generateID() {
39322 var parts = [this.type];
39325 // subclasses can pass in their own differentiator
39326 parts.push(this.hash);
39329 if (this.subtype) {
39330 parts.push(this.subtype);
39331 } // include the entities this issue is for
39332 // (sort them so the id is deterministic)
39335 if (this.entityIds) {
39336 var entityKeys = this.entityIds.slice().sort();
39337 parts.push.apply(parts, entityKeys);
39340 return parts.join(':');
39343 this.extent = function (resolver) {
39345 return geoExtent(this.loc);
39348 if (this.entityIds && this.entityIds.length) {
39349 return this.entityIds.reduce(function (extent, entityId) {
39350 return extent.extend(resolver.entity(entityId).extent(resolver));
39357 this.fixes = function (context) {
39358 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
39361 if (issue.severity === 'warning') {
39362 // allow ignoring any issue that's not an error
39363 fixes.push(new validationIssueFix({
39364 title: _t.html('issues.fix.ignore_issue.title'),
39365 icon: 'iD-icon-close',
39366 onClick: function onClick() {
39367 context.validator().ignoreIssue(this.issue.id);
39372 fixes.forEach(function (fix) {
39373 // the id doesn't matter as long as it's unique to this issue/fix
39374 fix.id = fix.title; // add a reference to the issue for use in actions
39378 if (fix.autoArgs) {
39379 issue.autoFix = fix;
39385 function validationIssueFix(attrs) {
39386 this.title = attrs.title; // Required
39388 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
39390 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
39392 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
39394 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
39396 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
39398 this.issue = null; // Generated link - added by validationIssue
39401 var buildRuleChecks = function buildRuleChecks() {
39403 equals: function equals(_equals) {
39404 return function (tags) {
39405 return Object.keys(_equals).every(function (k) {
39406 return _equals[k] === tags[k];
39410 notEquals: function notEquals(_notEquals) {
39411 return function (tags) {
39412 return Object.keys(_notEquals).some(function (k) {
39413 return _notEquals[k] !== tags[k];
39417 absence: function absence(_absence) {
39418 return function (tags) {
39419 return Object.keys(tags).indexOf(_absence) === -1;
39422 presence: function presence(_presence) {
39423 return function (tags) {
39424 return Object.keys(tags).indexOf(_presence) > -1;
39427 greaterThan: function greaterThan(_greaterThan) {
39428 var key = Object.keys(_greaterThan)[0];
39429 var value = _greaterThan[key];
39430 return function (tags) {
39431 return tags[key] > value;
39434 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
39435 var key = Object.keys(_greaterThanEqual)[0];
39436 var value = _greaterThanEqual[key];
39437 return function (tags) {
39438 return tags[key] >= value;
39441 lessThan: function lessThan(_lessThan) {
39442 var key = Object.keys(_lessThan)[0];
39443 var value = _lessThan[key];
39444 return function (tags) {
39445 return tags[key] < value;
39448 lessThanEqual: function lessThanEqual(_lessThanEqual) {
39449 var key = Object.keys(_lessThanEqual)[0];
39450 var value = _lessThanEqual[key];
39451 return function (tags) {
39452 return tags[key] <= value;
39455 positiveRegex: function positiveRegex(_positiveRegex) {
39456 var tagKey = Object.keys(_positiveRegex)[0];
39458 var expression = _positiveRegex[tagKey].join('|');
39460 var regex = new RegExp(expression);
39461 return function (tags) {
39462 return regex.test(tags[tagKey]);
39465 negativeRegex: function negativeRegex(_negativeRegex) {
39466 var tagKey = Object.keys(_negativeRegex)[0];
39468 var expression = _negativeRegex[tagKey].join('|');
39470 var regex = new RegExp(expression);
39471 return function (tags) {
39472 return !regex.test(tags[tagKey]);
39478 var buildLineKeys = function buildLineKeys() {
39494 var serviceMapRules = {
39495 init: function init() {
39496 this._ruleChecks = buildRuleChecks();
39497 this._validationRules = [];
39498 this._areaKeys = osmAreaKeys;
39499 this._lineKeys = buildLineKeys();
39501 // list of rules only relevant to tag checks...
39502 filterRuleChecks: function filterRuleChecks(selector) {
39503 var _ruleChecks = this._ruleChecks;
39504 return Object.keys(selector).reduce(function (rules, key) {
39505 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
39506 rules.push(_ruleChecks[key](selector[key]));
39512 // builds tagMap from mapcss-parse selector object...
39513 buildTagMap: function buildTagMap(selector) {
39514 var getRegexValues = function getRegexValues(regexes) {
39515 return regexes.map(function (regex) {
39516 return regex.replace(/\$|\^/g, '');
39520 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
39522 var isRegex = /regex/gi.test(key);
39523 var isEqual = /equals/gi.test(key);
39525 if (isRegex || isEqual) {
39526 Object.keys(selector[key]).forEach(function (selectorKey) {
39527 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
39529 if (expectedTags.hasOwnProperty(selectorKey)) {
39530 values = values.concat(expectedTags[selectorKey]);
39533 expectedTags[selectorKey] = values;
39535 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
39536 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
39537 values = [selector[key][tagKey]];
39539 if (expectedTags.hasOwnProperty(tagKey)) {
39540 values = values.concat(expectedTags[tagKey]);
39543 expectedTags[tagKey] = values;
39546 return expectedTags;
39550 // inspired by osmWay#isArea()
39551 inferGeometry: function inferGeometry(tagMap) {
39552 var _lineKeys = this._lineKeys;
39553 var _areaKeys = this._areaKeys;
39555 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
39556 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
39559 var keyValueImpliesLine = function keyValueImpliesLine(key) {
39560 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
39563 if (tagMap.hasOwnProperty('area')) {
39564 if (tagMap.area.indexOf('yes') > -1) {
39568 if (tagMap.area.indexOf('no') > -1) {
39573 for (var key in tagMap) {
39574 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
39578 if (key in _lineKeys && keyValueImpliesLine(key)) {
39585 // adds from mapcss-parse selector check...
39586 addRule: function addRule(selector) {
39588 // checks relevant to mapcss-selector
39589 checks: this.filterRuleChecks(selector),
39590 // true if all conditions for a tag error are true..
39591 matches: function matches(entity) {
39592 return this.checks.every(function (check) {
39593 return check(entity.tags);
39596 // borrowed from Way#isArea()
39597 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
39598 geometryMatches: function geometryMatches(entity, graph) {
39599 if (entity.type === 'node' || entity.type === 'relation') {
39600 return selector.geometry === entity.type;
39601 } else if (entity.type === 'way') {
39602 return this.inferredGeometry === entity.geometry(graph);
39605 // when geometries match and tag matches are present, return a warning...
39606 findIssues: function findIssues(entity, graph, issues) {
39607 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
39608 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
39609 var _message = selector[severity];
39610 issues.push(new validationIssue({
39612 severity: severity,
39613 message: function message() {
39616 entityIds: [entity.id]
39622 this._validationRules.push(rule);
39624 clearRules: function clearRules() {
39625 this._validationRules = [];
39627 // returns validationRules...
39628 validationRules: function validationRules() {
39629 return this._validationRules;
39631 // returns ruleChecks
39632 ruleChecks: function ruleChecks() {
39633 return this._ruleChecks;
39637 var apibase$1 = 'https://nominatim.openstreetmap.org/';
39638 var _inflight = {};
39640 var _nominatimCache;
39642 var serviceNominatim = {
39643 init: function init() {
39645 _nominatimCache = new RBush();
39647 reset: function reset() {
39648 Object.values(_inflight).forEach(function (controller) {
39649 controller.abort();
39652 _nominatimCache = new RBush();
39654 countryCode: function countryCode(location, callback) {
39655 this.reverse(location, function (err, result) {
39657 return callback(err);
39658 } else if (result.address) {
39659 return callback(null, result.address.country_code);
39661 return callback('Unable to geocode', null);
39665 reverse: function reverse(loc, callback) {
39666 var cached = _nominatimCache.search({
39673 if (cached.length > 0) {
39674 if (callback) callback(null, cached[0].data);
39685 var url = apibase$1 + 'reverse?' + utilQsString(params);
39686 if (_inflight[url]) return;
39687 var controller = new AbortController();
39688 _inflight[url] = controller;
39690 signal: controller.signal
39691 }).then(function (result) {
39692 delete _inflight[url];
39694 if (result && result.error) {
39695 throw new Error(result.error);
39698 var extent = geoExtent(loc).padByMeters(200);
39700 _nominatimCache.insert(Object.assign(extent.bbox(), {
39704 if (callback) callback(null, result);
39705 })["catch"](function (err) {
39706 delete _inflight[url];
39707 if (err.name === 'AbortError') return;
39708 if (callback) callback(err.message);
39711 search: function search(val, callback) {
39712 var searchVal = encodeURIComponent(val);
39713 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
39714 if (_inflight[url]) return;
39715 var controller = new AbortController();
39716 _inflight[url] = controller;
39718 signal: controller.signal
39719 }).then(function (result) {
39720 delete _inflight[url];
39722 if (result && result.error) {
39723 throw new Error(result.error);
39726 if (callback) callback(null, result);
39727 })["catch"](function (err) {
39728 delete _inflight[url];
39729 if (err.name === 'AbortError') return;
39730 if (callback) callback(err.message);
39735 var apibase$2 = 'https://openstreetcam.org';
39736 var maxResults$1 = 1000;
39737 var tileZoom$1 = 14;
39738 var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
39739 var dispatch$5 = dispatch('loadedImages');
39740 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
39744 var _oscSelectedImage;
39746 var _loadViewerPromise$1;
39748 function abortRequest$4(controller) {
39749 controller.abort();
39752 function maxPageAtZoom$1(z) {
39753 if (z < 15) return 2;
39754 if (z === 15) return 5;
39755 if (z === 16) return 10;
39756 if (z === 17) return 20;
39757 if (z === 18) return 40;
39758 if (z > 18) return 80;
39761 function loadTiles$1(which, url, projection) {
39762 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
39763 var tiles = tiler$4.getTiles(projection); // abort inflight requests that are no longer needed
39765 var cache = _oscCache[which];
39766 Object.keys(cache.inflight).forEach(function (k) {
39767 var wanted = tiles.find(function (tile) {
39768 return k.indexOf(tile.id + ',') === 0;
39772 abortRequest$4(cache.inflight[k]);
39773 delete cache.inflight[k];
39776 tiles.forEach(function (tile) {
39777 loadNextTilePage$1(which, currZoom, url, tile);
39781 function loadNextTilePage$1(which, currZoom, url, tile) {
39782 var cache = _oscCache[which];
39783 var bbox = tile.extent.bbox();
39784 var maxPages = maxPageAtZoom$1(currZoom);
39785 var nextPage = cache.nextPage[tile.id] || 1;
39786 var params = utilQsString({
39789 // client_id: clientId,
39790 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
39791 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
39793 if (nextPage > maxPages) return;
39794 var id = tile.id + ',' + String(nextPage);
39795 if (cache.loaded[id] || cache.inflight[id]) return;
39796 var controller = new AbortController();
39797 cache.inflight[id] = controller;
39800 signal: controller.signal,
39803 'Content-Type': 'application/x-www-form-urlencoded'
39806 d3_json(url, options).then(function (data) {
39807 cache.loaded[id] = true;
39808 delete cache.inflight[id];
39810 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
39811 throw new Error('No Data');
39814 var features = data.currentPageItems.map(function (item) {
39815 var loc = [+item.lng, +item.lat];
39818 if (which === 'images') {
39823 captured_at: item.shot_date || item.date_added,
39824 captured_by: item.username,
39825 imagePath: item.lth_name,
39826 sequence_id: item.sequence_id,
39827 sequence_index: +item.sequence_index
39828 }; // cache sequence info
39830 var seq = _oscCache.sequences[d.sequence_id];
39837 _oscCache.sequences[d.sequence_id] = seq;
39840 seq.images[d.sequence_index] = d;
39841 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
39852 cache.rtree.load(features);
39854 if (data.currentPageItems.length === maxResults$1) {
39855 // more pages to load
39856 cache.nextPage[tile.id] = nextPage + 1;
39857 loadNextTilePage$1(which, currZoom, url, tile);
39859 cache.nextPage[tile.id] = Infinity; // no more pages to load
39862 if (which === 'images') {
39863 dispatch$5.call('loadedImages');
39865 })["catch"](function () {
39866 cache.loaded[id] = true;
39867 delete cache.inflight[id];
39869 } // partition viewport into higher zoom tiles
39872 function partitionViewport$1(projection) {
39873 var z = geoScaleToZoom(projection.scale());
39874 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
39876 var tiler = utilTiler().zoomExtent([z2, z2]);
39877 return tiler.getTiles(projection).map(function (tile) {
39878 return tile.extent;
39880 } // no more than `limit` results per partition.
39883 function searchLimited$1(limit, projection, rtree) {
39884 limit = limit || 5;
39885 return partitionViewport$1(projection).reduce(function (result, extent) {
39886 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
39889 return found.length ? result.concat(found) : result;
39893 var serviceOpenstreetcam = {
39894 init: function init() {
39899 this.event = utilRebind(this, dispatch$5, 'on');
39901 reset: function reset() {
39903 Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
39911 rtree: new RBush(),
39916 _oscSelectedImage = null;
39918 images: function images(projection) {
39920 return searchLimited$1(limit, projection, _oscCache.images.rtree);
39922 sequences: function sequences(projection) {
39923 var viewport = projection.clipExtent();
39924 var min = [viewport[0][0], viewport[1][1]];
39925 var max = [viewport[1][0], viewport[0][1]];
39926 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
39927 var sequenceKeys = {}; // all sequences for images in viewport
39929 _oscCache.images.rtree.search(bbox).forEach(function (d) {
39930 sequenceKeys[d.data.sequence_id] = true;
39931 }); // make linestrings from those sequences
39934 var lineStrings = [];
39935 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
39936 var seq = _oscCache.sequences[sequenceKey];
39937 var images = seq && seq.images;
39941 type: 'LineString',
39942 coordinates: images.map(function (d) {
39944 }).filter(Boolean),
39946 captured_at: images[0] ? images[0].captured_at : null,
39947 captured_by: images[0] ? images[0].captured_by : null,
39953 return lineStrings;
39955 cachedImage: function cachedImage(imageKey) {
39956 return _oscCache.images.forImageKey[imageKey];
39958 loadImages: function loadImages(projection) {
39959 var url = apibase$2 + '/1.0/list/nearby-photos/';
39960 loadTiles$1('images', url, projection);
39962 ensureViewerLoaded: function ensureViewerLoaded(context) {
39963 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
39965 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
39967 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
39968 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
39969 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
39970 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
39971 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
39972 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
39973 controlsEnter.append('button').on('click.forward', step(1)).html('►');
39974 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
39976 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
39977 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
39980 function zoomPan(d3_event) {
39981 var t = d3_event.transform;
39982 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
39985 function rotate(deg) {
39986 return function () {
39987 if (!_oscSelectedImage) return;
39988 var sequenceKey = _oscSelectedImage.sequence_id;
39989 var sequence = _oscCache.sequences[sequenceKey];
39990 if (!sequence) return;
39991 var r = sequence.rotation || 0;
39993 if (r > 180) r -= 360;
39994 if (r < -180) r += 360;
39995 sequence.rotation = r;
39996 var wrap = context.container().select('.photoviewer .osc-wrapper');
39997 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39998 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
40002 function step(stepBy) {
40003 return function () {
40004 if (!_oscSelectedImage) return;
40005 var sequenceKey = _oscSelectedImage.sequence_id;
40006 var sequence = _oscCache.sequences[sequenceKey];
40007 if (!sequence) return;
40008 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
40009 var nextImage = sequence.images[nextIndex];
40010 if (!nextImage) return;
40011 context.map().centerEase(nextImage.loc);
40012 that.selectImage(context, nextImage.key);
40014 } // don't need any async loading so resolve immediately
40017 _loadViewerPromise$1 = Promise.resolve();
40018 return _loadViewerPromise$1;
40020 showViewer: function showViewer(context) {
40021 var viewer = context.container().select('.photoviewer').classed('hide', false);
40022 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
40025 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
40026 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
40031 hideViewer: function hideViewer(context) {
40032 _oscSelectedImage = null;
40033 this.updateUrlImage(null);
40034 var viewer = context.container().select('.photoviewer');
40035 if (!viewer.empty()) viewer.datum(null);
40036 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
40037 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
40038 return this.setStyles(context, null, true);
40040 selectImage: function selectImage(context, imageKey) {
40041 var d = this.cachedImage(imageKey);
40042 _oscSelectedImage = d;
40043 this.updateUrlImage(imageKey);
40044 var viewer = context.container().select('.photoviewer');
40045 if (!viewer.empty()) viewer.datum(d);
40046 this.setStyles(context, null, true);
40047 context.container().selectAll('.icon-sign').classed('currentView', false);
40048 if (!d) return this;
40049 var wrap = context.container().select('.photoviewer .osc-wrapper');
40050 var imageWrap = wrap.selectAll('.osc-image-wrap');
40051 var attribution = wrap.selectAll('.photo-attribution').html('');
40052 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
40053 imageWrap.selectAll('.osc-image').remove();
40056 var sequence = _oscCache.sequences[d.sequence_id];
40057 var r = sequence && sequence.rotation || 0;
40058 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$2 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
40060 if (d.captured_by) {
40061 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
40062 attribution.append('span').html('|');
40065 if (d.captured_at) {
40066 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
40067 attribution.append('span').html('|');
40070 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');
40075 function localeDateString(s) {
40076 if (!s) return null;
40082 var d = new Date(s);
40083 if (isNaN(d.getTime())) return null;
40084 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
40087 getSelectedImage: function getSelectedImage() {
40088 return _oscSelectedImage;
40090 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
40091 return d && d.sequence_id;
40093 // Updates the currently highlighted sequence and selected bubble.
40094 // Reset is only necessary when interacting with the viewport because
40095 // this implicitly changes the currently selected bubble/sequence
40096 setStyles: function setStyles(context, hovered, reset) {
40098 // reset all layers
40099 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
40100 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
40103 var hoveredImageKey = hovered && hovered.key;
40104 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
40105 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
40106 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
40109 var viewer = context.container().select('.photoviewer');
40110 var selected = viewer.empty() ? undefined : viewer.datum();
40111 var selectedImageKey = selected && selected.key;
40112 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
40113 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
40114 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
40116 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
40118 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
40119 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
40120 return highlightedImageKeys.indexOf(d.key) !== -1;
40121 }).classed('hovered', function (d) {
40122 return d.key === hoveredImageKey;
40123 }).classed('currentView', function (d) {
40124 return d.key === selectedImageKey;
40126 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
40127 return d.properties.key === hoveredSequenceKey;
40128 }).classed('currentView', function (d) {
40129 return d.properties.key === selectedSequenceKey;
40130 }); // update viewfields if needed
40132 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
40134 function viewfieldPath() {
40135 var d = this.parentNode.__data__;
40137 if (d.pano && d.key !== selectedImageKey) {
40138 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
40140 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
40146 updateUrlImage: function updateUrlImage(imageKey) {
40147 if (!window.mocha) {
40148 var hash = utilStringQs(window.location.hash);
40151 hash.photo = 'openstreetcam/' + imageKey;
40156 window.location.replace('#' + utilQsString(hash, true));
40159 cache: function cache() {
40164 var FORCED$f = fails(function () {
40165 return new Date(NaN).toJSON() !== null
40166 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
40169 // `Date.prototype.toJSON` method
40170 // https://tc39.github.io/ecma262/#sec-date.prototype.tojson
40171 _export({ target: 'Date', proto: true, forced: FORCED$f }, {
40172 // eslint-disable-next-line no-unused-vars
40173 toJSON: function toJSON(key) {
40174 var O = toObject(this);
40175 var pv = toPrimitive(O);
40176 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
40180 // `URL.prototype.toJSON` method
40181 // https://url.spec.whatwg.org/#dom-url-tojson
40182 _export({ target: 'URL', proto: true, enumerable: true }, {
40183 toJSON: function toJSON() {
40184 return URL.prototype.toString.call(this);
40189 * Checks if `value` is the
40190 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
40191 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
40197 * @param {*} value The value to check.
40198 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
40204 * _.isObject([1, 2, 3]);
40207 * _.isObject(_.noop);
40210 * _.isObject(null);
40213 function isObject$1(value) {
40214 var type = _typeof(value);
40216 return value != null && (type == 'object' || type == 'function');
40219 /** Detect free variable `global` from Node.js. */
40220 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
40222 /** Detect free variable `self`. */
40224 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
40225 /** Used as a reference to the global object. */
40227 var root$1 = freeGlobal || freeSelf || Function('return this')();
40230 * Gets the timestamp of the number of milliseconds that have elapsed since
40231 * the Unix epoch (1 January 1970 00:00:00 UTC).
40237 * @returns {number} Returns the timestamp.
40240 * _.defer(function(stamp) {
40241 * console.log(_.now() - stamp);
40243 * // => Logs the number of milliseconds it took for the deferred invocation.
40246 var now$1 = function now() {
40247 return root$1.Date.now();
40250 /** Built-in value references. */
40252 var _Symbol = root$1.Symbol;
40254 /** Used for built-in method references. */
40256 var objectProto = Object.prototype;
40257 /** Used to check objects for own properties. */
40259 var hasOwnProperty$1 = objectProto.hasOwnProperty;
40261 * Used to resolve the
40262 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40266 var nativeObjectToString = objectProto.toString;
40267 /** Built-in value references. */
40269 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
40271 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
40274 * @param {*} value The value to query.
40275 * @returns {string} Returns the raw `toStringTag`.
40278 function getRawTag(value) {
40279 var isOwn = hasOwnProperty$1.call(value, symToStringTag),
40280 tag = value[symToStringTag];
40283 value[symToStringTag] = undefined;
40284 var unmasked = true;
40287 var result = nativeObjectToString.call(value);
40291 value[symToStringTag] = tag;
40293 delete value[symToStringTag];
40300 /** Used for built-in method references. */
40301 var objectProto$1 = Object.prototype;
40303 * Used to resolve the
40304 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40308 var nativeObjectToString$1 = objectProto$1.toString;
40310 * Converts `value` to a string using `Object.prototype.toString`.
40313 * @param {*} value The value to convert.
40314 * @returns {string} Returns the converted string.
40317 function objectToString$1(value) {
40318 return nativeObjectToString$1.call(value);
40321 /** `Object#toString` result references. */
40323 var nullTag = '[object Null]',
40324 undefinedTag = '[object Undefined]';
40325 /** Built-in value references. */
40327 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
40329 * The base implementation of `getTag` without fallbacks for buggy environments.
40332 * @param {*} value The value to query.
40333 * @returns {string} Returns the `toStringTag`.
40336 function baseGetTag(value) {
40337 if (value == null) {
40338 return value === undefined ? undefinedTag : nullTag;
40341 return symToStringTag$1 && symToStringTag$1 in Object(value) ? getRawTag(value) : objectToString$1(value);
40345 * Checks if `value` is object-like. A value is object-like if it's not `null`
40346 * and has a `typeof` result of "object".
40352 * @param {*} value The value to check.
40353 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
40356 * _.isObjectLike({});
40359 * _.isObjectLike([1, 2, 3]);
40362 * _.isObjectLike(_.noop);
40365 * _.isObjectLike(null);
40368 function isObjectLike(value) {
40369 return value != null && _typeof(value) == 'object';
40372 /** `Object#toString` result references. */
40374 var symbolTag = '[object Symbol]';
40376 * Checks if `value` is classified as a `Symbol` primitive or object.
40382 * @param {*} value The value to check.
40383 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
40386 * _.isSymbol(Symbol.iterator);
40389 * _.isSymbol('abc');
40393 function isSymbol$1(value) {
40394 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
40397 /** Used as references for various `Number` constants. */
40400 /** Used to match leading and trailing whitespace. */
40402 var reTrim = /^\s+|\s+$/g;
40403 /** Used to detect bad signed hexadecimal string values. */
40405 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
40406 /** Used to detect binary string values. */
40408 var reIsBinary = /^0b[01]+$/i;
40409 /** Used to detect octal string values. */
40411 var reIsOctal = /^0o[0-7]+$/i;
40412 /** Built-in method references without a dependency on `root`. */
40414 var freeParseInt = parseInt;
40416 * Converts `value` to a number.
40422 * @param {*} value The value to process.
40423 * @returns {number} Returns the number.
40429 * _.toNumber(Number.MIN_VALUE);
40432 * _.toNumber(Infinity);
40435 * _.toNumber('3.2');
40439 function toNumber$1(value) {
40440 if (typeof value == 'number') {
40444 if (isSymbol$1(value)) {
40448 if (isObject$1(value)) {
40449 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
40450 value = isObject$1(other) ? other + '' : other;
40453 if (typeof value != 'string') {
40454 return value === 0 ? value : +value;
40457 value = value.replace(reTrim, '');
40458 var isBinary = reIsBinary.test(value);
40459 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
40462 /** Error message constants. */
40464 var FUNC_ERROR_TEXT = 'Expected a function';
40465 /* Built-in method references for those with the same name as other `lodash` methods. */
40467 var nativeMax = Math.max,
40468 nativeMin = Math.min;
40470 * Creates a debounced function that delays invoking `func` until after `wait`
40471 * milliseconds have elapsed since the last time the debounced function was
40472 * invoked. The debounced function comes with a `cancel` method to cancel
40473 * delayed `func` invocations and a `flush` method to immediately invoke them.
40474 * Provide `options` to indicate whether `func` should be invoked on the
40475 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
40476 * with the last arguments provided to the debounced function. Subsequent
40477 * calls to the debounced function return the result of the last `func`
40480 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40481 * invoked on the trailing edge of the timeout only if the debounced function
40482 * is invoked more than once during the `wait` timeout.
40484 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40485 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40487 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40488 * for details over the differences between `_.debounce` and `_.throttle`.
40493 * @category Function
40494 * @param {Function} func The function to debounce.
40495 * @param {number} [wait=0] The number of milliseconds to delay.
40496 * @param {Object} [options={}] The options object.
40497 * @param {boolean} [options.leading=false]
40498 * Specify invoking on the leading edge of the timeout.
40499 * @param {number} [options.maxWait]
40500 * The maximum time `func` is allowed to be delayed before it's invoked.
40501 * @param {boolean} [options.trailing=true]
40502 * Specify invoking on the trailing edge of the timeout.
40503 * @returns {Function} Returns the new debounced function.
40506 * // Avoid costly calculations while the window size is in flux.
40507 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
40509 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
40510 * jQuery(element).on('click', _.debounce(sendMail, 300, {
40512 * 'trailing': false
40515 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
40516 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
40517 * var source = new EventSource('/stream');
40518 * jQuery(source).on('message', debounced);
40520 * // Cancel the trailing debounced invocation.
40521 * jQuery(window).on('popstate', debounced.cancel);
40524 function debounce(func, wait, options) {
40531 lastInvokeTime = 0,
40536 if (typeof func != 'function') {
40537 throw new TypeError(FUNC_ERROR_TEXT);
40540 wait = toNumber$1(wait) || 0;
40542 if (isObject$1(options)) {
40543 leading = !!options.leading;
40544 maxing = 'maxWait' in options;
40545 maxWait = maxing ? nativeMax(toNumber$1(options.maxWait) || 0, wait) : maxWait;
40546 trailing = 'trailing' in options ? !!options.trailing : trailing;
40549 function invokeFunc(time) {
40550 var args = lastArgs,
40551 thisArg = lastThis;
40552 lastArgs = lastThis = undefined;
40553 lastInvokeTime = time;
40554 result = func.apply(thisArg, args);
40558 function leadingEdge(time) {
40559 // Reset any `maxWait` timer.
40560 lastInvokeTime = time; // Start the timer for the trailing edge.
40562 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
40564 return leading ? invokeFunc(time) : result;
40567 function remainingWait(time) {
40568 var timeSinceLastCall = time - lastCallTime,
40569 timeSinceLastInvoke = time - lastInvokeTime,
40570 timeWaiting = wait - timeSinceLastCall;
40571 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
40574 function shouldInvoke(time) {
40575 var timeSinceLastCall = time - lastCallTime,
40576 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
40577 // trailing edge, the system time has gone backwards and we're treating
40578 // it as the trailing edge, or we've hit the `maxWait` limit.
40580 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
40583 function timerExpired() {
40584 var time = now$1();
40586 if (shouldInvoke(time)) {
40587 return trailingEdge(time);
40588 } // Restart the timer.
40591 timerId = setTimeout(timerExpired, remainingWait(time));
40594 function trailingEdge(time) {
40595 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
40596 // debounced at least once.
40598 if (trailing && lastArgs) {
40599 return invokeFunc(time);
40602 lastArgs = lastThis = undefined;
40606 function cancel() {
40607 if (timerId !== undefined) {
40608 clearTimeout(timerId);
40611 lastInvokeTime = 0;
40612 lastArgs = lastCallTime = lastThis = timerId = undefined;
40616 return timerId === undefined ? result : trailingEdge(now$1());
40619 function debounced() {
40620 var time = now$1(),
40621 isInvoking = shouldInvoke(time);
40622 lastArgs = arguments;
40624 lastCallTime = time;
40627 if (timerId === undefined) {
40628 return leadingEdge(lastCallTime);
40632 // Handle invocations in a tight loop.
40633 clearTimeout(timerId);
40634 timerId = setTimeout(timerExpired, wait);
40635 return invokeFunc(lastCallTime);
40639 if (timerId === undefined) {
40640 timerId = setTimeout(timerExpired, wait);
40646 debounced.cancel = cancel;
40647 debounced.flush = flush;
40651 /** Error message constants. */
40653 var FUNC_ERROR_TEXT$1 = 'Expected a function';
40655 * Creates a throttled function that only invokes `func` at most once per
40656 * every `wait` milliseconds. The throttled function comes with a `cancel`
40657 * method to cancel delayed `func` invocations and a `flush` method to
40658 * immediately invoke them. Provide `options` to indicate whether `func`
40659 * should be invoked on the leading and/or trailing edge of the `wait`
40660 * timeout. The `func` is invoked with the last arguments provided to the
40661 * throttled function. Subsequent calls to the throttled function return the
40662 * result of the last `func` invocation.
40664 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40665 * invoked on the trailing edge of the timeout only if the throttled function
40666 * is invoked more than once during the `wait` timeout.
40668 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40669 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40671 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40672 * for details over the differences between `_.throttle` and `_.debounce`.
40677 * @category Function
40678 * @param {Function} func The function to throttle.
40679 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
40680 * @param {Object} [options={}] The options object.
40681 * @param {boolean} [options.leading=true]
40682 * Specify invoking on the leading edge of the timeout.
40683 * @param {boolean} [options.trailing=true]
40684 * Specify invoking on the trailing edge of the timeout.
40685 * @returns {Function} Returns the new throttled function.
40688 * // Avoid excessively updating the position while scrolling.
40689 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
40691 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
40692 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
40693 * jQuery(element).on('click', throttled);
40695 * // Cancel the trailing throttled invocation.
40696 * jQuery(window).on('popstate', throttled.cancel);
40699 function throttle(func, wait, options) {
40700 var leading = true,
40703 if (typeof func != 'function') {
40704 throw new TypeError(FUNC_ERROR_TEXT$1);
40707 if (isObject$1(options)) {
40708 leading = 'leading' in options ? !!options.leading : leading;
40709 trailing = 'trailing' in options ? !!options.trailing : trailing;
40712 return debounce(func, wait, {
40713 'leading': leading,
40715 'trailing': trailing
40719 var hashes = createCommonjsModule(function (module, exports) {
40721 * jshashes - https://github.com/h2non/jshashes
40722 * Released under the "New BSD" license
40724 * Algorithms specification:
40726 * MD5 - http://www.ietf.org/rfc/rfc1321.txt
40727 * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
40728 * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40729 * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40730 * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40731 * HMAC - http://www.ietf.org/rfc/rfc2104.txt
40736 function utf8Encode(str) {
40743 if (str && str.length) {
40746 while ((i += 1) < l) {
40747 /* Decode utf-16 surrogate pairs */
40748 x = str.charCodeAt(i);
40749 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
40751 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
40752 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
40755 /* Encode output as utf-8 */
40759 output += String.fromCharCode(x);
40760 } else if (x <= 0x7FF) {
40761 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
40762 } else if (x <= 0xFFFF) {
40763 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40764 } else if (x <= 0x1FFFFF) {
40765 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40773 function utf8Decode(str) {
40781 i = ac = c1 = c2 = c3 = 0;
40783 if (str && str.length) {
40788 c1 = str.charCodeAt(i);
40792 arr[ac] = String.fromCharCode(c1);
40794 } else if (c1 > 191 && c1 < 224) {
40795 c2 = str.charCodeAt(i + 1);
40796 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
40799 c2 = str.charCodeAt(i + 1);
40800 c3 = str.charCodeAt(i + 2);
40801 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
40807 return arr.join('');
40810 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
40811 * to work around bugs in some JS interpreters.
40815 function safe_add(x, y) {
40816 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
40817 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
40818 return msw << 16 | lsw & 0xFFFF;
40821 * Bitwise rotate a 32-bit number to the left.
40825 function bit_rol(num, cnt) {
40826 return num << cnt | num >>> 32 - cnt;
40829 * Convert a raw string to a hex string
40833 function rstr2hex(input, hexcase) {
40834 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
40840 for (; i < l; i += 1) {
40841 x = input.charCodeAt(i);
40842 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
40848 * Convert an array of big-endian words to a string
40852 function binb2rstr(input) {
40854 l = input.length * 32,
40857 for (i = 0; i < l; i += 8) {
40858 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
40864 * Convert an array of little-endian words to a string
40868 function binl2rstr(input) {
40870 l = input.length * 32,
40873 for (i = 0; i < l; i += 8) {
40874 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
40880 * Convert a raw string to an array of little-endian words
40881 * Characters >255 have their high-byte silently ignored.
40885 function rstr2binl(input) {
40887 l = input.length * 8,
40888 output = Array(input.length >> 2),
40889 lo = output.length;
40891 for (i = 0; i < lo; i += 1) {
40895 for (i = 0; i < l; i += 8) {
40896 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
40902 * Convert a raw string to an array of big-endian words
40903 * Characters >255 have their high-byte silently ignored.
40907 function rstr2binb(input) {
40909 l = input.length * 8,
40910 output = Array(input.length >> 2),
40911 lo = output.length;
40913 for (i = 0; i < lo; i += 1) {
40917 for (i = 0; i < l; i += 8) {
40918 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
40924 * Convert a raw string to an arbitrary string encoding
40928 function rstr2any(input, encoding) {
40929 var divisor = encoding.length,
40930 remainders = Array(),
40939 /* Convert to an array of 16-bit big-endian values, forming the dividend */
40941 dividend = Array(Math.ceil(input.length / 2));
40942 ld = dividend.length;
40944 for (i = 0; i < ld; i += 1) {
40945 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
40948 * Repeatedly perform a long division. The binary array forms the dividend,
40949 * the length of the encoding is the divisor. Once computed, the quotient
40950 * forms the dividend for the next step. We stop when the dividend is zerHashes.
40951 * All remainders are stored for later use.
40955 while (dividend.length > 0) {
40956 quotient = Array();
40959 for (i = 0; i < dividend.length; i += 1) {
40960 x = (x << 16) + dividend[i];
40961 q = Math.floor(x / divisor);
40964 if (quotient.length > 0 || q > 0) {
40965 quotient[quotient.length] = q;
40969 remainders[remainders.length] = x;
40970 dividend = quotient;
40972 /* Convert the remainders to the output string */
40977 for (i = remainders.length - 1; i >= 0; i--) {
40978 output += encoding.charAt(remainders[i]);
40980 /* Append leading zero equivalents */
40983 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
40985 for (i = output.length; i < full_length; i += 1) {
40986 output = encoding[0] + output;
40992 * Convert a raw string to a base-64 string
40996 function rstr2b64(input, b64pad) {
40997 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
40999 len = input.length,
41003 b64pad = b64pad || '=';
41005 for (i = 0; i < len; i += 3) {
41006 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
41008 for (j = 0; j < 4; j += 1) {
41009 if (i * 8 + j * 6 > input.length * 8) {
41012 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
41022 * @property {String} version
41032 Base64: function Base64() {
41033 // private properties
41034 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
41036 // URL encoding support @todo
41037 utf8 = true; // by default enable UTF-8 support encoding
41038 // public method for encoding
41040 this.encode = function (input) {
41045 len = input.length;
41047 input = utf8 ? utf8Encode(input) : input;
41049 for (i = 0; i < len; i += 3) {
41050 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
41052 for (j = 0; j < 4; j += 1) {
41053 if (i * 8 + j * 6 > len * 8) {
41056 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
41062 }; // public method for decoding
41065 this.decode = function (input) {
41066 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
41085 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
41089 // unpack four hexets into three octets using index points in b64
41090 h1 = tab.indexOf(input.charAt(i += 1));
41091 h2 = tab.indexOf(input.charAt(i += 1));
41092 h3 = tab.indexOf(input.charAt(i += 1));
41093 h4 = tab.indexOf(input.charAt(i += 1));
41094 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
41095 o1 = bits >> 16 & 0xff;
41096 o2 = bits >> 8 & 0xff;
41101 arr[ac] = String.fromCharCode(o1);
41102 } else if (h4 === 64) {
41103 arr[ac] = String.fromCharCode(o1, o2);
41105 arr[ac] = String.fromCharCode(o1, o2, o3);
41107 } while (i < input.length);
41109 dec = arr.join('');
41110 dec = utf8 ? utf8Decode(dec) : dec;
41112 }; // set custom pad string
41115 this.setPad = function (str) {
41118 }; // set custom tab string characters
41121 this.setTab = function (str) {
41126 this.setUTF8 = function (bool) {
41127 if (typeof bool === 'boolean') {
41136 * CRC-32 calculation
41140 * @param {String} str Input String
41143 CRC32: function CRC32(str) {
41150 str = utf8Encode(str);
41151 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('');
41154 for (i = 0, iTop = str.length; i < iTop; i += 1) {
41155 y = (crc ^ str.charCodeAt(i)) & 0xFF;
41156 x = '0x' + table.substr(y * 9, 8);
41157 crc = crc >>> 8 ^ x;
41158 } // always return a positive number (that's what >>> 0 does)
41161 return (crc ^ -1) >>> 0;
41168 * @param {Object} [config]
41170 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
41171 * Digest Algorithm, as defined in RFC 1321.
41172 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
41173 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41174 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
41176 MD5: function MD5(options) {
41178 * Private config properties. You may need to tweak these to be compatible with
41179 * the server-side, but the defaults work in most cases.
41180 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41182 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41183 // hexadecimal output case format. false - lowercase; true - uppercase
41184 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41185 // base-64 pad character. Defaults to '=' for strict RFC compliance
41186 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41187 // privileged (public) methods
41189 this.hex = function (s) {
41190 return rstr2hex(rstr(s), hexcase);
41193 this.b64 = function (s) {
41194 return rstr2b64(rstr(s), b64pad);
41197 this.any = function (s, e) {
41198 return rstr2any(rstr(s), e);
41201 this.raw = function (s) {
41205 this.hex_hmac = function (k, d) {
41206 return rstr2hex(rstr_hmac(k, d), hexcase);
41209 this.b64_hmac = function (k, d) {
41210 return rstr2b64(rstr_hmac(k, d), b64pad);
41213 this.any_hmac = function (k, d, e) {
41214 return rstr2any(rstr_hmac(k, d), e);
41217 * Perform a simple self-test to see if the VM is working
41218 * @return {String} Hexadecimal hash sample
41222 this.vm_test = function () {
41223 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41226 * Enable/disable uppercase hexadecimal returned string
41228 * @return {Object} this
41232 this.setUpperCase = function (a) {
41233 if (typeof a === 'boolean') {
41240 * Defines a base64 pad string
41241 * @param {String} Pad
41242 * @return {Object} this
41246 this.setPad = function (a) {
41247 b64pad = a || b64pad;
41251 * Defines a base64 pad string
41253 * @return {Object} [this]
41257 this.setUTF8 = function (a) {
41258 if (typeof a === 'boolean') {
41263 }; // private methods
41266 * Calculate the MD5 of a raw string
41271 s = utf8 ? utf8Encode(s) : s;
41272 return binl2rstr(binl(rstr2binl(s), s.length * 8));
41275 * Calculate the HMAC-MD5, of a key and some data (raw strings)
41279 function rstr_hmac(key, data) {
41280 var bkey, ipad, opad, hash, i;
41281 key = utf8 ? utf8Encode(key) : key;
41282 data = utf8 ? utf8Encode(data) : data;
41283 bkey = rstr2binl(key);
41285 if (bkey.length > 16) {
41286 bkey = binl(bkey, key.length * 8);
41289 ipad = Array(16), opad = Array(16);
41291 for (i = 0; i < 16; i += 1) {
41292 ipad[i] = bkey[i] ^ 0x36363636;
41293 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41296 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
41297 return binl2rstr(binl(opad.concat(hash), 512 + 128));
41300 * Calculate the MD5 of an array of little-endian words, and a bit length.
41304 function binl(x, len) {
41314 /* append padding */
41316 x[len >> 5] |= 0x80 << len % 32;
41317 x[(len + 64 >>> 9 << 4) + 14] = len;
41319 for (i = 0; i < x.length; i += 16) {
41324 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
41325 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
41326 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
41327 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
41328 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
41329 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
41330 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
41331 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
41332 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
41333 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
41334 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
41335 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
41336 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
41337 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
41338 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
41339 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
41340 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
41341 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
41342 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
41343 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
41344 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
41345 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
41346 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
41347 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
41348 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
41349 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
41350 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
41351 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
41352 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
41353 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
41354 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
41355 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
41356 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
41357 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
41358 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
41359 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
41360 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
41361 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
41362 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
41363 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
41364 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
41365 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
41366 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
41367 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
41368 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
41369 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
41370 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
41371 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
41372 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
41373 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
41374 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
41375 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
41376 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
41377 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
41378 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
41379 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
41380 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
41381 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
41382 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
41383 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
41384 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
41385 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
41386 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
41387 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
41388 a = safe_add(a, olda);
41389 b = safe_add(b, oldb);
41390 c = safe_add(c, oldc);
41391 d = safe_add(d, oldd);
41394 return Array(a, b, c, d);
41397 * These functions implement the four basic operations the algorithm uses.
41401 function md5_cmn(q, a, b, x, s, t) {
41402 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
41405 function md5_ff(a, b, c, d, x, s, t) {
41406 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
41409 function md5_gg(a, b, c, d, x, s, t) {
41410 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
41413 function md5_hh(a, b, c, d, x, s, t) {
41414 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
41417 function md5_ii(a, b, c, d, x, s, t) {
41418 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
41424 * @class Hashes.SHA1
41425 * @param {Object} [config]
41428 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
41429 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
41430 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41431 * See http://pajhome.org.uk/crypt/md5 for details.
41433 SHA1: function SHA1(options) {
41435 * Private config properties. You may need to tweak these to be compatible with
41436 * the server-side, but the defaults work in most cases.
41437 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41439 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41440 // hexadecimal output case format. false - lowercase; true - uppercase
41441 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41442 // base-64 pad character. Defaults to '=' for strict RFC compliance
41443 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41446 this.hex = function (s) {
41447 return rstr2hex(rstr(s), hexcase);
41450 this.b64 = function (s) {
41451 return rstr2b64(rstr(s), b64pad);
41454 this.any = function (s, e) {
41455 return rstr2any(rstr(s), e);
41458 this.raw = function (s) {
41462 this.hex_hmac = function (k, d) {
41463 return rstr2hex(rstr_hmac(k, d));
41466 this.b64_hmac = function (k, d) {
41467 return rstr2b64(rstr_hmac(k, d), b64pad);
41470 this.any_hmac = function (k, d, e) {
41471 return rstr2any(rstr_hmac(k, d), e);
41474 * Perform a simple self-test to see if the VM is working
41475 * @return {String} Hexadecimal hash sample
41480 this.vm_test = function () {
41481 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41484 * @description Enable/disable uppercase hexadecimal returned string
41486 * @return {Object} this
41491 this.setUpperCase = function (a) {
41492 if (typeof a === 'boolean') {
41499 * @description Defines a base64 pad string
41500 * @param {string} Pad
41501 * @return {Object} this
41506 this.setPad = function (a) {
41507 b64pad = a || b64pad;
41511 * @description Defines a base64 pad string
41513 * @return {Object} this
41518 this.setUTF8 = function (a) {
41519 if (typeof a === 'boolean') {
41524 }; // private methods
41527 * Calculate the SHA-512 of a raw string
41532 s = utf8 ? utf8Encode(s) : s;
41533 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41536 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
41540 function rstr_hmac(key, data) {
41541 var bkey, ipad, opad, i, hash;
41542 key = utf8 ? utf8Encode(key) : key;
41543 data = utf8 ? utf8Encode(data) : data;
41544 bkey = rstr2binb(key);
41546 if (bkey.length > 16) {
41547 bkey = binb(bkey, key.length * 8);
41550 ipad = Array(16), opad = Array(16);
41552 for (i = 0; i < 16; i += 1) {
41553 ipad[i] = bkey[i] ^ 0x36363636;
41554 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41557 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41558 return binb2rstr(binb(opad.concat(hash), 512 + 160));
41561 * Calculate the SHA-1 of an array of big-endian words, and a bit length
41565 function binb(x, len) {
41580 /* append padding */
41582 x[len >> 5] |= 0x80 << 24 - len % 32;
41583 x[(len + 64 >> 9 << 4) + 15] = len;
41585 for (i = 0; i < x.length; i += 16) {
41592 for (j = 0; j < 80; j += 1) {
41596 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
41599 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)));
41602 c = bit_rol(b, 30);
41607 a = safe_add(a, olda);
41608 b = safe_add(b, oldb);
41609 c = safe_add(c, oldc);
41610 d = safe_add(d, oldd);
41611 e = safe_add(e, olde);
41614 return Array(a, b, c, d, e);
41617 * Perform the appropriate triplet combination function for the current
41622 function sha1_ft(t, b, c, d) {
41624 return b & c | ~b & d;
41632 return b & c | b & d | c & d;
41638 * Determine the appropriate additive constant for the current iteration
41642 function sha1_kt(t) {
41643 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
41648 * @class Hashes.SHA256
41651 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
41652 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
41653 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41654 * See http://pajhome.org.uk/crypt/md5 for details.
41655 * Also http://anmar.eu.org/projects/jssha2/
41657 SHA256: function SHA256(options) {
41659 * Private properties configuration variables. You may need to tweak these to be compatible with
41660 * the server-side, but the defaults work in most cases.
41661 * @see this.setUpperCase() method
41662 * @see this.setPad() method
41664 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41665 // hexadecimal output case format. false - lowercase; true - uppercase */
41666 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41668 /* base-64 pad character. Default '=' for strict RFC compliance */
41669 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41671 /* enable/disable utf8 encoding */
41673 /* privileged (public) methods */
41675 this.hex = function (s) {
41676 return rstr2hex(rstr(s, utf8));
41679 this.b64 = function (s) {
41680 return rstr2b64(rstr(s, utf8), b64pad);
41683 this.any = function (s, e) {
41684 return rstr2any(rstr(s, utf8), e);
41687 this.raw = function (s) {
41688 return rstr(s, utf8);
41691 this.hex_hmac = function (k, d) {
41692 return rstr2hex(rstr_hmac(k, d));
41695 this.b64_hmac = function (k, d) {
41696 return rstr2b64(rstr_hmac(k, d), b64pad);
41699 this.any_hmac = function (k, d, e) {
41700 return rstr2any(rstr_hmac(k, d), e);
41703 * Perform a simple self-test to see if the VM is working
41704 * @return {String} Hexadecimal hash sample
41709 this.vm_test = function () {
41710 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41713 * Enable/disable uppercase hexadecimal returned string
41715 * @return {Object} this
41720 this.setUpperCase = function (a) {
41721 if (typeof a === 'boolean') {
41728 * @description Defines a base64 pad string
41729 * @param {string} Pad
41730 * @return {Object} this
41735 this.setPad = function (a) {
41736 b64pad = a || b64pad;
41740 * Defines a base64 pad string
41742 * @return {Object} this
41747 this.setUTF8 = function (a) {
41748 if (typeof a === 'boolean') {
41753 }; // private methods
41756 * Calculate the SHA-512 of a raw string
41760 function rstr(s, utf8) {
41761 s = utf8 ? utf8Encode(s) : s;
41762 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41765 * Calculate the HMAC-sha256 of a key and some data (raw strings)
41769 function rstr_hmac(key, data) {
41770 key = utf8 ? utf8Encode(key) : key;
41771 data = utf8 ? utf8Encode(data) : data;
41774 bkey = rstr2binb(key),
41778 if (bkey.length > 16) {
41779 bkey = binb(bkey, key.length * 8);
41782 for (; i < 16; i += 1) {
41783 ipad[i] = bkey[i] ^ 0x36363636;
41784 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41787 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41788 return binb2rstr(binb(opad.concat(hash), 512 + 256));
41791 * Main sha256 function, with its support functions
41795 function sha256_S(X, n) {
41796 return X >>> n | X << 32 - n;
41799 function sha256_R(X, n) {
41803 function sha256_Ch(x, y, z) {
41804 return x & y ^ ~x & z;
41807 function sha256_Maj(x, y, z) {
41808 return x & y ^ x & z ^ y & z;
41811 function sha256_Sigma0256(x) {
41812 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
41815 function sha256_Sigma1256(x) {
41816 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
41819 function sha256_Gamma0256(x) {
41820 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
41823 function sha256_Gamma1256(x) {
41824 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
41827 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];
41829 function binb(m, l) {
41830 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
41831 var W = new Array(64);
41832 var a, b, c, d, e, f, g, h;
41834 /* append padding */
41836 m[l >> 5] |= 0x80 << 24 - l % 32;
41837 m[(l + 64 >> 9 << 4) + 15] = l;
41839 for (i = 0; i < m.length; i += 16) {
41849 for (j = 0; j < 64; j += 1) {
41853 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
41856 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
41857 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
41861 e = safe_add(d, T1);
41865 a = safe_add(T1, T2);
41868 HASH[0] = safe_add(a, HASH[0]);
41869 HASH[1] = safe_add(b, HASH[1]);
41870 HASH[2] = safe_add(c, HASH[2]);
41871 HASH[3] = safe_add(d, HASH[3]);
41872 HASH[4] = safe_add(e, HASH[4]);
41873 HASH[5] = safe_add(f, HASH[5]);
41874 HASH[6] = safe_add(g, HASH[6]);
41875 HASH[7] = safe_add(h, HASH[7]);
41883 * @class Hashes.SHA512
41886 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
41887 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
41888 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41889 * See http://pajhome.org.uk/crypt/md5 for details.
41891 SHA512: function SHA512(options) {
41893 * Private properties configuration variables. You may need to tweak these to be compatible with
41894 * the server-side, but the defaults work in most cases.
41895 * @see this.setUpperCase() method
41896 * @see this.setPad() method
41898 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41900 /* hexadecimal output case format. false - lowercase; true - uppercase */
41901 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41903 /* base-64 pad character. Default '=' for strict RFC compliance */
41904 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41906 /* enable/disable utf8 encoding */
41908 /* privileged (public) methods */
41910 this.hex = function (s) {
41911 return rstr2hex(rstr(s));
41914 this.b64 = function (s) {
41915 return rstr2b64(rstr(s), b64pad);
41918 this.any = function (s, e) {
41919 return rstr2any(rstr(s), e);
41922 this.raw = function (s) {
41926 this.hex_hmac = function (k, d) {
41927 return rstr2hex(rstr_hmac(k, d));
41930 this.b64_hmac = function (k, d) {
41931 return rstr2b64(rstr_hmac(k, d), b64pad);
41934 this.any_hmac = function (k, d, e) {
41935 return rstr2any(rstr_hmac(k, d), e);
41938 * Perform a simple self-test to see if the VM is working
41939 * @return {String} Hexadecimal hash sample
41944 this.vm_test = function () {
41945 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41948 * @description Enable/disable uppercase hexadecimal returned string
41950 * @return {Object} this
41955 this.setUpperCase = function (a) {
41956 if (typeof a === 'boolean') {
41963 * @description Defines a base64 pad string
41964 * @param {string} Pad
41965 * @return {Object} this
41970 this.setPad = function (a) {
41971 b64pad = a || b64pad;
41975 * @description Defines a base64 pad string
41977 * @return {Object} this
41982 this.setUTF8 = function (a) {
41983 if (typeof a === 'boolean') {
41989 /* private methods */
41992 * Calculate the SHA-512 of a raw string
41997 s = utf8 ? utf8Encode(s) : s;
41998 return binb2rstr(binb(rstr2binb(s), s.length * 8));
42001 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
42005 function rstr_hmac(key, data) {
42006 key = utf8 ? utf8Encode(key) : key;
42007 data = utf8 ? utf8Encode(data) : data;
42010 bkey = rstr2binb(key),
42014 if (bkey.length > 32) {
42015 bkey = binb(bkey, key.length * 8);
42018 for (; i < 32; i += 1) {
42019 ipad[i] = bkey[i] ^ 0x36363636;
42020 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42023 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
42024 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
42027 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
42031 function binb(x, len) {
42036 hash = new Array(16),
42037 //Initial hash values
42038 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)],
42039 T1 = new int64(0, 0),
42040 T2 = new int64(0, 0),
42041 a = new int64(0, 0),
42042 b = new int64(0, 0),
42043 c = new int64(0, 0),
42044 d = new int64(0, 0),
42045 e = new int64(0, 0),
42046 f = new int64(0, 0),
42047 g = new int64(0, 0),
42048 h = new int64(0, 0),
42049 //Temporary variables not specified by the document
42050 s0 = new int64(0, 0),
42051 s1 = new int64(0, 0),
42052 Ch = new int64(0, 0),
42053 Maj = new int64(0, 0),
42054 r1 = new int64(0, 0),
42055 r2 = new int64(0, 0),
42056 r3 = new int64(0, 0);
42058 if (sha512_k === undefined) {
42060 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)];
42063 for (i = 0; i < 80; i += 1) {
42064 W[i] = new int64(0, 0);
42065 } // append padding to the source string. The format is described in the FIPS.
42068 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
42069 x[(len + 128 >> 10 << 5) + 31] = len;
42072 for (i = 0; i < l; i += 32) {
42073 //32 dwords is the block size
42074 int64copy(a, H[0]);
42075 int64copy(b, H[1]);
42076 int64copy(c, H[2]);
42077 int64copy(d, H[3]);
42078 int64copy(e, H[4]);
42079 int64copy(f, H[5]);
42080 int64copy(g, H[6]);
42081 int64copy(h, H[7]);
42083 for (j = 0; j < 16; j += 1) {
42084 W[j].h = x[i + 2 * j];
42085 W[j].l = x[i + 2 * j + 1];
42088 for (j = 16; j < 80; j += 1) {
42090 int64rrot(r1, W[j - 2], 19);
42091 int64revrrot(r2, W[j - 2], 29);
42092 int64shr(r3, W[j - 2], 6);
42093 s1.l = r1.l ^ r2.l ^ r3.l;
42094 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
42096 int64rrot(r1, W[j - 15], 1);
42097 int64rrot(r2, W[j - 15], 8);
42098 int64shr(r3, W[j - 15], 7);
42099 s0.l = r1.l ^ r2.l ^ r3.l;
42100 s0.h = r1.h ^ r2.h ^ r3.h;
42101 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
42104 for (j = 0; j < 80; j += 1) {
42106 Ch.l = e.l & f.l ^ ~e.l & g.l;
42107 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
42109 int64rrot(r1, e, 14);
42110 int64rrot(r2, e, 18);
42111 int64revrrot(r3, e, 9);
42112 s1.l = r1.l ^ r2.l ^ r3.l;
42113 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
42115 int64rrot(r1, a, 28);
42116 int64revrrot(r2, a, 2);
42117 int64revrrot(r3, a, 7);
42118 s0.l = r1.l ^ r2.l ^ r3.l;
42119 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
42121 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
42122 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
42123 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
42124 int64add(T2, s0, Maj);
42128 int64add(e, d, T1);
42132 int64add(a, T1, T2);
42135 int64add(H[0], H[0], a);
42136 int64add(H[1], H[1], b);
42137 int64add(H[2], H[2], c);
42138 int64add(H[3], H[3], d);
42139 int64add(H[4], H[4], e);
42140 int64add(H[5], H[5], f);
42141 int64add(H[6], H[6], g);
42142 int64add(H[7], H[7], h);
42143 } //represent the hash as an array of 32-bit dwords
42146 for (i = 0; i < 8; i += 1) {
42147 hash[2 * i] = H[i].h;
42148 hash[2 * i + 1] = H[i].l;
42152 } //A constructor for 64-bit numbers
42155 function int64(h, l) {
42157 this.l = l; //this.toString = int64toString;
42158 } //Copies src into dst, assuming both are 64-bit numbers
42161 function int64copy(dst, src) {
42164 } //Right-rotates a 64-bit number by shift
42165 //Won't handle cases of shift>=32
42166 //The function revrrot() is for that
42169 function int64rrot(dst, x, shift) {
42170 dst.l = x.l >>> shift | x.h << 32 - shift;
42171 dst.h = x.h >>> shift | x.l << 32 - shift;
42172 } //Reverses the dwords of the source and then rotates right by shift.
42173 //This is equivalent to rotation by 32+shift
42176 function int64revrrot(dst, x, shift) {
42177 dst.l = x.h >>> shift | x.l << 32 - shift;
42178 dst.h = x.l >>> shift | x.h << 32 - shift;
42179 } //Bitwise-shifts right a 64-bit number by shift
42180 //Won't handle shift>=32, but it's never needed in SHA512
42183 function int64shr(dst, x, shift) {
42184 dst.l = x.l >>> shift | x.h << 32 - shift;
42185 dst.h = x.h >>> shift;
42186 } //Adds two 64-bit numbers
42187 //Like the original implementation, does not rely on 32-bit operations
42190 function int64add(dst, x, y) {
42191 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
42192 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
42193 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
42194 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
42195 dst.l = w0 & 0xffff | w1 << 16;
42196 dst.h = w2 & 0xffff | w3 << 16;
42197 } //Same, except with 4 addends. Works faster than adding them one by one.
42200 function int64add4(dst, a, b, c, d) {
42201 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
42202 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
42203 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
42204 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
42205 dst.l = w0 & 0xffff | w1 << 16;
42206 dst.h = w2 & 0xffff | w3 << 16;
42207 } //Same, except with 5 addends
42210 function int64add5(dst, a, b, c, d, e) {
42211 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
42212 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
42213 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
42214 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
42215 dst.l = w0 & 0xffff | w1 << 16;
42216 dst.h = w2 & 0xffff | w3 << 16;
42221 * @class Hashes.RMD160
42223 * @param {Object} [config]
42225 * A JavaScript implementation of the RIPEMD-160 Algorithm
42226 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
42227 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
42228 * See http://pajhome.org.uk/crypt/md5 for details.
42229 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
42231 RMD160: function RMD160(options) {
42233 * Private properties configuration variables. You may need to tweak these to be compatible with
42234 * the server-side, but the defaults work in most cases.
42235 * @see this.setUpperCase() method
42236 * @see this.setPad() method
42238 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
42240 /* hexadecimal output case format. false - lowercase; true - uppercase */
42241 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
42243 /* base-64 pad character. Default '=' for strict RFC compliance */
42244 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
42246 /* enable/disable utf8 encoding */
42247 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],
42248 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],
42249 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],
42250 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];
42251 /* privileged (public) methods */
42253 this.hex = function (s) {
42254 return rstr2hex(rstr(s));
42257 this.b64 = function (s) {
42258 return rstr2b64(rstr(s), b64pad);
42261 this.any = function (s, e) {
42262 return rstr2any(rstr(s), e);
42265 this.raw = function (s) {
42269 this.hex_hmac = function (k, d) {
42270 return rstr2hex(rstr_hmac(k, d));
42273 this.b64_hmac = function (k, d) {
42274 return rstr2b64(rstr_hmac(k, d), b64pad);
42277 this.any_hmac = function (k, d, e) {
42278 return rstr2any(rstr_hmac(k, d), e);
42281 * Perform a simple self-test to see if the VM is working
42282 * @return {String} Hexadecimal hash sample
42287 this.vm_test = function () {
42288 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
42291 * @description Enable/disable uppercase hexadecimal returned string
42293 * @return {Object} this
42298 this.setUpperCase = function (a) {
42299 if (typeof a === 'boolean') {
42306 * @description Defines a base64 pad string
42307 * @param {string} Pad
42308 * @return {Object} this
42313 this.setPad = function (a) {
42314 if (typeof a !== 'undefined') {
42321 * @description Defines a base64 pad string
42323 * @return {Object} this
42328 this.setUTF8 = function (a) {
42329 if (typeof a === 'boolean') {
42335 /* private methods */
42338 * Calculate the rmd160 of a raw string
42343 s = utf8 ? utf8Encode(s) : s;
42344 return binl2rstr(binl(rstr2binl(s), s.length * 8));
42347 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
42351 function rstr_hmac(key, data) {
42352 key = utf8 ? utf8Encode(key) : key;
42353 data = utf8 ? utf8Encode(data) : data;
42356 bkey = rstr2binl(key),
42360 if (bkey.length > 16) {
42361 bkey = binl(bkey, key.length * 8);
42364 for (i = 0; i < 16; i += 1) {
42365 ipad[i] = bkey[i] ^ 0x36363636;
42366 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42369 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
42370 return binl2rstr(binl(opad.concat(hash), 512 + 160));
42373 * Convert an array of little-endian words to a string
42377 function binl2rstr(input) {
42380 l = input.length * 32;
42382 for (i = 0; i < l; i += 8) {
42383 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
42389 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
42393 function binl(x, len) {
42413 /* append padding */
42415 x[len >> 5] |= 0x80 << len % 32;
42416 x[(len + 64 >>> 9 << 4) + 14] = len;
42419 for (i = 0; i < l; i += 16) {
42426 for (j = 0; j <= 79; j += 1) {
42427 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
42428 T = safe_add(T, x[i + rmd160_r1[j]]);
42429 T = safe_add(T, rmd160_K1(j));
42430 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
42433 D1 = bit_rol(C1, 10);
42436 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
42437 T = safe_add(T, x[i + rmd160_r2[j]]);
42438 T = safe_add(T, rmd160_K2(j));
42439 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
42442 D2 = bit_rol(C2, 10);
42447 T = safe_add(h1, safe_add(C1, D2));
42448 h1 = safe_add(h2, safe_add(D1, E2));
42449 h2 = safe_add(h3, safe_add(E1, A2));
42450 h3 = safe_add(h4, safe_add(A1, B2));
42451 h4 = safe_add(h0, safe_add(B1, C2));
42455 return [h0, h1, h2, h3, h4];
42456 } // specific algorithm methods
42459 function rmd160_f(j, x, y, z) {
42460 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';
42463 function rmd160_K1(j) {
42464 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';
42467 function rmd160_K2(j) {
42468 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';
42471 }; // exposes Hashes
42473 (function (window, undefined$1) {
42474 var freeExports = false;
42477 freeExports = exports;
42479 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
42480 window = commonjsGlobal;
42484 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
42485 // define as an anonymous module, so, through path mapping, it can be aliased
42486 undefined$1(function () {
42489 } else if (freeExports) {
42490 // in Node.js or RingoJS v0.8.0+
42491 if ( module && module.exports === freeExports) {
42492 module.exports = Hashes;
42493 } // in Narwhal or RingoJS v0.7.0-
42495 freeExports.Hashes = Hashes;
42498 // in a browser or Rhino
42499 window.Hashes = Hashes;
42506 var immutable = extend$2;
42507 var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
42509 function extend$2() {
42512 for (var i = 0; i < arguments.length; i++) {
42513 var source = arguments[i];
42515 for (var key in source) {
42516 if (hasOwnProperty$2.call(source, key)) {
42517 target[key] = source[key];
42525 var sha1 = new hashes.SHA1();
42528 ohauth.qsString = function (obj) {
42529 return Object.keys(obj).sort().map(function (key) {
42530 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
42534 ohauth.stringQs = function (str) {
42535 return str.split('&').filter(function (pair) {
42536 return pair !== '';
42537 }).reduce(function (obj, pair) {
42538 var parts = pair.split('=');
42539 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
42544 ohauth.rawxhr = function (method, url, data, headers, callback) {
42545 var xhr = new XMLHttpRequest(),
42546 twoHundred = /^20\d$/;
42548 xhr.onreadystatechange = function () {
42549 if (4 === xhr.readyState && 0 !== xhr.status) {
42550 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
42554 xhr.onerror = function (e) {
42555 return callback(e, null);
42558 xhr.open(method, url, true);
42560 for (var h in headers) {
42561 xhr.setRequestHeader(h, headers[h]);
42568 ohauth.xhr = function (method, url, auth, data, options, callback) {
42569 var headers = options && options.header || {
42570 'Content-Type': 'application/x-www-form-urlencoded'
42572 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
42573 return ohauth.rawxhr(method, url, data, headers, callback);
42576 ohauth.nonce = function () {
42577 for (var o = ''; o.length < 6;) {
42578 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
42584 ohauth.authHeader = function (obj) {
42585 return Object.keys(obj).sort().map(function (key) {
42586 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
42590 ohauth.timestamp = function () {
42591 return ~~(+new Date() / 1000);
42594 ohauth.percentEncode = function (s) {
42595 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
42598 ohauth.baseString = function (method, url, params) {
42599 if (params.oauth_signature) delete params.oauth_signature;
42600 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
42603 ohauth.signature = function (oauth_secret, token_secret, baseString) {
42604 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
42607 * Takes an options object for configuration (consumer_key,
42608 * consumer_secret, version, signature_method, token, token_secret)
42609 * and returns a function that generates the Authorization header
42612 * The returned function takes these parameters:
42613 * - method: GET/POST/...
42614 * - uri: full URI with protocol, port, path and query string
42615 * - extra_params: any extra parameters (that are passed in the POST data),
42616 * can be an object or a from-urlencoded string.
42618 * Returned function returns full OAuth header with "OAuth" string in it.
42622 ohauth.headerGenerator = function (options) {
42623 options = options || {};
42624 var consumer_key = options.consumer_key || '',
42625 consumer_secret = options.consumer_secret || '',
42626 signature_method = options.signature_method || 'HMAC-SHA1',
42627 version = options.version || '1.0',
42628 token = options.token || '',
42629 token_secret = options.token_secret || '';
42630 return function (method, uri, extra_params) {
42631 method = method.toUpperCase();
42633 if (typeof extra_params === 'string' && extra_params.length > 0) {
42634 extra_params = ohauth.stringQs(extra_params);
42637 var uri_parts = uri.split('?', 2),
42638 base_uri = uri_parts[0];
42639 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
42640 var oauth_params = {
42641 oauth_consumer_key: consumer_key,
42642 oauth_signature_method: signature_method,
42643 oauth_version: version,
42644 oauth_timestamp: ohauth.timestamp(),
42645 oauth_nonce: ohauth.nonce()
42647 if (token) oauth_params.oauth_token = token;
42648 var all_params = immutable({}, oauth_params, query_params, extra_params),
42649 base_str = ohauth.baseString(method, base_uri, all_params);
42650 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
42651 return 'OAuth ' + ohauth.authHeader(oauth_params);
42655 var ohauth_1 = ohauth;
42657 var resolveUrl$1 = createCommonjsModule(function (module, exports) {
42658 // Copyright 2014 Simon Lydell
42659 // X11 (“MIT”) Licensed. (See LICENSE.)
42660 void function (root, factory) {
42662 module.exports = factory();
42664 }(commonjsGlobal, function () {
42665 function resolveUrl()
42668 var numUrls = arguments.length;
42670 if (numUrls === 0) {
42671 throw new Error("resolveUrl requires at least one argument; got none.");
42674 var base = document.createElement("base");
42675 base.href = arguments[0];
42677 if (numUrls === 1) {
42681 var head = document.getElementsByTagName("head")[0];
42682 head.insertBefore(base, head.firstChild);
42683 var a = document.createElement("a");
42686 for (var index = 1; index < numUrls; index++) {
42687 a.href = arguments[index];
42689 base.href = resolved;
42692 head.removeChild(base);
42700 var assign = make_assign();
42701 var create$1 = make_create();
42702 var trim$3 = make_trim();
42703 var Global = typeof window !== 'undefined' ? window : commonjsGlobal;
42714 isFunction: isFunction,
42715 isObject: isObject$2,
42719 function make_assign() {
42720 if (Object.assign) {
42721 return Object.assign;
42723 return function shimAssign(obj, props1, props2, etc) {
42724 for (var i = 1; i < arguments.length; i++) {
42725 each(Object(arguments[i]), function (val, key) {
42735 function make_create() {
42736 if (Object.create) {
42737 return function create(obj, assignProps1, assignProps2, etc) {
42738 var assignArgsList = slice$2(arguments, 1);
42739 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
42742 var F = function F() {}; // eslint-disable-line no-inner-declarations
42745 return function create(obj, assignProps1, assignProps2, etc) {
42746 var assignArgsList = slice$2(arguments, 1);
42748 return assign.apply(this, [new F()].concat(assignArgsList));
42753 function make_trim() {
42754 if (String.prototype.trim) {
42755 return function trim(str) {
42756 return String.prototype.trim.call(str);
42759 return function trim(str) {
42760 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
42765 function bind$1(obj, fn) {
42766 return function () {
42767 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
42771 function slice$2(arr, index) {
42772 return Array.prototype.slice.call(arr, index || 0);
42775 function each(obj, fn) {
42776 pluck(obj, function (val, key) {
42782 function map$1(obj, fn) {
42783 var res = isList(obj) ? [] : {};
42784 pluck(obj, function (v, k) {
42791 function pluck(obj, fn) {
42793 for (var i = 0; i < obj.length; i++) {
42794 if (fn(obj[i], i)) {
42799 for (var key in obj) {
42800 if (obj.hasOwnProperty(key)) {
42801 if (fn(obj[key], key)) {
42809 function isList(val) {
42810 return val != null && typeof val != 'function' && typeof val.length == 'number';
42813 function isFunction(val) {
42814 return val && {}.toString.call(val) === '[object Function]';
42817 function isObject$2(val) {
42818 return val && {}.toString.call(val) === '[object Object]';
42821 var slice$3 = util.slice;
42822 var pluck$1 = util.pluck;
42823 var each$1 = util.each;
42824 var bind$2 = util.bind;
42825 var create$2 = util.create;
42826 var isList$1 = util.isList;
42827 var isFunction$1 = util.isFunction;
42828 var isObject$3 = util.isObject;
42829 var storeEngine = {
42830 createStore: _createStore
42835 // get returns the value of the given key. If that value
42836 // is undefined, it returns optionalDefaultValue instead.
42837 get: function get(key, optionalDefaultValue) {
42838 var data = this.storage.read(this._namespacePrefix + key);
42839 return this._deserialize(data, optionalDefaultValue);
42841 // set will store the given value at key and returns value.
42842 // Calling set with value === undefined is equivalent to calling remove.
42843 set: function set(key, value) {
42844 if (value === undefined) {
42845 return this.remove(key);
42848 this.storage.write(this._namespacePrefix + key, this._serialize(value));
42851 // remove deletes the key and value stored at the given key.
42852 remove: function remove(key) {
42853 this.storage.remove(this._namespacePrefix + key);
42855 // each will call the given callback once for each key-value pair
42857 each: function each(callback) {
42859 this.storage.each(function (val, namespacedKey) {
42860 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
42863 // clearAll will remove all the stored key-value pairs in this store.
42864 clearAll: function clearAll() {
42865 this.storage.clearAll();
42867 // additional functionality that can't live in plugins
42868 // ---------------------------------------------------
42869 // hasNamespace returns true if this store instance has the given namespace.
42870 hasNamespace: function hasNamespace(namespace) {
42871 return this._namespacePrefix == '__storejs_' + namespace + '_';
42873 // createStore creates a store.js instance with the first
42874 // functioning storage in the list of storage candidates,
42875 // and applies the the given mixins to the instance.
42876 createStore: function createStore() {
42877 return _createStore.apply(this, arguments);
42879 addPlugin: function addPlugin(plugin) {
42880 this._addPlugin(plugin);
42882 namespace: function namespace(_namespace) {
42883 return _createStore(this.storage, this.plugins, _namespace);
42888 var _console = typeof console == 'undefined' ? null : console;
42894 var fn = _console.warn ? _console.warn : _console.log;
42895 fn.apply(_console, arguments);
42898 function _createStore(storages, plugins, namespace) {
42903 if (storages && !isList$1(storages)) {
42904 storages = [storages];
42907 if (plugins && !isList$1(plugins)) {
42908 plugins = [plugins];
42911 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
42912 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
42913 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
42915 if (!legalNamespaces.test(namespace)) {
42916 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
42919 var _privateStoreProps = {
42920 _namespacePrefix: namespacePrefix,
42921 _namespaceRegexp: namespaceRegexp,
42922 _testStorage: function _testStorage(storage) {
42924 var testStr = '__storejs__test__';
42925 storage.write(testStr, testStr);
42926 var ok = storage.read(testStr) === testStr;
42927 storage.remove(testStr);
42933 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
42934 var oldFn = this[propName];
42936 this[propName] = function pluginFn() {
42937 var args = slice$3(arguments, 0);
42938 var self = this; // super_fn calls the old function which was overwritten by
42941 function super_fn() {
42946 each$1(arguments, function (arg, i) {
42949 return oldFn.apply(self, args);
42950 } // Give mixing function access to super_fn by prefixing all mixin function
42951 // arguments with super_fn.
42954 var newFnArgs = [super_fn].concat(args);
42955 return pluginFnProp.apply(self, newFnArgs);
42958 _serialize: function _serialize(obj) {
42959 return JSON.stringify(obj);
42961 _deserialize: function _deserialize(strVal, defaultVal) {
42964 } // It is possible that a raw string value has been previously stored
42965 // in a storage without using store.js, meaning it will be a raw
42966 // string value instead of a JSON serialized string. By defaulting
42967 // to the raw string value in case of a JSON parse error, we allow
42968 // for past stored values to be forwards-compatible with store.js
42974 val = JSON.parse(strVal);
42979 return val !== undefined ? val : defaultVal;
42981 _addStorage: function _addStorage(storage) {
42982 if (this.enabled) {
42986 if (this._testStorage(storage)) {
42987 this.storage = storage;
42988 this.enabled = true;
42991 _addPlugin: function _addPlugin(plugin) {
42992 var self = this; // If the plugin is an array, then add all plugins in the array.
42993 // This allows for a plugin to depend on other plugins.
42995 if (isList$1(plugin)) {
42996 each$1(plugin, function (plugin) {
42997 self._addPlugin(plugin);
43000 } // Keep track of all plugins we've seen so far, so that we
43001 // don't add any of them twice.
43004 var seenPlugin = pluck$1(this.plugins, function (seenPlugin) {
43005 return plugin === seenPlugin;
43012 this.plugins.push(plugin); // Check that the plugin is properly formed
43014 if (!isFunction$1(plugin)) {
43015 throw new Error('Plugins must be function values that return objects');
43018 var pluginProperties = plugin.call(this);
43020 if (!isObject$3(pluginProperties)) {
43021 throw new Error('Plugins must return an object of function properties');
43022 } // Add the plugin function properties to this store instance.
43025 each$1(pluginProperties, function (pluginFnProp, propName) {
43026 if (!isFunction$1(pluginFnProp)) {
43027 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
43030 self._assignPluginFnProp(pluginFnProp, propName);
43033 // Put deprecated properties in the private API, so as to not expose it to accidential
43034 // discovery through inspection of the store object.
43035 // Deprecated: addStorage
43036 addStorage: function addStorage(storage) {
43037 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
43039 this._addStorage(storage);
43042 var store = create$2(_privateStoreProps, storeAPI, {
43046 each$1(store, function (prop, propName) {
43047 if (isFunction$1(prop)) {
43048 store.raw[propName] = bind$2(store, prop);
43051 each$1(storages, function (storage) {
43052 store._addStorage(storage);
43054 each$1(plugins, function (plugin) {
43055 store._addPlugin(plugin);
43060 var Global$1 = util.Global;
43061 var localStorage_1 = {
43062 name: 'localStorage',
43070 function localStorage$1() {
43071 return Global$1.localStorage;
43074 function read(key) {
43075 return localStorage$1().getItem(key);
43078 function write(key, data) {
43079 return localStorage$1().setItem(key, data);
43082 function each$2(fn) {
43083 for (var i = localStorage$1().length - 1; i >= 0; i--) {
43084 var key = localStorage$1().key(i);
43085 fn(read(key), key);
43089 function remove$2(key) {
43090 return localStorage$1().removeItem(key);
43093 function clearAll() {
43094 return localStorage$1().clear();
43097 // versions 6 and 7, where no localStorage, etc
43100 var Global$2 = util.Global;
43101 var oldFFGlobalStorage = {
43102 name: 'oldFF-globalStorage',
43107 clearAll: clearAll$1
43109 var globalStorage = Global$2.globalStorage;
43111 function read$1(key) {
43112 return globalStorage[key];
43115 function write$1(key, data) {
43116 globalStorage[key] = data;
43119 function each$3(fn) {
43120 for (var i = globalStorage.length - 1; i >= 0; i--) {
43121 var key = globalStorage.key(i);
43122 fn(globalStorage[key], key);
43126 function remove$3(key) {
43127 return globalStorage.removeItem(key);
43130 function clearAll$1() {
43131 each$3(function (key, _) {
43132 delete globalStorage[key];
43136 // versions 6 and 7, where no localStorage, sessionStorage, etc
43139 var Global$3 = util.Global;
43140 var oldIEUserDataStorage = {
43141 name: 'oldIE-userDataStorage',
43146 clearAll: clearAll$2
43148 var storageName = 'storejs';
43149 var doc = Global$3.document;
43151 var _withStorageEl = _makeIEStorageElFunction();
43153 var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
43155 function write$2(unfixedKey, data) {
43160 var fixedKey = fixKey(unfixedKey);
43162 _withStorageEl(function (storageEl) {
43163 storageEl.setAttribute(fixedKey, data);
43164 storageEl.save(storageName);
43168 function read$2(unfixedKey) {
43173 var fixedKey = fixKey(unfixedKey);
43176 _withStorageEl(function (storageEl) {
43177 res = storageEl.getAttribute(fixedKey);
43183 function each$4(callback) {
43184 _withStorageEl(function (storageEl) {
43185 var attributes = storageEl.XMLDocument.documentElement.attributes;
43187 for (var i = attributes.length - 1; i >= 0; i--) {
43188 var attr = attributes[i];
43189 callback(storageEl.getAttribute(attr.name), attr.name);
43194 function remove$4(unfixedKey) {
43195 var fixedKey = fixKey(unfixedKey);
43197 _withStorageEl(function (storageEl) {
43198 storageEl.removeAttribute(fixedKey);
43199 storageEl.save(storageName);
43203 function clearAll$2() {
43204 _withStorageEl(function (storageEl) {
43205 var attributes = storageEl.XMLDocument.documentElement.attributes;
43206 storageEl.load(storageName);
43208 for (var i = attributes.length - 1; i >= 0; i--) {
43209 storageEl.removeAttribute(attributes[i].name);
43212 storageEl.save(storageName);
43216 // In IE7, keys cannot start with a digit or contain certain chars.
43217 // See https://github.com/marcuswestin/store.js/issues/40
43218 // See https://github.com/marcuswestin/store.js/issues/83
43221 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
43223 function fixKey(key) {
43224 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
43227 function _makeIEStorageElFunction() {
43228 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
43232 var scriptTag = 'script',
43235 storageEl; // Since #userData storage applies only to specific paths, we need to
43236 // somehow link our data to a specific path. We choose /favicon.ico
43237 // as a pretty safe option, since all browsers already make a request to
43238 // this URL anyway and being a 404 will not hurt us here. We wrap an
43239 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
43240 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
43241 // since the iframe access rules appear to allow direct access and
43242 // manipulation of the document element, even for a 404 page. This
43243 // document can be used instead of the current document (which would
43244 // have been limited to the current path) to perform #userData storage.
43247 /* global ActiveXObject */
43248 storageContainer = new ActiveXObject('htmlfile');
43249 storageContainer.open();
43250 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
43251 storageContainer.close();
43252 storageOwner = storageContainer.w.frames[0].document;
43253 storageEl = storageOwner.createElement('div');
43255 // somehow ActiveXObject instantiation failed (perhaps some special
43256 // security settings or otherwse), fall back to per-path storage
43257 storageEl = doc.createElement('div');
43258 storageOwner = doc.body;
43261 return function (storeFunction) {
43262 var args = [].slice.call(arguments, 0);
43263 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
43264 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
43266 storageOwner.appendChild(storageEl);
43267 storageEl.addBehavior('#default#userData');
43268 storageEl.load(storageName);
43269 storeFunction.apply(this, args);
43270 storageOwner.removeChild(storageEl);
43275 // doesn't work but cookies do. This implementation is adopted from
43276 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
43278 var Global$4 = util.Global;
43279 var trim$4 = util.trim;
43280 var cookieStorage = {
43281 name: 'cookieStorage',
43286 clearAll: clearAll$3
43288 var doc$1 = Global$4.document;
43290 function read$3(key) {
43291 if (!key || !_has(key)) {
43295 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
43296 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"));
43299 function each$5(callback) {
43300 var cookies = doc$1.cookie.split(/; ?/g);
43302 for (var i = cookies.length - 1; i >= 0; i--) {
43303 if (!trim$4(cookies[i])) {
43307 var kvp = cookies[i].split('=');
43308 var key = unescape(kvp[0]);
43309 var val = unescape(kvp[1]);
43310 callback(val, key);
43314 function write$3(key, data) {
43319 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
43322 function remove$5(key) {
43323 if (!key || !_has(key)) {
43327 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
43330 function clearAll$3() {
43331 each$5(function (_, key) {
43336 function _has(key) {
43337 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc$1.cookie);
43340 var Global$5 = util.Global;
43341 var sessionStorage_1 = {
43342 name: 'sessionStorage',
43347 clearAll: clearAll$4
43350 function sessionStorage() {
43351 return Global$5.sessionStorage;
43354 function read$4(key) {
43355 return sessionStorage().getItem(key);
43358 function write$4(key, data) {
43359 return sessionStorage().setItem(key, data);
43362 function each$6(fn) {
43363 for (var i = sessionStorage().length - 1; i >= 0; i--) {
43364 var key = sessionStorage().key(i);
43365 fn(read$4(key), key);
43369 function remove$6(key) {
43370 return sessionStorage().removeItem(key);
43373 function clearAll$4() {
43374 return sessionStorage().clear();
43377 // memoryStorage is a useful last fallback to ensure that the store
43378 // is functions (meaning store.get(), store.set(), etc will all function).
43379 // However, stored values will not persist when the browser navigates to
43380 // a new page or reloads the current page.
43381 var memoryStorage_1 = {
43382 name: 'memoryStorage',
43387 clearAll: clearAll$5
43389 var memoryStorage = {};
43391 function read$5(key) {
43392 return memoryStorage[key];
43395 function write$5(key, data) {
43396 memoryStorage[key] = data;
43399 function each$7(callback) {
43400 for (var key in memoryStorage) {
43401 if (memoryStorage.hasOwnProperty(key)) {
43402 callback(memoryStorage[key], key);
43407 function remove$7(key) {
43408 delete memoryStorage[key];
43411 function clearAll$5(key) {
43412 memoryStorage = {};
43415 var all = [// Listed in order of usage preference
43416 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
43418 /* eslint-disable */
43422 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
43423 // See http://www.JSON.org/js.html
43424 // This code should be minified before deployment.
43425 // See http://javascript.crockford.com/jsmin.html
43426 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
43428 // This file creates a global JSON object containing two methods: stringify
43429 // and parse. This file provides the ES5 JSON capability to ES3 systems.
43430 // If a project might run on IE8 or earlier, then this file should be included.
43431 // This file does nothing on ES5 systems.
43432 // JSON.stringify(value, replacer, space)
43433 // value any JavaScript value, usually an object or array.
43434 // replacer an optional parameter that determines how object
43435 // values are stringified for objects. It can be a
43436 // function or an array of strings.
43437 // space an optional parameter that specifies the indentation
43438 // of nested structures. If it is omitted, the text will
43439 // be packed without extra whitespace. If it is a number,
43440 // it will specify the number of spaces to indent at each
43441 // level. If it is a string (such as "\t" or " "),
43442 // it contains the characters used to indent at each level.
43443 // This method produces a JSON text from a JavaScript value.
43444 // When an object value is found, if the object contains a toJSON
43445 // method, its toJSON method will be called and the result will be
43446 // stringified. A toJSON method does not serialize: it returns the
43447 // value represented by the name/value pair that should be serialized,
43448 // or undefined if nothing should be serialized. The toJSON method
43449 // will be passed the key associated with the value, and this will be
43450 // bound to the value.
43451 // For example, this would serialize Dates as ISO strings.
43452 // Date.prototype.toJSON = function (key) {
43454 // // Format integers to have at least two digits.
43459 // return this.getUTCFullYear() + "-" +
43460 // f(this.getUTCMonth() + 1) + "-" +
43461 // f(this.getUTCDate()) + "T" +
43462 // f(this.getUTCHours()) + ":" +
43463 // f(this.getUTCMinutes()) + ":" +
43464 // f(this.getUTCSeconds()) + "Z";
43466 // You can provide an optional replacer method. It will be passed the
43467 // key and value of each member, with this bound to the containing
43468 // object. The value that is returned from your method will be
43469 // serialized. If your method returns undefined, then the member will
43470 // be excluded from the serialization.
43471 // If the replacer parameter is an array of strings, then it will be
43472 // used to select the members to be serialized. It filters the results
43473 // such that only members with keys listed in the replacer array are
43475 // Values that do not have JSON representations, such as undefined or
43476 // functions, will not be serialized. Such values in objects will be
43477 // dropped; in arrays they will be replaced with null. You can use
43478 // a replacer function to replace those with JSON values.
43479 // JSON.stringify(undefined) returns undefined.
43480 // The optional space parameter produces a stringification of the
43481 // value that is filled with line breaks and indentation to make it
43483 // If the space parameter is a non-empty string, then that string will
43484 // be used for indentation. If the space parameter is a number, then
43485 // the indentation will be that many spaces.
43487 // text = JSON.stringify(["e", {pluribus: "unum"}]);
43488 // // text is '["e",{"pluribus":"unum"}]'
43489 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
43490 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
43491 // text = JSON.stringify([new Date()], function (key, value) {
43492 // return this[key] instanceof Date
43493 // ? "Date(" + this[key] + ")"
43496 // // text is '["Date(---current time---)"]'
43497 // JSON.parse(text, reviver)
43498 // This method parses a JSON text to produce an object or array.
43499 // It can throw a SyntaxError exception.
43500 // The optional reviver parameter is a function that can filter and
43501 // transform the results. It receives each of the keys and values,
43502 // and its return value is used instead of the original value.
43503 // If it returns what it received, then the structure is not modified.
43504 // If it returns undefined then the member is deleted.
43506 // // Parse the text. Values that look like ISO date strings will
43507 // // be converted to Date objects.
43508 // myData = JSON.parse(text, function (key, value) {
43510 // if (typeof value === "string") {
43512 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
43514 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
43520 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
43522 // if (typeof value === "string" &&
43523 // value.slice(0, 5) === "Date(" &&
43524 // value.slice(-1) === ")") {
43525 // d = new Date(value.slice(5, -1));
43532 // This is a reference implementation. You are free to copy, modify, or
43540 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
43541 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
43542 lastIndex, length, parse, prototype, push, replace, slice, stringify,
43543 test, toJSON, toString, valueOf
43545 // Create a JSON object only if one does not already exist. We create the
43546 // methods in a closure to avoid creating global variables.
43547 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
43553 var rx_one = /^[\],:{}\s]*$/;
43554 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
43555 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
43556 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
43557 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43558 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43561 // Format integers to have at least two digits.
43562 return n < 10 ? "0" + n : n;
43565 function this_value() {
43566 return this.valueOf();
43569 if (typeof Date.prototype.toJSON !== "function") {
43570 Date.prototype.toJSON = function () {
43571 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;
43574 Boolean.prototype.toJSON = this_value;
43575 Number.prototype.toJSON = this_value;
43576 String.prototype.toJSON = this_value;
43584 function quote(string) {
43585 // If the string contains no control characters, no quote characters, and no
43586 // backslash characters, then we can safely slap some quotes around it.
43587 // Otherwise we must also replace the offending characters with safe escape
43589 rx_escapable.lastIndex = 0;
43590 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
43592 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43593 }) + "\"" : "\"" + string + "\"";
43596 function str(key, holder) {
43597 // Produce a string from holder[key].
43598 var i; // The loop counter.
43600 var k; // The member key.
43602 var v; // The member value.
43607 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
43609 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
43610 value = value.toJSON(key);
43611 } // If we were called with a replacer function, then call the replacer to
43612 // obtain a replacement value.
43615 if (typeof rep === "function") {
43616 value = rep.call(holder, key, value);
43617 } // What happens next depends on the value's type.
43620 switch (_typeof(value)) {
43622 return quote(value);
43625 // JSON numbers must be finite. Encode non-finite numbers as null.
43626 return isFinite(value) ? String(value) : "null";
43630 // If the value is a boolean or null, convert it to a string. Note:
43631 // typeof null does not produce "null". The case is included here in
43632 // the remote chance that this gets fixed someday.
43633 return String(value);
43634 // If the type is "object", we might be dealing with an object or an array or
43638 // Due to a specification blunder in ECMAScript, typeof null is "object",
43639 // so watch out for that case.
43642 } // Make an array to hold the partial results of stringifying this object value.
43646 partial = []; // Is the value an array?
43648 if (Object.prototype.toString.apply(value) === "[object Array]") {
43649 // The value is an array. Stringify every element. Use null as a placeholder
43650 // for non-JSON values.
43651 length = value.length;
43653 for (i = 0; i < length; i += 1) {
43654 partial[i] = str(i, value) || "null";
43655 } // Join all of the elements together, separated with commas, and wrap them in
43659 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
43662 } // If the replacer is an array, use it to select the members to be stringified.
43665 if (rep && _typeof(rep) === "object") {
43666 length = rep.length;
43668 for (i = 0; i < length; i += 1) {
43669 if (typeof rep[i] === "string") {
43674 partial.push(quote(k) + (gap ? ": " : ":") + v);
43679 // Otherwise, iterate through all of the keys in the object.
43681 if (Object.prototype.hasOwnProperty.call(value, k)) {
43685 partial.push(quote(k) + (gap ? ": " : ":") + v);
43689 } // Join all of the member texts together, separated with commas,
43690 // and wrap them in braces.
43693 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
43697 } // If the JSON object does not yet have a stringify method, give it one.
43700 if (typeof JSON.stringify !== "function") {
43702 // table of character substitutions
43712 JSON.stringify = function (value, replacer, space) {
43713 // The stringify method takes a value and an optional replacer, and an optional
43714 // space parameter, and returns a JSON text. The replacer can be a function
43715 // that can replace values, or an array of strings that will select the keys.
43716 // A default replacer method can be provided. Use of the space parameter can
43717 // produce text that is more easily readable.
43720 indent = ""; // If the space parameter is a number, make an indent string containing that
43723 if (typeof space === "number") {
43724 for (i = 0; i < space; i += 1) {
43726 } // If the space parameter is a string, it will be used as the indent string.
43728 } else if (typeof space === "string") {
43730 } // If there is a replacer, it must be a function or an array.
43731 // Otherwise, throw an error.
43736 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
43737 throw new Error("JSON.stringify");
43738 } // Make a fake root object containing our value under the key of "".
43739 // Return the result of stringifying the value.
43746 } // If the JSON object does not yet have a parse method, give it one.
43749 if (typeof JSON.parse !== "function") {
43750 JSON.parse = function (text, reviver) {
43751 // The parse method takes a text and an optional reviver function, and returns
43752 // a JavaScript value if the text is a valid JSON text.
43755 function walk(holder, key) {
43756 // The walk method is used to recursively walk the resulting structure so
43757 // that modifications can be made.
43760 var value = holder[key];
43762 if (value && _typeof(value) === "object") {
43764 if (Object.prototype.hasOwnProperty.call(value, k)) {
43765 v = walk(value, k);
43767 if (v !== undefined) {
43776 return reviver.call(holder, key, value);
43777 } // Parsing happens in four stages. In the first stage, we replace certain
43778 // Unicode characters with escape sequences. JavaScript handles many characters
43779 // incorrectly, either silently deleting them, or treating them as line endings.
43782 text = String(text);
43783 rx_dangerous.lastIndex = 0;
43785 if (rx_dangerous.test(text)) {
43786 text = text.replace(rx_dangerous, function (a) {
43787 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43789 } // In the second stage, we run the text against regular expressions that look
43790 // for non-JSON patterns. We are especially concerned with "()" and "new"
43791 // because they can cause invocation, and "=" because it can cause mutation.
43792 // But just to be safe, we want to reject all unexpected forms.
43793 // We split the second stage into 4 regexp operations in order to work around
43794 // crippling inefficiencies in IE's and Safari's regexp engines. First we
43795 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
43796 // replace all simple value tokens with "]" characters. Third, we delete all
43797 // open brackets that follow a colon or comma or that begin the text. Finally,
43798 // we look to see that the remaining characters are only whitespace or "]" or
43799 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
43802 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
43803 // In the third stage we use the eval function to compile the text into a
43804 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
43805 // in JavaScript: it can begin a block or an object literal. We wrap the text
43806 // in parens to eliminate the ambiguity.
43807 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
43808 // each name/value pair to a reviver function for possible transformation.
43810 return typeof reviver === "function" ? walk({
43813 } // If the text is not JSON parseable, then a SyntaxError is thrown.
43816 throw new SyntaxError("JSON.parse");
43821 var json2 = json2Plugin;
43823 function json2Plugin() {
43827 var plugins = [json2];
43828 var store_legacy = storeEngine.createStore(all, plugins);
43831 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
43832 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
43833 // does not support custom headers, which this uses everywhere.
43836 var osmAuth = function osmAuth(o) {
43837 var oauth = {}; // authenticated users will also have a request token secret, but it's
43838 // not used in transactions with the server
43840 oauth.authenticated = function () {
43841 return !!(token('oauth_token') && token('oauth_token_secret'));
43844 oauth.logout = function () {
43845 token('oauth_token', '');
43846 token('oauth_token_secret', '');
43847 token('oauth_request_token_secret', '');
43849 }; // TODO: detect lack of click event
43852 oauth.authenticate = function (callback) {
43853 if (oauth.authenticated()) return callback();
43854 oauth.logout(); // ## Getting a request token
43856 var params = timenonce(getAuth(o)),
43857 url = o.url + '/oauth/request_token';
43858 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
43860 if (!o.singlepage) {
43861 // Create a 600x550 popup window in the center of the screen
43864 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
43865 return x.join('=');
43867 popup = window.open('about:blank', 'oauth_window', settings);
43868 oauth.popupWindow = popup;
43871 var error = new Error('Popup was blocked');
43872 error.status = 'popup-blocked';
43875 } // Request a request token. When this is complete, the popup
43876 // window is redirected to OSM's authorization page.
43879 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
43882 function reqTokenDone(err, xhr) {
43884 if (err) return callback(err);
43885 var resp = ohauth_1.stringQs(xhr.response);
43886 token('oauth_request_token_secret', resp.oauth_token_secret);
43887 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
43888 oauth_token: resp.oauth_token,
43889 oauth_callback: resolveUrl$1(o.landing)
43892 if (o.singlepage) {
43893 location.href = authorize_url;
43895 popup.location = authorize_url;
43897 } // Called by a function in a landing page, in the popup window. The
43898 // window closes itself.
43901 window.authComplete = function (token) {
43902 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
43903 get_access_token(oauth_token.oauth_token);
43904 delete window.authComplete;
43905 }; // ## Getting an request token
43907 // At this point we have an `oauth_token`, brought in from a function
43908 // call on a landing page popup.
43911 function get_access_token(oauth_token) {
43912 var url = o.url + '/oauth/access_token',
43913 params = timenonce(getAuth(o)),
43914 request_token_secret = token('oauth_request_token_secret');
43915 params.oauth_token = oauth_token;
43916 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43918 // The final token required for authentication. At this point
43919 // we have a `request token secret`
43921 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43925 function accessTokenDone(err, xhr) {
43927 if (err) return callback(err);
43928 var access_token = ohauth_1.stringQs(xhr.response);
43929 token('oauth_token', access_token.oauth_token);
43930 token('oauth_token_secret', access_token.oauth_token_secret);
43931 callback(null, oauth);
43935 oauth.bringPopupWindowToFront = function () {
43936 var brougtPopupToFront = false;
43939 // This may cause a cross-origin error:
43940 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
43941 if (oauth.popupWindow && !oauth.popupWindow.closed) {
43942 oauth.popupWindow.focus();
43943 brougtPopupToFront = true;
43945 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
43948 return brougtPopupToFront;
43951 oauth.bootstrapToken = function (oauth_token, callback) {
43952 // ## Getting an request token
43953 // At this point we have an `oauth_token`, brought in from a function
43954 // call on a landing page popup.
43955 function get_access_token(oauth_token) {
43956 var url = o.url + '/oauth/access_token',
43957 params = timenonce(getAuth(o)),
43958 request_token_secret = token('oauth_request_token_secret');
43959 params.oauth_token = oauth_token;
43960 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43961 // The final token required for authentication. At this point
43962 // we have a `request token secret`
43964 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43968 function accessTokenDone(err, xhr) {
43970 if (err) return callback(err);
43971 var access_token = ohauth_1.stringQs(xhr.response);
43972 token('oauth_token', access_token.oauth_token);
43973 token('oauth_token_secret', access_token.oauth_token_secret);
43974 callback(null, oauth);
43977 get_access_token(oauth_token);
43980 // A single XMLHttpRequest wrapper that does authenticated calls if the
43981 // user has logged in.
43984 oauth.xhr = function (options, callback) {
43985 if (!oauth.authenticated()) {
43987 return oauth.authenticate(run);
43989 callback('not authenticated', null);
43997 var params = timenonce(getAuth(o)),
43998 oauth_token_secret = token('oauth_token_secret'),
43999 url = options.prefix !== false ? o.url + options.path : options.path,
44000 url_parts = url.replace(/#.*$/, '').split('?', 2),
44001 base_url = url_parts[0],
44002 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
44004 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
44005 params = immutable(params, ohauth_1.stringQs(options.content));
44008 params.oauth_token = token('oauth_token');
44009 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))));
44010 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
44013 function done(err, xhr) {
44014 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
44016 }; // pre-authorize this object, if we can just get a token and token_secret
44020 oauth.preauth = function (c) {
44022 if (c.oauth_token) token('oauth_token', c.oauth_token);
44023 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
44027 oauth.options = function (_) {
44028 if (!arguments.length) return o;
44030 o.url = o.url || 'https://www.openstreetmap.org';
44031 o.landing = o.landing || 'land.html';
44032 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
44033 // by default, no-ops
44035 o.loading = o.loading || function () {};
44037 o.done = o.done || function () {};
44039 return oauth.preauth(o);
44040 }; // 'stamp' an authentication object from `getAuth()`
44041 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
44045 function timenonce(o) {
44046 o.oauth_timestamp = ohauth_1.timestamp();
44047 o.oauth_nonce = ohauth_1.nonce();
44049 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
44050 // can be used with multiple APIs and the keys in `localStorage`
44056 if (store_legacy.enabled) {
44057 token = function token(x, y) {
44058 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
44063 token = function token(x, y) {
44064 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
44066 } // Get an authentication object. If you just add and remove properties
44067 // from a single object, you'll need to use `delete` to make sure that
44068 // it doesn't contain undesired properties for authentication
44071 function getAuth(o) {
44073 oauth_consumer_key: o.oauth_consumer_key,
44074 oauth_signature_method: 'HMAC-SHA1'
44076 } // potentially pre-authorize
44083 var JXON = new function () {
44084 var sValueProp = 'keyValue',
44085 sAttributesProp = 'keyAttributes',
44088 /* you can customize these values */
44091 rIsBool = /^(?:true|false)$/i;
44093 function parseText(sValue) {
44094 if (rIsNull.test(sValue)) {
44098 if (rIsBool.test(sValue)) {
44099 return sValue.toLowerCase() === 'true';
44102 if (isFinite(sValue)) {
44103 return parseFloat(sValue);
44106 if (isFinite(Date.parse(sValue))) {
44107 return new Date(sValue);
44113 function EmptyTree() {}
44115 EmptyTree.prototype.toString = function () {
44119 EmptyTree.prototype.valueOf = function () {
44123 function objectify(vValue) {
44124 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
44127 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
44128 var nLevelStart = aCache.length,
44129 bChildren = oParentNode.hasChildNodes(),
44130 bAttributes = oParentNode.hasAttributes(),
44131 bHighVerb = Boolean(nVerb & 2);
44135 sCollectedTxt = '',
44136 vResult = bHighVerb ? {} :
44137 /* put here the default value for empty nodes: */
44141 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
44142 oNode = oParentNode.childNodes.item(nItem);
44144 if (oNode.nodeType === 4) {
44145 sCollectedTxt += oNode.nodeValue;
44147 /* nodeType is 'CDATASection' (4) */
44148 else if (oNode.nodeType === 3) {
44149 sCollectedTxt += oNode.nodeValue.trim();
44151 /* nodeType is 'Text' (3) */
44152 else if (oNode.nodeType === 1 && !oNode.prefix) {
44153 aCache.push(oNode);
44155 /* nodeType is 'Element' (1) */
44160 var nLevelEnd = aCache.length,
44161 vBuiltVal = parseText(sCollectedTxt);
44163 if (!bHighVerb && (bChildren || bAttributes)) {
44164 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
44167 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
44168 sProp = aCache[nElId].nodeName.toLowerCase();
44169 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
44171 if (vResult.hasOwnProperty(sProp)) {
44172 if (vResult[sProp].constructor !== Array) {
44173 vResult[sProp] = [vResult[sProp]];
44176 vResult[sProp].push(vContent);
44178 vResult[sProp] = vContent;
44184 var nAttrLen = oParentNode.attributes.length,
44185 sAPrefix = bNesteAttr ? '' : sAttrPref,
44186 oAttrParent = bNesteAttr ? {} : vResult;
44188 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
44189 oAttrib = oParentNode.attributes.item(nAttrib);
44190 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
44195 Object.freeze(oAttrParent);
44198 vResult[sAttributesProp] = oAttrParent;
44199 nLength -= nAttrLen - 1;
44203 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
44204 vResult[sValueProp] = vBuiltVal;
44205 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
44206 vResult = vBuiltVal;
44209 if (bFreeze && (bHighVerb || nLength > 0)) {
44210 Object.freeze(vResult);
44213 aCache.length = nLevelStart;
44217 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
44218 var vValue, oChild;
44220 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
44221 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
44222 /* verbosity level is 0 */
44223 } else if (oParentObj.constructor === Date) {
44224 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
44227 for (var sName in oParentObj) {
44228 vValue = oParentObj[sName];
44230 if (isFinite(sName) || vValue instanceof Function) {
44233 /* verbosity level is 0 */
44236 if (sName === sValueProp) {
44237 if (vValue !== null && vValue !== true) {
44238 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
44240 } else if (sName === sAttributesProp) {
44241 /* verbosity level is 3 */
44242 for (var sAttrib in vValue) {
44243 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
44245 } else if (sName.charAt(0) === sAttrPref) {
44246 oParentEl.setAttribute(sName.slice(1), vValue);
44247 } else if (vValue.constructor === Array) {
44248 for (var nItem = 0; nItem < vValue.length; nItem++) {
44249 oChild = oXMLDoc.createElement(sName);
44250 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
44251 oParentEl.appendChild(oChild);
44254 oChild = oXMLDoc.createElement(sName);
44256 if (vValue instanceof Object) {
44257 loadObjTree(oXMLDoc, oChild, vValue);
44258 } else if (vValue !== null && vValue !== true) {
44259 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
44262 oParentEl.appendChild(oChild);
44267 this.build = function (oXMLParent, nVerbosity
44274 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
44275 /* put here the default verbosity level: */
44278 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
44281 this.unbuild = function (oObjTree) {
44282 var oNewDoc = document.implementation.createDocument('', '', null);
44283 loadObjTree(oNewDoc, oNewDoc, oObjTree);
44287 this.stringify = function (oObjTree) {
44288 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
44290 }(); // var myObject = JXON.build(doc);
44291 // we got our javascript object! try: alert(JSON.stringify(myObject));
44292 // var newDoc = JXON.unbuild(myObject);
44293 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
44295 var tiler$5 = utilTiler();
44296 var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
44297 var urlroot = 'https://www.openstreetmap.org';
44298 var oauth = osmAuth({
44300 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
44301 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
44302 loading: authLoading,
44304 }); // hardcode default block of Google Maps
44306 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
44328 var _cachedApiStatus;
44330 var _changeset = {};
44332 var _deferred = new Set();
44334 var _connectionID = 1;
44335 var _tileZoom$3 = 16;
44336 var _noteZoom = 12;
44338 var _rateLimitError;
44340 var _userChangesets;
44344 var _off; // set a default but also load this from the API status
44347 var _maxWayNodes = 2000;
44349 function authLoading() {
44350 dispatch$6.call('authLoading');
44353 function authDone() {
44354 dispatch$6.call('authDone');
44357 function abortRequest$5(controllerOrXHR) {
44358 if (controllerOrXHR) {
44359 controllerOrXHR.abort();
44363 function hasInflightRequests(cache) {
44364 return Object.keys(cache.inflight).length;
44367 function abortUnwantedRequests$3(cache, visibleTiles) {
44368 Object.keys(cache.inflight).forEach(function (k) {
44369 if (cache.toLoad[k]) return;
44370 if (visibleTiles.find(function (tile) {
44371 return k === tile.id;
44373 abortRequest$5(cache.inflight[k]);
44374 delete cache.inflight[k];
44378 function getLoc(attrs) {
44379 var lon = attrs.lon && attrs.lon.value;
44380 var lat = attrs.lat && attrs.lat.value;
44381 return [parseFloat(lon), parseFloat(lat)];
44384 function getNodes(obj) {
44385 var elems = obj.getElementsByTagName('nd');
44386 var nodes = new Array(elems.length);
44388 for (var i = 0, l = elems.length; i < l; i++) {
44389 nodes[i] = 'n' + elems[i].attributes.ref.value;
44395 function getNodesJSON(obj) {
44396 var elems = obj.nodes;
44397 var nodes = new Array(elems.length);
44399 for (var i = 0, l = elems.length; i < l; i++) {
44400 nodes[i] = 'n' + elems[i];
44406 function getTags(obj) {
44407 var elems = obj.getElementsByTagName('tag');
44410 for (var i = 0, l = elems.length; i < l; i++) {
44411 var attrs = elems[i].attributes;
44412 tags[attrs.k.value] = attrs.v.value;
44418 function getMembers(obj) {
44419 var elems = obj.getElementsByTagName('member');
44420 var members = new Array(elems.length);
44422 for (var i = 0, l = elems.length; i < l; i++) {
44423 var attrs = elems[i].attributes;
44425 id: attrs.type.value[0] + attrs.ref.value,
44426 type: attrs.type.value,
44427 role: attrs.role.value
44434 function getMembersJSON(obj) {
44435 var elems = obj.members;
44436 var members = new Array(elems.length);
44438 for (var i = 0, l = elems.length; i < l; i++) {
44439 var attrs = elems[i];
44441 id: attrs.type[0] + attrs.ref,
44450 function getVisible(attrs) {
44451 return !attrs.visible || attrs.visible.value !== 'false';
44454 function parseComments(comments) {
44455 var parsedComments = []; // for each comment
44457 for (var i = 0; i < comments.length; i++) {
44458 var comment = comments[i];
44460 if (comment.nodeName === 'comment') {
44461 var childNodes = comment.childNodes;
44462 var parsedComment = {};
44464 for (var j = 0; j < childNodes.length; j++) {
44465 var node = childNodes[j];
44466 var nodeName = node.nodeName;
44467 if (nodeName === '#text') continue;
44468 parsedComment[nodeName] = node.textContent;
44470 if (nodeName === 'uid') {
44471 var uid = node.textContent;
44473 if (uid && !_userCache.user[uid]) {
44474 _userCache.toLoad[uid] = true;
44479 if (parsedComment) {
44480 parsedComments.push(parsedComment);
44485 return parsedComments;
44488 function encodeNoteRtree(note) {
44498 var jsonparsers = {
44499 node: function nodeData(obj, uid) {
44500 return new osmNode({
44502 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44503 version: obj.version && obj.version.toString(),
44504 changeset: obj.changeset && obj.changeset.toString(),
44505 timestamp: obj.timestamp,
44507 uid: obj.uid && obj.uid.toString(),
44508 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
44512 way: function wayData(obj, uid) {
44513 return new osmWay({
44515 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44516 version: obj.version && obj.version.toString(),
44517 changeset: obj.changeset && obj.changeset.toString(),
44518 timestamp: obj.timestamp,
44520 uid: obj.uid && obj.uid.toString(),
44522 nodes: getNodesJSON(obj)
44525 relation: function relationData(obj, uid) {
44526 return new osmRelation({
44528 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44529 version: obj.version && obj.version.toString(),
44530 changeset: obj.changeset && obj.changeset.toString(),
44531 timestamp: obj.timestamp,
44533 uid: obj.uid && obj.uid.toString(),
44535 members: getMembersJSON(obj)
44540 function parseJSON(payload, callback, options) {
44541 options = Object.assign({
44547 message: 'No JSON',
44552 var json = payload;
44553 if (_typeof(json) !== 'object') json = JSON.parse(payload);
44554 if (!json.elements) return callback({
44555 message: 'No JSON',
44558 var children = json.elements;
44559 var handle = window.requestIdleCallback(function () {
44563 for (var i = 0; i < children.length; i++) {
44564 result = parseChild(children[i]);
44565 if (result) results.push(result);
44568 callback(null, results);
44571 _deferred.add(handle);
44573 function parseChild(child) {
44574 var parser = jsonparsers[child.type];
44575 if (!parser) return null;
44577 uid = osmEntity.id.fromOSM(child.type, child.id);
44579 if (options.skipSeen) {
44580 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44582 _tileCache.seen[uid] = true;
44585 return parser(child, uid);
44590 node: function nodeData(obj, uid) {
44591 var attrs = obj.attributes;
44592 return new osmNode({
44594 visible: getVisible(attrs),
44595 version: attrs.version.value,
44596 changeset: attrs.changeset && attrs.changeset.value,
44597 timestamp: attrs.timestamp && attrs.timestamp.value,
44598 user: attrs.user && attrs.user.value,
44599 uid: attrs.uid && attrs.uid.value,
44600 loc: getLoc(attrs),
44604 way: function wayData(obj, uid) {
44605 var attrs = obj.attributes;
44606 return new osmWay({
44608 visible: getVisible(attrs),
44609 version: attrs.version.value,
44610 changeset: attrs.changeset && attrs.changeset.value,
44611 timestamp: attrs.timestamp && attrs.timestamp.value,
44612 user: attrs.user && attrs.user.value,
44613 uid: attrs.uid && attrs.uid.value,
44614 tags: getTags(obj),
44615 nodes: getNodes(obj)
44618 relation: function relationData(obj, uid) {
44619 var attrs = obj.attributes;
44620 return new osmRelation({
44622 visible: getVisible(attrs),
44623 version: attrs.version.value,
44624 changeset: attrs.changeset && attrs.changeset.value,
44625 timestamp: attrs.timestamp && attrs.timestamp.value,
44626 user: attrs.user && attrs.user.value,
44627 uid: attrs.uid && attrs.uid.value,
44628 tags: getTags(obj),
44629 members: getMembers(obj)
44632 note: function parseNote(obj, uid) {
44633 var attrs = obj.attributes;
44634 var childNodes = obj.childNodes;
44637 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
44639 var coincident = false;
44640 var epsilon = 0.00001;
44644 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
44647 var bbox = geoExtent(props.loc).bbox();
44648 coincident = _noteCache.rtree.search(bbox).length;
44649 } while (coincident); // parse note contents
44652 for (var i = 0; i < childNodes.length; i++) {
44653 var node = childNodes[i];
44654 var nodeName = node.nodeName;
44655 if (nodeName === '#text') continue; // if the element is comments, parse the comments
44657 if (nodeName === 'comments') {
44658 props[nodeName] = parseComments(node.childNodes);
44660 props[nodeName] = node.textContent;
44664 var note = new osmNote(props);
44665 var item = encodeNoteRtree(note);
44666 _noteCache.note[note.id] = note;
44668 _noteCache.rtree.insert(item);
44672 user: function parseUser(obj, uid) {
44673 var attrs = obj.attributes;
44676 display_name: attrs.display_name && attrs.display_name.value,
44677 account_created: attrs.account_created && attrs.account_created.value,
44678 changesets_count: '0',
44681 var img = obj.getElementsByTagName('img');
44683 if (img && img[0] && img[0].getAttribute('href')) {
44684 user.image_url = img[0].getAttribute('href');
44687 var changesets = obj.getElementsByTagName('changesets');
44689 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
44690 user.changesets_count = changesets[0].getAttribute('count');
44693 var blocks = obj.getElementsByTagName('blocks');
44695 if (blocks && blocks[0]) {
44696 var received = blocks[0].getElementsByTagName('received');
44698 if (received && received[0] && received[0].getAttribute('active')) {
44699 user.active_blocks = received[0].getAttribute('active');
44703 _userCache.user[uid] = user;
44704 delete _userCache.toLoad[uid];
44709 function parseXML(xml, callback, options) {
44710 options = Object.assign({
44714 if (!xml || !xml.childNodes) {
44721 var root = xml.childNodes[0];
44722 var children = root.childNodes;
44723 var handle = window.requestIdleCallback(function () {
44727 for (var i = 0; i < children.length; i++) {
44728 result = parseChild(children[i]);
44729 if (result) results.push(result);
44732 callback(null, results);
44735 _deferred.add(handle);
44737 function parseChild(child) {
44738 var parser = parsers[child.nodeName];
44739 if (!parser) return null;
44742 if (child.nodeName === 'user') {
44743 uid = child.attributes.id.value;
44745 if (options.skipSeen && _userCache.user[uid]) {
44746 delete _userCache.toLoad[uid];
44749 } else if (child.nodeName === 'note') {
44750 uid = child.getElementsByTagName('id')[0].textContent;
44752 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
44754 if (options.skipSeen) {
44755 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44757 _tileCache.seen[uid] = true;
44761 return parser(child, uid);
44763 } // replace or remove note from rtree
44766 function updateRtree$3(item, replace) {
44767 _noteCache.rtree.remove(item, function isEql(a, b) {
44768 return a.data.id === b.data.id;
44772 _noteCache.rtree.insert(item);
44776 function wrapcb(thisArg, callback, cid) {
44777 return function (err, result) {
44779 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
44780 if (err.status === 400 || err.status === 401 || err.status === 403) {
44784 return callback.call(thisArg, err);
44785 } else if (thisArg.getConnectionId() !== cid) {
44786 return callback.call(thisArg, {
44787 message: 'Connection Switched',
44791 return callback.call(thisArg, err, result);
44797 init: function init() {
44798 utilRebind(this, dispatch$6, 'on');
44800 reset: function reset() {
44801 Array.from(_deferred).forEach(function (handle) {
44802 window.cancelIdleCallback(handle);
44804 _deferred["delete"](handle);
44807 _userChangesets = undefined;
44808 _userDetails = undefined;
44809 _rateLimitError = undefined;
44810 Object.values(_tileCache.inflight).forEach(abortRequest$5);
44811 Object.values(_noteCache.inflight).forEach(abortRequest$5);
44812 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
44813 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
44834 _cachedApiStatus = undefined;
44838 getConnectionId: function getConnectionId() {
44839 return _connectionID;
44841 changesetURL: function changesetURL(changesetID) {
44842 return urlroot + '/changeset/' + changesetID;
44844 changesetsURL: function changesetsURL(center, zoom) {
44845 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
44846 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
44848 entityURL: function entityURL(entity) {
44849 return urlroot + '/' + entity.type + '/' + entity.osmId();
44851 historyURL: function historyURL(entity) {
44852 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
44854 userURL: function userURL(username) {
44855 return urlroot + '/user/' + username;
44857 noteURL: function noteURL(note) {
44858 return urlroot + '/note/' + note.id;
44860 noteReportURL: function noteReportURL(note) {
44861 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
44863 // Generic method to load data from the OSM API
44864 // Can handle either auth or unauth calls.
44865 loadFromAPI: function loadFromAPI(path, callback, options) {
44866 options = Object.assign({
44870 var cid = _connectionID;
44872 function done(err, payload) {
44873 if (that.getConnectionId() !== cid) {
44874 if (callback) callback({
44875 message: 'Connection Switched',
44881 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
44882 // Logout and retry the request..
44884 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
44886 that.loadFromAPI(path, callback, options); // else, no retry..
44888 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
44889 // Set the rateLimitError flag and trigger a warning..
44890 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
44891 _rateLimitError = err;
44892 dispatch$6.call('change');
44893 that.reloadApiStatus();
44894 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
44895 // If the response's error state doesn't match the status,
44896 // it's likely we lost or gained the connection so reload the status
44897 that.reloadApiStatus();
44902 return callback(err);
44904 if (path.indexOf('.json') !== -1) {
44905 return parseJSON(payload, callback, options);
44907 return parseXML(payload, callback, options);
44914 if (this.authenticated()) {
44920 var url = urlroot + path;
44921 var controller = new AbortController();
44923 signal: controller.signal
44924 }).then(function (data) {
44926 })["catch"](function (err) {
44927 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
44928 // but we can't access the response itself
44929 // https://github.com/d3/d3-fetch/issues/27
44931 var match = err.message.match(/^\d{3}/);
44936 statusText: err.message
44945 // Load a single entity by id (ways and relations use the `/full` call)
44946 // GET /api/0.6/node/#id
44947 // GET /api/0.6/[way|relation]/#id/full
44948 loadEntity: function loadEntity(id, callback) {
44949 var type = osmEntity.id.type(id);
44950 var osmID = osmEntity.id.toOSM(id);
44954 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
44955 if (callback) callback(err, {
44960 // Load a single entity with a specific version
44961 // GET /api/0.6/[node|way|relation]/#id/#version
44962 loadEntityVersion: function loadEntityVersion(id, version, callback) {
44963 var type = osmEntity.id.type(id);
44964 var osmID = osmEntity.id.toOSM(id);
44968 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
44969 if (callback) callback(err, {
44974 // Load multiple entities in chunks
44975 // (note: callback may be called multiple times)
44976 // Unlike `loadEntity`, child nodes and members are not fetched
44977 // GET /api/0.6/[nodes|ways|relations]?#parameters
44978 loadMultiple: function loadMultiple(ids, callback) {
44980 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
44981 Object.keys(groups).forEach(function (k) {
44982 var type = k + 's'; // nodes, ways, relations
44984 var osmIDs = groups[k].map(function (id) {
44985 return osmEntity.id.toOSM(id);
44990 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
44991 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
44992 if (callback) callback(err, {
44999 // Create, upload, and close a changeset
45000 // PUT /api/0.6/changeset/create
45001 // POST /api/0.6/changeset/#id/upload
45002 // PUT /api/0.6/changeset/#id/close
45003 putChangeset: function putChangeset(changeset, changes, callback) {
45004 var cid = _connectionID;
45006 if (_changeset.inflight) {
45008 message: 'Changeset already inflight',
45011 } else if (_changeset.open) {
45012 // reuse existing open changeset..
45013 return createdChangeset.call(this, null, _changeset.open);
45015 // Open a new changeset..
45018 path: '/api/0.6/changeset/create',
45021 'Content-Type': 'text/xml'
45024 content: JXON.stringify(changeset.asJXON())
45026 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
45029 function createdChangeset(err, changesetID) {
45030 _changeset.inflight = null;
45033 return callback(err, changeset);
45036 _changeset.open = changesetID;
45037 changeset = changeset.update({
45039 }); // Upload the changeset..
45043 path: '/api/0.6/changeset/' + changesetID + '/upload',
45046 'Content-Type': 'text/xml'
45049 content: JXON.stringify(changeset.osmChangeJXON(changes))
45051 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
45054 function uploadedChangeset(err) {
45055 _changeset.inflight = null;
45056 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
45057 // Add delay to allow for postgres replication #1646 #2678
45059 window.setTimeout(function () {
45060 callback(null, changeset);
45062 _changeset.open = null; // At this point, we don't really care if the connection was switched..
45063 // Only try to close the changeset if we're still talking to the same server.
45065 if (this.getConnectionId() === cid) {
45066 // Still attempt to close changeset, but ignore response because #2667
45069 path: '/api/0.6/changeset/' + changeset.id + '/close',
45072 'Content-Type': 'text/xml'
45081 // Load multiple users in chunks
45082 // (note: callback may be called multiple times)
45083 // GET /api/0.6/users?users=#id1,#id2,...,#idn
45084 loadUsers: function loadUsers(uids, callback) {
45087 utilArrayUniq(uids).forEach(function (uid) {
45088 if (_userCache.user[uid]) {
45089 delete _userCache.toLoad[uid];
45090 cached.push(_userCache.user[uid]);
45096 if (cached.length || !this.authenticated()) {
45097 callback(undefined, cached);
45098 if (!this.authenticated()) return; // require auth
45101 utilArrayChunk(toLoad, 150).forEach(function (arr) {
45104 path: '/api/0.6/users?users=' + arr.join()
45105 }, wrapcb(this, done, _connectionID));
45108 function done(err, xml) {
45110 return callback(err);
45116 return parseXML(xml, function (err, results) {
45118 return callback(err);
45120 return callback(undefined, results);
45125 // Load a given user by id
45126 // GET /api/0.6/user/#id
45127 loadUser: function loadUser(uid, callback) {
45128 if (_userCache.user[uid] || !this.authenticated()) {
45130 delete _userCache.toLoad[uid];
45131 return callback(undefined, _userCache.user[uid]);
45136 path: '/api/0.6/user/' + uid
45137 }, wrapcb(this, done, _connectionID));
45139 function done(err, xml) {
45141 return callback(err);
45147 return parseXML(xml, function (err, results) {
45149 return callback(err);
45151 return callback(undefined, results[0]);
45156 // Load the details of the logged-in user
45157 // GET /api/0.6/user/details
45158 userDetails: function userDetails(callback) {
45159 if (_userDetails) {
45161 return callback(undefined, _userDetails);
45166 path: '/api/0.6/user/details'
45167 }, wrapcb(this, done, _connectionID));
45169 function done(err, xml) {
45171 return callback(err);
45177 return parseXML(xml, function (err, results) {
45179 return callback(err);
45181 _userDetails = results[0];
45182 return callback(undefined, _userDetails);
45187 // Load previous changesets for the logged in user
45188 // GET /api/0.6/changesets?user=#id
45189 userChangesets: function userChangesets(callback) {
45190 if (_userChangesets) {
45192 return callback(undefined, _userChangesets);
45195 this.userDetails(wrapcb(this, gotDetails, _connectionID));
45197 function gotDetails(err, user) {
45199 return callback(err);
45204 path: '/api/0.6/changesets?user=' + user.id
45205 }, wrapcb(this, done, _connectionID));
45208 function done(err, xml) {
45210 return callback(err);
45213 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
45215 tags: getTags(changeset)
45217 }).filter(function (changeset) {
45218 var comment = changeset.tags.comment;
45219 return comment && comment !== '';
45221 return callback(undefined, _userChangesets);
45224 // Fetch the status of the OSM API
45225 // GET /api/capabilities
45226 status: function status(callback) {
45227 var url = urlroot + '/api/capabilities';
45228 var errback = wrapcb(this, done, _connectionID);
45229 d3_xml(url).then(function (data) {
45230 errback(null, data);
45231 })["catch"](function (err) {
45232 errback(err.message);
45235 function done(err, xml) {
45237 // the status is null if no response could be retrieved
45238 return callback(err, null);
45239 } // update blocklists
45242 var elements = xml.getElementsByTagName('blacklist');
45245 for (var i = 0; i < elements.length; i++) {
45246 var regexString = elements[i].getAttribute('regex'); // needs unencode?
45250 var regex = new RegExp(regexString);
45251 regexes.push(regex);
45258 if (regexes.length) {
45259 _imageryBlocklists = regexes;
45262 if (_rateLimitError) {
45263 return callback(_rateLimitError, 'rateLimited');
45265 var waynodes = xml.getElementsByTagName('waynodes');
45266 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
45267 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
45268 var apiStatus = xml.getElementsByTagName('status');
45269 var val = apiStatus[0].getAttribute('api');
45270 return callback(undefined, val);
45274 // Calls `status` and dispatches an `apiStatusChange` event if the returned
45275 // status differs from the cached status.
45276 reloadApiStatus: function reloadApiStatus() {
45277 // throttle to avoid unnecessary API calls
45278 if (!this.throttledReloadApiStatus) {
45280 this.throttledReloadApiStatus = throttle(function () {
45281 that.status(function (err, status) {
45282 if (status !== _cachedApiStatus) {
45283 _cachedApiStatus = status;
45284 dispatch$6.call('apiStatusChange', that, err, status);
45290 this.throttledReloadApiStatus();
45292 // Returns the maximum number of nodes a single way can have
45293 maxWayNodes: function maxWayNodes() {
45294 return _maxWayNodes;
45296 // Load data (entities) from the API in tiles
45297 // GET /api/0.6/map?bbox=
45298 loadTiles: function loadTiles(projection, callback) {
45299 if (_off) return; // determine the needed tiles to cover the view
45301 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
45303 var hadRequests = hasInflightRequests(_tileCache);
45304 abortUnwantedRequests$3(_tileCache, tiles);
45306 if (hadRequests && !hasInflightRequests(_tileCache)) {
45307 dispatch$6.call('loaded'); // stop the spinner
45308 } // issue new requests..
45311 tiles.forEach(function (tile) {
45312 this.loadTile(tile, callback);
45315 // Load a single data tile
45316 // GET /api/0.6/map?bbox=
45317 loadTile: function loadTile(tile, callback) {
45319 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45321 if (!hasInflightRequests(_tileCache)) {
45322 dispatch$6.call('loading'); // start the spinner
45325 var path = '/api/0.6/map.json?bbox=';
45329 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
45331 function tileCallback(err, parsed) {
45332 delete _tileCache.inflight[tile.id];
45335 delete _tileCache.toLoad[tile.id];
45336 _tileCache.loaded[tile.id] = true;
45337 var bbox = tile.extent.bbox();
45340 _tileCache.rtree.insert(bbox);
45344 callback(err, Object.assign({
45349 if (!hasInflightRequests(_tileCache)) {
45350 dispatch$6.call('loaded'); // stop the spinner
45354 isDataLoaded: function isDataLoaded(loc) {
45361 return _tileCache.rtree.collides(bbox);
45363 // load the tile that covers the given `loc`
45364 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
45365 // Back off if the toLoad queue is filling up.. re #6417
45366 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
45367 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
45368 if (Object.keys(_tileCache.toLoad).length > 50) return;
45369 var k = geoZoomToScale(_tileZoom$3 + 1);
45370 var offset = geoRawMercator().scale(k)(loc);
45371 var projection = geoRawMercator().transform({
45376 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
45377 tiles.forEach(function (tile) {
45378 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45379 _tileCache.toLoad[tile.id] = true;
45380 this.loadTile(tile, callback);
45383 // Load notes from the API in tiles
45384 // GET /api/0.6/notes?bbox=
45385 loadNotes: function loadNotes(projection, noteOptions) {
45386 noteOptions = Object.assign({
45392 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
45394 var throttleLoadUsers = throttle(function () {
45395 var uids = Object.keys(_userCache.toLoad);
45396 if (!uids.length) return;
45397 that.loadUsers(uids, function () {}); // eagerly load user details
45398 }, 750); // determine the needed tiles to cover the view
45401 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
45403 abortUnwantedRequests$3(_noteCache, tiles); // issue new requests..
45405 tiles.forEach(function (tile) {
45406 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
45410 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
45411 delete _noteCache.inflight[tile.id];
45414 _noteCache.loaded[tile.id] = true;
45417 throttleLoadUsers();
45418 dispatch$6.call('loadedNotes');
45423 // POST /api/0.6/notes?params
45424 postNoteCreate: function postNoteCreate(note, callback) {
45425 if (!this.authenticated()) {
45427 message: 'Not Authenticated',
45432 if (_noteCache.inflightPost[note.id]) {
45434 message: 'Note update already inflight',
45439 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
45441 var comment = note.newComment;
45443 if (note.newCategory && note.newCategory !== 'None') {
45444 comment += ' #' + note.newCategory;
45447 var path = '/api/0.6/notes?' + utilQsString({
45452 _noteCache.inflightPost[note.id] = oauth.xhr({
45455 }, wrapcb(this, done, _connectionID));
45457 function done(err, xml) {
45458 delete _noteCache.inflightPost[note.id];
45461 return callback(err);
45462 } // we get the updated note back, remove from caches and reparse..
45465 this.removeNote(note);
45469 return parseXML(xml, function (err, results) {
45471 return callback(err);
45473 return callback(undefined, results[0]);
45479 // POST /api/0.6/notes/#id/comment?text=comment
45480 // POST /api/0.6/notes/#id/close?text=comment
45481 // POST /api/0.6/notes/#id/reopen?text=comment
45482 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
45483 if (!this.authenticated()) {
45485 message: 'Not Authenticated',
45490 if (_noteCache.inflightPost[note.id]) {
45492 message: 'Note update already inflight',
45499 if (note.status !== 'closed' && newStatus === 'closed') {
45501 } else if (note.status !== 'open' && newStatus === 'open') {
45504 action = 'comment';
45505 if (!note.newComment) return; // when commenting, comment required
45508 var path = '/api/0.6/notes/' + note.id + '/' + action;
45510 if (note.newComment) {
45511 path += '?' + utilQsString({
45512 text: note.newComment
45516 _noteCache.inflightPost[note.id] = oauth.xhr({
45519 }, wrapcb(this, done, _connectionID));
45521 function done(err, xml) {
45522 delete _noteCache.inflightPost[note.id];
45525 return callback(err);
45526 } // we get the updated note back, remove from caches and reparse..
45529 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
45531 if (action === 'close') {
45532 _noteCache.closed[note.id] = true;
45533 } else if (action === 'reopen') {
45534 delete _noteCache.closed[note.id];
45540 return parseXML(xml, function (err, results) {
45542 return callback(err);
45544 return callback(undefined, results[0]);
45549 "switch": function _switch(options) {
45550 urlroot = options.urlroot;
45551 oauth.options(Object.assign({
45553 loading: authLoading,
45557 this.userChangesets(function () {}); // eagerly load user details/changesets
45559 dispatch$6.call('change');
45562 toggle: function toggle(val) {
45566 isChangesetInflight: function isChangesetInflight() {
45567 return !!_changeset.inflight;
45569 // get/set cached data
45570 // This is used to save/restore the state when entering/exiting the walkthrough
45571 // Also used for testing purposes.
45572 caches: function caches(obj) {
45573 function cloneCache(source) {
45575 Object.keys(source).forEach(function (k) {
45576 if (k === 'rtree') {
45577 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
45578 } else if (k === 'note') {
45580 Object.keys(source.note).forEach(function (id) {
45581 target.note[id] = osmNote(source.note[id]); // copy notes
45584 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
45590 if (!arguments.length) {
45592 tile: cloneCache(_tileCache),
45593 note: cloneCache(_noteCache),
45594 user: cloneCache(_userCache)
45596 } // access caches directly for testing (e.g., loading notes rtree)
45599 if (obj === 'get') {
45608 _tileCache = obj.tile;
45609 _tileCache.inflight = {};
45613 _noteCache = obj.note;
45614 _noteCache.inflight = {};
45615 _noteCache.inflightPost = {};
45619 _userCache = obj.user;
45624 logout: function logout() {
45625 _userChangesets = undefined;
45626 _userDetails = undefined;
45628 dispatch$6.call('change');
45631 authenticated: function authenticated() {
45632 return oauth.authenticated();
45634 authenticate: function authenticate(callback) {
45636 var cid = _connectionID;
45637 _userChangesets = undefined;
45638 _userDetails = undefined;
45640 function done(err, res) {
45642 if (callback) callback(err);
45646 if (that.getConnectionId() !== cid) {
45647 if (callback) callback({
45648 message: 'Connection Switched',
45654 _rateLimitError = undefined;
45655 dispatch$6.call('change');
45656 if (callback) callback(err, res);
45657 that.userChangesets(function () {}); // eagerly load user details/changesets
45660 return oauth.authenticate(done);
45662 imageryBlocklists: function imageryBlocklists() {
45663 return _imageryBlocklists;
45665 tileZoom: function tileZoom(val) {
45666 if (!arguments.length) return _tileZoom$3;
45670 // get all cached notes covering the viewport
45671 notes: function notes(projection) {
45672 var viewport = projection.clipExtent();
45673 var min = [viewport[0][0], viewport[1][1]];
45674 var max = [viewport[1][0], viewport[0][1]];
45675 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
45676 return _noteCache.rtree.search(bbox).map(function (d) {
45680 // get a single note from the cache
45681 getNote: function getNote(id) {
45682 return _noteCache.note[id];
45684 // remove a single note from the cache
45685 removeNote: function removeNote(note) {
45686 if (!(note instanceof osmNote) || !note.id) return;
45687 delete _noteCache.note[note.id];
45688 updateRtree$3(encodeNoteRtree(note), false); // false = remove
45690 // replace a single note in the cache
45691 replaceNote: function replaceNote(note) {
45692 if (!(note instanceof osmNote) || !note.id) return;
45693 _noteCache.note[note.id] = note;
45694 updateRtree$3(encodeNoteRtree(note), true); // true = replace
45698 // Get an array of note IDs closed during this session.
45699 // Used to populate `closed:note` changeset tag
45700 getClosedIDs: function getClosedIDs() {
45701 return Object.keys(_noteCache.closed).sort();
45705 var _apibase = 'https://wiki.openstreetmap.org/w/api.php';
45706 var _inflight$1 = {};
45707 var _wikibaseCache = {};
45712 var debouncedRequest = debounce(request, 500, {
45716 function request(url, callback) {
45717 if (_inflight$1[url]) return;
45718 var controller = new AbortController();
45719 _inflight$1[url] = controller;
45721 signal: controller.signal
45722 }).then(function (result) {
45723 delete _inflight$1[url];
45724 if (callback) callback(null, result);
45725 })["catch"](function (err) {
45726 delete _inflight$1[url];
45727 if (err.name === 'AbortError') return;
45728 if (callback) callback(err.message);
45732 var serviceOsmWikibase = {
45733 init: function init() {
45735 _wikibaseCache = {};
45738 reset: function reset() {
45739 Object.values(_inflight$1).forEach(function (controller) {
45740 controller.abort();
45746 * Get the best value for the property, or undefined if not found
45747 * @param entity object from wikibase
45748 * @param property string e.g. 'P4' for image
45749 * @param langCode string e.g. 'fr' for French
45751 claimToValue: function claimToValue(entity, property, langCode) {
45752 if (!entity.claims[property]) return undefined;
45753 var locale = _localeIDs[langCode];
45754 var preferredPick, localePick;
45755 entity.claims[property].forEach(function (stmt) {
45756 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
45757 // Or if not found, use the first value with the "preferred" rank
45758 if (!preferredPick && stmt.rank === 'preferred') {
45759 preferredPick = stmt;
45762 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
45766 var result = localePick || preferredPick;
45769 var datavalue = result.mainsnak.datavalue;
45770 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
45777 * Convert monolingual property into a key-value object (language -> value)
45778 * @param entity object from wikibase
45779 * @param property string e.g. 'P31' for monolingual wiki page title
45781 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
45782 if (!entity || !entity.claims[property]) return undefined;
45783 return entity.claims[property].reduce(function (acc, obj) {
45784 var value = obj.mainsnak.datavalue.value;
45785 acc[value.language] = value.text;
45789 toSitelink: function toSitelink(key, value) {
45790 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
45791 return result.replace(/_/g, ' ').trim();
45794 // Pass params object of the form:
45797 // value: 'string',
45798 // langCode: 'string'
45801 getEntity: function getEntity(params, callback) {
45802 var doRequest = params.debounce ? debouncedRequest : request;
45806 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
45807 var keySitelink = params.key ? this.toSitelink(params.key) : false;
45808 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
45809 var localeSitelink;
45811 if (params.langCodes) {
45812 params.langCodes.forEach(function (langCode) {
45813 if (_localeIDs[langCode] === undefined) {
45814 // If this is the first time we are asking about this locale,
45815 // fetch corresponding entity (if it exists), and cache it.
45816 // If there is no such entry, cache `false` value to avoid re-requesting it.
45817 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
45818 titles.push(localeSitelink);
45823 if (rtypeSitelink) {
45824 if (_wikibaseCache[rtypeSitelink]) {
45825 result.rtype = _wikibaseCache[rtypeSitelink];
45827 titles.push(rtypeSitelink);
45832 if (_wikibaseCache[keySitelink]) {
45833 result.key = _wikibaseCache[keySitelink];
45835 titles.push(keySitelink);
45840 if (_wikibaseCache[tagSitelink]) {
45841 result.tag = _wikibaseCache[tagSitelink];
45843 titles.push(tagSitelink);
45847 if (!titles.length) {
45848 // Nothing to do, we already had everything in the cache
45849 return callback(null, result);
45850 } // Requesting just the user language code
45851 // If backend recognizes the code, it will perform proper fallbacks,
45852 // and the result will contain the requested code. If not, all values are returned:
45853 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
45854 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
45858 action: 'wbgetentities',
45860 titles: titles.join('|'),
45861 languages: params.langCodes.join('|'),
45862 languagefallback: 1,
45864 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
45865 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
45866 // formatversion: 2,
45869 var url = _apibase + '?' + utilQsString(obj);
45870 doRequest(url, function (err, d) {
45873 } else if (!d.success || d.error) {
45874 callback(d.error.messages.map(function (v) {
45875 return v.html['*'];
45878 var localeID = false;
45879 Object.values(d.entities).forEach(function (res) {
45880 if (res.missing !== '') {
45881 var title = res.sitelinks.wiki.title;
45883 if (title === rtypeSitelink) {
45884 _wikibaseCache[rtypeSitelink] = res;
45885 result.rtype = res;
45886 } else if (title === keySitelink) {
45887 _wikibaseCache[keySitelink] = res;
45889 } else if (title === tagSitelink) {
45890 _wikibaseCache[tagSitelink] = res;
45892 } else if (title === localeSitelink) {
45895 console.log('Unexpected title ' + title); // eslint-disable-line no-console
45900 if (localeSitelink) {
45901 // If locale ID is not found, store false to prevent repeated queries
45902 that.addLocale(params.langCodes[0], localeID);
45905 callback(null, result);
45910 // Pass params object of the form:
45912 // key: 'string', // required
45913 // value: 'string' // optional
45916 // Get an result object used to display tag documentation
45918 // title: 'string',
45919 // description: 'string',
45920 // editURL: 'string',
45921 // imageURL: 'string',
45922 // wiki: { title: 'string', text: 'string', url: 'string' }
45925 getDocs: function getDocs(params, callback) {
45927 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
45928 return code.toLowerCase();
45930 params.langCodes = langCodes;
45931 this.getEntity(params, function (err, data) {
45937 var entity = data.rtype || data.tag || data.key;
45940 callback('No entity');
45947 for (i in langCodes) {
45948 var _code = langCodes[i];
45950 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
45951 description = entity.descriptions[_code];
45956 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
45959 title: entity.title,
45960 description: description ? description.value : '',
45961 descriptionLocaleCode: description ? description.language : '',
45962 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
45965 if (entity.claims) {
45967 var image = that.claimToValue(entity, 'P4', langCodes[0]);
45970 imageroot = 'https://commons.wikimedia.org/w/index.php';
45972 image = that.claimToValue(entity, 'P28', langCodes[0]);
45975 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
45979 if (imageroot && image) {
45980 result.imageURL = imageroot + '?' + utilQsString({
45981 title: 'Special:Redirect/file/' + image,
45985 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
45986 // If neither tag nor key data item contain a wiki page in the needed language nor English,
45987 // get the first found wiki page from either the tag or the key item.
45990 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
45991 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
45992 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
45993 var wikis = [rtypeWiki, tagWiki, keyWiki];
45996 var wiki = wikis[i];
45998 for (var j in langCodes) {
45999 var code = langCodes[j];
46000 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
46001 var info = getWikiInfo(wiki, code, referenceId);
46004 result.wiki = info;
46009 if (result.wiki) break;
46012 callback(null, result); // Helper method to get wiki info if a given language exists
46014 function getWikiInfo(wiki, langCode, tKey) {
46015 if (wiki && wiki[langCode]) {
46017 title: wiki[langCode],
46019 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
46025 addLocale: function addLocale(langCode, qid) {
46026 // Makes it easier to unit test
46027 _localeIDs[langCode] = qid;
46029 apibase: function apibase(val) {
46030 if (!arguments.length) return _apibase;
46036 var jsonpCache = {};
46037 window.jsonpCache = jsonpCache;
46038 function jsonpRequest(url, callback) {
46040 abort: function abort() {}
46043 if (window.JSONP_FIX) {
46044 if (window.JSONP_DELAY === 0) {
46045 callback(window.JSONP_FIX);
46047 var t = window.setTimeout(function () {
46048 callback(window.JSONP_FIX);
46049 }, window.JSONP_DELAY || 0);
46051 request.abort = function () {
46052 window.clearTimeout(t);
46060 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
46065 c += chars.charAt(Math.floor(Math.random() * 52));
46071 function create(url) {
46072 var e = url.match(/callback=(\w+)/);
46073 var c = e ? e[1] : rand();
46075 jsonpCache[c] = function (data) {
46076 if (jsonpCache[c]) {
46083 function finalize() {
46084 delete jsonpCache[c];
46088 request.abort = finalize;
46089 return 'jsonpCache.' + c;
46092 var cb = create(url);
46093 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
46097 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
46098 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
46099 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
46100 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
46101 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
46102 var maxResults$2 = 2000;
46103 var tileZoom$2 = 16.5;
46104 var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
46105 var dispatch$7 = dispatch('loadedImages', 'viewerChanged');
46106 var minHfov = 10; // zoom in degrees: 20, 10, 5
46108 var maxHfov = 90; // zoom out degrees
46110 var defaultHfov = 45;
46111 var _hires = false;
46112 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
46114 var _currScene = 0;
46118 var _pannellumViewer;
46120 var _sceneOptions = {
46121 showFullscreenCtrl: false,
46132 var _loadViewerPromise$2;
46138 function abortRequest$6(i) {
46142 * localeTimeStamp().
46146 function localeTimestamp(s) {
46147 if (!s) return null;
46153 var d = new Date(s);
46154 if (isNaN(d.getTime())) return null;
46155 return d.toLocaleString(_mainLocalizer.localeCode(), options);
46158 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
46162 function loadTiles$2(which, url, projection, margin) {
46163 var tiles = tiler$6.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
46165 var cache = _ssCache[which];
46166 Object.keys(cache.inflight).forEach(function (k) {
46167 var wanted = tiles.find(function (tile) {
46168 return k.indexOf(tile.id + ',') === 0;
46172 abortRequest$6(cache.inflight[k]);
46173 delete cache.inflight[k];
46176 tiles.forEach(function (tile) {
46177 return loadNextTilePage$2(which, url, tile);
46181 * loadNextTilePage() load data for the next tile page in line.
46185 function loadNextTilePage$2(which, url, tile) {
46186 var cache = _ssCache[which];
46187 var nextPage = cache.nextPage[tile.id] || 0;
46188 var id = tile.id + ',' + String(nextPage);
46189 if (cache.loaded[id] || cache.inflight[id]) return;
46190 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
46191 cache.loaded[id] = true;
46192 delete cache.inflight[id];
46193 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
46196 var features = bubbles.map(function (bubble) {
46197 if (cache.points[bubble.id]) return null; // skip duplicates
46199 var loc = [bubble.lo, bubble.la];
46204 captured_at: bubble.cd,
46205 captured_by: 'microsoft',
46206 // nbn: bubble.nbn,
46207 // pbn: bubble.pbn,
46217 cache.points[bubble.id] = d; // a sequence starts here
46219 if (bubble.pr === undefined) {
46220 cache.leaders.push(bubble.id);
46230 }).filter(Boolean);
46231 cache.rtree.load(features);
46232 connectSequences();
46234 if (which === 'bubbles') {
46235 dispatch$7.call('loadedImages');
46238 } // call this sometimes to connect the bubbles into sequences
46241 function connectSequences() {
46242 var cache = _ssCache.bubbles;
46243 var keepLeaders = [];
46245 for (var i = 0; i < cache.leaders.length; i++) {
46246 var bubble = cache.points[cache.leaders[i]];
46247 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
46253 var complete = false;
46256 sequence.bubbles.push(bubble);
46257 seen[bubble.key] = true;
46259 if (bubble.ne === undefined) {
46262 bubble = cache.points[bubble.ne]; // advance to next
46264 } while (bubble && !seen[bubble.key] && !complete);
46267 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
46269 for (var j = 0; j < sequence.bubbles.length; j++) {
46270 sequence.bubbles[j].sequenceKey = sequence.key;
46271 } // create a GeoJSON LineString
46274 sequence.geojson = {
46275 type: 'LineString',
46277 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
46278 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
46281 coordinates: sequence.bubbles.map(function (d) {
46286 keepLeaders.push(cache.leaders[i]);
46288 } // couldn't complete these, save for later
46291 cache.leaders = keepLeaders;
46294 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
46298 function getBubbles(url, tile, callback) {
46299 var rect = tile.extent.rectangle();
46300 var urlForRequest = url + utilQsString({
46306 appkey: bubbleAppKey,
46307 jsCallback: '{callback}'
46309 return jsonpRequest(urlForRequest, function (data) {
46310 if (!data || data.error) {
46316 } // partition viewport into higher zoom tiles
46319 function partitionViewport$2(projection) {
46320 var z = geoScaleToZoom(projection.scale());
46321 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
46323 var tiler = utilTiler().zoomExtent([z2, z2]);
46324 return tiler.getTiles(projection).map(function (tile) {
46325 return tile.extent;
46327 } // no more than `limit` results per partition.
46330 function searchLimited$2(limit, projection, rtree) {
46331 limit = limit || 5;
46332 return partitionViewport$2(projection).reduce(function (result, extent) {
46333 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
46336 return found.length ? result.concat(found) : result;
46344 function loadImage(imgInfo) {
46345 return new Promise(function (resolve) {
46346 var img = new Image();
46348 img.onload = function () {
46349 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
46350 var ctx = canvas.getContext('2d');
46351 ctx.drawImage(img, imgInfo.x, imgInfo.y);
46358 img.onerror = function () {
46365 img.setAttribute('crossorigin', '');
46366 img.src = imgInfo.url;
46374 function loadCanvas(imageGroup) {
46375 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
46376 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
46385 var face = data[0].imgInfo.face;
46386 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
46388 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
46397 function loadFaces(faceGroup) {
46398 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
46400 status: 'loadFaces done'
46405 function setupCanvas(selection, reset) {
46407 selection.selectAll('#ideditor-stitcher-canvases').remove();
46408 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
46409 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
46412 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) {
46413 return 'ideditor-' + d;
46414 }).attr('width', _resolution).attr('height', _resolution);
46417 function qkToXY(qk) {
46422 for (var i = qk.length; i > 0; i--) {
46423 var key = qk[i - 1];
46424 x += +(key === '1' || key === '3') * scale;
46425 y += +(key === '2' || key === '3') * scale;
46432 function getQuadKeys() {
46433 var dim = _resolution / 256;
46437 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'];
46438 } else if (dim === 8) {
46439 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'];
46440 } else if (dim === 4) {
46441 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
46444 quadKeys = ['0', '1', '2', '3'];
46450 var serviceStreetside = {
46452 * init() initialize streetside.
46454 init: function init() {
46459 this.event = utilRebind(this, dispatch$7, 'on');
46463 * reset() reset the cache.
46465 reset: function reset() {
46467 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
46475 rtree: new RBush(),
46486 bubbles: function bubbles(projection) {
46488 return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
46490 cachedImage: function cachedImage(imageKey) {
46491 return _ssCache.bubbles.points[imageKey];
46493 sequences: function sequences(projection) {
46494 var viewport = projection.clipExtent();
46495 var min = [viewport[0][0], viewport[1][1]];
46496 var max = [viewport[1][0], viewport[0][1]];
46497 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46499 var results = []; // all sequences for bubbles in viewport
46501 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
46502 var key = d.data.sequenceKey;
46504 if (key && !seen[key]) {
46506 results.push(_ssCache.sequences[key].geojson);
46516 loadBubbles: function loadBubbles(projection, margin) {
46517 // by default: request 2 nearby tiles so we can connect sequences.
46518 if (margin === undefined) margin = 2;
46519 loadTiles$2('bubbles', bubbleApi, projection, margin);
46521 viewer: function viewer() {
46522 return _pannellumViewer;
46524 initViewer: function initViewer() {
46525 if (!window.pannellum) return;
46526 if (_pannellumViewer) return;
46529 var sceneID = _currScene.toString();
46533 firstScene: sceneID
46537 options.scenes[sceneID] = _sceneOptions;
46538 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
46540 ensureViewerLoaded: function ensureViewerLoaded(context) {
46541 if (_loadViewerPromise$2) return _loadViewerPromise$2; // create ms-wrapper, a photo wrapper class
46543 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
46544 // (used by all to house each custom photo viewer)
46546 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
46548 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
46550 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
46551 select(window).on(pointerPrefix + 'move.streetside', function () {
46552 dispatch$7.call('viewerChanged');
46554 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
46555 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
46557 var t = timer(function (elapsed) {
46558 dispatch$7.call('viewerChanged');
46560 if (elapsed > 2000) {
46564 }).append('div').attr('class', 'photo-attribution fillD');
46565 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
46566 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
46567 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
46569 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
46571 context.ui().photoviewer.on('resize.streetside', function () {
46572 if (_pannellumViewer) {
46573 _pannellumViewer.resize();
46576 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
46577 var loadedCount = 0;
46579 function loaded() {
46580 loadedCount += 1; // wait until both files are loaded
46582 if (loadedCount === 2) resolve();
46585 var head = select('head'); // load streetside pannellum viewer css
46587 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 () {
46589 }); // load streetside pannellum viewer js
46591 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 () {
46594 })["catch"](function () {
46595 _loadViewerPromise$2 = null;
46597 return _loadViewerPromise$2;
46599 function step(stepBy) {
46600 return function () {
46601 var viewer = context.container().select('.photoviewer');
46602 var selected = viewer.empty() ? undefined : viewer.datum();
46603 if (!selected) return;
46604 var nextID = stepBy === 1 ? selected.ne : selected.pr;
46606 var yaw = _pannellumViewer.getYaw();
46608 var ca = selected.ca + yaw;
46609 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
46612 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
46613 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46614 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46615 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
46616 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
46618 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
46619 poly = geoRotate(poly, -angle, origin);
46620 var extent = poly.reduce(function (extent, point) {
46621 return extent.extend(geoExtent(point));
46622 }, geoExtent()); // find nearest other bubble in the search polygon
46624 var minDist = Infinity;
46626 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
46627 if (d.data.key === selected.key) return;
46628 if (!geoPointInPolygon(d.data.loc, poly)) return;
46629 var dist = geoVecLength(d.data.loc, selected.loc);
46630 var theta = selected.ca - d.data.ca;
46631 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
46633 if (minTheta > 20) {
46634 dist += 5; // penalize distance if camera angles don't match
46637 if (dist < minDist) {
46638 nextID = d.data.key;
46643 var nextBubble = nextID && that.cachedImage(nextID);
46644 if (!nextBubble) return;
46645 context.map().centerEase(nextBubble.loc);
46646 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
46650 yaw: function yaw(_yaw) {
46651 if (typeof _yaw !== 'number') return _yaw;
46652 _sceneOptions.yaw = _yaw;
46659 showViewer: function showViewer(context) {
46660 var wrap = context.container().select('.photoviewer').classed('hide', false);
46661 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
46664 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
46665 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
46674 hideViewer: function hideViewer(context) {
46675 var viewer = context.container().select('.photoviewer');
46676 if (!viewer.empty()) viewer.datum(null);
46677 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
46678 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
46679 this.updateUrlImage(null);
46680 return this.setStyles(context, null, true);
46686 selectImage: function selectImage(context, key) {
46688 var d = this.cachedImage(key);
46689 var viewer = context.container().select('.photoviewer');
46690 if (!viewer.empty()) viewer.datum(d);
46691 this.setStyles(context, null, true);
46692 var wrap = context.container().select('.photoviewer .ms-wrapper');
46693 var attribution = wrap.selectAll('.photo-attribution').html('');
46694 wrap.selectAll('.pnlm-load-box') // display "loading.."
46695 .style('display', 'block');
46696 if (!d) return this;
46697 this.updateUrlImage(key);
46698 _sceneOptions.northOffset = d.ca;
46699 var line1 = attribution.append('div').attr('class', 'attribution-row');
46700 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
46702 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
46703 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
46704 d3_event.stopPropagation();
46706 _resolution = _hires ? 1024 : 512;
46707 wrap.call(setupCanvas, true);
46709 yaw: _pannellumViewer.getYaw(),
46710 pitch: _pannellumViewer.getPitch(),
46711 hfov: _pannellumViewer.getHfov()
46713 _sceneOptions = Object.assign(_sceneOptions, viewstate);
46714 that.selectImage(context, d.key).showViewer(context);
46716 label.append('span').html(_t.html('streetside.hires'));
46717 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
46719 if (d.captured_by) {
46720 var yyyy = new Date().getFullYear();
46721 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
46722 captureInfo.append('span').html('|');
46725 if (d.captured_at) {
46726 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
46727 } // Add image links
46730 var line2 = attribution.append('div').attr('class', 'attribution-row');
46731 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'));
46732 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'));
46733 var bubbleIdQuadKey = d.key.toString(4);
46734 var paddingNeeded = 16 - bubbleIdQuadKey.length;
46736 for (var i = 0; i < paddingNeeded; i++) {
46737 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
46740 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
46741 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
46743 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
46745 var quadKeys = getQuadKeys();
46746 var faces = faceKeys.map(function (faceKey) {
46747 return quadKeys.map(function (quadKey) {
46748 var xy = qkToXY(quadKey);
46751 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
46757 loadFaces(faces).then(function () {
46758 if (!_pannellumViewer) {
46761 // make a new scene
46764 var sceneID = _currScene.toString();
46766 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
46769 if (_currScene > 2) {
46770 sceneID = (_currScene - 1).toString();
46772 _pannellumViewer.removeScene(sceneID);
46778 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
46779 return d && d.sequenceKey;
46781 // Updates the currently highlighted sequence and selected bubble.
46782 // Reset is only necessary when interacting with the viewport because
46783 // this implicitly changes the currently selected bubble/sequence
46784 setStyles: function setStyles(context, hovered, reset) {
46786 // reset all layers
46787 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
46788 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
46791 var hoveredBubbleKey = hovered && hovered.key;
46792 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
46793 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
46794 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
46797 var viewer = context.container().select('.photoviewer');
46798 var selected = viewer.empty() ? undefined : viewer.datum();
46799 var selectedBubbleKey = selected && selected.key;
46800 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
46801 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
46802 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
46804 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
46806 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
46807 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
46808 return highlightedBubbleKeys.indexOf(d.key) !== -1;
46809 }).classed('hovered', function (d) {
46810 return d.key === hoveredBubbleKey;
46811 }).classed('currentView', function (d) {
46812 return d.key === selectedBubbleKey;
46814 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
46815 return d.properties.key === hoveredSequenceKey;
46816 }).classed('currentView', function (d) {
46817 return d.properties.key === selectedSequenceKey;
46818 }); // update viewfields if needed
46820 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
46822 function viewfieldPath() {
46823 var d = this.parentNode.__data__;
46825 if (d.pano && d.key !== selectedBubbleKey) {
46826 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
46828 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
46834 updateUrlImage: function updateUrlImage(imageKey) {
46835 if (!window.mocha) {
46836 var hash = utilStringQs(window.location.hash);
46839 hash.photo = 'streetside/' + imageKey;
46844 window.location.replace('#' + utilQsString(hash, true));
46851 cache: function cache() {
46856 var _apibase$1 = 'https://taginfo.openstreetmap.org/api/4/';
46857 var _inflight$2 = {};
46858 var _popularKeys = {};
46859 var _taginfoCache = {};
46861 point: 'count_nodes',
46862 vertex: 'count_nodes',
46863 area: 'count_ways',
46866 var tag_sort_members = {
46867 point: 'count_node_members',
46868 vertex: 'count_node_members',
46869 area: 'count_way_members',
46870 line: 'count_way_members',
46871 relation: 'count_relation_members'
46873 var tag_filters = {
46879 var tag_members_fractions = {
46880 point: 'count_node_members_fraction',
46881 vertex: 'count_node_members_fraction',
46882 area: 'count_way_members_fraction',
46883 line: 'count_way_members_fraction',
46884 relation: 'count_relation_members_fraction'
46887 function sets(params, n, o) {
46888 if (params.geometry && o[params.geometry]) {
46889 params[n] = o[params.geometry];
46895 function setFilter(params) {
46896 return sets(params, 'filter', tag_filters);
46899 function setSort(params) {
46900 return sets(params, 'sortname', tag_sorts);
46903 function setSortMembers(params) {
46904 return sets(params, 'sortname', tag_sort_members);
46907 function clean(params) {
46908 return utilObjectOmit(params, ['geometry', 'debounce']);
46911 function filterKeys(type) {
46912 var count_type = type ? 'count_' + type : 'count_all';
46913 return function (d) {
46914 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
46918 function filterMultikeys(prefix) {
46919 return function (d) {
46920 // d.key begins with prefix, and d.key contains no additional ':'s
46921 var re = new RegExp('^' + prefix + '(.*)$');
46922 var matches = d.key.match(re) || [];
46923 return matches.length === 2 && matches[1].indexOf(':') === -1;
46927 function filterValues(allowUpperCase) {
46928 return function (d) {
46929 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
46931 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
46933 return parseFloat(d.fraction) > 0.0;
46937 function filterRoles(geometry) {
46938 return function (d) {
46939 if (d.role === '') return false; // exclude empty role
46941 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
46943 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
46947 function valKey(d) {
46954 function valKeyDescription(d) {
46957 title: d.description || d.value
46961 obj.count = d.count;
46967 function roleKey(d) {
46972 } // sort keys with ':' lower than keys without ':'
46975 function sortKeys(a, b) {
46976 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
46979 var debouncedRequest$1 = debounce(request$1, 300, {
46983 function request$1(url, params, exactMatch, callback, loaded) {
46984 if (_inflight$2[url]) return;
46985 if (checkCache(url, params, exactMatch, callback)) return;
46986 var controller = new AbortController();
46987 _inflight$2[url] = controller;
46989 signal: controller.signal
46990 }).then(function (result) {
46991 delete _inflight$2[url];
46992 if (loaded) loaded(null, result);
46993 })["catch"](function (err) {
46994 delete _inflight$2[url];
46995 if (err.name === 'AbortError') return;
46996 if (loaded) loaded(err.message);
47000 function checkCache(url, params, exactMatch, callback) {
47001 var rp = params.rp || 25;
47002 var testQuery = params.query || '';
47006 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
47008 if (hit && (url === testUrl || hit.length < rp)) {
47009 callback(null, hit);
47011 } // don't try to shorten the query
47014 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
47015 // that has returned fewer than max results (rp)
47017 testQuery = testQuery.slice(0, -1);
47018 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
47019 } while (testQuery.length >= 0);
47024 var serviceTaginfo = {
47025 init: function init() {
47027 _taginfoCache = {};
47029 // manually exclude some keys – #5377, #7485
47035 sorting_name: true,
47039 'bridge:name': true
47040 }; // Fetch popular keys. We'll exclude these from `values`
47041 // lookups because they stress taginfo, and they aren't likely
47042 // to yield meaningful autocomplete results.. see #3955
47046 sortname: 'values_all',
47050 lang: _mainLocalizer.languageCode()
47052 this.keys(params, function (err, data) {
47054 data.forEach(function (d) {
47055 if (d.value === 'opening_hours') return; // exception
47057 _popularKeys[d.value] = true;
47061 reset: function reset() {
47062 Object.values(_inflight$2).forEach(function (controller) {
47063 controller.abort();
47067 keys: function keys(params, callback) {
47068 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47069 params = clean(setSort(params));
47070 params = Object.assign({
47072 sortname: 'count_all',
47075 lang: _mainLocalizer.languageCode()
47077 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47078 doRequest(url, params, false, callback, function (err, d) {
47082 var f = filterKeys(params.filter);
47083 var result = d.data.filter(f).sort(sortKeys).map(valKey);
47084 _taginfoCache[url] = result;
47085 callback(null, result);
47089 multikeys: function multikeys(params, callback) {
47090 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47091 params = clean(setSort(params));
47092 params = Object.assign({
47094 sortname: 'count_all',
47097 lang: _mainLocalizer.languageCode()
47099 var prefix = params.query;
47100 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47101 doRequest(url, params, true, callback, function (err, d) {
47105 var f = filterMultikeys(prefix);
47106 var result = d.data.filter(f).map(valKey);
47107 _taginfoCache[url] = result;
47108 callback(null, result);
47112 values: function values(params, callback) {
47113 // Exclude popular keys from values lookups.. see #3955
47114 var key = params.key;
47116 if (key && _popularKeys[key]) {
47117 callback(null, []);
47121 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47122 params = clean(setSort(setFilter(params)));
47123 params = Object.assign({
47125 sortname: 'count_all',
47128 lang: _mainLocalizer.languageCode()
47130 var url = _apibase$1 + 'key/values?' + utilQsString(params);
47131 doRequest(url, params, false, callback, function (err, d) {
47135 // In most cases we prefer taginfo value results with lowercase letters.
47136 // A few OSM keys expect values to contain uppercase values (see #3377).
47137 // This is not an exhaustive list (e.g. `name` also has uppercase values)
47138 // but these are the fields where taginfo value lookup is most useful.
47139 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
47140 var allowUpperCase = re.test(params.key);
47141 var f = filterValues(allowUpperCase);
47142 var result = d.data.filter(f).map(valKeyDescription);
47143 _taginfoCache[url] = result;
47144 callback(null, result);
47148 roles: function roles(params, callback) {
47149 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47150 var geometry = params.geometry;
47151 params = clean(setSortMembers(params));
47152 params = Object.assign({
47154 sortname: 'count_all_members',
47157 lang: _mainLocalizer.languageCode()
47159 var url = _apibase$1 + 'relation/roles?' + utilQsString(params);
47160 doRequest(url, params, true, callback, function (err, d) {
47164 var f = filterRoles(geometry);
47165 var result = d.data.filter(f).map(roleKey);
47166 _taginfoCache[url] = result;
47167 callback(null, result);
47171 docs: function docs(params, callback) {
47172 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47173 params = clean(setSort(params));
47174 var path = 'key/wiki_pages?';
47176 if (params.value) {
47177 path = 'tag/wiki_pages?';
47178 } else if (params.rtype) {
47179 path = 'relation/wiki_pages?';
47182 var url = _apibase$1 + path + utilQsString(params);
47183 doRequest(url, params, true, callback, function (err, d) {
47187 _taginfoCache[url] = d.data;
47188 callback(null, d.data);
47192 apibase: function apibase(_) {
47193 if (!arguments.length) return _apibase$1;
47199 var helpers$1 = createCommonjsModule(function (module, exports) {
47201 Object.defineProperty(exports, "__esModule", {
47209 * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
47211 * @memberof helpers
47215 exports.earthRadius = 6371008.8;
47217 * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
47219 * @memberof helpers
47223 exports.factors = {
47224 centimeters: exports.earthRadius * 100,
47225 centimetres: exports.earthRadius * 100,
47226 degrees: exports.earthRadius / 111325,
47227 feet: exports.earthRadius * 3.28084,
47228 inches: exports.earthRadius * 39.370,
47229 kilometers: exports.earthRadius / 1000,
47230 kilometres: exports.earthRadius / 1000,
47231 meters: exports.earthRadius,
47232 metres: exports.earthRadius,
47233 miles: exports.earthRadius / 1609.344,
47234 millimeters: exports.earthRadius * 1000,
47235 millimetres: exports.earthRadius * 1000,
47236 nauticalmiles: exports.earthRadius / 1852,
47238 yards: exports.earthRadius / 1.0936
47241 * Units of measurement factors based on 1 meter.
47243 * @memberof helpers
47247 exports.unitsFactors = {
47250 degrees: 1 / 111325,
47253 kilometers: 1 / 1000,
47254 kilometres: 1 / 1000,
47257 miles: 1 / 1609.344,
47260 nauticalmiles: 1 / 1852,
47261 radians: 1 / exports.earthRadius,
47265 * Area of measurement factors based on 1 square meter.
47267 * @memberof helpers
47271 exports.areaFactors = {
47272 acres: 0.000247105,
47273 centimeters: 10000,
47274 centimetres: 10000,
47275 feet: 10.763910417,
47276 inches: 1550.003100006,
47277 kilometers: 0.000001,
47278 kilometres: 0.000001,
47282 millimeters: 1000000,
47283 millimetres: 1000000,
47287 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
47290 * @param {Geometry} geometry input geometry
47291 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47292 * @param {Object} [options={}] Optional Parameters
47293 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47294 * @param {string|number} [options.id] Identifier associated with the Feature
47295 * @returns {Feature} a GeoJSON Feature
47299 * "coordinates": [110, 50]
47302 * var feature = turf.feature(geometry);
47307 function feature(geom, properties, options) {
47308 if (options === void 0) {
47316 if (options.id === 0 || options.id) {
47317 feat.id = options.id;
47320 if (options.bbox) {
47321 feat.bbox = options.bbox;
47324 feat.properties = properties || {};
47325 feat.geometry = geom;
47329 exports.feature = feature;
47331 * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
47332 * For GeometryCollection type use `helpers.geometryCollection`
47335 * @param {string} type Geometry Type
47336 * @param {Array<any>} coordinates Coordinates
47337 * @param {Object} [options={}] Optional Parameters
47338 * @returns {Geometry} a GeoJSON Geometry
47340 * var type = "Point";
47341 * var coordinates = [110, 50];
47342 * var geometry = turf.geometry(type, coordinates);
47346 function geometry(type, coordinates, options) {
47350 return point(coordinates).geometry;
47353 return lineString(coordinates).geometry;
47356 return polygon(coordinates).geometry;
47359 return multiPoint(coordinates).geometry;
47361 case "MultiLineString":
47362 return multiLineString(coordinates).geometry;
47364 case "MultiPolygon":
47365 return multiPolygon(coordinates).geometry;
47368 throw new Error(type + " is invalid");
47372 exports.geometry = geometry;
47374 * Creates a {@link Point} {@link Feature} from a Position.
47377 * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
47378 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47379 * @param {Object} [options={}] Optional Parameters
47380 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47381 * @param {string|number} [options.id] Identifier associated with the Feature
47382 * @returns {Feature<Point>} a Point feature
47384 * var point = turf.point([-75.343, 39.984]);
47389 function point(coordinates, properties, options) {
47390 if (options === void 0) {
47396 coordinates: coordinates
47398 return feature(geom, properties, options);
47401 exports.point = point;
47403 * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
47406 * @param {Array<Array<number>>} coordinates an array of Points
47407 * @param {Object} [properties={}] Translate these properties to each Feature
47408 * @param {Object} [options={}] Optional Parameters
47409 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47410 * associated with the FeatureCollection
47411 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47412 * @returns {FeatureCollection<Point>} Point Feature
47414 * var points = turf.points([
47423 function points(coordinates, properties, options) {
47424 if (options === void 0) {
47428 return featureCollection(coordinates.map(function (coords) {
47429 return point(coords, properties);
47433 exports.points = points;
47435 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
47438 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47439 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47440 * @param {Object} [options={}] Optional Parameters
47441 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47442 * @param {string|number} [options.id] Identifier associated with the Feature
47443 * @returns {Feature<Polygon>} Polygon Feature
47445 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
47450 function polygon(coordinates, properties, options) {
47451 if (options === void 0) {
47455 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
47456 var ring = coordinates_1[_i];
47458 if (ring.length < 4) {
47459 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
47462 for (var j = 0; j < ring[ring.length - 1].length; j++) {
47463 // Check if first point of Polygon contains two numbers
47464 if (ring[ring.length - 1][j] !== ring[0][j]) {
47465 throw new Error("First and last Position are not equivalent.");
47472 coordinates: coordinates
47474 return feature(geom, properties, options);
47477 exports.polygon = polygon;
47479 * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
47482 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
47483 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47484 * @param {Object} [options={}] Optional Parameters
47485 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47486 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47487 * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
47489 * var polygons = turf.polygons([
47490 * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
47491 * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
47497 function polygons(coordinates, properties, options) {
47498 if (options === void 0) {
47502 return featureCollection(coordinates.map(function (coords) {
47503 return polygon(coords, properties);
47507 exports.polygons = polygons;
47509 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
47512 * @param {Array<Array<number>>} coordinates an array of Positions
47513 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47514 * @param {Object} [options={}] Optional Parameters
47515 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47516 * @param {string|number} [options.id] Identifier associated with the Feature
47517 * @returns {Feature<LineString>} LineString Feature
47519 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
47520 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
47526 function lineString(coordinates, properties, options) {
47527 if (options === void 0) {
47531 if (coordinates.length < 2) {
47532 throw new Error("coordinates must be an array of two or more positions");
47536 type: "LineString",
47537 coordinates: coordinates
47539 return feature(geom, properties, options);
47542 exports.lineString = lineString;
47544 * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
47546 * @name lineStrings
47547 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47548 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47549 * @param {Object} [options={}] Optional Parameters
47550 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47551 * associated with the FeatureCollection
47552 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47553 * @returns {FeatureCollection<LineString>} LineString FeatureCollection
47555 * var linestrings = turf.lineStrings([
47556 * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
47557 * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
47563 function lineStrings(coordinates, properties, options) {
47564 if (options === void 0) {
47568 return featureCollection(coordinates.map(function (coords) {
47569 return lineString(coords, properties);
47573 exports.lineStrings = lineStrings;
47575 * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
47577 * @name featureCollection
47578 * @param {Feature[]} features input features
47579 * @param {Object} [options={}] Optional Parameters
47580 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47581 * @param {string|number} [options.id] Identifier associated with the Feature
47582 * @returns {FeatureCollection} FeatureCollection of Features
47584 * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
47585 * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
47586 * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
47588 * var collection = turf.featureCollection([
47597 function featureCollection(features, options) {
47598 if (options === void 0) {
47603 type: "FeatureCollection"
47607 fc.id = options.id;
47610 if (options.bbox) {
47611 fc.bbox = options.bbox;
47614 fc.features = features;
47618 exports.featureCollection = featureCollection;
47620 * Creates a {@link Feature<MultiLineString>} based on a
47621 * coordinate array. Properties can be added optionally.
47623 * @name multiLineString
47624 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
47625 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47626 * @param {Object} [options={}] Optional Parameters
47627 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47628 * @param {string|number} [options.id] Identifier associated with the Feature
47629 * @returns {Feature<MultiLineString>} a MultiLineString feature
47630 * @throws {Error} if no coordinates are passed
47632 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
47637 function multiLineString(coordinates, properties, options) {
47638 if (options === void 0) {
47643 type: "MultiLineString",
47644 coordinates: coordinates
47646 return feature(geom, properties, options);
47649 exports.multiLineString = multiLineString;
47651 * Creates a {@link Feature<MultiPoint>} based on a
47652 * coordinate array. Properties can be added optionally.
47655 * @param {Array<Array<number>>} coordinates an array of Positions
47656 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47657 * @param {Object} [options={}] Optional Parameters
47658 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47659 * @param {string|number} [options.id] Identifier associated with the Feature
47660 * @returns {Feature<MultiPoint>} a MultiPoint feature
47661 * @throws {Error} if no coordinates are passed
47663 * var multiPt = turf.multiPoint([[0,0],[10,10]]);
47668 function multiPoint(coordinates, properties, options) {
47669 if (options === void 0) {
47674 type: "MultiPoint",
47675 coordinates: coordinates
47677 return feature(geom, properties, options);
47680 exports.multiPoint = multiPoint;
47682 * Creates a {@link Feature<MultiPolygon>} based on a
47683 * coordinate array. Properties can be added optionally.
47685 * @name multiPolygon
47686 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
47687 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47688 * @param {Object} [options={}] Optional Parameters
47689 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47690 * @param {string|number} [options.id] Identifier associated with the Feature
47691 * @returns {Feature<MultiPolygon>} a multipolygon feature
47692 * @throws {Error} if no coordinates are passed
47694 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
47700 function multiPolygon(coordinates, properties, options) {
47701 if (options === void 0) {
47706 type: "MultiPolygon",
47707 coordinates: coordinates
47709 return feature(geom, properties, options);
47712 exports.multiPolygon = multiPolygon;
47714 * Creates a {@link Feature<GeometryCollection>} based on a
47715 * coordinate array. Properties can be added optionally.
47717 * @name geometryCollection
47718 * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
47719 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47720 * @param {Object} [options={}] Optional Parameters
47721 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47722 * @param {string|number} [options.id] Identifier associated with the Feature
47723 * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
47725 * var pt = turf.geometry("Point", [100, 0]);
47726 * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
47727 * var collection = turf.geometryCollection([pt, line]);
47732 function geometryCollection(geometries, properties, options) {
47733 if (options === void 0) {
47738 type: "GeometryCollection",
47739 geometries: geometries
47741 return feature(geom, properties, options);
47744 exports.geometryCollection = geometryCollection;
47746 * Round number to precision
47748 * @param {number} num Number
47749 * @param {number} [precision=0] Precision
47750 * @returns {number} rounded number
47752 * turf.round(120.4321)
47755 * turf.round(120.4321, 2)
47759 function round(num, precision) {
47760 if (precision === void 0) {
47764 if (precision && !(precision >= 0)) {
47765 throw new Error("precision must be a positive number");
47768 var multiplier = Math.pow(10, precision || 0);
47769 return Math.round(num * multiplier) / multiplier;
47772 exports.round = round;
47774 * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
47775 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47777 * @name radiansToLength
47778 * @param {number} radians in radians across the sphere
47779 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47780 * meters, kilometres, kilometers.
47781 * @returns {number} distance
47784 function radiansToLength(radians, units) {
47785 if (units === void 0) {
47786 units = "kilometers";
47789 var factor = exports.factors[units];
47792 throw new Error(units + " units is invalid");
47795 return radians * factor;
47798 exports.radiansToLength = radiansToLength;
47800 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
47801 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47803 * @name lengthToRadians
47804 * @param {number} distance in real units
47805 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47806 * meters, kilometres, kilometers.
47807 * @returns {number} radians
47810 function lengthToRadians(distance, units) {
47811 if (units === void 0) {
47812 units = "kilometers";
47815 var factor = exports.factors[units];
47818 throw new Error(units + " units is invalid");
47821 return distance / factor;
47824 exports.lengthToRadians = lengthToRadians;
47826 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
47827 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
47829 * @name lengthToDegrees
47830 * @param {number} distance in real units
47831 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47832 * meters, kilometres, kilometers.
47833 * @returns {number} degrees
47836 function lengthToDegrees(distance, units) {
47837 return radiansToDegrees(lengthToRadians(distance, units));
47840 exports.lengthToDegrees = lengthToDegrees;
47842 * Converts any bearing angle from the north line direction (positive clockwise)
47843 * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
47845 * @name bearingToAzimuth
47846 * @param {number} bearing angle, between -180 and +180 degrees
47847 * @returns {number} angle between 0 and 360 degrees
47850 function bearingToAzimuth(bearing) {
47851 var angle = bearing % 360;
47860 exports.bearingToAzimuth = bearingToAzimuth;
47862 * Converts an angle in radians to degrees
47864 * @name radiansToDegrees
47865 * @param {number} radians angle in radians
47866 * @returns {number} degrees between 0 and 360 degrees
47869 function radiansToDegrees(radians) {
47870 var degrees = radians % (2 * Math.PI);
47871 return degrees * 180 / Math.PI;
47874 exports.radiansToDegrees = radiansToDegrees;
47876 * Converts an angle in degrees to radians
47878 * @name degreesToRadians
47879 * @param {number} degrees angle between 0 and 360 degrees
47880 * @returns {number} angle in radians
47883 function degreesToRadians(degrees) {
47884 var radians = degrees % 360;
47885 return radians * Math.PI / 180;
47888 exports.degreesToRadians = degreesToRadians;
47890 * Converts a length to the requested unit.
47891 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47893 * @param {number} length to be converted
47894 * @param {Units} [originalUnit="kilometers"] of the length
47895 * @param {Units} [finalUnit="kilometers"] returned unit
47896 * @returns {number} the converted length
47899 function convertLength(length, originalUnit, finalUnit) {
47900 if (originalUnit === void 0) {
47901 originalUnit = "kilometers";
47904 if (finalUnit === void 0) {
47905 finalUnit = "kilometers";
47908 if (!(length >= 0)) {
47909 throw new Error("length must be a positive number");
47912 return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
47915 exports.convertLength = convertLength;
47917 * Converts a area to the requested unit.
47918 * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
47919 * @param {number} area to be converted
47920 * @param {Units} [originalUnit="meters"] of the distance
47921 * @param {Units} [finalUnit="kilometers"] returned unit
47922 * @returns {number} the converted distance
47925 function convertArea(area, originalUnit, finalUnit) {
47926 if (originalUnit === void 0) {
47927 originalUnit = "meters";
47930 if (finalUnit === void 0) {
47931 finalUnit = "kilometers";
47934 if (!(area >= 0)) {
47935 throw new Error("area must be a positive number");
47938 var startFactor = exports.areaFactors[originalUnit];
47940 if (!startFactor) {
47941 throw new Error("invalid original units");
47944 var finalFactor = exports.areaFactors[finalUnit];
47946 if (!finalFactor) {
47947 throw new Error("invalid final units");
47950 return area / startFactor * finalFactor;
47953 exports.convertArea = convertArea;
47957 * @param {*} num Number to validate
47958 * @returns {boolean} true/false
47960 * turf.isNumber(123)
47962 * turf.isNumber('foo')
47966 function isNumber(num) {
47967 return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
47970 exports.isNumber = isNumber;
47974 * @param {*} input variable to validate
47975 * @returns {boolean} true/false
47977 * turf.isObject({elevation: 10})
47979 * turf.isObject('foo')
47983 function isObject(input) {
47984 return !!input && input.constructor === Object;
47987 exports.isObject = isObject;
47992 * @param {Array<number>} bbox BBox to validate
47994 * @throws Error if BBox is not valid
47996 * validateBBox([-180, -40, 110, 50])
47998 * validateBBox([-180, -40])
48000 * validateBBox('Foo')
48004 * validateBBox(null)
48006 * validateBBox(undefined)
48010 function validateBBox(bbox) {
48012 throw new Error("bbox is required");
48015 if (!Array.isArray(bbox)) {
48016 throw new Error("bbox must be an Array");
48019 if (bbox.length !== 4 && bbox.length !== 6) {
48020 throw new Error("bbox must be an Array of 4 or 6 numbers");
48023 bbox.forEach(function (num) {
48024 if (!isNumber(num)) {
48025 throw new Error("bbox must only contain numbers");
48030 exports.validateBBox = validateBBox;
48035 * @param {string|number} id Id to validate
48037 * @throws Error if Id is not valid
48039 * validateId([-180, -40, 110, 50])
48041 * validateId([-180, -40])
48043 * validateId('Foo')
48049 * validateId(undefined)
48053 function validateId(id) {
48055 throw new Error("id is required");
48058 if (["string", "number"].indexOf(_typeof(id)) === -1) {
48059 throw new Error("id must be a number or a string");
48063 exports.validateId = validateId; // Deprecated methods
48065 function radians2degrees() {
48066 throw new Error("method has been renamed to `radiansToDegrees`");
48069 exports.radians2degrees = radians2degrees;
48071 function degrees2radians() {
48072 throw new Error("method has been renamed to `degreesToRadians`");
48075 exports.degrees2radians = degrees2radians;
48077 function distanceToDegrees() {
48078 throw new Error("method has been renamed to `lengthToDegrees`");
48081 exports.distanceToDegrees = distanceToDegrees;
48083 function distanceToRadians() {
48084 throw new Error("method has been renamed to `lengthToRadians`");
48087 exports.distanceToRadians = distanceToRadians;
48089 function radiansToDistance() {
48090 throw new Error("method has been renamed to `radiansToLength`");
48093 exports.radiansToDistance = radiansToDistance;
48095 function bearingToAngle() {
48096 throw new Error("method has been renamed to `bearingToAzimuth`");
48099 exports.bearingToAngle = bearingToAngle;
48101 function convertDistance() {
48102 throw new Error("method has been renamed to `convertLength`");
48105 exports.convertDistance = convertDistance;
48108 var invariant = createCommonjsModule(function (module, exports) {
48110 Object.defineProperty(exports, "__esModule", {
48114 * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
48117 * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
48118 * @returns {Array<number>} coordinates
48120 * var pt = turf.point([10, 10]);
48122 * var coord = turf.getCoord(pt);
48126 function getCoord(coord) {
48128 throw new Error("coord is required");
48131 if (!Array.isArray(coord)) {
48132 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
48133 return coord.geometry.coordinates;
48136 if (coord.type === "Point") {
48137 return coord.coordinates;
48141 if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
48145 throw new Error("coord must be GeoJSON Point or an Array of numbers");
48148 exports.getCoord = getCoord;
48150 * Unwrap coordinates from a Feature, Geometry Object or an Array
48153 * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
48154 * @returns {Array<any>} coordinates
48156 * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
48158 * var coords = turf.getCoords(poly);
48159 * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
48162 function getCoords(coords) {
48163 if (Array.isArray(coords)) {
48168 if (coords.type === "Feature") {
48169 if (coords.geometry !== null) {
48170 return coords.geometry.coordinates;
48174 if (coords.coordinates) {
48175 return coords.coordinates;
48179 throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
48182 exports.getCoords = getCoords;
48184 * Checks if coordinates contains a number
48186 * @name containsNumber
48187 * @param {Array<any>} coordinates GeoJSON Coordinates
48188 * @returns {boolean} true if Array contains a number
48191 function containsNumber(coordinates) {
48192 if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
48196 if (Array.isArray(coordinates[0]) && coordinates[0].length) {
48197 return containsNumber(coordinates[0]);
48200 throw new Error("coordinates must only contain numbers");
48203 exports.containsNumber = containsNumber;
48205 * Enforce expectations about types of GeoJSON objects for Turf.
48207 * @name geojsonType
48208 * @param {GeoJSON} value any GeoJSON object
48209 * @param {string} type expected GeoJSON type
48210 * @param {string} name name of calling function
48211 * @throws {Error} if value is not the expected type.
48214 function geojsonType(value, type, name) {
48215 if (!type || !name) {
48216 throw new Error("type and name required");
48219 if (!value || value.type !== type) {
48220 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
48224 exports.geojsonType = geojsonType;
48226 * Enforce expectations about types of {@link Feature} inputs for Turf.
48227 * Internally this uses {@link geojsonType} to judge geometry types.
48230 * @param {Feature} feature a feature with an expected geometry type
48231 * @param {string} type expected GeoJSON type
48232 * @param {string} name name of calling function
48233 * @throws {Error} error if value is not the expected type.
48236 function featureOf(feature, type, name) {
48238 throw new Error("No feature passed");
48242 throw new Error(".featureOf() requires a name");
48245 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48246 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48249 if (!feature.geometry || feature.geometry.type !== type) {
48250 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48254 exports.featureOf = featureOf;
48256 * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
48257 * Internally this uses {@link geojsonType} to judge geometry types.
48259 * @name collectionOf
48260 * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
48261 * @param {string} type expected GeoJSON type
48262 * @param {string} name name of calling function
48263 * @throws {Error} if value is not the expected type.
48266 function collectionOf(featureCollection, type, name) {
48267 if (!featureCollection) {
48268 throw new Error("No featureCollection passed");
48272 throw new Error(".collectionOf() requires a name");
48275 if (!featureCollection || featureCollection.type !== "FeatureCollection") {
48276 throw new Error("Invalid input to " + name + ", FeatureCollection required");
48279 for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
48280 var feature = _a[_i];
48282 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48283 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48286 if (!feature.geometry || feature.geometry.type !== type) {
48287 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48292 exports.collectionOf = collectionOf;
48294 * Get Geometry from Feature or Geometry Object
48296 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
48297 * @returns {Geometry|null} GeoJSON Geometry Object
48298 * @throws {Error} if geojson is not a Feature or Geometry Object
48301 * "type": "Feature",
48302 * "properties": {},
48305 * "coordinates": [110, 40]
48308 * var geom = turf.getGeom(point)
48309 * //={"type": "Point", "coordinates": [110, 40]}
48312 function getGeom(geojson) {
48313 if (geojson.type === "Feature") {
48314 return geojson.geometry;
48320 exports.getGeom = getGeom;
48322 * Get GeoJSON object's type, Geometry type is prioritize.
48324 * @param {GeoJSON} geojson GeoJSON object
48325 * @param {string} [name="geojson"] name of the variable to display in error message
48326 * @returns {string} GeoJSON type
48329 * "type": "Feature",
48330 * "properties": {},
48333 * "coordinates": [110, 40]
48336 * var geom = turf.getType(point)
48340 function getType(geojson, name) {
48341 if (geojson.type === "FeatureCollection") {
48342 return "FeatureCollection";
48345 if (geojson.type === "GeometryCollection") {
48346 return "GeometryCollection";
48349 if (geojson.type === "Feature" && geojson.geometry !== null) {
48350 return geojson.geometry.type;
48353 return geojson.type;
48356 exports.getType = getType;
48359 var lineclip_1 = lineclip;
48360 var _default = lineclip;
48361 lineclip.polyline = lineclip;
48362 lineclip.polygon = polygonclip; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
48363 // handle polylines rather than just segments
48365 function lineclip(points, bbox, result) {
48366 var len = points.length,
48367 codeA = bitCode(points[0], bbox),
48374 if (!result) result = [];
48376 for (i = 1; i < len; i++) {
48379 codeB = lastCode = bitCode(b, bbox);
48382 if (!(codeA | codeB)) {
48386 if (codeB !== lastCode) {
48387 // segment went outside
48391 // start a new line
48395 } else if (i === len - 1) {
48400 } else if (codeA & codeB) {
48403 } else if (codeA) {
48404 // a outside, intersect with clip edge
48405 a = intersect(a, b, codeA, bbox);
48406 codeA = bitCode(a, bbox);
48409 b = intersect(a, b, codeB, bbox);
48410 codeB = bitCode(b, bbox);
48417 if (part.length) result.push(part);
48419 } // Sutherland-Hodgeman polygon clipping algorithm
48422 function polygonclip(points, bbox) {
48423 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
48425 for (edge = 1; edge <= 8; edge *= 2) {
48427 prev = points[points.length - 1];
48428 prevInside = !(bitCode(prev, bbox) & edge);
48430 for (i = 0; i < points.length; i++) {
48432 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
48434 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
48435 if (inside) result.push(p); // add a point if it's inside
48438 prevInside = inside;
48442 if (!points.length) break;
48446 } // intersect a segment against one of the 4 lines that make up the bbox
48449 function intersect(a, b, edge, bbox) {
48450 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
48451 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
48452 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
48453 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
48455 } // bit code reflects the point position relative to the bbox:
48457 // top 1001 1000 1010
48458 // mid 0001 0000 0010
48459 // bottom 0101 0100 0110
48462 function bitCode(p, bbox) {
48464 if (p[0] < bbox[0]) code |= 1; // left
48465 else if (p[0] > bbox[2]) code |= 2; // right
48467 if (p[1] < bbox[1]) code |= 4; // bottom
48468 else if (p[1] > bbox[3]) code |= 8; // top
48472 lineclip_1["default"] = _default;
48474 var bboxClip_1 = createCommonjsModule(function (module, exports) {
48476 var __importStar = commonjsGlobal && commonjsGlobal.__importStar || function (mod) {
48477 if (mod && mod.__esModule) return mod;
48479 if (mod != null) for (var k in mod) {
48480 if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
48482 result["default"] = mod;
48486 Object.defineProperty(exports, "__esModule", {
48490 var lineclip = __importStar(lineclip_1);
48492 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
48493 * [lineclip](https://github.com/mapbox/lineclip).
48494 * May result in degenerate edges when clipping Polygons.
48497 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
48498 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
48499 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
48501 * var bbox = [0, 0, 10, 10];
48502 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
48504 * var clipped = turf.bboxClip(poly, bbox);
48507 * var addToMap = [bbox, poly, clipped]
48511 function bboxClip(feature, bbox) {
48512 var geom = invariant.getGeom(feature);
48513 var type = geom.type;
48514 var properties = feature.type === "Feature" ? feature.properties : {};
48515 var coords = geom.coordinates;
48519 case "MultiLineString":
48522 if (type === "LineString") {
48526 coords.forEach(function (line) {
48527 lineclip.polyline(line, bbox, lines_1);
48530 if (lines_1.length === 1) {
48531 return helpers$1.lineString(lines_1[0], properties);
48534 return helpers$1.multiLineString(lines_1, properties);
48537 return helpers$1.polygon(clipPolygon(coords, bbox), properties);
48539 case "MultiPolygon":
48540 return helpers$1.multiPolygon(coords.map(function (poly) {
48541 return clipPolygon(poly, bbox);
48545 throw new Error("geometry " + type + " not supported");
48549 exports["default"] = bboxClip;
48551 function clipPolygon(rings, bbox) {
48554 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
48555 var ring = rings_1[_i];
48556 var clipped = lineclip.polygon(ring, bbox);
48558 if (clipped.length > 0) {
48559 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
48560 clipped.push(clipped[0]);
48563 if (clipped.length >= 4) {
48564 outRings.push(clipped);
48572 var turf_bboxClip = /*@__PURE__*/getDefaultExportFromCjs(bboxClip_1);
48574 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
48575 if (!opts) opts = {};
48576 if (typeof opts === 'function') opts = {
48579 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
48581 var cmp = opts.cmp && function (f) {
48582 return function (node) {
48583 return function (a, b) {
48592 return f(aobj, bobj);
48598 return function stringify(node) {
48599 if (node && node.toJSON && typeof node.toJSON === 'function') {
48600 node = node.toJSON();
48603 if (node === undefined) return;
48604 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
48605 if (_typeof(node) !== 'object') return JSON.stringify(node);
48608 if (Array.isArray(node)) {
48611 for (i = 0; i < node.length; i++) {
48613 out += stringify(node[i]) || 'null';
48619 if (node === null) return 'null';
48621 if (seen.indexOf(node) !== -1) {
48622 if (cycles) return JSON.stringify('__cycle__');
48623 throw new TypeError('Converting circular structure to JSON');
48626 var seenIndex = seen.push(node) - 1;
48627 var keys = Object.keys(node).sort(cmp && cmp(node));
48630 for (i = 0; i < keys.length; i++) {
48632 var value = stringify(node[key]);
48633 if (!value) continue;
48634 if (out) out += ',';
48635 out += JSON.stringify(key) + ':' + value;
48638 seen.splice(seenIndex, 1);
48639 return '{' + out + '}';
48643 function DEFAULT_COMPARE(a, b) {
48644 return a > b ? 1 : a < b ? -1 : 0;
48647 var SplayTree = /*#__PURE__*/function () {
48648 function SplayTree() {
48649 var compare = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE;
48650 var noDuplicates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
48652 _classCallCheck(this, SplayTree);
48654 this._compare = compare;
48657 this._noDuplicates = !!noDuplicates;
48660 _createClass(SplayTree, [{
48662 value: function rotateLeft(x) {
48667 if (y.left) y.left.parent = x;
48668 y.parent = x.parent;
48671 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48676 key: "rotateRight",
48677 value: function rotateRight(x) {
48682 if (y.right) y.right.parent = x;
48683 y.parent = x.parent;
48686 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48687 if (y) y.right = x;
48692 value: function _splay(x) {
48697 if (p.left === x) this.rotateRight(p);else this.rotateLeft(p);
48698 } else if (p.left === x && p.parent.left === p) {
48699 this.rotateRight(p.parent);
48700 this.rotateRight(p);
48701 } else if (p.right === x && p.parent.right === p) {
48702 this.rotateLeft(p.parent);
48703 this.rotateLeft(p);
48704 } else if (p.left === x && p.parent.right === p) {
48705 this.rotateRight(p);
48706 this.rotateLeft(p);
48708 this.rotateLeft(p);
48709 this.rotateRight(p);
48715 value: function splay(x) {
48716 var p, gp, ggp, l, r;
48722 if (gp && gp.parent) {
48724 if (ggp.left === gp) ggp.left = x;else ggp.right = x;
48734 if (x === p.left) {
48737 if (gp.left === p) {
48741 gp.left.parent = gp;
48742 } else gp.left = null;
48751 } else gp.right = null;
48761 } else p.left = null;
48768 if (gp.right === p) {
48772 gp.right.parent = gp;
48773 } else gp.right = null;
48782 } else gp.left = null;
48792 } else p.right = null;
48801 value: function replace(u, v) {
48802 if (!u.parent) this._root = v;else if (u === u.parent.left) u.parent.left = v;else u.parent.right = v;
48803 if (v) v.parent = u.parent;
48807 value: function minNode() {
48808 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48809 if (u) while (u.left) {
48816 value: function maxNode() {
48817 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48818 if (u) while (u.right) {
48825 value: function insert(key, data) {
48826 var z = this._root;
48828 var comp = this._compare;
48831 if (this._noDuplicates) {
48834 cmp = comp(z.key, key);
48835 if (cmp === 0) return;else if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48840 if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48851 if (!p) this._root = z;else if (comp(p.key, z.key) < 0) p.right = z;else p.left = z;
48858 value: function find(key) {
48859 var z = this._root;
48860 var comp = this._compare;
48863 var cmp = comp(z.key, key);
48864 if (cmp < 0) z = z.right;else if (cmp > 0) z = z.left;else return z;
48870 * Whether the tree contains a node with the given key
48872 * @return {boolean} true/false
48877 value: function contains(key) {
48878 var node = this._root;
48879 var comparator = this._compare;
48882 var cmp = comparator(key, node.key);
48883 if (cmp === 0) return true;else if (cmp < 0) node = node.left;else node = node.right;
48890 value: function remove(key) {
48891 var z = this.find(key);
48892 if (!z) return false;
48894 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48895 var y = this.minNode(z.right);
48897 if (y.parent !== z) {
48898 this.replace(y, y.right);
48900 y.right.parent = y;
48903 this.replace(z, y);
48912 value: function removeNode(z) {
48913 if (!z) return false;
48915 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48916 var y = this.minNode(z.right);
48918 if (y.parent !== z) {
48919 this.replace(y, y.right);
48921 y.right.parent = y;
48924 this.replace(z, y);
48933 value: function erase(key) {
48934 var z = this.find(key);
48943 sMax = this.maxNode(s);
48949 if (s) sMax.right = t;else this._root = t;
48956 * Removes and returns the node with smallest key
48962 value: function pop() {
48963 var node = this._root,
48964 returnValue = null;
48967 while (node.left) {
48975 this.remove(node.key);
48978 return returnValue;
48980 /* eslint-disable class-methods-use-this */
48984 * @param {Node} node
48990 value: function next(node) {
48991 var successor = node;
48994 if (successor.right) {
48995 successor = successor.right;
48997 while (successor && successor.left) {
48998 successor = successor.left;
49001 successor = node.parent;
49003 while (successor && successor.right === node) {
49005 successor = successor.parent;
49014 * @param {Node} node
49020 value: function prev(node) {
49021 var predecessor = node;
49024 if (predecessor.left) {
49025 predecessor = predecessor.left;
49027 while (predecessor && predecessor.right) {
49028 predecessor = predecessor.right;
49031 predecessor = node.parent;
49033 while (predecessor && predecessor.left === node) {
49034 node = predecessor;
49035 predecessor = predecessor.parent;
49040 return predecessor;
49042 /* eslint-enable class-methods-use-this */
49045 * @param {forEachCallback} callback
49046 * @return {SplayTree}
49051 value: function forEach(callback) {
49052 var current = this._root;
49058 // Reach the left most Node of the current Node
49060 // Place pointer to a tree node on the stack
49061 // before traversing the node's left subtree
49063 current = current.left;
49065 // BackTrack from the empty subtree and visit the Node
49066 // at the top of the stack; however, if the stack is
49067 // empty you are done
49068 if (s.length > 0) {
49070 callback(current, i++); // We have visited the node and its left
49071 // subtree. Now, it's right subtree's turn
49073 current = current.right;
49074 } else done = true;
49081 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
49083 * @param {Key} high
49084 * @param {Function} fn
49086 * @return {SplayTree}
49091 value: function range(low, high, fn, ctx) {
49093 var compare = this._compare;
49094 var node = this._root,
49097 while (Q.length !== 0 || node) {
49103 cmp = compare(node.key, high);
49107 } else if (compare(node.key, low) >= 0) {
49108 if (fn.call(ctx, node)) return this; // stop if smth is returned
49118 * Returns all keys in order
49119 * @return {Array<Key>}
49124 value: function keys() {
49125 var current = this._root;
49133 current = current.left;
49135 if (s.length > 0) {
49137 r.push(current.key);
49138 current = current.right;
49139 } else done = true;
49146 * Returns `data` fields of all nodes in order.
49147 * @return {Array<Value>}
49152 value: function values() {
49153 var current = this._root;
49161 current = current.left;
49163 if (s.length > 0) {
49165 r.push(current.data);
49166 current = current.right;
49167 } else done = true;
49174 * Returns node at given index
49175 * @param {number} index
49181 value: function at(index) {
49182 // removed after a consideration, more misleading than useful
49183 // index = index % this.size;
49184 // if (index < 0) index = this.size - index;
49185 var current = this._root;
49193 current = current.left;
49195 if (s.length > 0) {
49197 if (i === index) return current;
49199 current = current.right;
49200 } else done = true;
49207 * Bulk-load items. Both array have to be same size
49208 * @param {Array<Key>} keys
49209 * @param {Array<Value>} [values]
49210 * @param {Boolean} [presort=false] Pre-sort keys and values, using
49211 * tree's comparator. Sorting is done
49213 * @return {AVLTree}
49218 value: function load() {
49219 var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
49220 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
49221 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
49222 if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
49223 var size = keys.length;
49224 if (presort) sort(keys, values, 0, size - 1, this._compare);
49225 this._root = loadRecursive(null, keys, values, 0, size);
49231 value: function min() {
49232 var node = this.minNode(this._root);
49233 if (node) return node.key;else return null;
49237 value: function max() {
49238 var node = this.maxNode(this._root);
49239 if (node) return node.key;else return null;
49243 value: function isEmpty() {
49244 return this._root === null;
49248 get: function get() {
49252 * Create a tree and load it with items
49253 * @param {Array<Key>} keys
49254 * @param {Array<Value>?} [values]
49255 * @param {Function?} [comparator]
49256 * @param {Boolean?} [presort=false] Pre-sort keys and values, using
49257 * tree's comparator. Sorting is done
49259 * @param {Boolean?} [noDuplicates=false] Allow duplicates
49260 * @return {SplayTree}
49265 value: function createTree(keys, values, comparator, presort, noDuplicates) {
49266 return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
49273 function loadRecursive(parent, keys, values, start, end) {
49274 var size = end - start;
49277 var middle = start + Math.floor(size / 2);
49278 var key = keys[middle];
49279 var data = values[middle];
49285 node.left = loadRecursive(node, keys, values, start, middle);
49286 node.right = loadRecursive(node, keys, values, middle + 1, end);
49293 function sort(keys, values, left, right, compare) {
49294 if (left >= right) return;
49295 var pivot = keys[left + right >> 1];
49302 } while (compare(keys[i], pivot) < 0);
49306 } while (compare(keys[j], pivot) > 0);
49313 values[i] = values[j];
49317 sort(keys, values, left, j, compare);
49318 sort(keys, values, j + 1, right, compare);
49322 var NON_CONTRIBUTING = 1;
49323 var SAME_TRANSITION = 2;
49324 var DIFFERENT_TRANSITION = 3;
49326 var INTERSECTION = 0;
49328 var DIFFERENCE = 2;
49332 * @param {SweepEvent} event
49333 * @param {SweepEvent} prev
49334 * @param {Operation} operation
49337 function computeFields(event, prev, operation) {
49338 // compute inOut and otherInOut fields
49339 if (prev === null) {
49340 event.inOut = false;
49341 event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon
49343 if (event.isSubject === prev.isSubject) {
49344 event.inOut = !prev.inOut;
49345 event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon
49347 event.inOut = !prev.otherInOut;
49348 event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
49349 } // compute prevInResult field
49353 event.prevInResult = !inResult(prev, operation) || prev.isVertical() ? prev.prevInResult : prev;
49355 } // check if the line segment belongs to the Boolean operation
49358 var isInResult = inResult(event, operation);
49361 event.resultTransition = determineResultTransition(event, operation);
49363 event.resultTransition = 0;
49366 /* eslint-disable indent */
49368 function inResult(event, operation) {
49369 switch (event.type) {
49371 switch (operation) {
49373 return !event.otherInOut;
49376 return event.otherInOut;
49379 // return (event.isSubject && !event.otherInOut) ||
49380 // (!event.isSubject && event.otherInOut);
49381 return event.isSubject && event.otherInOut || !event.isSubject && !event.otherInOut;
49389 case SAME_TRANSITION:
49390 return operation === INTERSECTION || operation === UNION;
49392 case DIFFERENT_TRANSITION:
49393 return operation === DIFFERENCE;
49395 case NON_CONTRIBUTING:
49401 /* eslint-enable indent */
49404 function determineResultTransition(event, operation) {
49405 var thisIn = !event.inOut;
49406 var thatIn = !event.otherInOut;
49409 switch (operation) {
49411 isIn = thisIn && thatIn;
49415 isIn = thisIn || thatIn;
49419 isIn = thisIn ^ thatIn;
49423 if (event.isSubject) {
49424 isIn = thisIn && !thatIn;
49426 isIn = thatIn && !thisIn;
49432 return isIn ? +1 : -1;
49435 var SweepEvent = /*#__PURE__*/function () {
49439 * @class {SweepEvent}
49440 * @param {Array.<Number>} point
49441 * @param {Boolean} left
49442 * @param {SweepEvent=} otherEvent
49443 * @param {Boolean} isSubject
49444 * @param {Number} edgeType
49446 function SweepEvent(point, left, otherEvent, isSubject, edgeType) {
49447 _classCallCheck(this, SweepEvent);
49450 * Is left endpoint?
49455 * @type {Array.<Number>}
49458 this.point = point;
49460 * Other edge reference
49461 * @type {SweepEvent}
49464 this.otherEvent = otherEvent;
49466 * Belongs to source or clipping polygon
49470 this.isSubject = isSubject;
49472 * Edge contribution type
49476 this.type = edgeType || NORMAL;
49478 * In-out transition for the sweepline crossing polygon
49482 this.inOut = false;
49487 this.otherInOut = false;
49489 * Previous event in result?
49490 * @type {SweepEvent}
49493 this.prevInResult = null;
49495 * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
49499 this.resultTransition = 0; // connection step
49505 this.otherPos = -1;
49510 this.outputContourId = -1;
49511 this.isExteriorRing = true; // TODO: Looks unused, remove?
49514 * @param {Array.<Number>} p
49515 * @return {Boolean}
49519 _createClass(SweepEvent, [{
49521 value: function isBelow(p) {
49522 var p0 = this.point,
49523 p1 = this.otherEvent.point;
49524 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 :
49525 : (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;
49528 * @param {Array.<Number>} p
49529 * @return {Boolean}
49534 value: function isAbove(p) {
49535 return !this.isBelow(p);
49538 * @return {Boolean}
49543 value: function isVertical() {
49544 return this.point[0] === this.otherEvent.point[0];
49547 * Does event belong to result?
49548 * @return {Boolean}
49553 value: function clone() {
49554 var copy = new SweepEvent(this.point, this.left, this.otherEvent, this.isSubject, this.type);
49555 copy.contourId = this.contourId;
49556 copy.resultTransition = this.resultTransition;
49557 copy.prevInResult = this.prevInResult;
49558 copy.isExteriorRing = this.isExteriorRing;
49559 copy.inOut = this.inOut;
49560 copy.otherInOut = this.otherInOut;
49565 get: function get() {
49566 return this.resultTransition !== 0;
49573 function equals(p1, p2) {
49574 if (p1[0] === p2[0]) {
49575 if (p1[1] === p2[1]) {
49583 } // const EPSILON = 1e-9;
49584 // const abs = Math.abs;
49585 // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
49586 // Precision problem.
49588 // module.exports = function equals(p1, p2) {
49589 // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
49592 var epsilon$1 = 1.1102230246251565e-16;
49593 var splitter = 134217729;
49594 var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; // fast_expansion_sum_zeroelim routine from oritinal code
49596 function sum(elen, e, flen, f, h) {
49597 var Q, Qnew, hh, bvirt;
49603 if (fnow > enow === fnow > -enow) {
49605 enow = e[++eindex];
49608 fnow = f[++findex];
49613 if (eindex < elen && findex < flen) {
49614 if (fnow > enow === fnow > -enow) {
49616 hh = Q - (Qnew - enow);
49617 enow = e[++eindex];
49620 hh = Q - (Qnew - fnow);
49621 fnow = f[++findex];
49630 while (eindex < elen && findex < flen) {
49631 if (fnow > enow === fnow > -enow) {
49634 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49635 enow = e[++eindex];
49639 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49640 fnow = f[++findex];
49651 while (eindex < elen) {
49654 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49655 enow = e[++eindex];
49663 while (findex < flen) {
49666 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49667 fnow = f[++findex];
49675 if (Q !== 0 || hindex === 0) {
49681 function estimate(elen, e) {
49684 for (var i = 1; i < elen; i++) {
49691 return new Float64Array(n);
49694 var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
49695 var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
49696 var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
49703 function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
49704 var acxtail, acytail, bcxtail, bcytail;
49706 var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
49713 c = splitter * acx;
49714 ahi = c - (c - acx);
49716 c = splitter * bcy;
49717 bhi = c - (c - bcy);
49719 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49721 c = splitter * acy;
49722 ahi = c - (c - acy);
49724 c = splitter * bcx;
49725 bhi = c - (c - bcx);
49727 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49730 B[0] = s0 - (_i + bvirt) + (bvirt - t0);
49733 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49736 B[1] = _0 - (_i + bvirt) + (bvirt - t1);
49739 B[2] = _j - (u3 - bvirt) + (_i - bvirt);
49741 var det = estimate(4, B);
49742 var errbound = ccwerrboundB * detsum;
49744 if (det >= errbound || -det >= errbound) {
49749 acxtail = ax - (acx + bvirt) + (bvirt - cx);
49751 bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
49753 acytail = ay - (acy + bvirt) + (bvirt - cy);
49755 bcytail = by - (bcy + bvirt) + (bvirt - cy);
49757 if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
49761 errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
49762 det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
49763 if (det >= errbound || -det >= errbound) return det;
49764 s1 = acxtail * bcy;
49765 c = splitter * acxtail;
49766 ahi = c - (c - acxtail);
49767 alo = acxtail - ahi;
49768 c = splitter * bcy;
49769 bhi = c - (c - bcy);
49771 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49772 t1 = acytail * bcx;
49773 c = splitter * acytail;
49774 ahi = c - (c - acytail);
49775 alo = acytail - ahi;
49776 c = splitter * bcx;
49777 bhi = c - (c - bcx);
49779 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49782 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49785 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49788 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49791 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49793 var C1len = sum(4, B, 4, u, C1);
49794 s1 = acx * bcytail;
49795 c = splitter * acx;
49796 ahi = c - (c - acx);
49798 c = splitter * bcytail;
49799 bhi = c - (c - bcytail);
49800 blo = bcytail - bhi;
49801 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49802 t1 = acy * bcxtail;
49803 c = splitter * acy;
49804 ahi = c - (c - acy);
49806 c = splitter * bcxtail;
49807 bhi = c - (c - bcxtail);
49808 blo = bcxtail - bhi;
49809 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49812 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49815 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49818 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49821 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49823 var C2len = sum(C1len, C1, 4, u, C2);
49824 s1 = acxtail * bcytail;
49825 c = splitter * acxtail;
49826 ahi = c - (c - acxtail);
49827 alo = acxtail - ahi;
49828 c = splitter * bcytail;
49829 bhi = c - (c - bcytail);
49830 blo = bcytail - bhi;
49831 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49832 t1 = acytail * bcxtail;
49833 c = splitter * acytail;
49834 ahi = c - (c - acytail);
49835 alo = acytail - ahi;
49836 c = splitter * bcxtail;
49837 bhi = c - (c - bcxtail);
49838 blo = bcxtail - bhi;
49839 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49842 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49845 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49848 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49851 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49853 var Dlen = sum(C2len, C2, 4, u, D);
49854 return D[Dlen - 1];
49857 function orient2d(ax, ay, bx, by, cx, cy) {
49858 var detleft = (ay - cy) * (bx - cx);
49859 var detright = (ax - cx) * (by - cy);
49860 var det = detleft - detright;
49861 if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) return det;
49862 var detsum = Math.abs(detleft + detright);
49863 if (Math.abs(det) >= ccwerrboundA * detsum) return det;
49864 return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
49868 * Signed area of the triangle (p0, p1, p2)
49869 * @param {Array.<Number>} p0
49870 * @param {Array.<Number>} p1
49871 * @param {Array.<Number>} p2
49875 function signedArea(p0, p1, p2) {
49876 var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
49877 if (res > 0) return -1;
49878 if (res < 0) return 1;
49883 * @param {SweepEvent} e1
49884 * @param {SweepEvent} e2
49888 function compareEvents(e1, e2) {
49890 var p2 = e2.point; // Different x-coordinate
49892 if (p1[0] > p2[0]) return 1;
49893 if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate
49894 // Event with lower y-coordinate is processed first
49896 if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
49897 return specialCases(e1, e2, p1);
49899 /* eslint-disable no-unused-vars */
49901 function specialCases(e1, e2, p1, p2) {
49902 // Same coordinates, but one is a left endpoint and the other is
49903 // a right endpoint. The right endpoint is processed first
49904 if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
49905 // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
49906 // Same coordinates, both events
49907 // are left endpoints or right endpoints.
49910 if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
49911 // the event associate to the bottom segment is processed first
49912 return !e1.isBelow(e2.otherEvent.point) ? 1 : -1;
49915 return !e1.isSubject && e2.isSubject ? 1 : -1;
49917 /* eslint-enable no-unused-vars */
49920 * @param {SweepEvent} se
49921 * @param {Array.<Number>} p
49922 * @param {Queue} queue
49926 function divideSegment(se, p, queue) {
49927 var r = new SweepEvent(p, false, se, se.isSubject);
49928 var l = new SweepEvent(p, true, se.otherEvent, se.isSubject);
49929 /* eslint-disable no-console */
49931 if (equals(se.point, se.otherEvent.point)) {
49932 console.warn('what is that, a collapsed segment?', se);
49934 /* eslint-enable no-console */
49937 r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event
49939 if (compareEvents(l, se.otherEvent) > 0) {
49940 se.otherEvent.left = true;
49942 } // avoid a rounding error. The left event would be processed after the right event
49943 // if (compareEvents(se, r) > 0) {}
49946 se.otherEvent.otherEvent = l;
49953 //const EPS = 1e-9;
49956 * Finds the magnitude of the cross product of two vectors (if we pretend
49957 * they're in three dimensions)
49959 * @param {Object} a First vector
49960 * @param {Object} b Second vector
49962 * @returns {Number} The magnitude of the cross product
49964 function crossProduct(a, b) {
49965 return a[0] * b[1] - a[1] * b[0];
49968 * Finds the dot product of two vectors.
49970 * @param {Object} a First vector
49971 * @param {Object} b Second vector
49973 * @returns {Number} The dot product
49977 function dotProduct(a, b) {
49978 return a[0] * b[0] + a[1] * b[1];
49981 * Finds the intersection (if any) between two line segments a and b, given the
49982 * line segments' end points a1, a2 and b1, b2.
49984 * This algorithm is based on Schneider and Eberly.
49985 * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
49988 * @param {Array.<Number>} a1 point of first line
49989 * @param {Array.<Number>} a2 point of first line
49990 * @param {Array.<Number>} b1 point of second line
49991 * @param {Array.<Number>} b2 point of second line
49992 * @param {Boolean=} noEndpointTouch whether to skip single touchpoints
49993 * (meaning connected segments) as
49995 * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
49996 * intersection. If they overlap, the two end points of the overlapping segment.
50001 function intersection (a1, a2, b1, b2, noEndpointTouch) {
50002 // The algorithm expects our lines in the form P + sd, where P is a point,
50003 // s is on the interval [0, 1], and d is a vector.
50004 // We are passed two points. P can be the first point of each pair. The
50005 // vector, then, could be thought of as the distance (in x and y components)
50006 // from the first point to the second point.
50007 // So first, let's make our vectors:
50008 var va = [a2[0] - a1[0], a2[1] - a1[1]];
50009 var vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form:
50011 /* eslint-disable arrow-body-style */
50013 function toPoint(p, s, d) {
50014 return [p[0] + s * d[0], p[1] + s * d[1]];
50016 /* eslint-enable arrow-body-style */
50017 // The rest is pretty much a straight port of the algorithm.
50020 var e = [b1[0] - a1[0], b1[1] - a1[1]];
50021 var kross = crossProduct(va, vb);
50022 var sqrKross = kross * kross;
50023 var sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb);
50024 // Check for line intersection. This works because of the properties of the
50025 // cross product -- specifically, two vectors are parallel if and only if the
50026 // cross product is the 0 vector. The full calculation involves relative error
50027 // to account for possible very small line segments. See Schneider & Eberly
50031 /* EPS * sqrLenB * sqLenA */
50033 // If they're not parallel, then (because these are line segments) they
50034 // still might not actually intersect. This code checks that the
50035 // intersection point of the lines is actually on both line segments.
50036 var s = crossProduct(e, vb) / kross;
50038 if (s < 0 || s > 1) {
50039 // not on line segment a
50043 var t = crossProduct(e, va) / kross;
50045 if (t < 0 || t > 1) {
50046 // not on line segment b
50050 if (s === 0 || s === 1) {
50051 // on an endpoint of line segment a
50052 return noEndpointTouch ? null : [toPoint(a1, s, va)];
50055 if (t === 0 || t === 1) {
50056 // on an endpoint of line segment b
50057 return noEndpointTouch ? null : [toPoint(b1, t, vb)];
50060 return [toPoint(a1, s, va)];
50061 } // If we've reached this point, then the lines are either parallel or the
50062 // same, but the segments could overlap partially or fully, or not at all.
50063 // So we need to find the overlap, if any. To do that, we can use e, which is
50064 // the (vector) difference between the two initial points. If this is parallel
50065 // with the line itself, then the two lines are the same line, and there will
50067 //const sqrLenE = dotProduct(e, e);
50070 kross = crossProduct(e, va);
50071 sqrKross = kross * kross;
50074 /* EPS * sqLenB * sqLenE */
50076 // Lines are just parallel, not the same. No overlap.
50080 var sa = dotProduct(va, e) / sqrLenA;
50081 var sb = sa + dotProduct(va, vb) / sqrLenA;
50082 var smin = Math.min(sa, sb);
50083 var smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from
50084 // Schneider & Eberly, just inlined into this function.
50086 if (smin <= 1 && smax >= 0) {
50087 // overlap on an end point
50089 return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
50093 return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
50096 if (noEndpointTouch && smin === 0 && smax === 1) return null; // There's overlap on a segment -- two points of intersection. Return both.
50098 return [toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va)];
50105 * @param {SweepEvent} se1
50106 * @param {SweepEvent} se2
50107 * @param {Queue} queue
50111 function possibleIntersection(se1, se2, queue) {
50112 // that disallows self-intersecting polygons,
50113 // did cost us half a day, so I'll leave it
50115 // if (se1.isSubject === se2.isSubject) return;
50116 var inter = intersection(se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50117 var nintersections = inter ? inter.length : 0;
50118 if (nintersections === 0) return 0; // no intersection
50119 // the line segments intersect at an endpoint of both line segments
50121 if (nintersections === 1 && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) {
50125 if (nintersections === 2 && se1.isSubject === se2.isSubject) {
50126 // if(se1.contourId === se2.contourId){
50127 // console.warn('Edges of the same polygon overlap',
50128 // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50130 //throw new Error('Edges of the same polygon overlap');
50132 } // The line segments associated to se1 and se2 intersect
50135 if (nintersections === 1) {
50136 // if the intersection point is not an endpoint of se1
50137 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
50138 divideSegment(se1, inter[0], queue);
50139 } // if the intersection point is not an endpoint of se2
50142 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
50143 divideSegment(se2, inter[0], queue);
50147 } // The line segments associated to se1 and se2 overlap
50151 var leftCoincide = false;
50152 var rightCoincide = false;
50154 if (equals(se1.point, se2.point)) {
50155 leftCoincide = true; // linked
50156 } else if (compareEvents(se1, se2) === 1) {
50157 events.push(se2, se1);
50159 events.push(se1, se2);
50162 if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
50163 rightCoincide = true;
50164 } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
50165 events.push(se2.otherEvent, se1.otherEvent);
50167 events.push(se1.otherEvent, se2.otherEvent);
50170 if (leftCoincide && rightCoincide || leftCoincide) {
50171 // both line segments are equal or share the left endpoint
50172 se2.type = NON_CONTRIBUTING;
50173 se1.type = se2.inOut === se1.inOut ? SAME_TRANSITION : DIFFERENT_TRANSITION;
50175 if (leftCoincide && !rightCoincide) {
50176 // honestly no idea, but changing events selection from [2, 1]
50177 // to [0, 1] fixes the overlapping self-intersecting polygons issue
50178 divideSegment(events[1].otherEvent, events[0].point, queue);
50182 } // the line segments share the right endpoint
50185 if (rightCoincide) {
50186 divideSegment(events[0], events[1].point, queue);
50188 } // no line segment includes totally the other one
50191 if (events[0] !== events[3].otherEvent) {
50192 divideSegment(events[0], events[1].point, queue);
50193 divideSegment(events[1], events[2].point, queue);
50195 } // one line segment includes the other one
50198 divideSegment(events[0], events[1].point, queue);
50199 divideSegment(events[3].otherEvent, events[2].point, queue);
50204 * @param {SweepEvent} le1
50205 * @param {SweepEvent} le2
50209 function compareSegments(le1, le2) {
50210 if (le1 === le2) return 0; // Segments are not collinear
50212 if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
50213 // If they share their left endpoint use the right endpoint to sort
50214 if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort
50216 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
50217 // into S after the line segment associated to e2 ?
50219 if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted
50220 // into S after the line segment associated to e1
50222 return le1.isBelow(le2.point) ? -1 : 1;
50225 if (le1.isSubject === le2.isSubject) {
50227 var p1 = le1.point,
50230 if (p1[0] === p2[0] && p1[1] === p2[1]
50231 /*equals(le1.point, le2.point)*/
50233 p1 = le1.otherEvent.point;
50234 p2 = le2.otherEvent.point;
50235 if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;else return le1.contourId > le2.contourId ? 1 : -1;
50238 // Segments are collinear, but belong to separate polygons
50239 return le1.isSubject ? -1 : 1;
50242 return compareEvents(le1, le2) === 1 ? 1 : -1;
50245 function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
50246 var sweepLine = new SplayTree(compareSegments);
50247 var sortedEvents = [];
50248 var rightbound = Math.min(sbbox[2], cbbox[2]);
50249 var prev, next, begin;
50251 while (eventQueue.length !== 0) {
50252 var event = eventQueue.pop();
50253 sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here
50255 if (operation === INTERSECTION && event.point[0] > rightbound || operation === DIFFERENCE && event.point[0] > sbbox[2]) {
50260 next = prev = sweepLine.insert(event);
50261 begin = sweepLine.minNode();
50262 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50263 next = sweepLine.next(next);
50264 var prevEvent = prev ? prev.key : null;
50265 var prevprevEvent = void 0;
50266 computeFields(event, prevEvent, operation);
50269 if (possibleIntersection(event, next.key, eventQueue) === 2) {
50270 computeFields(event, prevEvent, operation);
50271 computeFields(event, next.key, operation);
50276 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
50277 var prevprev = prev;
50278 if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);else prevprev = null;
50279 prevprevEvent = prevprev ? prevprev.key : null;
50280 computeFields(prevEvent, prevprevEvent, operation);
50281 computeFields(event, prevEvent, operation);
50285 event = event.otherEvent;
50286 next = prev = sweepLine.find(event);
50288 if (prev && next) {
50289 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50290 next = sweepLine.next(next);
50291 sweepLine.remove(event);
50293 if (next && prev) {
50294 possibleIntersection(prev.key, next.key, eventQueue);
50300 return sortedEvents;
50303 var Contour = /*#__PURE__*/function () {
50309 function Contour() {
50310 _classCallCheck(this, Contour);
50314 this.holeOf = null;
50318 _createClass(Contour, [{
50320 value: function isExterior() {
50321 return this.holeOf == null;
50329 * @param {Array.<SweepEvent>} sortedEvents
50330 * @return {Array.<SweepEvent>}
50333 function orderEvents(sortedEvents) {
50334 var event, i, len, tmp;
50335 var resultEvents = [];
50337 for (i = 0, len = sortedEvents.length; i < len; i++) {
50338 event = sortedEvents[i];
50340 if (event.left && event.inResult || !event.left && event.otherEvent.inResult) {
50341 resultEvents.push(event);
50343 } // Due to overlapping edges the resultEvents array can be not wholly sorted
50346 var sorted = false;
50351 for (i = 0, len = resultEvents.length; i < len; i++) {
50352 if (i + 1 < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
50353 tmp = resultEvents[i];
50354 resultEvents[i] = resultEvents[i + 1];
50355 resultEvents[i + 1] = tmp;
50361 for (i = 0, len = resultEvents.length; i < len; i++) {
50362 event = resultEvents[i];
50363 event.otherPos = i;
50364 } // imagine, the right event is found in the beginning of the queue,
50365 // when his left counterpart is not marked yet
50368 for (i = 0, len = resultEvents.length; i < len; i++) {
50369 event = resultEvents[i];
50372 tmp = event.otherPos;
50373 event.otherPos = event.otherEvent.otherPos;
50374 event.otherEvent.otherPos = tmp;
50378 return resultEvents;
50381 * @param {Number} pos
50382 * @param {Array.<SweepEvent>} resultEvents
50383 * @param {Object>} processed
50388 function nextPos(pos, resultEvents, processed, origPos) {
50389 var newPos = pos + 1,
50390 p = resultEvents[pos].point,
50392 var length = resultEvents.length;
50393 if (newPos < length) p1 = resultEvents[newPos].point;
50395 while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
50396 if (!processed[newPos]) {
50402 p1 = resultEvents[newPos].point;
50407 while (processed[newPos] && newPos > origPos) {
50414 function initializeContourFromContext(event, contours, contourId) {
50415 var contour = new Contour();
50417 if (event.prevInResult != null) {
50418 var prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id,
50419 // because we must have already processed it (i.e., assigned an output contour id)
50420 // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
50423 var lowerContourId = prevInResult.outputContourId;
50424 var lowerResultTransition = prevInResult.resultTransition;
50426 if (lowerResultTransition > 0) {
50427 // We are inside. Now we have to check if the thing below us is another hole or
50428 // an exterior contour.
50429 var lowerContour = contours[lowerContourId];
50431 if (lowerContour.holeOf != null) {
50432 // The lower contour is a hole => Connect the new contour as a hole to its parent,
50433 // and use same depth.
50434 var parentContourId = lowerContour.holeOf;
50435 contours[parentContourId].holeIds.push(contourId);
50436 contour.holeOf = parentContourId;
50437 contour.depth = contours[lowerContourId].depth;
50439 // The lower contour is an exterior contour => Connect the new contour as a hole,
50440 // and increment depth.
50441 contours[lowerContourId].holeIds.push(contourId);
50442 contour.holeOf = lowerContourId;
50443 contour.depth = contours[lowerContourId].depth + 1;
50446 // We are outside => this contour is an exterior contour of same depth.
50447 contour.holeOf = null;
50448 contour.depth = contours[lowerContourId].depth;
50451 // There is no lower/previous contour => this contour is an exterior contour of depth 0.
50452 contour.holeOf = null;
50459 * @param {Array.<SweepEvent>} sortedEvents
50460 * @return {Array.<*>} polygons
50464 function connectEdges(sortedEvents) {
50466 var resultEvents = orderEvents(sortedEvents); // "false"-filled array
50468 var processed = {};
50471 var _loop = function _loop() {
50472 if (processed[i]) {
50476 var contourId = contours.length;
50477 var contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID
50479 var markAsProcessed = function markAsProcessed(pos) {
50480 processed[pos] = true;
50481 resultEvents[pos].outputContourId = contourId;
50486 var initial = resultEvents[i].point;
50487 contour.points.push(initial);
50488 /* eslint no-constant-condition: "off" */
50491 markAsProcessed(pos);
50492 pos = resultEvents[pos].otherPos;
50493 markAsProcessed(pos);
50494 contour.points.push(resultEvents[pos].point);
50495 pos = nextPos(pos, resultEvents, processed, origPos);
50497 if (pos == origPos) {
50502 contours.push(contour);
50505 for (i = 0, len = resultEvents.length; i < len; i++) {
50506 var _ret = _loop();
50508 if (_ret === "continue") continue;
50514 var tinyqueue = TinyQueue;
50515 var _default$1 = TinyQueue;
50517 function TinyQueue(data, compare) {
50518 if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
50519 this.data = data || [];
50520 this.length = this.data.length;
50521 this.compare = compare || defaultCompare$1;
50523 if (this.length > 0) {
50524 for (var i = (this.length >> 1) - 1; i >= 0; i--) {
50530 function defaultCompare$1(a, b) {
50531 return a < b ? -1 : a > b ? 1 : 0;
50534 TinyQueue.prototype = {
50535 push: function push(item) {
50536 this.data.push(item);
50539 this._up(this.length - 1);
50541 pop: function pop() {
50542 if (this.length === 0) return undefined;
50543 var top = this.data[0];
50546 if (this.length > 0) {
50547 this.data[0] = this.data[this.length];
50555 peek: function peek() {
50556 return this.data[0];
50558 _up: function _up(pos) {
50559 var data = this.data;
50560 var compare = this.compare;
50561 var item = data[pos];
50564 var parent = pos - 1 >> 1;
50565 var current = data[parent];
50566 if (compare(item, current) >= 0) break;
50567 data[pos] = current;
50573 _down: function _down(pos) {
50574 var data = this.data;
50575 var compare = this.compare;
50576 var halfLength = this.length >> 1;
50577 var item = data[pos];
50579 while (pos < halfLength) {
50580 var left = (pos << 1) + 1;
50581 var right = left + 1;
50582 var best = data[left];
50584 if (right < this.length && compare(data[right], best) < 0) {
50586 best = data[right];
50589 if (compare(best, item) >= 0) break;
50597 tinyqueue["default"] = _default$1;
50599 var max$5 = Math.max;
50600 var min$a = Math.min;
50603 function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
50604 var i, len, s1, s2, e1, e2;
50606 for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
50607 s1 = contourOrHole[i];
50608 s2 = contourOrHole[i + 1];
50609 e1 = new SweepEvent(s1, false, undefined, isSubject);
50610 e2 = new SweepEvent(s2, false, e1, isSubject);
50611 e1.otherEvent = e2;
50613 if (s1[0] === s2[0] && s1[1] === s2[1]) {
50614 continue; // skip collapsed edges, or it breaks
50617 e1.contourId = e2.contourId = depth;
50619 if (!isExteriorRing) {
50620 e1.isExteriorRing = false;
50621 e2.isExteriorRing = false;
50624 if (compareEvents(e1, e2) > 0) {
50632 bbox[0] = min$a(bbox[0], x);
50633 bbox[1] = min$a(bbox[1], y);
50634 bbox[2] = max$5(bbox[2], x);
50635 bbox[3] = max$5(bbox[3], y); // Pushing it so the queue is sorted from left to right,
50636 // with object on the left having the highest priority.
50643 function fillQueue(subject, clipping, sbbox, cbbox, operation) {
50644 var eventQueue = new tinyqueue(null, compareEvents);
50645 var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
50647 for (i = 0, ii = subject.length; i < ii; i++) {
50648 polygonSet = subject[i];
50650 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50651 isExteriorRing = j === 0;
50652 if (isExteriorRing) contourId++;
50653 processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
50657 for (i = 0, ii = clipping.length; i < ii; i++) {
50658 polygonSet = clipping[i];
50660 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50661 isExteriorRing = j === 0;
50662 if (operation === DIFFERENCE) isExteriorRing = false;
50663 if (isExteriorRing) contourId++;
50664 processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
50673 function trivialOperation(subject, clipping, operation) {
50676 if (subject.length * clipping.length === 0) {
50677 if (operation === INTERSECTION) {
50679 } else if (operation === DIFFERENCE) {
50681 } else if (operation === UNION || operation === XOR) {
50682 result = subject.length === 0 ? clipping : subject;
50689 function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
50692 if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) {
50693 if (operation === INTERSECTION) {
50695 } else if (operation === DIFFERENCE) {
50697 } else if (operation === UNION || operation === XOR) {
50698 result = subject.concat(clipping);
50705 function _boolean(subject, clipping, operation) {
50706 if (typeof subject[0][0][0] === 'number') {
50707 subject = [subject];
50710 if (typeof clipping[0][0][0] === 'number') {
50711 clipping = [clipping];
50714 var trivial = trivialOperation(subject, clipping, operation);
50717 return trivial === EMPTY ? null : trivial;
50720 var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
50721 var cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time('fill queue');
50723 var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd('fill queue');
50725 trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
50728 return trivial === EMPTY ? null : trivial;
50729 } // console.time('subdivide edges');
50732 var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd('subdivide edges');
50733 // console.time('connect vertices');
50735 var contours = connectEdges(sortedEvents); //console.timeEnd('connect vertices');
50736 // Convert contours to polygons
50740 for (var i = 0; i < contours.length; i++) {
50741 var contour = contours[i];
50743 if (contour.isExterior()) {
50744 // The exterior ring goes first
50745 var rings = [contour.points]; // Followed by holes if any
50747 for (var j = 0; j < contour.holeIds.length; j++) {
50748 var holeId = contour.holeIds[j];
50749 rings.push(contours[holeId].points);
50752 polygons.push(rings);
50759 function union(subject, clipping) {
50760 return _boolean(subject, clipping, UNION);
50763 /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
50764 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
50766 var eLen = nBytes * 8 - mLen - 1;
50767 var eMax = (1 << eLen) - 1;
50768 var eBias = eMax >> 1;
50770 var i = isLE ? nBytes - 1 : 0;
50771 var d = isLE ? -1 : 1;
50772 var s = buffer[offset + i];
50774 e = s & (1 << -nBits) - 1;
50778 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50780 m = e & (1 << -nBits) - 1;
50784 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50788 } else if (e === eMax) {
50789 return m ? NaN : (s ? -1 : 1) * Infinity;
50791 m = m + Math.pow(2, mLen);
50795 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
50798 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
50800 var eLen = nBytes * 8 - mLen - 1;
50801 var eMax = (1 << eLen) - 1;
50802 var eBias = eMax >> 1;
50803 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
50804 var i = isLE ? 0 : nBytes - 1;
50805 var d = isLE ? 1 : -1;
50806 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
50807 value = Math.abs(value);
50809 if (isNaN(value) || value === Infinity) {
50810 m = isNaN(value) ? 1 : 0;
50813 e = Math.floor(Math.log(value) / Math.LN2);
50815 if (value * (c = Math.pow(2, -e)) < 1) {
50820 if (e + eBias >= 1) {
50823 value += rt * Math.pow(2, 1 - eBias);
50826 if (value * c >= 2) {
50831 if (e + eBias >= eMax) {
50834 } else if (e + eBias >= 1) {
50835 m = (value * c - 1) * Math.pow(2, mLen);
50838 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
50843 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
50848 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
50850 buffer[offset + i - d] |= s * 128;
50860 function Pbf(buf) {
50861 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
50864 this.length = this.buf.length;
50867 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50869 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50871 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50873 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50875 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50876 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50877 // data structures (which currently switch structure types at 12 bytes or more)
50879 var TEXT_DECODER_MIN_LENGTH = 12;
50880 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50882 destroy: function destroy() {
50885 // === READING =================================================================
50886 readFields: function readFields(readField, result, end) {
50887 end = end || this.length;
50889 while (this.pos < end) {
50890 var val = this.readVarint(),
50892 startPos = this.pos;
50893 this.type = val & 0x7;
50894 readField(tag, result, this);
50895 if (this.pos === startPos) this.skip(val);
50900 readMessage: function readMessage(readField, result) {
50901 return this.readFields(readField, result, this.readVarint() + this.pos);
50903 readFixed32: function readFixed32() {
50904 var val = readUInt32(this.buf, this.pos);
50908 readSFixed32: function readSFixed32() {
50909 var val = readInt32(this.buf, this.pos);
50913 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50914 readFixed64: function readFixed64() {
50915 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50919 readSFixed64: function readSFixed64() {
50920 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50924 readFloat: function readFloat() {
50925 var val = ieee754$1.read(this.buf, this.pos, true, 23, 4);
50929 readDouble: function readDouble() {
50930 var val = ieee754$1.read(this.buf, this.pos, true, 52, 8);
50934 readVarint: function readVarint(isSigned) {
50935 var buf = this.buf,
50938 b = buf[this.pos++];
50940 if (b < 0x80) return val;
50941 b = buf[this.pos++];
50942 val |= (b & 0x7f) << 7;
50943 if (b < 0x80) return val;
50944 b = buf[this.pos++];
50945 val |= (b & 0x7f) << 14;
50946 if (b < 0x80) return val;
50947 b = buf[this.pos++];
50948 val |= (b & 0x7f) << 21;
50949 if (b < 0x80) return val;
50951 val |= (b & 0x0f) << 28;
50952 return readVarintRemainder(val, isSigned, this);
50954 readVarint64: function readVarint64() {
50955 // for compatibility with v2.0.1
50956 return this.readVarint(true);
50958 readSVarint: function readSVarint() {
50959 var num = this.readVarint();
50960 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50962 readBoolean: function readBoolean() {
50963 return Boolean(this.readVarint());
50965 readString: function readString() {
50966 var end = this.readVarint() + this.pos;
50967 var pos = this.pos;
50970 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50971 // longer strings are fast with the built-in browser TextDecoder API
50972 return readUtf8TextDecoder(this.buf, pos, end);
50973 } // short strings are fast with our custom implementation
50976 return readUtf8(this.buf, pos, end);
50978 readBytes: function readBytes() {
50979 var end = this.readVarint() + this.pos,
50980 buffer = this.buf.subarray(this.pos, end);
50984 // verbose for performance reasons; doesn't affect gzipped size
50985 readPackedVarint: function readPackedVarint(arr, isSigned) {
50986 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50987 var end = readPackedEnd(this);
50990 while (this.pos < end) {
50991 arr.push(this.readVarint(isSigned));
50996 readPackedSVarint: function readPackedSVarint(arr) {
50997 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50998 var end = readPackedEnd(this);
51001 while (this.pos < end) {
51002 arr.push(this.readSVarint());
51007 readPackedBoolean: function readPackedBoolean(arr) {
51008 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
51009 var end = readPackedEnd(this);
51012 while (this.pos < end) {
51013 arr.push(this.readBoolean());
51018 readPackedFloat: function readPackedFloat(arr) {
51019 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
51020 var end = readPackedEnd(this);
51023 while (this.pos < end) {
51024 arr.push(this.readFloat());
51029 readPackedDouble: function readPackedDouble(arr) {
51030 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
51031 var end = readPackedEnd(this);
51034 while (this.pos < end) {
51035 arr.push(this.readDouble());
51040 readPackedFixed32: function readPackedFixed32(arr) {
51041 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
51042 var end = readPackedEnd(this);
51045 while (this.pos < end) {
51046 arr.push(this.readFixed32());
51051 readPackedSFixed32: function readPackedSFixed32(arr) {
51052 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
51053 var end = readPackedEnd(this);
51056 while (this.pos < end) {
51057 arr.push(this.readSFixed32());
51062 readPackedFixed64: function readPackedFixed64(arr) {
51063 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
51064 var end = readPackedEnd(this);
51067 while (this.pos < end) {
51068 arr.push(this.readFixed64());
51073 readPackedSFixed64: function readPackedSFixed64(arr) {
51074 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
51075 var end = readPackedEnd(this);
51078 while (this.pos < end) {
51079 arr.push(this.readSFixed64());
51084 skip: function skip(val) {
51085 var type = val & 0x7;
51086 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);
51088 // === WRITING =================================================================
51089 writeTag: function writeTag(tag, type) {
51090 this.writeVarint(tag << 3 | type);
51092 realloc: function realloc(min) {
51093 var length = this.length || 16;
51095 while (length < this.pos + min) {
51099 if (length !== this.length) {
51100 var buf = new Uint8Array(length);
51103 this.length = length;
51106 finish: function finish() {
51107 this.length = this.pos;
51109 return this.buf.subarray(0, this.length);
51111 writeFixed32: function writeFixed32(val) {
51113 writeInt32(this.buf, val, this.pos);
51116 writeSFixed32: function writeSFixed32(val) {
51118 writeInt32(this.buf, val, this.pos);
51121 writeFixed64: function writeFixed64(val) {
51123 writeInt32(this.buf, val & -1, this.pos);
51124 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51127 writeSFixed64: function writeSFixed64(val) {
51129 writeInt32(this.buf, val & -1, this.pos);
51130 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51133 writeVarint: function writeVarint(val) {
51136 if (val > 0xfffffff || val < 0) {
51137 writeBigVarint(val, this);
51142 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
51143 if (val <= 0x7f) return;
51144 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51145 if (val <= 0x7f) return;
51146 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51147 if (val <= 0x7f) return;
51148 this.buf[this.pos++] = val >>> 7 & 0x7f;
51150 writeSVarint: function writeSVarint(val) {
51151 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
51153 writeBoolean: function writeBoolean(val) {
51154 this.writeVarint(Boolean(val));
51156 writeString: function writeString(str) {
51158 this.realloc(str.length * 4);
51159 this.pos++; // reserve 1 byte for short string length
51161 var startPos = this.pos; // write the string directly to the buffer and see how much was written
51163 this.pos = writeUtf8(this.buf, str, this.pos);
51164 var len = this.pos - startPos;
51165 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51167 this.pos = startPos - 1;
51168 this.writeVarint(len);
51171 writeFloat: function writeFloat(val) {
51173 ieee754$1.write(this.buf, val, this.pos, true, 23, 4);
51176 writeDouble: function writeDouble(val) {
51178 ieee754$1.write(this.buf, val, this.pos, true, 52, 8);
51181 writeBytes: function writeBytes(buffer) {
51182 var len = buffer.length;
51183 this.writeVarint(len);
51186 for (var i = 0; i < len; i++) {
51187 this.buf[this.pos++] = buffer[i];
51190 writeRawMessage: function writeRawMessage(fn, obj) {
51191 this.pos++; // reserve 1 byte for short message length
51192 // write the message directly to the buffer and see how much was written
51194 var startPos = this.pos;
51196 var len = this.pos - startPos;
51197 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51199 this.pos = startPos - 1;
51200 this.writeVarint(len);
51203 writeMessage: function writeMessage(tag, fn, obj) {
51204 this.writeTag(tag, Pbf.Bytes);
51205 this.writeRawMessage(fn, obj);
51207 writePackedVarint: function writePackedVarint(tag, arr) {
51208 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
51210 writePackedSVarint: function writePackedSVarint(tag, arr) {
51211 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
51213 writePackedBoolean: function writePackedBoolean(tag, arr) {
51214 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
51216 writePackedFloat: function writePackedFloat(tag, arr) {
51217 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
51219 writePackedDouble: function writePackedDouble(tag, arr) {
51220 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
51222 writePackedFixed32: function writePackedFixed32(tag, arr) {
51223 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
51225 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
51226 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
51228 writePackedFixed64: function writePackedFixed64(tag, arr) {
51229 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
51231 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
51232 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
51234 writeBytesField: function writeBytesField(tag, buffer) {
51235 this.writeTag(tag, Pbf.Bytes);
51236 this.writeBytes(buffer);
51238 writeFixed32Field: function writeFixed32Field(tag, val) {
51239 this.writeTag(tag, Pbf.Fixed32);
51240 this.writeFixed32(val);
51242 writeSFixed32Field: function writeSFixed32Field(tag, val) {
51243 this.writeTag(tag, Pbf.Fixed32);
51244 this.writeSFixed32(val);
51246 writeFixed64Field: function writeFixed64Field(tag, val) {
51247 this.writeTag(tag, Pbf.Fixed64);
51248 this.writeFixed64(val);
51250 writeSFixed64Field: function writeSFixed64Field(tag, val) {
51251 this.writeTag(tag, Pbf.Fixed64);
51252 this.writeSFixed64(val);
51254 writeVarintField: function writeVarintField(tag, val) {
51255 this.writeTag(tag, Pbf.Varint);
51256 this.writeVarint(val);
51258 writeSVarintField: function writeSVarintField(tag, val) {
51259 this.writeTag(tag, Pbf.Varint);
51260 this.writeSVarint(val);
51262 writeStringField: function writeStringField(tag, str) {
51263 this.writeTag(tag, Pbf.Bytes);
51264 this.writeString(str);
51266 writeFloatField: function writeFloatField(tag, val) {
51267 this.writeTag(tag, Pbf.Fixed32);
51268 this.writeFloat(val);
51270 writeDoubleField: function writeDoubleField(tag, val) {
51271 this.writeTag(tag, Pbf.Fixed64);
51272 this.writeDouble(val);
51274 writeBooleanField: function writeBooleanField(tag, val) {
51275 this.writeVarintField(tag, Boolean(val));
51279 function readVarintRemainder(l, s, p) {
51284 h = (b & 0x70) >> 4;
51285 if (b < 0x80) return toNum(l, h, s);
51287 h |= (b & 0x7f) << 3;
51288 if (b < 0x80) return toNum(l, h, s);
51290 h |= (b & 0x7f) << 10;
51291 if (b < 0x80) return toNum(l, h, s);
51293 h |= (b & 0x7f) << 17;
51294 if (b < 0x80) return toNum(l, h, s);
51296 h |= (b & 0x7f) << 24;
51297 if (b < 0x80) return toNum(l, h, s);
51299 h |= (b & 0x01) << 31;
51300 if (b < 0x80) return toNum(l, h, s);
51301 throw new Error('Expected varint not more than 10 bytes');
51304 function readPackedEnd(pbf) {
51305 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
51308 function toNum(low, high, isSigned) {
51310 return high * 0x100000000 + (low >>> 0);
51313 return (high >>> 0) * 0x100000000 + (low >>> 0);
51316 function writeBigVarint(val, pbf) {
51320 low = val % 0x100000000 | 0;
51321 high = val / 0x100000000 | 0;
51323 low = ~(-val % 0x100000000);
51324 high = ~(-val / 0x100000000);
51326 if (low ^ 0xffffffff) {
51330 high = high + 1 | 0;
51334 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
51335 throw new Error('Given varint doesn\'t fit into 10 bytes');
51339 writeBigVarintLow(low, high, pbf);
51340 writeBigVarintHigh(high, pbf);
51343 function writeBigVarintLow(low, high, pbf) {
51344 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51346 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51348 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51350 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51352 pbf.buf[pbf.pos] = low & 0x7f;
51355 function writeBigVarintHigh(high, pbf) {
51356 var lsb = (high & 0x07) << 4;
51357 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
51359 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51361 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51363 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51365 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51367 pbf.buf[pbf.pos++] = high & 0x7f;
51370 function makeRoomForExtraLength(startPos, len, pbf) {
51371 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
51373 pbf.realloc(extraLen);
51375 for (var i = pbf.pos - 1; i >= startPos; i--) {
51376 pbf.buf[i + extraLen] = pbf.buf[i];
51380 function _writePackedVarint(arr, pbf) {
51381 for (var i = 0; i < arr.length; i++) {
51382 pbf.writeVarint(arr[i]);
51386 function _writePackedSVarint(arr, pbf) {
51387 for (var i = 0; i < arr.length; i++) {
51388 pbf.writeSVarint(arr[i]);
51392 function _writePackedFloat(arr, pbf) {
51393 for (var i = 0; i < arr.length; i++) {
51394 pbf.writeFloat(arr[i]);
51398 function _writePackedDouble(arr, pbf) {
51399 for (var i = 0; i < arr.length; i++) {
51400 pbf.writeDouble(arr[i]);
51404 function _writePackedBoolean(arr, pbf) {
51405 for (var i = 0; i < arr.length; i++) {
51406 pbf.writeBoolean(arr[i]);
51410 function _writePackedFixed(arr, pbf) {
51411 for (var i = 0; i < arr.length; i++) {
51412 pbf.writeFixed32(arr[i]);
51416 function _writePackedSFixed(arr, pbf) {
51417 for (var i = 0; i < arr.length; i++) {
51418 pbf.writeSFixed32(arr[i]);
51422 function _writePackedFixed2(arr, pbf) {
51423 for (var i = 0; i < arr.length; i++) {
51424 pbf.writeFixed64(arr[i]);
51428 function _writePackedSFixed2(arr, pbf) {
51429 for (var i = 0; i < arr.length; i++) {
51430 pbf.writeSFixed64(arr[i]);
51432 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
51435 function readUInt32(buf, pos) {
51436 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
51439 function writeInt32(buf, val, pos) {
51441 buf[pos + 1] = val >>> 8;
51442 buf[pos + 2] = val >>> 16;
51443 buf[pos + 3] = val >>> 24;
51446 function readInt32(buf, pos) {
51447 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
51450 function readUtf8(buf, pos, end) {
51456 var c = null; // codepoint
51458 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
51459 if (i + bytesPerSequence > end) break;
51462 if (bytesPerSequence === 1) {
51466 } else if (bytesPerSequence === 2) {
51469 if ((b1 & 0xC0) === 0x80) {
51470 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
51476 } else if (bytesPerSequence === 3) {
51480 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
51481 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
51483 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
51487 } else if (bytesPerSequence === 4) {
51492 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
51493 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
51495 if (c <= 0xFFFF || c >= 0x110000) {
51503 bytesPerSequence = 1;
51504 } else if (c > 0xFFFF) {
51506 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
51507 c = 0xDC00 | c & 0x3FF;
51510 str += String.fromCharCode(c);
51511 i += bytesPerSequence;
51517 function readUtf8TextDecoder(buf, pos, end) {
51518 return utf8TextDecoder.decode(buf.subarray(pos, end));
51521 function writeUtf8(buf, str, pos) {
51522 for (var i = 0, c, lead; i < str.length; i++) {
51523 c = str.charCodeAt(i); // code point
51525 if (c > 0xD7FF && c < 0xE000) {
51534 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
51538 if (c > 0xDBFF || i + 1 === str.length) {
51559 buf[pos++] = c >> 0x6 | 0xC0;
51562 buf[pos++] = c >> 0xC | 0xE0;
51564 buf[pos++] = c >> 0x12 | 0xF0;
51565 buf[pos++] = c >> 0xC & 0x3F | 0x80;
51568 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
51571 buf[pos++] = c & 0x3F | 0x80;
51578 var pointGeometry = Point;
51580 * A standalone point geometry with useful accessor, comparison, and
51581 * modification methods.
51584 * @param {Number} x the x-coordinate. this could be longitude or screen
51585 * pixels, or any other sort of unit.
51586 * @param {Number} y the y-coordinate. this could be latitude or screen
51587 * pixels, or any other sort of unit.
51589 * var point = new Point(-77, 38);
51592 function Point(x, y) {
51597 Point.prototype = {
51599 * Clone this point, returning a new point that can be modified
51600 * without affecting the old one.
51601 * @return {Point} the clone
51603 clone: function clone() {
51604 return new Point(this.x, this.y);
51608 * Add this point's x & y coordinates to another point,
51609 * yielding a new point.
51610 * @param {Point} p the other point
51611 * @return {Point} output point
51613 add: function add(p) {
51614 return this.clone()._add(p);
51618 * Subtract this point's x & y coordinates to from point,
51619 * yielding a new point.
51620 * @param {Point} p the other point
51621 * @return {Point} output point
51623 sub: function sub(p) {
51624 return this.clone()._sub(p);
51628 * Multiply this point's x & y coordinates by point,
51629 * yielding a new point.
51630 * @param {Point} p the other point
51631 * @return {Point} output point
51633 multByPoint: function multByPoint(p) {
51634 return this.clone()._multByPoint(p);
51638 * Divide this point's x & y coordinates by point,
51639 * yielding a new point.
51640 * @param {Point} p the other point
51641 * @return {Point} output point
51643 divByPoint: function divByPoint(p) {
51644 return this.clone()._divByPoint(p);
51648 * Multiply this point's x & y coordinates by a factor,
51649 * yielding a new point.
51650 * @param {Point} k factor
51651 * @return {Point} output point
51653 mult: function mult(k) {
51654 return this.clone()._mult(k);
51658 * Divide this point's x & y coordinates by a factor,
51659 * yielding a new point.
51660 * @param {Point} k factor
51661 * @return {Point} output point
51663 div: function div(k) {
51664 return this.clone()._div(k);
51668 * Rotate this point around the 0, 0 origin by an angle a,
51670 * @param {Number} a angle to rotate around, in radians
51671 * @return {Point} output point
51673 rotate: function rotate(a) {
51674 return this.clone()._rotate(a);
51678 * Rotate this point around p point by an angle a,
51680 * @param {Number} a angle to rotate around, in radians
51681 * @param {Point} p Point to rotate around
51682 * @return {Point} output point
51684 rotateAround: function rotateAround(a, p) {
51685 return this.clone()._rotateAround(a, p);
51689 * Multiply this point by a 4x1 transformation matrix
51690 * @param {Array<Number>} m transformation matrix
51691 * @return {Point} output point
51693 matMult: function matMult(m) {
51694 return this.clone()._matMult(m);
51698 * Calculate this point but as a unit vector from 0, 0, meaning
51699 * that the distance from the resulting point to the 0, 0
51700 * coordinate will be equal to 1 and the angle from the resulting
51701 * point to the 0, 0 coordinate will be the same as before.
51702 * @return {Point} unit vector point
51704 unit: function unit() {
51705 return this.clone()._unit();
51709 * Compute a perpendicular point, where the new y coordinate
51710 * is the old x coordinate and the new x coordinate is the old y
51711 * coordinate multiplied by -1
51712 * @return {Point} perpendicular point
51714 perp: function perp() {
51715 return this.clone()._perp();
51719 * Return a version of this point with the x & y coordinates
51720 * rounded to integers.
51721 * @return {Point} rounded point
51723 round: function round() {
51724 return this.clone()._round();
51728 * Return the magitude of this point: this is the Euclidean
51729 * distance from the 0, 0 coordinate to this point's x and y
51731 * @return {Number} magnitude
51733 mag: function mag() {
51734 return Math.sqrt(this.x * this.x + this.y * this.y);
51738 * Judge whether this point is equal to another point, returning
51740 * @param {Point} other the other point
51741 * @return {boolean} whether the points are equal
51743 equals: function equals(other) {
51744 return this.x === other.x && this.y === other.y;
51748 * Calculate the distance from this point to another point
51749 * @param {Point} p the other point
51750 * @return {Number} distance
51752 dist: function dist(p) {
51753 return Math.sqrt(this.distSqr(p));
51757 * Calculate the distance from this point to another point,
51758 * without the square root step. Useful if you're comparing
51759 * relative distances.
51760 * @param {Point} p the other point
51761 * @return {Number} distance
51763 distSqr: function distSqr(p) {
51764 var dx = p.x - this.x,
51766 return dx * dx + dy * dy;
51770 * Get the angle from the 0, 0 coordinate to this point, in radians
51772 * @return {Number} angle
51774 angle: function angle() {
51775 return Math.atan2(this.y, this.x);
51779 * Get the angle from this point to another point, in radians
51780 * @param {Point} b the other point
51781 * @return {Number} angle
51783 angleTo: function angleTo(b) {
51784 return Math.atan2(this.y - b.y, this.x - b.x);
51788 * Get the angle between this point and another point, in radians
51789 * @param {Point} b the other point
51790 * @return {Number} angle
51792 angleWith: function angleWith(b) {
51793 return this.angleWithSep(b.x, b.y);
51797 * Find the angle of the two vectors, solving the formula for
51798 * the cross product a x b = |a||b|sin(θ) for θ.
51799 * @param {Number} x the x-coordinate
51800 * @param {Number} y the y-coordinate
51801 * @return {Number} the angle in radians
51803 angleWithSep: function angleWithSep(x, y) {
51804 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
51806 _matMult: function _matMult(m) {
51807 var x = m[0] * this.x + m[1] * this.y,
51808 y = m[2] * this.x + m[3] * this.y;
51813 _add: function _add(p) {
51818 _sub: function _sub(p) {
51823 _mult: function _mult(k) {
51828 _div: function _div(k) {
51833 _multByPoint: function _multByPoint(p) {
51838 _divByPoint: function _divByPoint(p) {
51843 _unit: function _unit() {
51844 this._div(this.mag());
51848 _perp: function _perp() {
51854 _rotate: function _rotate(angle) {
51855 var cos = Math.cos(angle),
51856 sin = Math.sin(angle),
51857 x = cos * this.x - sin * this.y,
51858 y = sin * this.x + cos * this.y;
51863 _rotateAround: function _rotateAround(angle, p) {
51864 var cos = Math.cos(angle),
51865 sin = Math.sin(angle),
51866 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51867 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51872 _round: function _round() {
51873 this.x = Math.round(this.x);
51874 this.y = Math.round(this.y);
51879 * Construct a point from an array if necessary, otherwise if the input
51880 * is already a Point, or an unknown type, return it unchanged
51881 * @param {Array<Number>|Point|*} a any kind of input value
51882 * @return {Point} constructed point, or passed-through value.
51885 * var point = Point.convert([0, 1]);
51886 * // is equivalent to
51887 * var point = new Point(0, 1);
51890 Point.convert = function (a) {
51891 if (a instanceof Point) {
51895 if (Array.isArray(a)) {
51896 return new Point(a[0], a[1]);
51902 var vectortilefeature = VectorTileFeature;
51904 function VectorTileFeature(pbf, end, extent, keys, values) {
51906 this.properties = {};
51907 this.extent = extent;
51908 this.type = 0; // Private
51911 this._geometry = -1;
51913 this._values = values;
51914 pbf.readFields(readFeature, this, end);
51917 function readFeature(tag, feature, pbf) {
51918 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;
51921 function readTag(pbf, feature) {
51922 var end = pbf.readVarint() + pbf.pos;
51924 while (pbf.pos < end) {
51925 var key = feature._keys[pbf.readVarint()],
51926 value = feature._values[pbf.readVarint()];
51928 feature.properties[key] = value;
51932 VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51934 VectorTileFeature.prototype.loadGeometry = function () {
51935 var pbf = this._pbf;
51936 pbf.pos = this._geometry;
51937 var end = pbf.readVarint() + pbf.pos,
51945 while (pbf.pos < end) {
51947 var cmdLen = pbf.readVarint();
51948 cmd = cmdLen & 0x7;
51949 length = cmdLen >> 3;
51954 if (cmd === 1 || cmd === 2) {
51955 x += pbf.readSVarint();
51956 y += pbf.readSVarint();
51960 if (line) lines.push(line);
51964 line.push(new pointGeometry(x, y));
51965 } else if (cmd === 7) {
51966 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51968 line.push(line[0].clone()); // closePolygon
51971 throw new Error('unknown command ' + cmd);
51975 if (line) lines.push(line);
51979 VectorTileFeature.prototype.bbox = function () {
51980 var pbf = this._pbf;
51981 pbf.pos = this._geometry;
51982 var end = pbf.readVarint() + pbf.pos,
51992 while (pbf.pos < end) {
51994 var cmdLen = pbf.readVarint();
51995 cmd = cmdLen & 0x7;
51996 length = cmdLen >> 3;
52001 if (cmd === 1 || cmd === 2) {
52002 x += pbf.readSVarint();
52003 y += pbf.readSVarint();
52004 if (x < x1) x1 = x;
52005 if (x > x2) x2 = x;
52006 if (y < y1) y1 = y;
52007 if (y > y2) y2 = y;
52008 } else if (cmd !== 7) {
52009 throw new Error('unknown command ' + cmd);
52013 return [x1, y1, x2, y2];
52016 VectorTileFeature.prototype.toGeoJSON = function (x, y, z) {
52017 var size = this.extent * Math.pow(2, z),
52018 x0 = this.extent * x,
52019 y0 = this.extent * y,
52020 coords = this.loadGeometry(),
52021 type = VectorTileFeature.types[this.type],
52025 function project(line) {
52026 for (var j = 0; j < line.length; j++) {
52028 y2 = 180 - (p.y + y0) * 360 / size;
52029 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
52033 switch (this.type) {
52037 for (i = 0; i < coords.length; i++) {
52038 points[i] = coords[i][0];
52046 for (i = 0; i < coords.length; i++) {
52047 project(coords[i]);
52053 coords = classifyRings(coords);
52055 for (i = 0; i < coords.length; i++) {
52056 for (j = 0; j < coords[i].length; j++) {
52057 project(coords[i][j]);
52064 if (coords.length === 1) {
52065 coords = coords[0];
52067 type = 'Multi' + type;
52074 coordinates: coords
52076 properties: this.properties
52079 if ('id' in this) {
52080 result.id = this.id;
52084 }; // classifies an array of rings into polygons with outer rings and holes
52087 function classifyRings(rings) {
52088 var len = rings.length;
52089 if (len <= 1) return [rings];
52094 for (var i = 0; i < len; i++) {
52095 var area = signedArea$1(rings[i]);
52096 if (area === 0) continue;
52097 if (ccw === undefined) ccw = area < 0;
52099 if (ccw === area < 0) {
52100 if (polygon) polygons.push(polygon);
52101 polygon = [rings[i]];
52103 polygon.push(rings[i]);
52107 if (polygon) polygons.push(polygon);
52111 function signedArea$1(ring) {
52114 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
52117 sum += (p2.x - p1.x) * (p1.y + p2.y);
52123 var vectortilelayer = VectorTileLayer;
52125 function VectorTileLayer(pbf, end) {
52129 this.extent = 4096;
52130 this.length = 0; // Private
52135 this._features = [];
52136 pbf.readFields(readLayer, this, end);
52137 this.length = this._features.length;
52140 function readLayer(tag, layer, pbf) {
52141 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));
52144 function readValueMessage(pbf) {
52146 end = pbf.readVarint() + pbf.pos;
52148 while (pbf.pos < end) {
52149 var tag = pbf.readVarint() >> 3;
52150 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;
52154 } // return feature `i` from this layer as a `VectorTileFeature`
52157 VectorTileLayer.prototype.feature = function (i) {
52158 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
52159 this._pbf.pos = this._features[i];
52161 var end = this._pbf.readVarint() + this._pbf.pos;
52163 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
52166 var vectortile = VectorTile;
52168 function VectorTile(pbf, end) {
52169 this.layers = pbf.readFields(readTile, {}, end);
52172 function readTile(tag, layers, pbf) {
52174 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
52175 if (layer.length) layers[layer.name] = layer;
52179 var VectorTile$1 = vectortile;
52180 var VectorTileFeature$1 = vectortilefeature;
52181 var VectorTileLayer$1 = vectortilelayer;
52183 VectorTile: VectorTile$1,
52184 VectorTileFeature: VectorTileFeature$1,
52185 VectorTileLayer: VectorTileLayer$1
52188 var tiler$7 = utilTiler().tileSize(512).margin(1);
52189 var dispatch$8 = dispatch('loadedData');
52193 function abortRequest$7(controller) {
52194 controller.abort();
52197 function vtToGeoJSON(data, tile, mergeCache) {
52198 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
52199 var layers = Object.keys(vectorTile$1.layers);
52201 if (!Array.isArray(layers)) {
52206 layers.forEach(function (layerID) {
52207 var layer = vectorTile$1.layers[layerID];
52210 for (var i = 0; i < layer.length; i++) {
52211 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
52212 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
52214 if (geometry.type === 'Polygon') {
52215 geometry.type = 'MultiPolygon';
52216 geometry.coordinates = [geometry.coordinates];
52219 var isClipped = false; // Clip to tile bounds
52221 if (geometry.type === 'MultiPolygon') {
52222 var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
52224 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
52225 // feature = featureClip;
52229 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
52231 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
52232 } // Generate some unique IDs and add some metadata
52235 var featurehash = utilHashcode(fastJsonStableStringify(feature));
52236 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
52237 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
52238 feature.__featurehash__ = featurehash;
52239 feature.__propertyhash__ = propertyhash;
52240 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
52242 if (isClipped && geometry.type === 'MultiPolygon') {
52243 var merged = mergeCache[propertyhash];
52245 if (merged && merged.length) {
52246 var other = merged[0];
52247 var coords = union(feature.geometry.coordinates, other.geometry.coordinates);
52249 if (!coords || !coords.length) {
52250 continue; // something failed in martinez union
52253 merged.push(feature);
52255 for (var j = 0; j < merged.length; j++) {
52256 // all these features get...
52257 merged[j].geometry.coordinates = coords; // same coords
52259 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
52262 mergeCache[propertyhash] = [feature];
52271 function loadTile(source, tile) {
52272 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
52273 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
52274 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
52275 var subdomains = r.split(',');
52276 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
52278 var controller = new AbortController();
52279 source.inflight[tile.id] = controller;
52281 signal: controller.signal
52282 }).then(function (response) {
52283 if (!response.ok) {
52284 throw new Error(response.status + ' ' + response.statusText);
52287 source.loaded[tile.id] = [];
52288 delete source.inflight[tile.id];
52289 return response.arrayBuffer();
52290 }).then(function (data) {
52292 throw new Error('No Data');
52295 var z = tile.xyz[2];
52297 if (!source.canMerge[z]) {
52298 source.canMerge[z] = {}; // initialize mergeCache
52301 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
52302 dispatch$8.call('loadedData');
52303 })["catch"](function () {
52304 source.loaded[tile.id] = [];
52305 delete source.inflight[tile.id];
52309 var serviceVectorTile = {
52310 init: function init() {
52315 this.event = utilRebind(this, dispatch$8, 'on');
52317 reset: function reset() {
52318 for (var sourceID in _vtCache) {
52319 var source = _vtCache[sourceID];
52321 if (source && source.inflight) {
52322 Object.values(source.inflight).forEach(abortRequest$7);
52328 addSource: function addSource(sourceID, template) {
52329 _vtCache[sourceID] = {
52330 template: template,
52335 return _vtCache[sourceID];
52337 data: function data(sourceID, projection) {
52338 var source = _vtCache[sourceID];
52339 if (!source) return [];
52340 var tiles = tiler$7.getTiles(projection);
52344 for (var i = 0; i < tiles.length; i++) {
52345 var features = source.loaded[tiles[i].id];
52346 if (!features || !features.length) continue;
52348 for (var j = 0; j < features.length; j++) {
52349 var feature = features[j];
52350 var hash = feature.__featurehash__;
52351 if (seen[hash]) continue;
52352 seen[hash] = true; // return a shallow copy, because the hash may change
52353 // later if this feature gets merged with another
52355 results.push(Object.assign({}, feature)); // shallow copy
52361 loadTiles: function loadTiles(sourceID, template, projection) {
52362 var source = _vtCache[sourceID];
52365 source = this.addSource(sourceID, template);
52368 var tiles = tiler$7.getTiles(projection); // abort inflight requests that are no longer needed
52370 Object.keys(source.inflight).forEach(function (k) {
52371 var wanted = tiles.find(function (tile) {
52372 return k === tile.id;
52376 abortRequest$7(source.inflight[k]);
52377 delete source.inflight[k];
52380 tiles.forEach(function (tile) {
52381 loadTile(source, tile);
52384 cache: function cache() {
52389 var apibase$3 = 'https://www.wikidata.org/w/api.php?';
52390 var _wikidataCache = {};
52391 var serviceWikidata = {
52392 init: function init() {},
52393 reset: function reset() {
52394 _wikidataCache = {};
52396 // Search for Wikidata items matching the query
52397 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
52399 if (callback) callback('No query', {});
52403 var lang = this.languagesToQuery()[0];
52404 var url = apibase$3 + utilQsString({
52405 action: 'wbsearchentities',
52410 // the language to search
52412 // the language for the label and description in the result
52417 d3_json(url).then(function (result) {
52418 if (result && result.error) {
52419 throw new Error(result.error);
52422 if (callback) callback(null, result.search || {});
52423 })["catch"](function (err) {
52424 if (callback) callback(err.message, {});
52427 // Given a Wikipedia language and article title,
52428 // return an array of corresponding Wikidata entities.
52429 itemsByTitle: function itemsByTitle(lang, title, callback) {
52431 if (callback) callback('No title', {});
52435 lang = lang || 'en';
52436 var url = apibase$3 + utilQsString({
52437 action: 'wbgetentities',
52440 sites: lang.replace(/-/g, '_') + 'wiki',
52443 // shrink response by filtering to one language
52446 d3_json(url).then(function (result) {
52447 if (result && result.error) {
52448 throw new Error(result.error);
52451 if (callback) callback(null, result.entities || {});
52452 })["catch"](function (err) {
52453 if (callback) callback(err.message, {});
52456 languagesToQuery: function languagesToQuery() {
52457 return _mainLocalizer.localeCodes().map(function (code) {
52458 return code.toLowerCase();
52459 }).filter(function (code) {
52460 // HACK: en-us isn't a wikidata language. We should really be filtering by
52461 // the languages known to be supported by wikidata.
52462 return code !== 'en-us';
52465 entityByQID: function entityByQID(qid, callback) {
52467 callback('No qid', {});
52471 if (_wikidataCache[qid]) {
52472 if (callback) callback(null, _wikidataCache[qid]);
52476 var langs = this.languagesToQuery();
52477 var url = apibase$3 + utilQsString({
52478 action: 'wbgetentities',
52482 props: 'labels|descriptions|claims|sitelinks',
52483 sitefilter: langs.map(function (d) {
52486 languages: langs.join('|'),
52487 languagefallback: 1,
52490 d3_json(url).then(function (result) {
52491 if (result && result.error) {
52492 throw new Error(result.error);
52495 if (callback) callback(null, result.entities[qid] || {});
52496 })["catch"](function (err) {
52497 if (callback) callback(err.message, {});
52500 // Pass `params` object of the form:
52502 // qid: 'string' // brand wikidata (e.g. 'Q37158')
52505 // Get an result object used to display tag documentation
52507 // title: 'string',
52508 // description: 'string',
52509 // editURL: 'string',
52510 // imageURL: 'string',
52511 // wiki: { title: 'string', text: 'string', url: 'string' }
52514 getDocs: function getDocs(params, callback) {
52515 var langs = this.languagesToQuery();
52516 this.entityByQID(params.qid, function (err, entity) {
52517 if (err || !entity) {
52518 callback(err || 'No entity');
52526 var code = langs[i];
52528 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
52529 description = entity.descriptions[code];
52534 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
52538 description: description ? description.value : '',
52539 descriptionLocaleCode: description ? description.language : '',
52540 editURL: 'https://www.wikidata.org/wiki/' + entity.id
52543 if (entity.claims) {
52544 var imageroot = 'https://commons.wikimedia.org/w/index.php';
52545 var props = ['P154', 'P18']; // logo image, image
52549 for (i = 0; i < props.length; i++) {
52550 prop = entity.claims[props[i]];
52552 if (prop && Object.keys(prop).length > 0) {
52553 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
52556 result.imageURL = imageroot + '?' + utilQsString({
52557 title: 'Special:Redirect/file/' + image,
52566 if (entity.sitelinks) {
52567 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
52569 for (i = 0; i < langs.length; i++) {
52570 // check each, in order of preference
52571 var w = langs[i] + 'wiki';
52573 if (entity.sitelinks[w]) {
52574 var title = entity.sitelinks[w].title;
52575 var tKey = 'inspector.wiki_reference';
52577 if (!englishLocale && langs[i] === 'en') {
52578 // user's locale isn't English but
52579 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
52585 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
52592 callback(null, result);
52597 var endpoint = 'https://en.wikipedia.org/w/api.php?';
52598 var serviceWikipedia = {
52599 init: function init() {},
52600 reset: function reset() {},
52601 search: function search(lang, query, callback) {
52603 if (callback) callback('No Query', []);
52607 lang = lang || 'en';
52608 var url = endpoint.replace('en', lang) + utilQsString({
52612 srinfo: 'suggestion',
52617 d3_json(url).then(function (result) {
52618 if (result && result.error) {
52619 throw new Error(result.error);
52620 } else if (!result || !result.query || !result.query.search) {
52621 throw new Error('No Results');
52625 var titles = result.query.search.map(function (d) {
52628 callback(null, titles);
52630 })["catch"](function (err) {
52631 if (callback) callback(err, []);
52634 suggestions: function suggestions(lang, query, callback) {
52636 if (callback) callback('', []);
52640 lang = lang || 'en';
52641 var url = endpoint.replace('en', lang) + utilQsString({
52642 action: 'opensearch',
52649 d3_json(url).then(function (result) {
52650 if (result && result.error) {
52651 throw new Error(result.error);
52652 } else if (!result || result.length < 2) {
52653 throw new Error('No Results');
52656 if (callback) callback(null, result[1] || []);
52657 })["catch"](function (err) {
52658 if (callback) callback(err.message, []);
52661 translations: function translations(lang, title, callback) {
52663 if (callback) callback('No Title');
52667 var url = endpoint.replace('en', lang) + utilQsString({
52675 d3_json(url).then(function (result) {
52676 if (result && result.error) {
52677 throw new Error(result.error);
52678 } else if (!result || !result.query || !result.query.pages) {
52679 throw new Error('No Results');
52683 var list = result.query.pages[Object.keys(result.query.pages)[0]];
52684 var translations = {};
52686 if (list && list.langlinks) {
52687 list.langlinks.forEach(function (d) {
52688 translations[d.lang] = d['*'];
52692 callback(null, translations);
52694 })["catch"](function (err) {
52695 if (callback) callback(err.message);
52701 geocoder: serviceNominatim,
52702 keepRight: serviceKeepRight,
52703 improveOSM: serviceImproveOSM,
52704 osmose: serviceOsmose,
52705 mapillary: serviceMapillary,
52706 openstreetcam: serviceOpenstreetcam,
52708 osmWikibase: serviceOsmWikibase,
52709 maprules: serviceMapRules,
52710 streetside: serviceStreetside,
52711 taginfo: serviceTaginfo,
52712 vectorTile: serviceVectorTile,
52713 wikidata: serviceWikidata,
52714 wikipedia: serviceWikipedia
52717 function svgIcon(name, svgklass, useklass) {
52718 return function drawIcon(selection) {
52719 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);
52723 function uiNoteComments() {
52726 function noteComments(selection) {
52727 if (_note.isNew()) return; // don't draw .comments-container
52729 var comments = selection.selectAll('.comments-container').data([0]);
52730 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
52731 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
52732 commentEnter.append('div').attr('class', function (d) {
52733 return 'comment-avatar user-' + d.uid;
52734 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
52735 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
52736 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
52737 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
52738 var selection = select(this);
52739 var osm = services.osm;
52741 if (osm && d.user) {
52742 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
52745 selection.html(function (d) {
52746 return d.user || _t.html('note.anonymous');
52749 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
52750 return _t('note.status.' + d.action, {
52751 when: localeDateString(d.date)
52754 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
52756 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
52757 comments.call(replaceAvatars);
52760 function replaceAvatars(selection) {
52761 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
52762 var osm = services.osm;
52763 if (showThirdPartyIcons !== 'true' || !osm) return;
52764 var uids = {}; // gather uids in the comment thread
52766 _note.comments.forEach(function (d) {
52767 if (d.uid) uids[d.uid] = true;
52770 Object.keys(uids).forEach(function (uid) {
52771 osm.loadUser(uid, function (err, user) {
52772 if (!user || !user.image_url) return;
52773 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);
52778 function localeDateString(s) {
52779 if (!s) return null;
52785 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
52787 var d = new Date(s);
52788 if (isNaN(d.getTime())) return null;
52789 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
52792 noteComments.note = function (val) {
52793 if (!arguments.length) return _note;
52795 return noteComments;
52798 return noteComments;
52801 function uiNoteHeader() {
52804 function noteHeader(selection) {
52805 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
52806 return d.status + d.id;
52808 header.exit().remove();
52809 var headerEnter = header.enter().append('div').attr('class', 'note-header');
52810 var iconEnter = headerEnter.append('div').attr('class', function (d) {
52811 return 'note-header-icon ' + d.status;
52812 }).classed('new', function (d) {
52815 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
52816 iconEnter.each(function (d) {
52817 var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
52818 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
52820 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
52821 if (_note.isNew()) {
52822 return _t('note.new');
52825 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
52829 noteHeader.note = function (val) {
52830 if (!arguments.length) return _note;
52838 function uiNoteReport() {
52841 function noteReport(selection) {
52844 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
52845 url = services.osm.noteReportURL(_note);
52848 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
52850 link.exit().remove(); // enter
52852 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
52854 }).call(svgIcon('#iD-icon-out-link', 'inline'));
52855 linkEnter.append('span').html(_t.html('note.report'));
52858 noteReport.note = function (val) {
52859 if (!arguments.length) return _note;
52867 function uiViewOnOSM(context) {
52868 var _what; // an osmEntity or osmNote
52871 function viewOnOSM(selection) {
52874 if (_what instanceof osmEntity) {
52875 url = context.connection().entityURL(_what);
52876 } else if (_what instanceof osmNote) {
52877 url = context.connection().noteURL(_what);
52880 var data = !_what || _what.isNew() ? [] : [_what];
52881 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
52885 link.exit().remove(); // enter
52887 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
52888 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
52891 viewOnOSM.what = function (_) {
52892 if (!arguments.length) return _what;
52900 function uiNoteEditor(context) {
52901 var dispatch$1 = dispatch('change');
52902 var noteComments = uiNoteComments();
52903 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
52907 var _newNote; // var _fieldsArr;
52910 function noteEditor(selection) {
52911 var header = selection.selectAll('.header').data([0]);
52912 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
52913 headerEnter.append('button').attr('class', 'close').on('click', function () {
52914 context.enter(modeBrowse(context));
52915 }).call(svgIcon('#iD-icon-close'));
52916 headerEnter.append('h3').html(_t.html('note.title'));
52917 var body = selection.selectAll('.body').data([0]);
52918 body = body.enter().append('div').attr('class', 'body').merge(body);
52919 var editor = body.selectAll('.note-editor').data([0]);
52920 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
52921 var footer = selection.selectAll('.footer').data([0]);
52922 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
52924 var osm = services.osm;
52927 osm.on('change.note-save', function () {
52928 selection.call(noteEditor);
52933 function noteSaveSection(selection) {
52934 var isSelected = _note && _note.id === context.selectedNoteID();
52936 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
52937 return d.status + d.id;
52940 noteSave.exit().remove(); // enter
52942 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
52943 // if (_note.isNew()) {
52944 // var presets = presetManager;
52945 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
52947 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
52949 // _fieldsArr.forEach(function(field) {
52951 // .on('change', changeCategory);
52955 // .attr('class', 'note-category')
52956 // .call(formFields.fieldsArr(_fieldsArr));
52958 // function changeCategory() {
52959 // // NOTE: perhaps there is a better way to get value
52960 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
52961 // // store the unsaved category with the note itself
52962 // _note = _note.update({ newCategory: val });
52963 // var osm = services.osm;
52965 // osm.replaceNote(_note); // update note cache
52968 // .call(noteSaveButtons);
52971 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
52972 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
52974 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
52975 return d.newComment;
52976 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
52978 if (!commentTextarea.empty() && _newNote) {
52979 // autofocus the comment field for new notes
52980 commentTextarea.node().focus();
52984 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
52986 function keydown(d3_event) {
52987 if (!(d3_event.keyCode === 13 && // ↩ Return
52988 d3_event.metaKey)) return;
52989 var osm = services.osm;
52991 var hasAuth = osm.authenticated();
52992 if (!hasAuth) return;
52993 if (!_note.newComment) return;
52994 d3_event.preventDefault();
52995 select(this).on('keydown.note-input', null); // focus on button and submit
52997 window.setTimeout(function () {
52998 if (_note.isNew()) {
52999 noteSave.selectAll('.save-button').node().focus();
53002 noteSave.selectAll('.comment-button').node().focus();
53008 function changeInput() {
53009 var input = select(this);
53010 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
53012 _note = _note.update({
53015 var osm = services.osm;
53018 osm.replaceNote(_note); // update note cache
53021 noteSave.call(noteSaveButtons);
53025 function userDetails(selection) {
53026 var detailSection = selection.selectAll('.detail-section').data([0]);
53027 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
53028 var osm = services.osm;
53029 if (!osm) return; // Add warning if user is not logged in
53031 var hasAuth = osm.authenticated();
53032 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
53033 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
53034 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
53035 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
53036 authEnter.append('span').html(_t.html('note.login'));
53037 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) {
53038 d3_event.preventDefault();
53039 osm.authenticate();
53041 authEnter.transition().duration(200).style('opacity', 1);
53042 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
53043 prose.exit().remove();
53044 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
53045 osm.userDetails(function (err, user) {
53047 var userLink = select(document.createElement('div'));
53049 if (user.image_url) {
53050 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
53053 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
53054 prose.html(_t.html('note.upload_explanation_with_user', {
53055 user: userLink.html()
53060 function noteSaveButtons(selection) {
53061 var osm = services.osm;
53062 var hasAuth = osm && osm.authenticated();
53064 var isSelected = _note && _note.id === context.selectedNoteID();
53066 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
53067 return d.status + d.id;
53070 buttonSection.exit().remove(); // enter
53072 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
53074 if (_note.isNew()) {
53075 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
53076 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
53078 buttonEnter.append('button').attr('class', 'button status-button action');
53079 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
53083 buttonSection = buttonSection.merge(buttonEnter);
53084 buttonSection.select('.cancel-button') // select and propagate data
53085 .on('click.cancel', clickCancel);
53086 buttonSection.select('.save-button') // select and propagate data
53087 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
53088 buttonSection.select('.status-button') // select and propagate data
53089 .attr('disabled', hasAuth ? null : true).html(function (d) {
53090 var action = d.status === 'open' ? 'close' : 'open';
53091 var andComment = d.newComment ? '_comment' : '';
53092 return _t('note.' + action + andComment);
53093 }).on('click.status', clickStatus);
53094 buttonSection.select('.comment-button') // select and propagate data
53095 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
53097 function isSaveDisabled(d) {
53098 return hasAuth && d.status === 'open' && d.newComment ? null : true;
53102 function clickCancel(d3_event, d) {
53103 this.blur(); // avoid keeping focus on the button - #4641
53105 var osm = services.osm;
53111 context.enter(modeBrowse(context));
53112 dispatch$1.call('change');
53115 function clickSave(d3_event, d) {
53116 this.blur(); // avoid keeping focus on the button - #4641
53118 var osm = services.osm;
53121 osm.postNoteCreate(d, function (err, note) {
53122 dispatch$1.call('change', note);
53127 function clickStatus(d3_event, d) {
53128 this.blur(); // avoid keeping focus on the button - #4641
53130 var osm = services.osm;
53133 var setStatus = d.status === 'open' ? 'closed' : 'open';
53134 osm.postNoteUpdate(d, setStatus, function (err, note) {
53135 dispatch$1.call('change', note);
53140 function clickComment(d3_event, d) {
53141 this.blur(); // avoid keeping focus on the button - #4641
53143 var osm = services.osm;
53146 osm.postNoteUpdate(d, d.status, function (err, note) {
53147 dispatch$1.call('change', note);
53152 noteEditor.note = function (val) {
53153 if (!arguments.length) return _note;
53158 noteEditor.newNote = function (val) {
53159 if (!arguments.length) return _newNote;
53164 return utilRebind(noteEditor, dispatch$1, 'on');
53167 function modeSelectNote(context, selectedNoteID) {
53173 var _keybinding = utilKeybinding('select-note');
53175 var _noteEditor = uiNoteEditor(context).on('change', function () {
53176 context.map().pan([0, 0]); // trigger a redraw
53178 var note = checkSelectedID();
53180 context.ui().sidebar.show(_noteEditor.note(note));
53183 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
53184 var _newFeature = false;
53186 function checkSelectedID() {
53187 if (!services.osm) return;
53188 var note = services.osm.getNote(selectedNoteID);
53191 context.enter(modeBrowse(context));
53195 } // class the note as selected, or return to browse mode if the note is gone
53198 function selectNote(d3_event, drawn) {
53199 if (!checkSelectedID()) return;
53200 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
53202 if (selection.empty()) {
53203 // Return to browse mode if selected DOM elements have
53204 // disappeared because the user moved them out of view..
53205 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
53207 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
53208 context.enter(modeBrowse(context));
53211 selection.classed('selected', true);
53212 context.selectedNoteID(selectedNoteID);
53217 if (context.container().select('.combobox').size()) return;
53218 context.enter(modeBrowse(context));
53221 mode.zoomToSelected = function () {
53222 if (!services.osm) return;
53223 var note = services.osm.getNote(selectedNoteID);
53226 context.map().centerZoomEase(note.loc, 20);
53230 mode.newFeature = function (val) {
53231 if (!arguments.length) return _newFeature;
53236 mode.enter = function () {
53237 var note = checkSelectedID();
53240 _behaviors.forEach(context.install);
53242 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
53244 select(document).call(_keybinding);
53246 var sidebar = context.ui().sidebar;
53247 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
53249 sidebar.expand(sidebar.intersects(note.extent()));
53250 context.map().on('drawn.select', selectNote);
53253 mode.exit = function () {
53254 _behaviors.forEach(context.uninstall);
53256 select(document).call(_keybinding.unbind);
53257 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
53258 context.map().on('drawn.select', null);
53259 context.ui().sidebar.hide();
53260 context.selectedNoteID(null);
53266 function modeDragNote(context) {
53271 var edit = behaviorEdit(context);
53273 var _nudgeInterval;
53277 var _note; // most current note.. dragged note may have stale datum.
53280 function startNudge(d3_event, nudge) {
53281 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
53282 _nudgeInterval = window.setInterval(function () {
53283 context.map().pan(nudge);
53284 doMove(d3_event, nudge);
53288 function stopNudge() {
53289 if (_nudgeInterval) {
53290 window.clearInterval(_nudgeInterval);
53291 _nudgeInterval = null;
53295 function origin(note) {
53296 return context.projection(note.loc);
53299 function start(d3_event, note) {
53301 var osm = services.osm;
53304 // Get latest note from cache.. The marker may have a stale datum bound to it
53305 // and dragging it around can sometimes delete the users note comment.
53306 _note = osm.getNote(_note.id);
53309 context.surface().selectAll('.note-' + _note.id).classed('active', true);
53310 context.perform(actionNoop());
53311 context.enter(mode);
53312 context.selectedNoteID(_note.id);
53315 function move(d3_event, entity, point) {
53316 d3_event.stopPropagation();
53317 _lastLoc = context.projection.invert(point);
53319 var nudge = geoViewportEdge(point, context.map().dimensions());
53322 startNudge(d3_event, nudge);
53328 function doMove(d3_event, nudge) {
53329 nudge = nudge || [0, 0];
53330 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
53331 var currMouse = geoVecSubtract(currPoint, nudge);
53332 var loc = context.projection.invert(currMouse);
53333 _note = _note.move(loc);
53334 var osm = services.osm;
53337 osm.replaceNote(_note); // update note cache
53340 context.replace(actionNoop()); // trigger redraw
53344 context.replace(actionNoop()); // trigger redraw
53346 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
53349 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);
53351 mode.enter = function () {
53352 context.install(edit);
53355 mode.exit = function () {
53356 context.ui().sidebar.hover.cancel();
53357 context.uninstall(edit);
53358 context.surface().selectAll('.active').classed('active', false);
53362 mode.behavior = drag;
53366 function uiDataHeader() {
53369 function dataHeader(selection) {
53370 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
53371 return d.__featurehash__;
53373 header.exit().remove();
53374 var headerEnter = header.enter().append('div').attr('class', 'data-header');
53375 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
53376 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
53377 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
53380 dataHeader.datum = function (val) {
53381 if (!arguments.length) return _datum;
53389 // It is keyed on the `value` of the entry. Data should be an array of objects like:
53391 // value: 'string value', // required
53392 // display: 'label html' // optional
53393 // title: 'hover text' // optional
53394 // terms: ['search terms'] // optional
53397 var _comboHideTimerID;
53399 function uiCombobox(context, klass) {
53400 var dispatch$1 = dispatch('accept', 'cancel');
53401 var container = context.container();
53402 var _suggestions = [];
53405 var _selected = null;
53406 var _canAutocomplete = true;
53407 var _caseSensitive = false;
53408 var _cancelFetch = false;
53412 var _mouseEnterHandler, _mouseLeaveHandler;
53414 var _fetcher = function _fetcher(val, cb) {
53415 cb(_data.filter(function (d) {
53416 var terms = d.terms || [];
53417 terms.push(d.value);
53418 return terms.some(function (term) {
53419 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
53424 var combobox = function combobox(input, attachTo) {
53425 if (!input || input.empty()) return;
53426 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 () {
53427 var parent = this.parentNode;
53428 var sibling = this.nextSibling;
53429 select(parent).selectAll('.combobox-caret').filter(function (d) {
53430 return d === input.node();
53431 }).data([input.node()]).enter().insert('div', function () {
53433 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
53434 d3_event.preventDefault(); // don't steal focus from input
53436 input.node().focus(); // focus the input as if it was clicked
53438 mousedown(d3_event);
53439 }).on('mouseup.combo-caret', function (d3_event) {
53440 d3_event.preventDefault(); // don't steal focus from input
53446 function mousedown(d3_event) {
53447 if (d3_event.button !== 0) return; // left click only
53449 _tDown = +new Date(); // clear selection
53451 var start = input.property('selectionStart');
53452 var end = input.property('selectionEnd');
53454 if (start !== end) {
53455 var val = utilGetSetValue(input);
53456 input.node().setSelectionRange(val.length, val.length);
53460 input.on('mouseup.combo-input', mouseup);
53463 function mouseup(d3_event) {
53464 input.on('mouseup.combo-input', null);
53465 if (d3_event.button !== 0) return; // left click only
53467 if (input.node() !== document.activeElement) return; // exit if this input is not focused
53469 var start = input.property('selectionStart');
53470 var end = input.property('selectionEnd');
53471 if (start !== end) return; // exit if user is selecting
53472 // not showing or showing for a different field - try to show it.
53474 var combo = container.selectAll('.combobox');
53476 if (combo.empty() || combo.datum() !== input.node()) {
53477 var tOrig = _tDown;
53478 window.setTimeout(function () {
53479 if (tOrig !== _tDown) return; // exit if user double clicked
53481 fetchComboData('', function () {
53492 fetchComboData(''); // prefetch values (may warm taginfo cache)
53496 _comboHideTimerID = window.setTimeout(hide, 75);
53500 hide(); // remove any existing
53502 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) {
53503 // prevent moving focus out of the input field
53504 d3_event.preventDefault();
53506 container.on('scroll.combo-scroll', render, true);
53510 if (_comboHideTimerID) {
53511 window.clearTimeout(_comboHideTimerID);
53512 _comboHideTimerID = undefined;
53515 container.selectAll('.combobox').remove();
53516 container.on('scroll.combo-scroll', null);
53519 function keydown(d3_event) {
53520 var shown = !container.selectAll('.combobox').empty();
53521 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
53523 switch (d3_event.keyCode) {
53524 case 8: // ⌫ Backspace
53528 d3_event.stopPropagation();
53531 input.on('input.combo-input', function () {
53532 var start = input.property('selectionStart');
53533 input.node().setSelectionRange(start, start);
53534 input.on('input.combo-input', change);
53545 d3_event.preventDefault();
53546 d3_event.stopPropagation();
53551 if (tagName === 'textarea' && !shown) return;
53552 d3_event.preventDefault();
53554 if (tagName === 'input' && !shown) {
53563 if (tagName === 'textarea' && !shown) return;
53564 d3_event.preventDefault();
53566 if (tagName === 'input' && !shown) {
53575 function keyup(d3_event) {
53576 switch (d3_event.keyCode) {
53587 } // Called whenever the input value is changed (e.g. on typing)
53590 function change() {
53591 fetchComboData(value(), function () {
53593 var val = input.property('value');
53595 if (_suggestions.length) {
53596 if (input.property('selectionEnd') === val.length) {
53597 _selected = tryAutocomplete();
53606 var combo = container.selectAll('.combobox');
53608 if (combo.empty()) {
53617 } // Called when the user presses up/down arrows to navigate the list
53620 function nav(dir) {
53621 if (_suggestions.length) {
53622 // try to determine previously selected index..
53625 for (var i = 0; i < _suggestions.length; i++) {
53626 if (_selected && _suggestions[i].value === _selected) {
53630 } // pick new _selected
53633 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
53634 _selected = _suggestions[index].value;
53635 input.property('value', _selected);
53642 function ensureVisible() {
53643 var combo = container.selectAll('.combobox');
53644 if (combo.empty()) return;
53645 var containerRect = container.node().getBoundingClientRect();
53646 var comboRect = combo.node().getBoundingClientRect();
53648 if (comboRect.bottom > containerRect.bottom) {
53649 var node = attachTo ? attachTo.node() : input.node();
53650 node.scrollIntoView({
53651 behavior: 'instant',
53655 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
53658 var selected = combo.selectAll('.combobox-option.selected').node();
53661 selected.scrollIntoView({
53662 behavior: 'smooth',
53669 var value = input.property('value');
53670 var start = input.property('selectionStart');
53671 var end = input.property('selectionEnd');
53673 if (start && end) {
53674 value = value.substring(0, start);
53680 function fetchComboData(v, cb) {
53681 _cancelFetch = false;
53683 _fetcher.call(input, v, function (results) {
53684 // already chose a value, don't overwrite or autocomplete it
53685 if (_cancelFetch) return;
53686 _suggestions = results;
53687 results.forEach(function (d) {
53688 _fetched[d.value] = d;
53697 function tryAutocomplete() {
53698 if (!_canAutocomplete) return;
53699 var val = _caseSensitive ? value() : value().toLowerCase();
53700 if (!val) return; // Don't autocomplete if user is typing a number - #4935
53702 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
53703 var bestIndex = -1;
53705 for (var i = 0; i < _suggestions.length; i++) {
53706 var suggestion = _suggestions[i].value;
53707 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
53709 if (compare === val) {
53711 break; // otherwise lock in the first result that starts with the search string..
53712 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
53717 if (bestIndex !== -1) {
53718 var bestVal = _suggestions[bestIndex].value;
53719 input.property('value', bestVal);
53720 input.node().setSelectionRange(val.length, bestVal.length);
53725 function render() {
53726 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
53731 var shown = !container.selectAll('.combobox').empty();
53732 if (!shown) return;
53733 var combo = container.selectAll('.combobox');
53734 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
53737 options.exit().remove(); // enter/update
53739 options.enter().append('a').attr('class', 'combobox-option').attr('title', function (d) {
53741 }).html(function (d) {
53742 return d.display || d.value;
53743 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
53744 return d.value === _selected;
53745 }).on('click.combo-option', accept).order();
53746 var node = attachTo ? attachTo.node() : input.node();
53747 var containerRect = container.node().getBoundingClientRect();
53748 var rect = node.getBoundingClientRect();
53749 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
53750 } // Dispatches an 'accept' event
53751 // Then hides the combobox.
53754 function accept(d3_event, d) {
53755 _cancelFetch = true;
53756 var thiz = input.node();
53759 // user clicked on a suggestion
53760 utilGetSetValue(input, d.value); // replace field contents
53762 utilTriggerEvent(input, 'change');
53763 } // clear (and keep) selection
53766 var val = utilGetSetValue(input);
53767 thiz.setSelectionRange(val.length, val.length);
53769 dispatch$1.call('accept', thiz, d, val);
53771 } // Dispatches an 'cancel' event
53772 // Then hides the combobox.
53775 function cancel() {
53776 _cancelFetch = true;
53777 var thiz = input.node(); // clear (and remove) selection, and replace field contents
53779 var val = utilGetSetValue(input);
53780 var start = input.property('selectionStart');
53781 var end = input.property('selectionEnd');
53782 val = val.slice(0, start) + val.slice(end);
53783 utilGetSetValue(input, val);
53784 thiz.setSelectionRange(val.length, val.length);
53785 dispatch$1.call('cancel', thiz);
53790 combobox.canAutocomplete = function (val) {
53791 if (!arguments.length) return _canAutocomplete;
53792 _canAutocomplete = val;
53796 combobox.caseSensitive = function (val) {
53797 if (!arguments.length) return _caseSensitive;
53798 _caseSensitive = val;
53802 combobox.data = function (val) {
53803 if (!arguments.length) return _data;
53808 combobox.fetcher = function (val) {
53809 if (!arguments.length) return _fetcher;
53814 combobox.minItems = function (val) {
53815 if (!arguments.length) return _minItems;
53820 combobox.itemsMouseEnter = function (val) {
53821 if (!arguments.length) return _mouseEnterHandler;
53822 _mouseEnterHandler = val;
53826 combobox.itemsMouseLeave = function (val) {
53827 if (!arguments.length) return _mouseLeaveHandler;
53828 _mouseLeaveHandler = val;
53832 return utilRebind(combobox, dispatch$1, 'on');
53835 uiCombobox.off = function (input, context) {
53836 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);
53837 context.container().on('scroll.combo-scroll', null);
53840 // hide class, which sets display=none, and a d3 transition for opacity.
53841 // this will cause blinking when called repeatedly, so check that the
53842 // value actually changes between calls.
53844 function uiToggle(show, callback) {
53845 return function (selection) {
53846 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
53847 select(this).classed('hide', !show).style('opacity', null);
53848 if (callback) callback.apply(this);
53853 function uiDisclosure(context, key, expandedDefault) {
53854 var dispatch$1 = dispatch('toggled');
53858 var _label = utilFunctor('');
53860 var _updatePreference = true;
53862 var _content = function _content() {};
53864 var disclosure = function disclosure(selection) {
53865 if (_expanded === undefined || _expanded === null) {
53866 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
53867 var preference = corePreferences('disclosure.' + key + '.expanded');
53868 _expanded = preference === null ? !!expandedDefault : preference === 'true';
53871 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
53873 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
53874 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
53876 hideToggle = hideToggleEnter.merge(hideToggle);
53877 hideToggle.on('click', toggle).classed('expanded', _expanded);
53878 hideToggle.selectAll('.hide-toggle-text').html(_label());
53879 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53880 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
53882 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
53885 wrap.call(_content);
53888 function toggle(d3_event) {
53889 d3_event.preventDefault();
53890 _expanded = !_expanded;
53892 if (_updatePreference) {
53893 corePreferences('disclosure.' + key + '.expanded', _expanded);
53896 hideToggle.classed('expanded', _expanded);
53897 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53898 wrap.call(uiToggle(_expanded));
53901 wrap.call(_content);
53904 dispatch$1.call('toggled', this, _expanded);
53908 disclosure.label = function (val) {
53909 if (!arguments.length) return _label;
53910 _label = utilFunctor(val);
53914 disclosure.expanded = function (val) {
53915 if (!arguments.length) return _expanded;
53920 disclosure.updatePreference = function (val) {
53921 if (!arguments.length) return _updatePreference;
53922 _updatePreference = val;
53926 disclosure.content = function (val) {
53927 if (!arguments.length) return _content;
53932 return utilRebind(disclosure, dispatch$1, 'on');
53935 // Can be labeled and collapsible.
53937 function uiSection(id, context) {
53938 var _classes = utilFunctor('');
53940 var _shouldDisplay;
53948 var _expandedByDefault = utilFunctor(true);
53950 var _disclosureContent;
53952 var _disclosureExpanded;
53954 var _containerSelection = select(null);
53960 section.classes = function (val) {
53961 if (!arguments.length) return _classes;
53962 _classes = utilFunctor(val);
53966 section.label = function (val) {
53967 if (!arguments.length) return _label;
53968 _label = utilFunctor(val);
53972 section.expandedByDefault = function (val) {
53973 if (!arguments.length) return _expandedByDefault;
53974 _expandedByDefault = utilFunctor(val);
53978 section.shouldDisplay = function (val) {
53979 if (!arguments.length) return _shouldDisplay;
53980 _shouldDisplay = utilFunctor(val);
53984 section.content = function (val) {
53985 if (!arguments.length) return _content;
53990 section.disclosureContent = function (val) {
53991 if (!arguments.length) return _disclosureContent;
53992 _disclosureContent = val;
53996 section.disclosureExpanded = function (val) {
53997 if (!arguments.length) return _disclosureExpanded;
53998 _disclosureExpanded = val;
54000 }; // may be called multiple times
54003 section.render = function (selection) {
54004 _containerSelection = selection.selectAll('.section-' + id).data([0]);
54006 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
54008 _containerSelection = sectionEnter.merge(_containerSelection);
54010 _containerSelection.call(renderContent);
54013 section.reRender = function () {
54014 _containerSelection.call(renderContent);
54017 section.selection = function () {
54018 return _containerSelection;
54021 section.disclosure = function () {
54022 return _disclosure;
54023 }; // may be called multiple times
54026 function renderContent(selection) {
54027 if (_shouldDisplay) {
54028 var shouldDisplay = _shouldDisplay();
54030 selection.classed('hide', !shouldDisplay);
54032 if (!shouldDisplay) {
54033 selection.html('');
54038 if (_disclosureContent) {
54039 if (!_disclosure) {
54040 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
54041 /*.on('toggled', function(expanded) {
54042 if (expanded) { selection.node().parentNode.scrollTop += 200; }
54044 .content(_disclosureContent);
54047 if (_disclosureExpanded !== undefined) {
54048 _disclosure.expanded(_disclosureExpanded);
54050 _disclosureExpanded = undefined;
54053 selection.call(_disclosure);
54058 selection.call(_content);
54066 // key: 'string', // required
54067 // value: 'string' // optional
54071 // qid: 'string' // brand wikidata (e.g. 'Q37158')
54075 function uiTagReference(what) {
54076 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
54077 var tagReference = {};
54079 var _button = select(null);
54081 var _body = select(null);
54088 if (!wikibase) return;
54090 _button.classed('tag-reference-loading', true);
54092 wikibase.getDocs(what, gotDocs);
54095 function gotDocs(err, docs) {
54098 if (!docs || !docs.title) {
54099 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
54105 if (docs.imageURL) {
54106 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
54108 }).on('error', function () {
54109 select(this).remove();
54116 _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'));
54119 _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));
54120 } // Add link to info about "good changeset comments" - #2923
54123 if (what.key === 'comment') {
54124 _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'));
54131 _button.classed('tag-reference-loading', false);
54133 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
54137 _button.selectAll('svg.icon use').each(function () {
54138 var iconUse = select(this);
54140 if (iconUse.attr('href') === '#iD-icon-info') {
54141 iconUse.attr('href', '#iD-icon-info-filled');
54147 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
54148 _body.classed('expanded', false);
54153 _button.selectAll('svg.icon use').each(function () {
54154 var iconUse = select(this);
54156 if (iconUse.attr('href') === '#iD-icon-info-filled') {
54157 iconUse.attr('href', '#iD-icon-info');
54162 tagReference.button = function (selection, klass, iconName) {
54163 _button = selection.selectAll('.tag-reference-button').data([0]);
54164 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
54166 _button.on('click', function (d3_event) {
54167 d3_event.stopPropagation();
54168 d3_event.preventDefault();
54169 this.blur(); // avoid keeping focus on the button - #4641
54173 } else if (_loaded) {
54181 tagReference.body = function (selection) {
54182 var itemID = what.qid || what.key + '-' + (what.value || '');
54183 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
54187 _body.exit().remove();
54189 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
54191 if (_showing === false) {
54196 tagReference.showing = function (val) {
54197 if (!arguments.length) return _showing;
54199 return tagReference;
54202 return tagReference;
54205 function uiSectionRawTagEditor(id, context) {
54206 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
54207 var count = Object.keys(_tags).filter(function (d) {
54210 return _t('inspector.title_count', {
54211 title: _t.html('inspector.tags'),
54214 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
54215 var taginfo = services.taginfo;
54216 var dispatch$1 = dispatch('change');
54217 var availableViews = [{
54219 icon: '#fas-th-list'
54222 icon: '#fas-i-cursor'
54225 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
54228 var _readOnlyTags = []; // the keys in the order we want them to display
54230 var _orderedKeys = [];
54231 var _showBlank = false;
54232 var _pendingChange = null;
54242 var _didInteract = false;
54244 function interacted() {
54245 _didInteract = true;
54248 function renderDisclosureContent(wrap) {
54249 // remove deleted keys
54250 _orderedKeys = _orderedKeys.filter(function (key) {
54251 return _tags[key] !== undefined;
54252 }); // When switching to a different entity or changing the state (hover/select)
54253 // reorder the keys alphabetically.
54254 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
54255 // Otherwise leave their order alone - #5857, #5927
54257 var all = Object.keys(_tags).sort();
54258 var missingKeys = utilArrayDifference(all, _orderedKeys);
54260 for (var i in missingKeys) {
54261 _orderedKeys.push(missingKeys[i]);
54262 } // assemble row data
54265 var rowData = _orderedKeys.map(function (key, i) {
54271 }); // append blank row last, if necessary
54274 if (!rowData.length || _showBlank) {
54275 _showBlank = false;
54277 index: rowData.length,
54284 var options = wrap.selectAll('.raw-tag-options').data([0]);
54285 options.exit().remove();
54286 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
54287 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
54290 optionEnter.append('button').attr('class', function (d) {
54291 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
54292 }).attr('title', function (d) {
54293 return _t('icons.' + d.id);
54294 }).on('click', function (d3_event, d) {
54296 corePreferences('raw-tag-editor-view', d.id);
54297 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
54298 return datum === d;
54300 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
54301 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
54302 }).each(function (d) {
54303 select(this).call(svgIcon(d.icon));
54304 }); // View as Text
54306 var textData = rowsToText(rowData);
54307 var textarea = wrap.selectAll('.tag-text').data([0]);
54308 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);
54309 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
54311 var list = wrap.selectAll('.tag-list').data([0]);
54312 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
54314 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
54315 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
54316 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
54318 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
54321 var items = list.selectAll('.tag-row').data(rowData, function (d) {
54324 items.exit().each(unbind).remove(); // Enter
54326 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
54327 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
54328 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);
54329 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);
54330 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
54332 items = items.merge(itemsEnter).sort(function (a, b) {
54333 return a.index - b.index;
54335 items.each(function (d) {
54336 var row = select(this);
54337 var key = row.select('input.key'); // propagate bound data
54339 var value = row.select('input.value'); // propagate bound data
54341 if (_entityIDs && taginfo && _state !== 'hover') {
54342 bindTypeahead(key, value);
54345 var referenceOptions = {
54349 if (typeof d.value === 'string') {
54350 referenceOptions.value = d.value;
54353 var reference = uiTagReference(referenceOptions);
54355 if (_state === 'hover') {
54356 reference.showing(false);
54359 row.select('.inner-wrap') // propagate bound data
54360 .call(reference.button);
54361 row.call(reference.body);
54362 row.select('button.remove'); // propagate bound data
54364 items.selectAll('input.key').attr('title', function (d) {
54366 }).call(utilGetSetValue, function (d) {
54368 }).attr('readonly', function (d) {
54369 return isReadOnly(d) || typeof d.value !== 'string' || null;
54371 items.selectAll('input.value').attr('title', function (d) {
54372 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
54373 }).classed('mixed', function (d) {
54374 return Array.isArray(d.value);
54375 }).attr('placeholder', function (d) {
54376 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
54377 }).call(utilGetSetValue, function (d) {
54378 return typeof d.value === 'string' ? d.value : '';
54379 }).attr('readonly', function (d) {
54380 return isReadOnly(d) || null;
54382 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
54385 function isReadOnly(d) {
54386 for (var i = 0; i < _readOnlyTags.length; i++) {
54387 if (d.key.match(_readOnlyTags[i]) !== null) {
54395 function setTextareaHeight() {
54396 if (_tagView !== 'text') return;
54397 var selection = select(this);
54398 var matches = selection.node().value.match(/\n/g);
54399 var lineCount = 2 + Number(matches && matches.length);
54400 var lineHeight = 20;
54401 selection.style('height', lineCount * lineHeight + 'px');
54404 function stringify(s) {
54405 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
54408 function unstringify(s) {
54412 if (s.length < 1 || s.charAt(0) !== '"') {
54416 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
54420 return JSON.parse(leading + s + trailing);
54423 function rowsToText(rows) {
54424 var str = rows.filter(function (row) {
54425 return row.key && row.key.trim() !== '';
54426 }).map(function (row) {
54427 var rawVal = row.value;
54428 if (typeof rawVal !== 'string') rawVal = '*';
54429 var val = rawVal ? stringify(rawVal) : '';
54430 return stringify(row.key) + '=' + val;
54433 if (_state !== 'hover' && str.length) {
54440 function textChanged() {
54441 var newText = this.value.trim();
54443 newText.split('\n').forEach(function (row) {
54444 var m = row.match(/^\s*([^=]+)=(.*)$/);
54447 var k = context.cleanTagKey(unstringify(m[1].trim()));
54448 var v = context.cleanTagValue(unstringify(m[2].trim()));
54452 var tagDiff = utilTagDiff(_tags, newTags);
54453 if (!tagDiff.length) return;
54454 _pendingChange = _pendingChange || {};
54455 tagDiff.forEach(function (change) {
54458 })) return; // skip unchanged multiselection placeholders
54460 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
54462 if (change.type === '-') {
54463 _pendingChange[change.key] = undefined;
54464 } else if (change.type === '+') {
54465 _pendingChange[change.key] = change.newVal || '';
54469 if (Object.keys(_pendingChange).length === 0) {
54470 _pendingChange = null;
54477 function pushMore(d3_event) {
54478 // if pressing Tab on the last value field with content, add a blank row
54479 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
54484 function bindTypeahead(key, value) {
54485 if (isReadOnly(key.datum())) return;
54487 if (Array.isArray(value.datum().value)) {
54488 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
54489 var keyString = utilGetSetValue(key);
54490 if (!_tags[keyString]) return;
54492 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
54504 var geometry = context.graph().geometry(_entityIDs[0]);
54505 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
54508 geometry: geometry,
54510 }, function (err, data) {
54512 var filtered = data.filter(function (d) {
54513 return _tags[d.value] === undefined;
54515 callback(sort(value, filtered));
54519 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
54522 key: utilGetSetValue(key),
54523 geometry: geometry,
54525 }, function (err, data) {
54526 if (!err) callback(sort(value, data));
54530 function sort(value, data) {
54531 var sameletter = [];
54534 for (var i = 0; i < data.length; i++) {
54535 if (data[i].value.substring(0, value.length) === value) {
54536 sameletter.push(data[i]);
54538 other.push(data[i]);
54542 return sameletter.concat(other);
54546 function unbind() {
54547 var row = select(this);
54548 row.selectAll('input.key').call(uiCombobox.off, context);
54549 row.selectAll('input.value').call(uiCombobox.off, context);
54552 function keyChange(d3_event, d) {
54553 if (select(this).attr('readonly')) return;
54554 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
54556 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
54557 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
54566 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
54567 // new key is already in use, switch focus to the existing row
54568 this.value = kOld; // reset the key
54570 section.selection().selectAll('.tag-list input.value').each(function (d) {
54571 if (d.key === kNew) {
54572 // send focus to that other value combo instead
54573 var input = select(this).node();
54581 var row = this.parentNode.parentNode;
54582 var inputVal = select(row).selectAll('input.value');
54583 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
54584 _pendingChange = _pendingChange || {};
54587 _pendingChange[kOld] = undefined;
54590 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
54592 var existingKeyIndex = _orderedKeys.indexOf(kOld);
54594 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
54595 d.key = kNew; // update datum to avoid exit/enter on tag update
54599 utilGetSetValue(inputVal, vNew);
54603 function valueChange(d3_event, d) {
54604 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
54606 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
54608 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
54609 _pendingChange = _pendingChange || {};
54610 _pendingChange[d.key] = context.cleanTagValue(this.value);
54614 function removeTag(d3_event, d) {
54615 if (isReadOnly(d)) return;
54617 if (d.key === '') {
54618 // removing the blank row
54619 _showBlank = false;
54620 section.reRender();
54622 // remove the key from the ordered key index
54623 _orderedKeys = _orderedKeys.filter(function (key) {
54624 return key !== d.key;
54626 _pendingChange = _pendingChange || {};
54627 _pendingChange[d.key] = undefined;
54632 function addTag() {
54633 // Delay render in case this click is blurring an edited combo.
54634 // Without the setTimeout, the `content` render would wipe out the pending tag change.
54635 window.setTimeout(function () {
54637 section.reRender();
54638 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
54642 function scheduleChange() {
54643 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
54644 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
54646 window.setTimeout(function () {
54647 if (!_pendingChange) return;
54648 dispatch$1.call('change', this, entityIDs, _pendingChange);
54649 _pendingChange = null;
54653 section.state = function (val) {
54654 if (!arguments.length) return _state;
54656 if (_state !== val) {
54664 section.presets = function (val) {
54665 if (!arguments.length) return _presets;
54668 if (_presets && _presets.length && _presets[0].isFallback()) {
54669 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
54670 } else if (!_didInteract) {
54671 section.disclosureExpanded(null);
54677 section.tags = function (val) {
54678 if (!arguments.length) return _tags;
54683 section.entityIDs = function (val) {
54684 if (!arguments.length) return _entityIDs;
54686 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
54692 }; // pass an array of regular expressions to test against the tag key
54695 section.readOnlyTags = function (val) {
54696 if (!arguments.length) return _readOnlyTags;
54697 _readOnlyTags = val;
54701 return utilRebind(section, dispatch$1, 'on');
54704 function uiDataEditor(context) {
54705 var dataHeader = uiDataHeader();
54706 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
54710 function dataEditor(selection) {
54711 var header = selection.selectAll('.header').data([0]);
54712 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
54713 headerEnter.append('button').attr('class', 'close').on('click', function () {
54714 context.enter(modeBrowse(context));
54715 }).call(svgIcon('#iD-icon-close'));
54716 headerEnter.append('h3').html(_t.html('map_data.title'));
54717 var body = selection.selectAll('.body').data([0]);
54718 body = body.enter().append('div').attr('class', 'body').merge(body);
54719 var editor = body.selectAll('.data-editor').data([0]); // enter/update
54721 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
54722 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
54724 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);
54727 dataEditor.datum = function (val) {
54728 if (!arguments.length) return _datum;
54736 function modeSelectData(context, selectedDatum) {
54741 var keybinding = utilKeybinding('select-data');
54742 var dataEditor = uiDataEditor(context);
54743 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
54745 function selectData(d3_event, drawn) {
54746 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
54748 if (selection.empty()) {
54749 // Return to browse mode if selected DOM elements have
54750 // disappeared because the user moved them out of view..
54751 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
54753 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
54754 context.enter(modeBrowse(context));
54757 selection.classed('selected', true);
54762 if (context.container().select('.combobox').size()) return;
54763 context.enter(modeBrowse(context));
54766 mode.zoomToSelected = function () {
54767 var extent = geoExtent(d3_geoBounds(selectedDatum));
54768 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
54771 mode.enter = function () {
54772 behaviors.forEach(context.install);
54773 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
54774 select(document).call(keybinding);
54776 var sidebar = context.ui().sidebar;
54777 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
54779 var extent = geoExtent(d3_geoBounds(selectedDatum));
54780 sidebar.expand(sidebar.intersects(extent));
54781 context.map().on('drawn.select-data', selectData);
54784 mode.exit = function () {
54785 behaviors.forEach(context.uninstall);
54786 select(document).call(keybinding.unbind);
54787 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
54788 context.map().on('drawn.select-data', null);
54789 context.ui().sidebar.hide();
54795 function uiImproveOsmComments() {
54798 function issueComments(selection) {
54799 // make the div immediately so it appears above the buttons
54800 var comments = selection.selectAll('.comments-container').data([0]);
54801 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
54803 services.improveOSM.getComments(_qaItem).then(function (d) {
54804 if (!d.comments) return; // nothing to do here
54806 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
54807 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
54808 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
54809 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
54810 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
54811 var osm = services.osm;
54812 var selection = select(this);
54814 if (osm && d.username) {
54815 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
54818 selection.html(function (d) {
54822 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
54823 return _t.html('note.status.commented', {
54824 when: localeDateString(d.timestamp)
54827 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
54830 })["catch"](function (err) {
54831 console.log(err); // eslint-disable-line no-console
54835 function localeDateString(s) {
54836 if (!s) return null;
54842 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
54844 if (isNaN(d.getTime())) return null;
54845 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
54848 issueComments.issue = function (val) {
54849 if (!arguments.length) return _qaItem;
54851 return issueComments;
54854 return issueComments;
54857 function uiImproveOsmDetails(context) {
54860 function issueDetail(d) {
54861 if (d.desc) return d.desc;
54862 var issueKey = d.issueKey;
54863 d.replacements = d.replacements || {};
54864 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54866 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
54869 function improveOsmDetails(selection) {
54870 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
54871 return "".concat(d.id, "-").concat(d.status || 0);
54873 details.exit().remove();
54874 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
54876 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54877 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
54878 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
54880 var relatedEntities = [];
54881 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
54882 var link = select(this);
54883 var isObjectLink = link.classed('error_object_link');
54884 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
54885 var entity = context.hasEntity(entityID);
54886 relatedEntities.push(entityID); // Add click handler
54888 link.on('mouseenter', function () {
54889 utilHighlightEntities([entityID], true, context);
54890 }).on('mouseleave', function () {
54891 utilHighlightEntities([entityID], false, context);
54892 }).on('click', function (d3_event) {
54893 d3_event.preventDefault();
54894 utilHighlightEntities([entityID], false, context);
54895 var osmlayer = context.layers().layer('osm');
54897 if (!osmlayer.enabled()) {
54898 osmlayer.enabled(true);
54901 context.map().centerZoom(_qaItem.loc, 20);
54904 context.enter(modeSelect(context, [entityID]));
54906 context.loadEntity(entityID, function () {
54907 context.enter(modeSelect(context, [entityID]));
54910 }); // Replace with friendly name if possible
54911 // (The entity may not yet be loaded into the graph)
54914 var name = utilDisplayName(entity); // try to use common name
54916 if (!name && !isObjectLink) {
54917 var preset = _mainPresetIndex.match(entity, context.graph());
54918 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
54922 this.innerText = name;
54925 }); // Don't hide entities related to this error - #5880
54927 context.features().forceVisible(relatedEntities);
54928 context.map().pan([0, 0]); // trigger a redraw
54931 improveOsmDetails.issue = function (val) {
54932 if (!arguments.length) return _qaItem;
54934 return improveOsmDetails;
54937 return improveOsmDetails;
54940 function uiImproveOsmHeader() {
54943 function issueTitle(d) {
54944 var issueKey = d.issueKey;
54945 d.replacements = d.replacements || {};
54946 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54948 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
54951 function improveOsmHeader(selection) {
54952 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
54953 return "".concat(d.id, "-").concat(d.status || 0);
54955 header.exit().remove();
54956 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
54957 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
54959 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
54960 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
54962 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');
54963 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
54964 var picon = d.icon;
54969 var isMaki = /^maki-/.test(picon);
54970 return "#".concat(picon).concat(isMaki ? '-11' : '');
54973 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54976 improveOsmHeader.issue = function (val) {
54977 if (!arguments.length) return _qaItem;
54979 return improveOsmHeader;
54982 return improveOsmHeader;
54985 function uiImproveOsmEditor(context) {
54986 var dispatch$1 = dispatch('change');
54987 var qaDetails = uiImproveOsmDetails(context);
54988 var qaComments = uiImproveOsmComments();
54989 var qaHeader = uiImproveOsmHeader();
54993 function improveOsmEditor(selection) {
54994 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
54995 headerEnter.append('button').attr('class', 'close').on('click', function () {
54996 return context.enter(modeBrowse(context));
54997 }).call(svgIcon('#iD-icon-close'));
54998 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
54999 var body = selection.selectAll('.body').data([0]);
55000 body = body.enter().append('div').attr('class', 'body').merge(body);
55001 var editor = body.selectAll('.qa-editor').data([0]);
55002 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);
55005 function improveOsmSaveSection(selection) {
55006 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55008 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
55009 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55010 return "".concat(d.id, "-").concat(d.status || 0);
55013 saveSection.exit().remove(); // enter
55015 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
55016 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
55017 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
55018 return d.newComment;
55019 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
55021 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55023 function changeInput() {
55024 var input = select(this);
55025 var val = input.property('value').trim();
55029 } // store the unsaved comment with the issue itself
55032 _qaItem = _qaItem.update({
55035 var qaService = services.improveOSM;
55038 qaService.replaceItem(_qaItem);
55041 saveSection.call(qaSaveButtons);
55045 function qaSaveButtons(selection) {
55046 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55048 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55049 return d.status + d.id;
55052 buttonSection.exit().remove(); // enter
55054 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55055 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
55056 buttonEnter.append('button').attr('class', 'button close-button action');
55057 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55059 buttonSection = buttonSection.merge(buttonEnter);
55060 buttonSection.select('.comment-button').attr('disabled', function (d) {
55061 return d.newComment ? null : true;
55062 }).on('click.comment', function (d3_event, d) {
55063 this.blur(); // avoid keeping focus on the button - #4641
55065 var qaService = services.improveOSM;
55068 qaService.postUpdate(d, function (err, item) {
55069 return dispatch$1.call('change', item);
55073 buttonSection.select('.close-button').html(function (d) {
55074 var andComment = d.newComment ? '_comment' : '';
55075 return _t.html("QA.keepRight.close".concat(andComment));
55076 }).on('click.close', function (d3_event, d) {
55077 this.blur(); // avoid keeping focus on the button - #4641
55079 var qaService = services.improveOSM;
55082 d.newStatus = 'SOLVED';
55083 qaService.postUpdate(d, function (err, item) {
55084 return dispatch$1.call('change', item);
55088 buttonSection.select('.ignore-button').html(function (d) {
55089 var andComment = d.newComment ? '_comment' : '';
55090 return _t.html("QA.keepRight.ignore".concat(andComment));
55091 }).on('click.ignore', function (d3_event, d) {
55092 this.blur(); // avoid keeping focus on the button - #4641
55094 var qaService = services.improveOSM;
55097 d.newStatus = 'INVALID';
55098 qaService.postUpdate(d, function (err, item) {
55099 return dispatch$1.call('change', item);
55103 } // NOTE: Don't change method name until UI v3 is merged
55106 improveOsmEditor.error = function (val) {
55107 if (!arguments.length) return _qaItem;
55109 return improveOsmEditor;
55112 return utilRebind(improveOsmEditor, dispatch$1, 'on');
55115 function uiKeepRightDetails(context) {
55118 function issueDetail(d) {
55119 var itemType = d.itemType,
55120 parentIssueType = d.parentIssueType;
55121 var unknown = _t.html('inspector.unknown');
55122 var replacements = d.replacements || {};
55123 replacements["default"] = unknown; // special key `default` works as a fallback string
55125 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
55127 if (detail === unknown) {
55128 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
55134 function keepRightDetails(selection) {
55135 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55136 return "".concat(d.id, "-").concat(d.status || 0);
55138 details.exit().remove();
55139 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
55141 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55142 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
55143 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
55145 var relatedEntities = [];
55146 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
55147 var link = select(this);
55148 var isObjectLink = link.classed('error_object_link');
55149 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
55150 var entity = context.hasEntity(entityID);
55151 relatedEntities.push(entityID); // Add click handler
55153 link.on('mouseenter', function () {
55154 utilHighlightEntities([entityID], true, context);
55155 }).on('mouseleave', function () {
55156 utilHighlightEntities([entityID], false, context);
55157 }).on('click', function (d3_event) {
55158 d3_event.preventDefault();
55159 utilHighlightEntities([entityID], false, context);
55160 var osmlayer = context.layers().layer('osm');
55162 if (!osmlayer.enabled()) {
55163 osmlayer.enabled(true);
55166 context.map().centerZoomEase(_qaItem.loc, 20);
55169 context.enter(modeSelect(context, [entityID]));
55171 context.loadEntity(entityID, function () {
55172 context.enter(modeSelect(context, [entityID]));
55175 }); // Replace with friendly name if possible
55176 // (The entity may not yet be loaded into the graph)
55179 var name = utilDisplayName(entity); // try to use common name
55181 if (!name && !isObjectLink) {
55182 var preset = _mainPresetIndex.match(entity, context.graph());
55183 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55187 this.innerText = name;
55190 }); // Don't hide entities related to this issue - #5880
55192 context.features().forceVisible(relatedEntities);
55193 context.map().pan([0, 0]); // trigger a redraw
55196 keepRightDetails.issue = function (val) {
55197 if (!arguments.length) return _qaItem;
55199 return keepRightDetails;
55202 return keepRightDetails;
55205 function uiKeepRightHeader() {
55208 function issueTitle(d) {
55209 var itemType = d.itemType,
55210 parentIssueType = d.parentIssueType;
55211 var unknown = _t.html('inspector.unknown');
55212 var replacements = d.replacements || {};
55213 replacements["default"] = unknown; // special key `default` works as a fallback string
55215 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
55217 if (title === unknown) {
55218 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
55224 function keepRightHeader(selection) {
55225 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55226 return "".concat(d.id, "-").concat(d.status || 0);
55228 header.exit().remove();
55229 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55230 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55233 iconEnter.append('div').attr('class', function (d) {
55234 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
55235 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
55236 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55239 keepRightHeader.issue = function (val) {
55240 if (!arguments.length) return _qaItem;
55242 return keepRightHeader;
55245 return keepRightHeader;
55248 function uiViewOnKeepRight() {
55251 function viewOnKeepRight(selection) {
55254 if (services.keepRight && _qaItem instanceof QAItem) {
55255 url = services.keepRight.issueURL(_qaItem);
55258 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
55260 link.exit().remove(); // enter
55262 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
55263 .attr('href', function (d) {
55265 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55266 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
55269 viewOnKeepRight.what = function (val) {
55270 if (!arguments.length) return _qaItem;
55272 return viewOnKeepRight;
55275 return viewOnKeepRight;
55278 function uiKeepRightEditor(context) {
55279 var dispatch$1 = dispatch('change');
55280 var qaDetails = uiKeepRightDetails(context);
55281 var qaHeader = uiKeepRightHeader();
55285 function keepRightEditor(selection) {
55286 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
55287 headerEnter.append('button').attr('class', 'close').on('click', function () {
55288 return context.enter(modeBrowse(context));
55289 }).call(svgIcon('#iD-icon-close'));
55290 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
55291 var body = selection.selectAll('.body').data([0]);
55292 body = body.enter().append('div').attr('class', 'body').merge(body);
55293 var editor = body.selectAll('.qa-editor').data([0]);
55294 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
55295 var footer = selection.selectAll('.footer').data([0]);
55296 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
55299 function keepRightSaveSection(selection) {
55300 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55302 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
55303 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55304 return "".concat(d.id, "-").concat(d.status || 0);
55307 saveSection.exit().remove(); // enter
55309 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
55310 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
55311 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
55312 return d.newComment || d.comment;
55313 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
55315 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55317 function changeInput() {
55318 var input = select(this);
55319 var val = input.property('value').trim();
55321 if (val === _qaItem.comment) {
55323 } // store the unsaved comment with the issue itself
55326 _qaItem = _qaItem.update({
55329 var qaService = services.keepRight;
55332 qaService.replaceItem(_qaItem); // update keepright cache
55335 saveSection.call(qaSaveButtons);
55339 function qaSaveButtons(selection) {
55340 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55342 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55343 return d.status + d.id;
55346 buttonSection.exit().remove(); // enter
55348 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55349 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
55350 buttonEnter.append('button').attr('class', 'button close-button action');
55351 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55353 buttonSection = buttonSection.merge(buttonEnter);
55354 buttonSection.select('.comment-button') // select and propagate data
55355 .attr('disabled', function (d) {
55356 return d.newComment ? null : true;
55357 }).on('click.comment', function (d3_event, d) {
55358 this.blur(); // avoid keeping focus on the button - #4641
55360 var qaService = services.keepRight;
55363 qaService.postUpdate(d, function (err, item) {
55364 return dispatch$1.call('change', item);
55368 buttonSection.select('.close-button') // select and propagate data
55369 .html(function (d) {
55370 var andComment = d.newComment ? '_comment' : '';
55371 return _t.html("QA.keepRight.close".concat(andComment));
55372 }).on('click.close', function (d3_event, d) {
55373 this.blur(); // avoid keeping focus on the button - #4641
55375 var qaService = services.keepRight;
55378 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
55380 qaService.postUpdate(d, function (err, item) {
55381 return dispatch$1.call('change', item);
55385 buttonSection.select('.ignore-button') // select and propagate data
55386 .html(function (d) {
55387 var andComment = d.newComment ? '_comment' : '';
55388 return _t.html("QA.keepRight.ignore".concat(andComment));
55389 }).on('click.ignore', function (d3_event, d) {
55390 this.blur(); // avoid keeping focus on the button - #4641
55392 var qaService = services.keepRight;
55395 d.newStatus = 'ignore'; // ignore permanently (false positive)
55397 qaService.postUpdate(d, function (err, item) {
55398 return dispatch$1.call('change', item);
55402 } // NOTE: Don't change method name until UI v3 is merged
55405 keepRightEditor.error = function (val) {
55406 if (!arguments.length) return _qaItem;
55408 return keepRightEditor;
55411 return utilRebind(keepRightEditor, dispatch$1, 'on');
55414 function uiOsmoseDetails(context) {
55417 function issueString(d, type) {
55418 if (!d) return ''; // Issue strings are cached from Osmose API
55420 var s = services.osmose.getStrings(d.itemType);
55421 return type in s ? s[type] : '';
55424 function osmoseDetails(selection) {
55425 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55426 return "".concat(d.id, "-").concat(d.status || 0);
55428 details.exit().remove();
55429 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
55431 if (issueString(_qaItem, 'detail')) {
55432 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55433 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
55434 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
55435 return issueString(d, 'detail');
55436 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55437 } // Elements (populated later as data is requested)
55440 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55441 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
55443 if (issueString(_qaItem, 'fix')) {
55444 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55446 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
55448 _div.append('p').html(function (d) {
55449 return issueString(d, 'fix');
55450 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55451 } // Common Pitfalls (mustn't exist for every issue type)
55454 if (issueString(_qaItem, 'trap')) {
55455 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55457 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
55459 _div2.append('p').html(function (d) {
55460 return issueString(d, 'trap');
55461 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55462 } // Save current item to check if UI changed by time request resolves
55465 var thisItem = _qaItem;
55466 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
55467 // No details to add if there are no associated issue elements
55468 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
55470 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
55473 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
55474 detailsDiv.append('p').html(function (d) {
55476 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55477 } // Create list of linked issue elements
55480 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
55481 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
55483 }).each(function () {
55484 var link = select(this);
55485 var entityID = this.textContent;
55486 var entity = context.hasEntity(entityID); // Add click handler
55488 link.on('mouseenter', function () {
55489 utilHighlightEntities([entityID], true, context);
55490 }).on('mouseleave', function () {
55491 utilHighlightEntities([entityID], false, context);
55492 }).on('click', function (d3_event) {
55493 d3_event.preventDefault();
55494 utilHighlightEntities([entityID], false, context);
55495 var osmlayer = context.layers().layer('osm');
55497 if (!osmlayer.enabled()) {
55498 osmlayer.enabled(true);
55501 context.map().centerZoom(d.loc, 20);
55504 context.enter(modeSelect(context, [entityID]));
55506 context.loadEntity(entityID, function () {
55507 context.enter(modeSelect(context, [entityID]));
55510 }); // Replace with friendly name if possible
55511 // (The entity may not yet be loaded into the graph)
55514 var name = utilDisplayName(entity); // try to use common name
55517 var preset = _mainPresetIndex.match(entity, context.graph());
55518 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55522 this.innerText = name;
55525 }); // Don't hide entities related to this issue - #5880
55527 context.features().forceVisible(d.elems);
55528 context.map().pan([0, 0]); // trigger a redraw
55529 })["catch"](function (err) {
55530 console.log(err); // eslint-disable-line no-console
55534 osmoseDetails.issue = function (val) {
55535 if (!arguments.length) return _qaItem;
55537 return osmoseDetails;
55540 return osmoseDetails;
55543 function uiOsmoseHeader() {
55546 function issueTitle(d) {
55547 var unknown = _t('inspector.unknown');
55548 if (!d) return unknown; // Issue titles supplied by Osmose
55550 var s = services.osmose.getStrings(d.itemType);
55551 return 'title' in s ? s.title : unknown;
55554 function osmoseHeader(selection) {
55555 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55556 return "".concat(d.id, "-").concat(d.status || 0);
55558 header.exit().remove();
55559 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55560 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55562 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
55563 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
55565 svgEnter.append('polygon').attr('fill', function (d) {
55566 return services.osmose.getColor(d.item);
55567 }).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');
55568 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
55569 var picon = d.icon;
55574 var isMaki = /^maki-/.test(picon);
55575 return "#".concat(picon).concat(isMaki ? '-11' : '');
55578 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55581 osmoseHeader.issue = function (val) {
55582 if (!arguments.length) return _qaItem;
55584 return osmoseHeader;
55587 return osmoseHeader;
55590 function uiViewOnOsmose() {
55593 function viewOnOsmose(selection) {
55596 if (services.osmose && _qaItem instanceof QAItem) {
55597 url = services.osmose.itemURL(_qaItem);
55600 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
55602 link.exit().remove(); // enter
55604 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
55605 .attr('href', function (d) {
55607 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55608 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
55611 viewOnOsmose.what = function (val) {
55612 if (!arguments.length) return _qaItem;
55614 return viewOnOsmose;
55617 return viewOnOsmose;
55620 function uiOsmoseEditor(context) {
55621 var dispatch$1 = dispatch('change');
55622 var qaDetails = uiOsmoseDetails(context);
55623 var qaHeader = uiOsmoseHeader();
55627 function osmoseEditor(selection) {
55628 var header = selection.selectAll('.header').data([0]);
55629 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
55630 headerEnter.append('button').attr('class', 'close').on('click', function () {
55631 return context.enter(modeBrowse(context));
55632 }).call(svgIcon('#iD-icon-close'));
55633 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
55634 var body = selection.selectAll('.body').data([0]);
55635 body = body.enter().append('div').attr('class', 'body').merge(body);
55636 var editor = body.selectAll('.qa-editor').data([0]);
55637 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
55638 var footer = selection.selectAll('.footer').data([0]);
55639 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
55642 function osmoseSaveSection(selection) {
55643 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55645 var isShown = _qaItem && isSelected;
55646 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55647 return "".concat(d.id, "-").concat(d.status || 0);
55650 saveSection.exit().remove(); // enter
55652 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
55654 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55657 function qaSaveButtons(selection) {
55658 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55660 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55661 return d.status + d.id;
55664 buttonSection.exit().remove(); // enter
55666 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55667 buttonEnter.append('button').attr('class', 'button close-button action');
55668 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55670 buttonSection = buttonSection.merge(buttonEnter);
55671 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
55672 this.blur(); // avoid keeping focus on the button - #4641
55674 var qaService = services.osmose;
55677 d.newStatus = 'done';
55678 qaService.postUpdate(d, function (err, item) {
55679 return dispatch$1.call('change', item);
55683 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
55684 this.blur(); // avoid keeping focus on the button - #4641
55686 var qaService = services.osmose;
55689 d.newStatus = 'false';
55690 qaService.postUpdate(d, function (err, item) {
55691 return dispatch$1.call('change', item);
55695 } // NOTE: Don't change method name until UI v3 is merged
55698 osmoseEditor.error = function (val) {
55699 if (!arguments.length) return _qaItem;
55701 return osmoseEditor;
55704 return utilRebind(osmoseEditor, dispatch$1, 'on');
55707 function modeSelectError(context, selectedErrorID, selectedErrorService) {
55709 id: 'select-error',
55712 var keybinding = utilKeybinding('select-error');
55713 var errorService = services[selectedErrorService];
55716 switch (selectedErrorService) {
55718 errorEditor = uiImproveOsmEditor(context).on('change', function () {
55719 context.map().pan([0, 0]); // trigger a redraw
55721 var error = checkSelectedID();
55722 if (!error) return;
55723 context.ui().sidebar.show(errorEditor.error(error));
55728 errorEditor = uiKeepRightEditor(context).on('change', function () {
55729 context.map().pan([0, 0]); // trigger a redraw
55731 var error = checkSelectedID();
55732 if (!error) return;
55733 context.ui().sidebar.show(errorEditor.error(error));
55738 errorEditor = uiOsmoseEditor(context).on('change', function () {
55739 context.map().pan([0, 0]); // trigger a redraw
55741 var error = checkSelectedID();
55742 if (!error) return;
55743 context.ui().sidebar.show(errorEditor.error(error));
55748 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
55750 function checkSelectedID() {
55751 if (!errorService) return;
55752 var error = errorService.getError(selectedErrorID);
55755 context.enter(modeBrowse(context));
55761 mode.zoomToSelected = function () {
55762 if (!errorService) return;
55763 var error = errorService.getError(selectedErrorID);
55766 context.map().centerZoomEase(error.loc, 20);
55770 mode.enter = function () {
55771 var error = checkSelectedID();
55772 if (!error) return;
55773 behaviors.forEach(context.install);
55774 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
55775 select(document).call(keybinding);
55777 var sidebar = context.ui().sidebar;
55778 sidebar.show(errorEditor.error(error));
55779 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
55781 function selectError(d3_event, drawn) {
55782 if (!checkSelectedID()) return;
55783 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
55785 if (selection.empty()) {
55786 // Return to browse mode if selected DOM elements have
55787 // disappeared because the user moved them out of view..
55788 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
55790 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
55791 context.enter(modeBrowse(context));
55794 selection.classed('selected', true);
55795 context.selectedErrorID(selectedErrorID);
55800 if (context.container().select('.combobox').size()) return;
55801 context.enter(modeBrowse(context));
55805 mode.exit = function () {
55806 behaviors.forEach(context.uninstall);
55807 select(document).call(keybinding.unbind);
55808 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
55809 context.map().on('drawn.select-error', null);
55810 context.ui().sidebar.hide();
55811 context.selectedErrorID(null);
55812 context.features().forceVisible([]);
55818 function behaviorSelect(context) {
55819 var _tolerancePx = 4; // see also behaviorDrag
55821 var _lastMouseEvent = null;
55822 var _showMenu = false;
55823 var _downPointers = {};
55824 var _longPressTimeout = null;
55825 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
55827 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
55829 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55831 function keydown(d3_event) {
55832 if (d3_event.keyCode === 32) {
55833 // don't react to spacebar events during text input
55834 var activeNode = document.activeElement;
55835 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
55838 if (d3_event.keyCode === 93 || // context menu key
55839 d3_event.keyCode === 32) {
55841 d3_event.preventDefault();
55844 if (d3_event.repeat) return; // ignore repeated events for held keys
55845 // if any key is pressed the user is probably doing something other than long-pressing
55849 if (d3_event.shiftKey) {
55850 context.surface().classed('behavior-multiselect', true);
55853 if (d3_event.keyCode === 32) {
55855 if (!_downPointers.spacebar && _lastMouseEvent) {
55857 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
55858 _downPointers.spacebar = {
55859 firstEvent: _lastMouseEvent,
55860 lastEvent: _lastMouseEvent
55866 function keyup(d3_event) {
55869 if (!d3_event.shiftKey) {
55870 context.surface().classed('behavior-multiselect', false);
55873 if (d3_event.keyCode === 93) {
55874 // context menu key
55875 d3_event.preventDefault();
55876 _lastInteractionType = 'menukey';
55877 contextmenu(d3_event);
55878 } else if (d3_event.keyCode === 32) {
55880 var pointer = _downPointers.spacebar;
55883 delete _downPointers.spacebar;
55884 if (pointer.done) return;
55885 d3_event.preventDefault();
55886 _lastInteractionType = 'spacebar';
55887 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
55892 function pointerdown(d3_event) {
55893 var id = (d3_event.pointerId || 'mouse').toString();
55895 if (d3_event.buttons && d3_event.buttons !== 1) return;
55896 context.ui().closeEditMenu();
55897 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
55898 _downPointers[id] = {
55899 firstEvent: d3_event,
55900 lastEvent: d3_event
55904 function didLongPress(id, interactionType) {
55905 var pointer = _downPointers[id];
55906 if (!pointer) return;
55908 for (var i in _downPointers) {
55909 // don't allow this or any currently down pointer to trigger another click
55910 _downPointers[i].done = true;
55911 } // treat long presses like right-clicks
55914 _longPressTimeout = null;
55915 _lastInteractionType = interactionType;
55917 click(pointer.firstEvent, pointer.lastEvent, id);
55920 function pointermove(d3_event) {
55921 var id = (d3_event.pointerId || 'mouse').toString();
55923 if (_downPointers[id]) {
55924 _downPointers[id].lastEvent = d3_event;
55927 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
55928 _lastMouseEvent = d3_event;
55930 if (_downPointers.spacebar) {
55931 _downPointers.spacebar.lastEvent = d3_event;
55936 function pointerup(d3_event) {
55937 var id = (d3_event.pointerId || 'mouse').toString();
55938 var pointer = _downPointers[id];
55939 if (!pointer) return;
55940 delete _downPointers[id];
55942 if (_multiselectionPointerId === id) {
55943 _multiselectionPointerId = null;
55946 if (pointer.done) return;
55947 click(pointer.firstEvent, d3_event, id);
55950 function pointercancel(d3_event) {
55951 var id = (d3_event.pointerId || 'mouse').toString();
55952 if (!_downPointers[id]) return;
55953 delete _downPointers[id];
55955 if (_multiselectionPointerId === id) {
55956 _multiselectionPointerId = null;
55960 function contextmenu(d3_event) {
55961 d3_event.preventDefault();
55963 if (!+d3_event.clientX && !+d3_event.clientY) {
55964 if (_lastMouseEvent) {
55965 d3_event.sourceEvent = _lastMouseEvent;
55970 _lastMouseEvent = d3_event;
55971 _lastInteractionType = 'rightclick';
55975 click(d3_event, d3_event);
55978 function click(firstEvent, lastEvent, pointerId) {
55980 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
55981 // are transformed when drag-panning.
55983 var pointGetter = utilFastMouse(mapNode);
55984 var p1 = pointGetter(firstEvent);
55985 var p2 = pointGetter(lastEvent);
55986 var dist = geoVecLength(p1, p2);
55988 if (dist > _tolerancePx || !mapContains(lastEvent)) {
55993 var targetDatum = lastEvent.target.__data__;
55994 var multiselectEntityId;
55996 if (!_multiselectionPointerId) {
55997 // If a different pointer than the one triggering this click is down on a
55998 // feature, treat this and all future clicks as multiselection until that
55999 // pointer is raised.
56000 var selectPointerInfo = pointerDownOnSelection(pointerId);
56002 if (selectPointerInfo) {
56003 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
56005 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
56006 _downPointers[selectPointerInfo.pointerId].done = true;
56008 } // support multiselect if data is already selected
56011 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
56012 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
56013 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
56014 _multiselectionPointerId && !multiselectEntityId);
56016 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
56018 function mapContains(event) {
56019 var rect = mapNode.getBoundingClientRect();
56020 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
56023 function pointerDownOnSelection(skipPointerId) {
56024 var mode = context.mode();
56025 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
56027 for (var pointerId in _downPointers) {
56028 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
56029 var pointerInfo = _downPointers[pointerId];
56030 var p1 = pointGetter(pointerInfo.firstEvent);
56031 var p2 = pointGetter(pointerInfo.lastEvent);
56032 if (geoVecLength(p1, p2) > _tolerancePx) continue;
56033 var datum = pointerInfo.firstEvent.target.__data__;
56034 var entity = datum && datum.properties && datum.properties.entity || datum;
56035 if (context.graph().hasEntity(entity.id)) return {
56036 pointerId: pointerId,
56037 entityId: entity.id,
56038 selected: selectedIDs.indexOf(entity.id) !== -1
56046 function processClick(datum, isMultiselect, point, alsoSelectId) {
56047 var mode = context.mode();
56048 var showMenu = _showMenu;
56049 var interactionType = _lastInteractionType;
56050 var entity = datum && datum.properties && datum.properties.entity;
56051 if (entity) datum = entity;
56053 if (datum && datum.type === 'midpoint') {
56054 // treat targeting midpoints as if targeting the parent way
56055 datum = datum.parents[0];
56060 if (datum instanceof osmEntity) {
56061 // targeting an entity
56062 var selectedIDs = context.selectedIDs();
56063 context.selectedNoteID(null);
56064 context.selectedErrorID(null);
56066 if (!isMultiselect) {
56067 // don't change the selection if we're toggling the menu atop a multiselection
56068 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
56069 if (alsoSelectId === datum.id) alsoSelectId = null;
56070 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
56071 // selected since listeners may expect `context.enter` events,
56072 // e.g. in the walkthrough
56074 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
56075 context.enter(newMode);
56078 if (selectedIDs.indexOf(datum.id) !== -1) {
56079 // clicked entity is already in the selectedIDs list..
56081 // deselect clicked entity, then reenter select mode or return to browse mode..
56082 selectedIDs = selectedIDs.filter(function (id) {
56083 return id !== datum.id;
56085 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
56086 context.enter(newMode);
56089 // clicked entity is not in the selected list, add it..
56090 selectedIDs = selectedIDs.concat([datum.id]);
56091 newMode = mode.selectedIDs(selectedIDs);
56092 context.enter(newMode);
56095 } else if (datum && datum.__featurehash__ && !isMultiselect) {
56096 // targeting custom data
56097 context.selectedNoteID(null).enter(modeSelectData(context, datum));
56098 } else if (datum instanceof osmNote && !isMultiselect) {
56099 // targeting a note
56100 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
56101 } else if (datum instanceof QAItem & !isMultiselect) {
56102 // targeting an external QA issue
56103 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
56105 // targeting nothing
56106 context.selectedNoteID(null);
56107 context.selectedErrorID(null);
56109 if (!isMultiselect && mode.id !== 'browse') {
56110 context.enter(modeBrowse(context));
56114 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
56116 if (showMenu) context.ui().showEditMenu(point, interactionType);
56120 function cancelLongPress() {
56121 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
56122 _longPressTimeout = null;
56125 function resetProperties() {
56128 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
56131 function behavior(selection) {
56133 _lastMouseEvent = context.map().lastPointerEvent();
56134 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) {
56135 // Edge and IE really like to show the contextmenu on the
56136 // menubar when user presses a keyboard menu button
56137 // even after we've already preventdefaulted the key event.
56140 if (+e.clientX === 0 && +e.clientY === 0) {
56141 d3_event.preventDefault();
56144 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
56145 /*if (d3_event && d3_event.shiftKey) {
56147 .classed('behavior-multiselect', true);
56151 behavior.off = function (selection) {
56153 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);
56154 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
56155 context.surface().classed('behavior-multiselect', false);
56161 function behaviorDrawWay(context, wayID, mode, startGraph) {
56162 var dispatch$1 = dispatch('rejectedSelfIntersection');
56163 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
56175 var _pointerHasMoved = false; // The osmNode to be placed.
56176 // This is temporary and just follows the mouse cursor until an "add" event occurs.
56180 var _didResolveTempEdit = false;
56182 function createDrawNode(loc) {
56183 // don't make the draw node until we actually need it
56184 _drawNode = osmNode({
56187 context.pauseChangeDispatch();
56188 context.replace(function actionAddDrawNode(graph) {
56189 // add the draw node to the graph and insert it into the way
56190 var way = graph.entity(wayID);
56191 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
56193 context.resumeChangeDispatch();
56194 setActiveElements();
56197 function removeDrawNode() {
56198 context.pauseChangeDispatch();
56199 context.replace(function actionDeleteDrawNode(graph) {
56200 var way = graph.entity(wayID);
56201 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56203 _drawNode = undefined;
56204 context.resumeChangeDispatch();
56207 function keydown(d3_event) {
56208 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56209 if (context.surface().classed('nope')) {
56210 context.surface().classed('nope-suppressed', true);
56213 context.surface().classed('nope', false).classed('nope-disabled', true);
56217 function keyup(d3_event) {
56218 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56219 if (context.surface().classed('nope-suppressed')) {
56220 context.surface().classed('nope', true);
56223 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56227 function allowsVertex(d) {
56228 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56230 // - `mode/drag_node.js` `doMove()`
56231 // - `behavior/draw.js` `click()`
56232 // - `behavior/draw_way.js` `move()`
56235 function move(d3_event, datum) {
56236 var loc = context.map().mouseCoordinates();
56237 if (!_drawNode) createDrawNode(loc);
56238 context.surface().classed('nope-disabled', d3_event.altKey);
56239 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56240 var targetNodes = datum && datum.properties && datum.properties.nodes;
56243 // snap to node/vertex - a point target with `.loc`
56245 } else if (targetNodes) {
56246 // snap to way - a line target with `.nodes`
56247 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56254 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56255 _drawNode = context.entity(_drawNode.id);
56257 /* includeDrawNode */
56259 } // Check whether this edit causes the geometry to break.
56260 // If so, class the surface with a nope cursor.
56261 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56264 function checkGeometry(includeDrawNode) {
56265 var nopeDisabled = context.surface().classed('nope-disabled');
56266 var isInvalid = isInvalidGeometry(includeDrawNode);
56268 if (nopeDisabled) {
56269 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56271 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56275 function isInvalidGeometry(includeDrawNode) {
56276 var testNode = _drawNode; // we only need to test the single way we're drawing
56278 var parentWay = context.graph().entity(wayID);
56279 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56281 if (includeDrawNode) {
56282 if (parentWay.isClosed()) {
56283 // don't test the last segment for closed ways - #4655
56284 // (still test the first segment)
56288 // discount the draw node
56289 if (parentWay.isClosed()) {
56290 if (nodes.length < 3) return false;
56291 if (_drawNode) nodes.splice(-2, 1);
56292 testNode = nodes[nodes.length - 2];
56294 // there's nothing we need to test if we ignore the draw node on open ways
56299 return testNode && geoHasSelfIntersections(nodes, testNode.id);
56302 function undone() {
56303 // undoing removed the temp edit
56304 _didResolveTempEdit = true;
56305 context.pauseChangeDispatch();
56308 if (context.graph() === startGraph) {
56309 // We've undone back to the initial state before we started drawing.
56310 // Just exit the draw mode without undoing whatever we did before
56311 // we entered the draw mode.
56312 nextMode = modeSelect(context, [wayID]);
56314 // The `undo` only removed the temporary edit, so here we have to
56315 // manually undo to actually remove the last node we added. We can't
56316 // use the `undo` function since the initial "add" graph doesn't have
56317 // an annotation and so cannot be undone to.
56318 context.pop(1); // continue drawing
56321 } // clear the redo stack by adding and removing a blank edit
56324 context.perform(actionNoop());
56326 context.resumeChangeDispatch();
56327 context.enter(nextMode);
56330 function setActiveElements() {
56331 if (!_drawNode) return;
56332 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56335 function resetToStartGraph() {
56336 while (context.graph() !== startGraph) {
56341 var drawWay = function drawWay(surface) {
56342 _drawNode = undefined;
56343 _didResolveTempEdit = false;
56344 _origWay = context.entity(wayID);
56345 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] : _origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1];
56346 _wayGeometry = _origWay.geometry(context.graph());
56347 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56348 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56349 // We must make sure to replace or remove it later.
56351 context.pauseChangeDispatch();
56352 context.perform(actionNoop(), _annotation);
56353 context.resumeChangeDispatch();
56354 behavior.hover().initialNodeID(_headNodeID);
56355 behavior.on('move', function () {
56356 _pointerHasMoved = true;
56357 move.apply(this, arguments);
56358 }).on('down', function () {
56359 move.apply(this, arguments);
56360 }).on('downcancel', function () {
56361 if (_drawNode) removeDrawNode();
56362 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56363 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56364 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56365 setActiveElements();
56366 surface.call(behavior);
56367 context.history().on('undone.draw', undone);
56370 drawWay.off = function (surface) {
56371 if (!_didResolveTempEdit) {
56372 // Drawing was interrupted unexpectedly.
56373 // This can happen if the user changes modes,
56374 // clicks geolocate button, a hashchange event occurs, etc.
56375 context.pauseChangeDispatch();
56376 resetToStartGraph();
56377 context.resumeChangeDispatch();
56380 _drawNode = undefined;
56381 _nodeIndex = undefined;
56382 context.map().on('drawn.draw', null);
56383 surface.call(behavior.off).selectAll('.active').classed('active', false);
56384 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56385 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56386 context.history().on('undone.draw', null);
56389 function attemptAdd(d, loc, doAdd) {
56391 // move the node to the final loc in case move wasn't called
56392 // consistently (e.g. on touch devices)
56393 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56394 _drawNode = context.entity(_drawNode.id);
56396 createDrawNode(loc);
56400 /* includeDrawNode */
56403 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56404 if (!_pointerHasMoved) {
56405 // prevent the temporary draw node from appearing on touch devices
56409 dispatch$1.call('rejectedSelfIntersection', this);
56410 return; // can't click here
56413 context.pauseChangeDispatch();
56414 doAdd(); // we just replaced the temporary edit with the real one
56416 _didResolveTempEdit = true;
56417 context.resumeChangeDispatch();
56418 context.enter(mode);
56419 } // Accept the current position of the drawing node
56422 drawWay.add = function (loc, d) {
56423 attemptAdd(d, loc, function () {// don't need to do anything extra
56425 }; // Connect the way to an existing way
56428 drawWay.addWay = function (loc, edge, d) {
56429 attemptAdd(d, loc, function () {
56430 context.replace(actionAddMidpoint({
56433 }, _drawNode), _annotation);
56435 }; // Connect the way to an existing node
56438 drawWay.addNode = function (node, d) {
56439 // finish drawing if the mapper targets the prior node
56440 if (node.id === _headNodeID || // or the first node when drawing an area
56441 _origWay.isClosed() && node.id === _origWay.first()) {
56446 attemptAdd(d, node.loc, function () {
56447 context.replace(function actionReplaceDrawNode(graph) {
56448 // remove the temporary draw node and insert the existing node
56449 // at the same index
56450 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56451 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56454 }; // Finish the draw operation, removing the temporary edit.
56455 // If the way has enough nodes to be valid, it's selected.
56456 // Otherwise, delete everything and return to browse mode.
56459 drawWay.finish = function () {
56460 checkGeometry(false
56461 /* includeDrawNode */
56464 if (context.surface().classed('nope')) {
56465 dispatch$1.call('rejectedSelfIntersection', this);
56466 return; // can't click here
56469 context.pauseChangeDispatch(); // remove the temporary edit
56472 _didResolveTempEdit = true;
56473 context.resumeChangeDispatch();
56474 var way = context.hasEntity(wayID);
56476 if (!way || way.isDegenerate()) {
56481 window.setTimeout(function () {
56482 context.map().dblclickZoomEnable(true);
56484 var isNewFeature = !mode.isContinuing;
56485 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56486 }; // Cancel the draw operation, delete everything, and return to browse mode.
56489 drawWay.cancel = function () {
56490 context.pauseChangeDispatch();
56491 resetToStartGraph();
56492 context.resumeChangeDispatch();
56493 window.setTimeout(function () {
56494 context.map().dblclickZoomEnable(true);
56496 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56497 context.enter(modeBrowse(context));
56500 drawWay.nodeIndex = function (val) {
56501 if (!arguments.length) return _nodeIndex;
56506 drawWay.activeID = function () {
56507 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56512 return utilRebind(drawWay, dispatch$1, 'on');
56515 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56520 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56521 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56523 mode.wayID = wayID;
56524 mode.isContinuing = continuing;
56526 mode.enter = function () {
56527 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56528 context.install(behavior);
56531 mode.exit = function () {
56532 context.uninstall(behavior);
56535 mode.selectedIDs = function () {
56539 mode.activeID = function () {
56540 return behavior && behavior.activeID() || [];
56546 function operationContinue(context, selectedIDs) {
56547 var _entities = selectedIDs.map(function (id) {
56548 return context.graph().entity(id);
56551 var _geometries = Object.assign({
56554 }, utilArrayGroupBy(_entities, function (entity) {
56555 return entity.geometry(context.graph());
56558 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
56560 function candidateWays() {
56561 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
56562 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
56566 var _candidates = candidateWays();
56568 var operation = function operation() {
56569 var candidate = _candidates[0];
56570 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
56573 operation.relatedEntityIds = function () {
56574 return _candidates.length ? [_candidates[0].id] : [];
56577 operation.available = function () {
56578 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
56581 operation.disabled = function () {
56582 if (_candidates.length === 0) {
56583 return 'not_eligible';
56584 } else if (_candidates.length > 1) {
56591 operation.tooltip = function () {
56592 var disable = operation.disabled();
56593 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
56596 operation.annotation = function () {
56597 return _t('operations.continue.annotation.line');
56600 operation.id = 'continue';
56601 operation.keys = [_t('operations.continue.key')];
56602 operation.title = _t('operations.continue.title');
56603 operation.behavior = behaviorOperation(context).which(operation);
56607 function operationCopy(context, selectedIDs) {
56608 function getFilteredIdsToCopy() {
56609 return selectedIDs.filter(function (selectedID) {
56610 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
56612 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
56616 var operation = function operation() {
56617 var graph = context.graph();
56618 var selected = groupEntities(getFilteredIdsToCopy(), graph);
56624 for (i = 0; i < selected.relation.length; i++) {
56625 entity = selected.relation[i];
56627 if (!skip[entity.id] && entity.isComplete(graph)) {
56628 canCopy.push(entity.id);
56629 skip = getDescendants(entity.id, graph, skip);
56633 for (i = 0; i < selected.way.length; i++) {
56634 entity = selected.way[i];
56636 if (!skip[entity.id]) {
56637 canCopy.push(entity.id);
56638 skip = getDescendants(entity.id, graph, skip);
56642 for (i = 0; i < selected.node.length; i++) {
56643 entity = selected.node[i];
56645 if (!skip[entity.id]) {
56646 canCopy.push(entity.id);
56650 context.copyIDs(canCopy);
56652 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
56653 // store the anchor coordinates if copying more than a single node
56654 context.copyLonLat(context.projection.invert(_point));
56656 context.copyLonLat(null);
56660 function groupEntities(ids, graph) {
56661 var entities = ids.map(function (id) {
56662 return graph.entity(id);
56664 return Object.assign({
56668 }, utilArrayGroupBy(entities, 'type'));
56671 function getDescendants(id, graph, descendants) {
56672 var entity = graph.entity(id);
56674 descendants = descendants || {};
56676 if (entity.type === 'relation') {
56677 children = entity.members.map(function (m) {
56680 } else if (entity.type === 'way') {
56681 children = entity.nodes;
56686 for (var i = 0; i < children.length; i++) {
56687 if (!descendants[children[i]]) {
56688 descendants[children[i]] = true;
56689 descendants = getDescendants(children[i], graph, descendants);
56693 return descendants;
56696 operation.available = function () {
56697 return getFilteredIdsToCopy().length > 0;
56700 operation.disabled = function () {
56701 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
56703 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
56704 return 'too_large';
56710 operation.availableForKeypress = function () {
56711 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
56713 return !selection || !selection.toString();
56716 operation.tooltip = function () {
56717 var disable = operation.disabled();
56718 return disable ? _t('operations.copy.' + disable, {
56719 n: selectedIDs.length
56720 }) : _t('operations.copy.description', {
56721 n: selectedIDs.length
56725 operation.annotation = function () {
56726 return _t('operations.copy.annotation', {
56727 n: selectedIDs.length
56733 operation.point = function (val) {
56738 operation.id = 'copy';
56739 operation.keys = [uiCmd('⌘C')];
56740 operation.title = _t('operations.copy.title');
56741 operation.behavior = behaviorOperation(context).which(operation);
56745 function operationDisconnect(context, selectedIDs) {
56746 var _vertexIDs = [];
56748 var _otherIDs = [];
56750 selectedIDs.forEach(function (id) {
56751 var entity = context.entity(id);
56753 if (entity.type === 'way') {
56755 } else if (entity.geometry(context.graph()) === 'vertex') {
56756 _vertexIDs.push(id);
56758 _otherIDs.push(id);
56763 _descriptionID = '',
56764 _annotationID = 'features';
56766 var _disconnectingVertexIds = [];
56767 var _disconnectingWayIds = [];
56769 if (_vertexIDs.length > 0) {
56770 // At the selected vertices, disconnect the selected ways, if any, else
56771 // disconnect all connected ways
56772 _disconnectingVertexIds = _vertexIDs;
56774 _vertexIDs.forEach(function (vertexID) {
56775 var action = actionDisconnect(vertexID);
56777 if (_wayIDs.length > 0) {
56778 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
56779 var way = context.entity(wayID);
56780 return way.nodes.indexOf(vertexID) !== -1;
56783 action.limitWays(waysIDsForVertex);
56786 _actions.push(action);
56788 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
56793 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
56794 return _wayIDs.indexOf(id) === -1;
56796 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
56798 if (_wayIDs.length === 1) {
56799 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
56801 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
56803 } else if (_wayIDs.length > 0) {
56804 // Disconnect the selected ways from each other, if they're connected,
56805 // else disconnect them from all connected ways
56806 var ways = _wayIDs.map(function (id) {
56807 return context.entity(id);
56810 var nodes = utilGetAllNodes(_wayIDs, context.graph());
56811 _coords = nodes.map(function (n) {
56813 }); // actions for connected nodes shared by at least two selected ways
56815 var sharedActions = [];
56816 var sharedNodes = []; // actions for connected nodes
56818 var unsharedActions = [];
56819 var unsharedNodes = [];
56820 nodes.forEach(function (node) {
56821 var action = actionDisconnect(node.id).limitWays(_wayIDs);
56823 if (action.disabled(context.graph()) !== 'not_connected') {
56826 for (var i in ways) {
56829 if (way.nodes.indexOf(node.id) !== -1) {
56833 if (count > 1) break;
56837 sharedActions.push(action);
56838 sharedNodes.push(node);
56840 unsharedActions.push(action);
56841 unsharedNodes.push(node);
56845 _descriptionID += 'no_points.';
56846 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
56848 if (sharedActions.length) {
56849 // if any nodes are shared, only disconnect the selected ways from each other
56850 _actions = sharedActions;
56851 _disconnectingVertexIds = sharedNodes.map(function (node) {
56854 _descriptionID += 'conjoined';
56855 _annotationID = 'from_each_other';
56857 // if no nodes are shared, disconnect the selected ways from all connected ways
56858 _actions = unsharedActions;
56859 _disconnectingVertexIds = unsharedNodes.map(function (node) {
56863 if (_wayIDs.length === 1) {
56864 _descriptionID += context.graph().geometry(_wayIDs[0]);
56866 _descriptionID += 'separate';
56871 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
56873 var operation = function operation() {
56874 context.perform(function (graph) {
56875 return _actions.reduce(function (graph, action) {
56876 return action(graph);
56878 }, operation.annotation());
56879 context.validator().validate();
56882 operation.relatedEntityIds = function () {
56883 if (_vertexIDs.length) {
56884 return _disconnectingWayIds;
56887 return _disconnectingVertexIds;
56890 operation.available = function () {
56891 if (_actions.length === 0) return false;
56892 if (_otherIDs.length !== 0) return false;
56893 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
56894 return _vertexIDs.some(function (vertexID) {
56895 var way = context.entity(wayID);
56896 return way.nodes.indexOf(vertexID) !== -1;
56902 operation.disabled = function () {
56905 for (var actionIndex in _actions) {
56906 reason = _actions[actionIndex].disabled(context.graph());
56907 if (reason) return reason;
56910 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
56911 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
56912 } else if (_coords && someMissing()) {
56913 return 'not_downloaded';
56914 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56915 return 'connected_to_hidden';
56920 function someMissing() {
56921 if (context.inIntro()) return false;
56922 var osm = context.connection();
56925 var missing = _coords.filter(function (loc) {
56926 return !osm.isDataLoaded(loc);
56929 if (missing.length) {
56930 missing.forEach(function (loc) {
56931 context.loadTileAtLoc(loc);
56941 operation.tooltip = function () {
56942 var disable = operation.disabled();
56945 return _t('operations.disconnect.' + disable);
56948 return _t('operations.disconnect.description.' + _descriptionID);
56951 operation.annotation = function () {
56952 return _t('operations.disconnect.annotation.' + _annotationID);
56955 operation.id = 'disconnect';
56956 operation.keys = [_t('operations.disconnect.key')];
56957 operation.title = _t('operations.disconnect.title');
56958 operation.behavior = behaviorOperation(context).which(operation);
56962 function operationDowngrade(context, selectedIDs) {
56963 var _affectedFeatureCount = 0;
56965 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
56967 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
56969 function downgradeTypeForEntityIDs(entityIds) {
56971 _affectedFeatureCount = 0;
56973 for (var i in entityIds) {
56974 var entityID = entityIds[i];
56975 var type = downgradeTypeForEntityID(entityID);
56978 _affectedFeatureCount += 1;
56980 if (downgradeType && type !== downgradeType) {
56981 if (downgradeType !== 'generic' && type !== 'generic') {
56982 downgradeType = 'building_address';
56984 downgradeType = 'generic';
56987 downgradeType = type;
56992 return downgradeType;
56995 function downgradeTypeForEntityID(entityID) {
56996 var graph = context.graph();
56997 var entity = graph.entity(entityID);
56998 var preset = _mainPresetIndex.match(entity, graph);
56999 if (!preset || preset.isFallback()) return null;
57001 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
57002 return key.match(/^addr:.{1,}/);
57007 var geometry = entity.geometry(graph);
57009 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
57013 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
57020 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
57021 var addressKeysToKeep = ['source'];
57023 var operation = function operation() {
57024 context.perform(function (graph) {
57025 for (var i in selectedIDs) {
57026 var entityID = selectedIDs[i];
57027 var type = downgradeTypeForEntityID(entityID);
57028 if (!type) continue;
57029 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
57031 for (var key in tags) {
57032 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
57034 if (type === 'building') {
57035 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
57038 if (type !== 'generic') {
57039 if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
57045 graph = actionChangeTags(entityID, tags)(graph);
57049 }, operation.annotation());
57050 context.validator().validate(); // refresh the select mode to enable the delete operation
57052 context.enter(modeSelect(context, selectedIDs));
57055 operation.available = function () {
57056 return _downgradeType;
57059 operation.disabled = function () {
57060 if (selectedIDs.some(hasWikidataTag)) {
57061 return 'has_wikidata_tag';
57066 function hasWikidataTag(id) {
57067 var entity = context.entity(id);
57068 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
57072 operation.tooltip = function () {
57073 var disable = operation.disabled();
57074 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
57077 operation.annotation = function () {
57080 if (_downgradeType === 'building_address') {
57081 suffix = 'generic';
57083 suffix = _downgradeType;
57086 return _t('operations.downgrade.annotation.' + suffix, {
57087 n: _affectedFeatureCount
57091 operation.id = 'downgrade';
57092 operation.keys = [uiCmd('⌫')];
57093 operation.title = _t('operations.downgrade.title');
57094 operation.behavior = behaviorOperation(context).which(operation);
57098 function operationExtract(context, selectedIDs) {
57099 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
57101 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
57102 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
57103 }).filter(Boolean));
57105 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
57109 var _actions = selectedIDs.map(function (entityID) {
57110 var graph = context.graph();
57111 var entity = graph.hasEntity(entityID);
57112 if (!entity || !entity.hasInterestingTags()) return null;
57113 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
57115 if (entity.type !== 'node') {
57116 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
57118 if (preset.geometry.indexOf('point') === -1) return null;
57121 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
57122 return actionExtract(entityID);
57123 }).filter(Boolean);
57125 var operation = function operation() {
57126 var combinedAction = function combinedAction(graph) {
57127 _actions.forEach(function (action) {
57128 graph = action(graph);
57134 context.perform(combinedAction, operation.annotation()); // do the extract
57136 var extractedNodeIDs = _actions.map(function (action) {
57137 return action.getExtractedNodeID();
57140 context.enter(modeSelect(context, extractedNodeIDs));
57143 operation.available = function () {
57144 return _actions.length && selectedIDs.length === _actions.length;
57147 operation.disabled = function () {
57148 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
57149 return 'too_large';
57150 } else if (selectedIDs.some(function (entityID) {
57151 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
57153 return 'connected_to_hidden';
57159 operation.tooltip = function () {
57160 var disableReason = operation.disabled();
57162 if (disableReason) {
57163 return _t('operations.extract.' + disableReason + '.' + _amount);
57165 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
57169 operation.annotation = function () {
57170 return _t('operations.extract.annotation', {
57171 n: selectedIDs.length
57175 operation.id = 'extract';
57176 operation.keys = [_t('operations.extract.key')];
57177 operation.title = _t('operations.extract.title');
57178 operation.behavior = behaviorOperation(context).which(operation);
57182 function operationMerge(context, selectedIDs) {
57183 var _action = getAction();
57185 function getAction() {
57186 // prefer a non-disabled action first
57187 var join = actionJoin(selectedIDs);
57188 if (!join.disabled(context.graph())) return join;
57189 var merge = actionMerge(selectedIDs);
57190 if (!merge.disabled(context.graph())) return merge;
57191 var mergePolygon = actionMergePolygon(selectedIDs);
57192 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
57193 var mergeNodes = actionMergeNodes(selectedIDs);
57194 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
57196 if (join.disabled(context.graph()) !== 'not_eligible') return join;
57197 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
57198 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
57202 var operation = function operation() {
57203 if (operation.disabled()) return;
57204 context.perform(_action, operation.annotation());
57205 context.validator().validate();
57206 var resultIDs = selectedIDs.filter(context.hasEntity);
57208 if (resultIDs.length > 1) {
57209 var interestingIDs = resultIDs.filter(function (id) {
57210 return context.entity(id).hasInterestingTags();
57212 if (interestingIDs.length) resultIDs = interestingIDs;
57215 context.enter(modeSelect(context, resultIDs));
57218 operation.available = function () {
57219 return selectedIDs.length >= 2;
57222 operation.disabled = function () {
57223 var actionDisabled = _action.disabled(context.graph());
57225 if (actionDisabled) return actionDisabled;
57226 var osm = context.connection();
57228 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
57229 return 'too_many_vertices';
57235 operation.tooltip = function () {
57236 var disabled = operation.disabled();
57239 if (disabled === 'restriction') {
57240 return _t('operations.merge.restriction', {
57241 relation: _mainPresetIndex.item('type/restriction').name()
57245 return _t('operations.merge.' + disabled);
57248 return _t('operations.merge.description');
57251 operation.annotation = function () {
57252 return _t('operations.merge.annotation', {
57253 n: selectedIDs.length
57257 operation.id = 'merge';
57258 operation.keys = [_t('operations.merge.key')];
57259 operation.title = _t('operations.merge.title');
57260 operation.behavior = behaviorOperation(context).which(operation);
57264 function operationPaste(context) {
57267 var operation = function operation() {
57268 if (!_pastePoint) return;
57269 var oldIDs = context.copyIDs();
57270 if (!oldIDs.length) return;
57271 var projection = context.projection;
57272 var extent = geoExtent();
57273 var oldGraph = context.copyGraph();
57275 var action = actionCopyEntities(oldIDs, oldGraph);
57276 context.perform(action);
57277 var copies = action.copies();
57278 var originals = new Set();
57279 Object.values(copies).forEach(function (entity) {
57280 originals.add(entity.id);
57283 for (var id in copies) {
57284 var oldEntity = oldGraph.entity(id);
57285 var newEntity = copies[id];
57287 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
57290 var parents = context.graph().parentWays(newEntity);
57291 var parentCopied = parents.some(function (parent) {
57292 return originals.has(parent.id);
57295 if (!parentCopied) {
57296 newIDs.push(newEntity.id);
57298 } // Use the location of the copy operation to offset the paste location,
57299 // or else use the center of the pasted extent
57302 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
57303 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
57305 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
57306 context.enter(modeSelect(context, newIDs));
57309 operation.point = function (val) {
57314 operation.available = function () {
57315 return context.mode().id === 'browse';
57318 operation.disabled = function () {
57319 return !context.copyIDs().length;
57322 operation.tooltip = function () {
57323 var oldGraph = context.copyGraph();
57324 var ids = context.copyIDs();
57327 return _t('operations.paste.nothing_copied');
57330 return _t('operations.paste.description', {
57331 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
57336 operation.annotation = function () {
57337 var ids = context.copyIDs();
57338 return _t('operations.paste.annotation', {
57343 operation.id = 'paste';
57344 operation.keys = [uiCmd('⌘V')];
57345 operation.title = _t('operations.paste.title');
57349 function operationReverse(context, selectedIDs) {
57350 var operation = function operation() {
57351 context.perform(function combinedReverseAction(graph) {
57352 actions().forEach(function (action) {
57353 graph = action(graph);
57356 }, operation.annotation());
57357 context.validator().validate();
57360 function actions(situation) {
57361 return selectedIDs.map(function (entityID) {
57362 var entity = context.hasEntity(entityID);
57363 if (!entity) return null;
57365 if (situation === 'toolbar') {
57366 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
57369 var geometry = entity.geometry(context.graph());
57370 if (entity.type !== 'node' && geometry !== 'line') return null;
57371 var action = actionReverse(entityID);
57372 if (action.disabled(context.graph())) return null;
57374 }).filter(Boolean);
57377 function reverseTypeID() {
57378 var acts = actions();
57379 var nodeActionCount = acts.filter(function (act) {
57380 var entity = context.hasEntity(act.entityID());
57381 return entity && entity.type === 'node';
57383 if (nodeActionCount === 0) return 'line';
57384 if (nodeActionCount === acts.length) return 'point';
57388 operation.available = function (situation) {
57389 return actions(situation).length > 0;
57392 operation.disabled = function () {
57396 operation.tooltip = function () {
57397 return _t('operations.reverse.description.' + reverseTypeID());
57400 operation.annotation = function () {
57401 var acts = actions();
57402 return _t('operations.reverse.annotation.' + reverseTypeID(), {
57407 operation.id = 'reverse';
57408 operation.keys = [_t('operations.reverse.key')];
57409 operation.title = _t('operations.reverse.title');
57410 operation.behavior = behaviorOperation(context).which(operation);
57414 function operationSplit(context, selectedIDs) {
57415 var _vertexIds = selectedIDs.filter(function (id) {
57416 return context.graph().geometry(id) === 'vertex';
57419 var _selectedWayIds = selectedIDs.filter(function (id) {
57420 var entity = context.graph().hasEntity(id);
57421 return entity && entity.type === 'way';
57424 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
57426 var _action = actionSplit(_vertexIds);
57429 var _geometry = 'feature';
57430 var _waysAmount = 'single';
57432 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
57434 if (_isAvailable) {
57435 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
57436 _ways = _action.ways(context.graph());
57437 var geometries = {};
57439 _ways.forEach(function (way) {
57440 geometries[way.geometry(context.graph())] = true;
57443 if (Object.keys(geometries).length === 1) {
57444 _geometry = Object.keys(geometries)[0];
57447 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
57450 var operation = function operation() {
57451 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
57453 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
57454 // filter out relations that may have had member additions
57455 return context.entity(id).type === 'way';
57458 context.enter(modeSelect(context, idsToSelect));
57461 operation.relatedEntityIds = function () {
57462 return _selectedWayIds.length ? [] : _ways.map(function (way) {
57467 operation.available = function () {
57468 return _isAvailable;
57471 operation.disabled = function () {
57472 var reason = _action.disabled(context.graph());
57476 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57477 return 'connected_to_hidden';
57483 operation.tooltip = function () {
57484 var disable = operation.disabled();
57485 if (disable) return _t('operations.split.' + disable);
57486 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
57489 operation.annotation = function () {
57490 return _t('operations.split.annotation.' + _geometry, {
57495 operation.id = 'split';
57496 operation.keys = [_t('operations.split.key')];
57497 operation.title = _t('operations.split.title');
57498 operation.behavior = behaviorOperation(context).which(operation);
57502 function operationStraighten(context, selectedIDs) {
57503 var _wayIDs = selectedIDs.filter(function (id) {
57504 return id.charAt(0) === 'w';
57507 var _nodeIDs = selectedIDs.filter(function (id) {
57508 return id.charAt(0) === 'n';
57511 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
57513 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
57515 var _coords = _nodes.map(function (n) {
57519 var _extent = utilTotalExtent(selectedIDs, context.graph());
57521 var _action = chooseAction();
57525 function chooseAction() {
57526 // straighten selected nodes
57527 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
57528 _geometry = 'point';
57529 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
57530 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
57531 var startNodeIDs = [];
57532 var endNodeIDs = [];
57534 for (var i = 0; i < selectedIDs.length; i++) {
57535 var entity = context.entity(selectedIDs[i]);
57537 if (entity.type === 'node') {
57539 } else if (entity.type !== 'way' || entity.isClosed()) {
57540 return null; // exit early, can't straighten these
57543 startNodeIDs.push(entity.first());
57544 endNodeIDs.push(entity.last());
57545 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
57548 startNodeIDs = startNodeIDs.filter(function (n) {
57549 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
57551 endNodeIDs = endNodeIDs.filter(function (n) {
57552 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
57553 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
57555 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
57557 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
57560 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
57562 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
57564 if (_nodeIDs.length) {
57565 // If we're only straightenting between two points, we only need that extent visible
57566 _extent = utilTotalExtent(_nodeIDs, context.graph());
57569 _geometry = 'line';
57570 return actionStraightenWay(selectedIDs, context.projection);
57576 function operation() {
57577 if (!_action) return;
57578 context.perform(_action, operation.annotation());
57579 window.setTimeout(function () {
57580 context.validator().validate();
57581 }, 300); // after any transition
57584 operation.available = function () {
57585 return Boolean(_action);
57588 operation.disabled = function () {
57589 var reason = _action.disabled(context.graph());
57593 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
57594 return 'too_large';
57595 } else if (someMissing()) {
57596 return 'not_downloaded';
57597 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57598 return 'connected_to_hidden';
57603 function someMissing() {
57604 if (context.inIntro()) return false;
57605 var osm = context.connection();
57608 var missing = _coords.filter(function (loc) {
57609 return !osm.isDataLoaded(loc);
57612 if (missing.length) {
57613 missing.forEach(function (loc) {
57614 context.loadTileAtLoc(loc);
57624 operation.tooltip = function () {
57625 var disable = operation.disabled();
57626 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
57629 operation.annotation = function () {
57630 return _t('operations.straighten.annotation.' + _geometry, {
57631 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
57635 operation.id = 'straighten';
57636 operation.keys = [_t('operations.straighten.key')];
57637 operation.title = _t('operations.straighten.title');
57638 operation.behavior = behaviorOperation(context).which(operation);
57642 var Operations = /*#__PURE__*/Object.freeze({
57644 operationCircularize: operationCircularize,
57645 operationContinue: operationContinue,
57646 operationCopy: operationCopy,
57647 operationDelete: operationDelete,
57648 operationDisconnect: operationDisconnect,
57649 operationDowngrade: operationDowngrade,
57650 operationExtract: operationExtract,
57651 operationMerge: operationMerge,
57652 operationMove: operationMove,
57653 operationOrthogonalize: operationOrthogonalize,
57654 operationPaste: operationPaste,
57655 operationReflectShort: operationReflectShort,
57656 operationReflectLong: operationReflectLong,
57657 operationReverse: operationReverse,
57658 operationRotate: operationRotate,
57659 operationSplit: operationSplit,
57660 operationStraighten: operationStraighten
57663 var _relatedParent;
57665 function modeSelect(context, selectedIDs) {
57670 var keybinding = utilKeybinding('select');
57672 var _breatheBehavior = behaviorBreathe();
57674 var _modeDragNode = modeDragNode(context);
57676 var _selectBehavior;
57678 var _behaviors = [];
57679 var _operations = [];
57680 var _newFeature = false;
57681 var _follow = false;
57683 function singular() {
57684 if (selectedIDs && selectedIDs.length === 1) {
57685 return context.hasEntity(selectedIDs[0]);
57689 function selectedEntities() {
57690 return selectedIDs.map(function (id) {
57691 return context.hasEntity(id);
57692 }).filter(Boolean);
57695 function checkSelectedIDs() {
57698 if (Array.isArray(selectedIDs)) {
57699 ids = selectedIDs.filter(function (id) {
57700 return context.hasEntity(id);
57705 context.enter(modeBrowse(context));
57707 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
57708 // switch between single- and multi-select UI
57709 context.enter(modeSelect(context, ids));
57715 } // find the common parent ways for nextVertex, previousVertex
57718 function commonParents() {
57719 var graph = context.graph();
57720 var commonParents = [];
57722 for (var i = 0; i < selectedIDs.length; i++) {
57723 var entity = context.hasEntity(selectedIDs[i]);
57725 if (!entity || entity.geometry(graph) !== 'vertex') {
57726 return []; // selection includes some not vertices
57729 var currParents = graph.parentWays(entity).map(function (w) {
57733 if (!commonParents.length) {
57734 commonParents = currParents;
57738 commonParents = utilArrayIntersection(commonParents, currParents);
57740 if (!commonParents.length) {
57745 return commonParents;
57748 function singularParent() {
57749 var parents = commonParents();
57751 if (!parents || parents.length === 0) {
57752 _relatedParent = null;
57754 } // relatedParent is used when we visit a vertex with multiple
57755 // parents, and we want to remember which parent line we started on.
57758 if (parents.length === 1) {
57759 _relatedParent = parents[0]; // remember this parent for later
57761 return _relatedParent;
57764 if (parents.indexOf(_relatedParent) !== -1) {
57765 return _relatedParent; // prefer the previously seen parent
57771 mode.selectedIDs = function (val) {
57772 if (!arguments.length) return selectedIDs;
57777 mode.zoomToSelected = function () {
57778 context.map().zoomToEase(selectedEntities());
57781 mode.newFeature = function (val) {
57782 if (!arguments.length) return _newFeature;
57787 mode.selectBehavior = function (val) {
57788 if (!arguments.length) return _selectBehavior;
57789 _selectBehavior = val;
57793 mode.follow = function (val) {
57794 if (!arguments.length) return _follow;
57799 function loadOperations() {
57800 _operations.forEach(function (operation) {
57801 if (operation.behavior) {
57802 context.uninstall(operation.behavior);
57806 _operations = Object.values(Operations).map(function (o) {
57807 return o(context, selectedIDs);
57808 }).filter(function (o) {
57809 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
57810 }).concat([// group copy/downgrade/delete operation together at the end of the list
57811 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
57812 return operation.available();
57815 _operations.forEach(function (operation) {
57816 if (operation.behavior) {
57817 context.install(operation.behavior);
57819 }); // remove any displayed menu
57822 context.ui().closeEditMenu();
57825 mode.operations = function () {
57826 return _operations;
57829 mode.enter = function () {
57830 if (!checkSelectedIDs()) return;
57831 context.features().forceVisible(selectedIDs);
57833 _modeDragNode.restoreSelectedIDs(selectedIDs);
57837 if (!_behaviors.length) {
57838 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
57839 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
57842 _behaviors.forEach(context.install);
57844 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) {
57845 return uiCmd('⇧' + key);
57846 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
57847 return uiCmd('⇧⌥' + key);
57848 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
57849 return uiCmd('⇧' + key);
57850 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
57851 return uiCmd('⇧⌥' + key);
57852 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], nextParent).on('⎋', esc, true);
57853 select(document).call(keybinding);
57854 context.ui().sidebar.select(selectedIDs, _newFeature);
57855 context.history().on('change.select', function () {
57856 loadOperations(); // reselect after change in case relation members were removed or added
57859 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
57860 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
57863 _breatheBehavior.restartIfNeeded(context.surface());
57865 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
57869 var extent = geoExtent();
57870 var graph = context.graph();
57871 selectedIDs.forEach(function (id) {
57872 var entity = context.entity(id);
57874 extent._extend(entity.extent(graph));
57876 var loc = extent.center();
57877 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
57882 function nudgeSelection(delta) {
57883 return function () {
57884 // prevent nudging during low zoom selection
57885 if (!context.map().withinEditableZoom()) return;
57886 var moveOp = operationMove(context, selectedIDs);
57888 if (moveOp.disabled()) {
57889 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
57891 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
57892 context.validator().validate();
57897 function scaleSelection(factor) {
57898 return function () {
57899 // prevent scaling during low zoom selection
57900 if (!context.map().withinEditableZoom()) return;
57901 var nodes = utilGetAllNodes(selectedIDs, context.graph());
57902 var isUp = factor > 1; // can only scale if multiple nodes are selected
57904 if (nodes.length <= 1) return;
57905 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
57906 // object, but we don't want an actual scale operation at this point.
57908 function scalingDisabled() {
57910 return 'too_small';
57911 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
57912 return 'too_large';
57913 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
57914 return 'not_downloaded';
57915 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57916 return 'connected_to_hidden';
57921 function tooSmall() {
57922 if (isUp) return false;
57923 var dLon = Math.abs(extent[1][0] - extent[0][0]);
57924 var dLat = Math.abs(extent[1][1] - extent[0][1]);
57925 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
57928 function someMissing() {
57929 if (context.inIntro()) return false;
57930 var osm = context.connection();
57933 var missing = nodes.filter(function (n) {
57934 return !osm.isDataLoaded(n.loc);
57937 if (missing.length) {
57938 missing.forEach(function (loc) {
57939 context.loadTileAtLoc(loc);
57948 function incompleteRelation(id) {
57949 var entity = context.entity(id);
57950 return entity.type === 'relation' && !entity.isComplete(context.graph());
57954 var disabled = scalingDisabled();
57957 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
57958 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
57960 var pivot = context.projection(extent.center());
57961 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
57962 n: selectedIDs.length
57964 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
57965 context.validator().validate();
57970 function didDoubleUp(d3_event, loc) {
57971 if (!context.map().withinEditableZoom()) return;
57972 var target = select(d3_event.target);
57973 var datum = target.datum();
57974 var entity = datum && datum.properties && datum.properties.entity;
57975 if (!entity) return;
57977 if (entity instanceof osmWay && target.classed('target')) {
57978 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
57979 var prev = entity.nodes[choice.index - 1];
57980 var next = entity.nodes[choice.index];
57981 context.perform(actionAddMidpoint({
57984 }, osmNode()), _t('operations.add.annotation.vertex'));
57985 } else if (entity.type === 'midpoint') {
57986 context.perform(actionAddMidpoint({
57989 }, osmNode()), _t('operations.add.annotation.vertex'));
57993 function selectElements() {
57994 if (!checkSelectedIDs()) return;
57995 var surface = context.surface();
57996 surface.selectAll('.selected-member').classed('selected-member', false);
57997 surface.selectAll('.selected').classed('selected', false);
57998 surface.selectAll('.related').classed('related', false);
58001 if (_relatedParent) {
58002 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
58005 if (context.map().withinEditableZoom()) {
58006 // Apply selection styling if not in wide selection
58007 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
58008 /* skipMultipolgonMembers */
58009 )).classed('selected-member', true);
58010 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
58015 if (context.container().select('.combobox').size()) return;
58016 context.enter(modeBrowse(context));
58019 function firstVertex(d3_event) {
58020 d3_event.preventDefault();
58021 var entity = singular();
58022 var parent = singularParent();
58025 if (entity && entity.type === 'way') {
58027 } else if (parent) {
58028 way = context.entity(parent);
58032 context.enter(modeSelect(context, [way.first()]).follow(true));
58036 function lastVertex(d3_event) {
58037 d3_event.preventDefault();
58038 var entity = singular();
58039 var parent = singularParent();
58042 if (entity && entity.type === 'way') {
58044 } else if (parent) {
58045 way = context.entity(parent);
58049 context.enter(modeSelect(context, [way.last()]).follow(true));
58053 function previousVertex(d3_event) {
58054 d3_event.preventDefault();
58055 var parent = singularParent();
58056 if (!parent) return;
58057 var way = context.entity(parent);
58058 var length = way.nodes.length;
58059 var curr = way.nodes.indexOf(selectedIDs[0]);
58064 } else if (way.isClosed()) {
58065 index = length - 2;
58068 if (index !== -1) {
58069 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
58073 function nextVertex(d3_event) {
58074 d3_event.preventDefault();
58075 var parent = singularParent();
58076 if (!parent) return;
58077 var way = context.entity(parent);
58078 var length = way.nodes.length;
58079 var curr = way.nodes.indexOf(selectedIDs[0]);
58082 if (curr < length - 1) {
58084 } else if (way.isClosed()) {
58088 if (index !== -1) {
58089 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
58093 function nextParent(d3_event) {
58094 d3_event.preventDefault();
58095 var parents = commonParents();
58096 if (!parents || parents.length < 2) return;
58097 var index = parents.indexOf(_relatedParent);
58099 if (index < 0 || index > parents.length - 2) {
58100 _relatedParent = parents[0];
58102 _relatedParent = parents[index + 1];
58105 var surface = context.surface();
58106 surface.selectAll('.related').classed('related', false);
58108 if (_relatedParent) {
58109 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
58114 mode.exit = function () {
58115 _newFeature = false;
58117 _operations.forEach(function (operation) {
58118 if (operation.behavior) {
58119 context.uninstall(operation.behavior);
58125 _behaviors.forEach(context.uninstall);
58127 select(document).call(keybinding.unbind);
58128 context.ui().closeEditMenu();
58129 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
58130 var surface = context.surface();
58131 surface.selectAll('.selected-member').classed('selected-member', false);
58132 surface.selectAll('.selected').classed('selected', false);
58133 surface.selectAll('.highlighted').classed('highlighted', false);
58134 surface.selectAll('.related').classed('related', false);
58135 context.map().on('drawn.select', null);
58136 context.ui().sidebar.hide();
58137 context.features().forceVisible([]);
58138 var entity = singular();
58140 if (_newFeature && entity && entity.type === 'relation' && // no tags
58141 Object.keys(entity.tags).length === 0 && // no parent relations
58142 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
58143 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
58144 // the user added this relation but didn't edit it at all, so just delete it
58145 var deleteAction = actionDeleteRelation(entity.id, true
58146 /* don't delete untagged members */
58148 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
58155 function uiLasso(context) {
58156 var group, polygon;
58157 lasso.coordinates = [];
58159 function lasso(selection) {
58160 context.container().classed('lasso', true);
58161 group = selection.append('g').attr('class', 'lasso hide');
58162 polygon = group.append('path').attr('class', 'lasso-path');
58163 group.call(uiToggle(true));
58168 polygon.data([lasso.coordinates]).attr('d', function (d) {
58169 return 'M' + d.join(' L') + ' Z';
58174 lasso.extent = function () {
58175 return lasso.coordinates.reduce(function (extent, point) {
58176 return extent.extend(geoExtent(point));
58180 lasso.p = function (_) {
58181 if (!arguments.length) return lasso;
58182 lasso.coordinates.push(_);
58187 lasso.close = function () {
58189 group.call(uiToggle(false, function () {
58190 select(this).remove();
58194 context.container().classed('lasso', false);
58200 function behaviorLasso(context) {
58201 // use pointer events on supported platforms; fallback to mouse events
58202 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
58204 var behavior = function behavior(selection) {
58207 function pointerdown(d3_event) {
58208 var button = 0; // left
58210 if (d3_event.button === button && d3_event.shiftKey === true) {
58212 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
58213 d3_event.stopPropagation();
58217 function pointermove() {
58219 lasso = uiLasso(context);
58220 context.surface().call(lasso);
58223 lasso.p(context.map().mouse());
58226 function normalize(a, b) {
58227 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])]];
58230 function lassoed() {
58231 if (!lasso) return [];
58232 var graph = context.graph();
58235 if (context.map().editableDataEnabled(true
58236 /* skipZoomCheck */
58237 ) && context.map().isInWideSelection()) {
58238 // only select from the visible nodes
58239 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
58240 } else if (!context.map().editableDataEnabled()) {
58244 var bounds = lasso.extent().map(context.projection.invert);
58245 var extent = geoExtent(normalize(bounds[0], bounds[1]));
58246 var intersects = context.history().intersects(extent).filter(function (entity) {
58247 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
58248 }); // sort the lassoed nodes as best we can
58250 intersects.sort(function (node1, node2) {
58251 var parents1 = graph.parentWays(node1);
58252 var parents2 = graph.parentWays(node2);
58254 if (parents1.length && parents2.length) {
58255 // both nodes are vertices
58256 var sharedParents = utilArrayIntersection(parents1, parents2);
58258 if (sharedParents.length) {
58259 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
58261 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
58263 // vertices do not share a way; group them by their respective parent ways
58264 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
58266 } else if (parents1.length || parents2.length) {
58267 // only one node is a vertex; sort standalone points before vertices
58268 return parents1.length - parents2.length;
58269 } // both nodes are standalone points; sort left to right
58272 return node1.loc[0] - node2.loc[0];
58274 return intersects.map(function (entity) {
58279 function pointerup() {
58280 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
58281 if (!lasso) return;
58282 var ids = lassoed();
58286 context.enter(modeSelect(context, ids));
58290 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
58293 behavior.off = function (selection) {
58294 selection.on(_pointerPrefix + 'down.lasso', null);
58300 function modeBrowse(context) {
58304 title: _t('modes.browse.title'),
58305 description: _t('modes.browse.description')
58309 var _selectBehavior;
58311 var _behaviors = [];
58313 mode.selectBehavior = function (val) {
58314 if (!arguments.length) return _selectBehavior;
58315 _selectBehavior = val;
58319 mode.enter = function () {
58320 if (!_behaviors.length) {
58321 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
58322 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
58325 _behaviors.forEach(context.install); // Get focus on the body.
58328 if (document.activeElement && document.activeElement.blur) {
58329 document.activeElement.blur();
58333 context.ui().sidebar.show(sidebar);
58335 context.ui().sidebar.select(null);
58339 mode.exit = function () {
58340 context.ui().sidebar.hover.cancel();
58342 _behaviors.forEach(context.uninstall);
58345 context.ui().sidebar.hide();
58349 mode.sidebar = function (_) {
58350 if (!arguments.length) return sidebar;
58355 mode.operations = function () {
58356 return [operationPaste(context)];
58362 function behaviorAddWay(context) {
58363 var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
58364 var draw = behaviorDraw(context);
58366 function behavior(surface) {
58367 draw.on('click', function () {
58368 dispatch$1.apply('start', this, arguments);
58369 }).on('clickWay', function () {
58370 dispatch$1.apply('startFromWay', this, arguments);
58371 }).on('clickNode', function () {
58372 dispatch$1.apply('startFromNode', this, arguments);
58373 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
58374 context.map().dblclickZoomEnable(false);
58375 surface.call(draw);
58378 behavior.off = function (surface) {
58379 surface.call(draw.off);
58382 behavior.cancel = function () {
58383 window.setTimeout(function () {
58384 context.map().dblclickZoomEnable(true);
58386 context.enter(modeBrowse(context));
58389 return utilRebind(behavior, dispatch$1, 'on');
58392 function behaviorHash(context) {
58393 // cached window.location.hash
58394 var _cachedHash = null; // allowable latitude range
58396 var _latitudeLimit = 90 - 1e-8;
58398 function computedHashParameters() {
58399 var map = context.map();
58400 var center = map.center();
58401 var zoom = map.zoom();
58402 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
58403 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
58404 var newParams = {};
58405 delete oldParams.id;
58406 var selected = context.selectedIDs().filter(function (id) {
58407 return context.hasEntity(id);
58410 if (selected.length) {
58411 newParams.id = selected.join(',');
58414 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
58415 return Object.assign(oldParams, newParams);
58418 function computedHash() {
58419 return '#' + utilQsString(computedHashParameters(), true);
58422 function computedTitle(includeChangeCount) {
58423 var baseTitle = context.documentTitleBase() || 'iD';
58427 var selected = context.selectedIDs().filter(function (id) {
58428 return context.hasEntity(id);
58431 if (selected.length) {
58432 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
58434 if (selected.length > 1) {
58435 contextual = _t('title.labeled_and_more', {
58436 labeled: firstLabel,
58437 count: selected.length - 1
58440 contextual = firstLabel;
58443 titleID = 'context';
58446 if (includeChangeCount) {
58447 changeCount = context.history().difference().summary().length;
58449 if (changeCount > 0) {
58450 titleID = contextual ? 'changes_context' : 'changes';
58455 return _t('title.format.' + titleID, {
58456 changes: changeCount,
58458 context: contextual
58465 function updateTitle(includeChangeCount) {
58466 if (!context.setsDocumentTitle()) return;
58467 var newTitle = computedTitle(includeChangeCount);
58469 if (document.title !== newTitle) {
58470 document.title = newTitle;
58474 function updateHashIfNeeded() {
58475 if (context.inIntro()) return;
58476 var latestHash = computedHash();
58478 if (_cachedHash !== latestHash) {
58479 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
58480 // though unavoidably creating a browser history entry
58482 window.history.replaceState(null, computedTitle(false
58483 /* includeChangeCount */
58484 ), latestHash); // set the title we want displayed for the browser tab/window
58487 /* includeChangeCount */
58492 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
58494 var _throttledUpdateTitle = throttle(function () {
58496 /* includeChangeCount */
58500 function hashchange() {
58501 // ignore spurious hashchange events
58502 if (window.location.hash === _cachedHash) return;
58503 _cachedHash = window.location.hash;
58504 var q = utilStringQs(_cachedHash);
58505 var mapArgs = (q.map || '').split('/').map(Number);
58507 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
58508 // replace bogus hash
58509 updateHashIfNeeded();
58511 // don't update if the new hash already reflects the state of iD
58512 if (_cachedHash === computedHash()) return;
58513 var mode = context.mode();
58514 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
58516 if (q.id && mode) {
58517 var ids = q.id.split(',').filter(function (id) {
58518 return context.hasEntity(id);
58521 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
58522 context.enter(modeSelect(context, ids));
58527 var center = context.map().center();
58528 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
58529 var maxdist = 500; // Don't allow the hash location to change too much while drawing
58530 // This can happen if the user accidentally hit the back button. #3996
58532 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
58533 context.enter(modeBrowse(context));
58539 function behavior() {
58540 context.map().on('move.behaviorHash', _throttledUpdate);
58541 context.history().on('change.behaviorHash', _throttledUpdateTitle);
58542 context.on('enter.behaviorHash', _throttledUpdate);
58543 select(window).on('hashchange.behaviorHash', hashchange);
58545 if (window.location.hash) {
58546 var q = utilStringQs(window.location.hash);
58549 //if (!context.history().hasRestorableChanges()) {
58550 // targeting specific features: download, select, and zoom to them
58551 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
58554 if (q.walkthrough === 'true') {
58555 behavior.startWalkthrough = true;
58559 behavior.hadHash = true;
58563 updateTitle(false);
58567 behavior.off = function () {
58568 _throttledUpdate.cancel();
58570 _throttledUpdateTitle.cancel();
58572 context.map().on('move.behaviorHash', null);
58573 context.on('enter.behaviorHash', null);
58574 select(window).on('hashchange.behaviorHash', null);
58575 window.location.hash = '';
58582 iD.coreDifference represents the difference between two graphs.
58583 It knows how to calculate the set of entities that were
58584 created, modified, or deleted, and also contains the logic
58585 for recursively extending a difference to the complete set
58586 of entities that will require a redraw, taking into account
58587 child and parent relationships.
58590 function coreDifference(base, head) {
58592 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
58596 function checkEntityID(id) {
58597 var h = head.entities[id];
58598 var b = base.entities[id];
58599 if (h === b) return;
58600 if (_changes[id]) return;
58607 _didChange.deletion = true;
58616 _didChange.addition = true;
58621 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
58626 _didChange.geometry = true;
58627 _didChange.properties = true;
58631 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
58636 _didChange.geometry = true;
58639 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
58644 _didChange.geometry = true;
58647 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
58652 _didChange.properties = true;
58658 // HOT CODE: there can be many thousands of downloaded entities, so looping
58659 // through them all can become a performance bottleneck. Optimize by
58660 // resolving duplicates and using a basic `for` loop
58661 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
58663 for (var i = 0; i < ids.length; i++) {
58664 checkEntityID(ids[i]);
58670 _diff.length = function length() {
58671 return Object.keys(_changes).length;
58674 _diff.changes = function changes() {
58678 _diff.didChange = _didChange; // pass true to include affected relation members
58680 _diff.extantIDs = function extantIDs(includeRelMembers) {
58681 var result = new Set();
58682 Object.keys(_changes).forEach(function (id) {
58683 if (_changes[id].head) {
58687 var h = _changes[id].head;
58688 var b = _changes[id].base;
58689 var entity = h || b;
58691 if (includeRelMembers && entity.type === 'relation') {
58692 var mh = h ? h.members.map(function (m) {
58695 var mb = b ? b.members.map(function (m) {
58698 utilArrayUnion(mh, mb).forEach(function (memberID) {
58699 if (head.hasEntity(memberID)) {
58700 result.add(memberID);
58705 return Array.from(result);
58708 _diff.modified = function modified() {
58710 Object.values(_changes).forEach(function (change) {
58711 if (change.base && change.head) {
58712 result.push(change.head);
58718 _diff.created = function created() {
58720 Object.values(_changes).forEach(function (change) {
58721 if (!change.base && change.head) {
58722 result.push(change.head);
58728 _diff.deleted = function deleted() {
58730 Object.values(_changes).forEach(function (change) {
58731 if (change.base && !change.head) {
58732 result.push(change.base);
58738 _diff.summary = function summary() {
58740 var keys = Object.keys(_changes);
58742 for (var i = 0; i < keys.length; i++) {
58743 var change = _changes[keys[i]];
58745 if (change.head && change.head.geometry(head) !== 'vertex') {
58746 addEntity(change.head, head, change.base ? 'modified' : 'created');
58747 } else if (change.base && change.base.geometry(base) !== 'vertex') {
58748 addEntity(change.base, base, 'deleted');
58749 } else if (change.base && change.head) {
58751 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
58752 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
58755 addParents(change.head);
58758 if (retagged || moved && change.head.hasInterestingTags()) {
58759 addEntity(change.head, head, 'modified');
58761 } else if (change.head && change.head.hasInterestingTags()) {
58763 addEntity(change.head, head, 'created');
58764 } else if (change.base && change.base.hasInterestingTags()) {
58766 addEntity(change.base, base, 'deleted');
58770 return Object.values(relevant);
58772 function addEntity(entity, graph, changeType) {
58773 relevant[entity.id] = {
58776 changeType: changeType
58780 function addParents(entity) {
58781 var parents = head.parentWays(entity);
58783 for (var j = parents.length - 1; j >= 0; j--) {
58784 var parent = parents[j];
58786 if (!(parent.id in relevant)) {
58787 addEntity(parent, head, 'modified');
58791 }; // returns complete set of entities that require a redraw
58792 // (optionally within given `extent`)
58795 _diff.complete = function complete(extent) {
58799 for (id in _changes) {
58800 change = _changes[id];
58801 var h = change.head;
58802 var b = change.base;
58803 var entity = h || b;
58805 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) continue;
58808 if (entity.type === 'way') {
58809 var nh = h ? h.nodes : [];
58810 var nb = b ? b.nodes : [];
58812 diff = utilArrayDifference(nh, nb);
58814 for (i = 0; i < diff.length; i++) {
58815 result[diff[i]] = head.hasEntity(diff[i]);
58818 diff = utilArrayDifference(nb, nh);
58820 for (i = 0; i < diff.length; i++) {
58821 result[diff[i]] = head.hasEntity(diff[i]);
58825 if (entity.type === 'relation' && entity.isMultipolygon()) {
58826 var mh = h ? h.members.map(function (m) {
58829 var mb = b ? b.members.map(function (m) {
58832 var ids = utilArrayUnion(mh, mb);
58834 for (i = 0; i < ids.length; i++) {
58835 var member = head.hasEntity(ids[i]);
58836 if (!member) continue; // not downloaded
58838 if (extent && !member.intersects(extent, head)) continue; // not visible
58840 result[ids[i]] = member;
58844 addParents(head.parentWays(entity), result);
58845 addParents(head.parentRelations(entity), result);
58850 function addParents(parents, result) {
58851 for (var i = 0; i < parents.length; i++) {
58852 var parent = parents[i];
58853 if (parent.id in result) continue;
58854 result[parent.id] = parent;
58855 addParents(head.parentRelations(parent), result);
58863 function coreTree(head) {
58864 // tree for entities
58865 var _rtree = new RBush();
58867 var _bboxes = {}; // maintain a separate tree for granular way segments
58869 var _segmentsRTree = new RBush();
58871 var _segmentsBBoxes = {};
58872 var _segmentsByWayId = {};
58875 function entityBBox(entity) {
58876 var bbox = entity.extent(head).bbox();
58877 bbox.id = entity.id;
58878 _bboxes[entity.id] = bbox;
58882 function segmentBBox(segment) {
58883 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
58885 if (!extent) return null;
58886 var bbox = extent.bbox();
58887 bbox.segment = segment;
58888 _segmentsBBoxes[segment.id] = bbox;
58892 function removeEntity(entity) {
58893 _rtree.remove(_bboxes[entity.id]);
58895 delete _bboxes[entity.id];
58897 if (_segmentsByWayId[entity.id]) {
58898 _segmentsByWayId[entity.id].forEach(function (segment) {
58899 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
58901 delete _segmentsBBoxes[segment.id];
58904 delete _segmentsByWayId[entity.id];
58908 function loadEntities(entities) {
58909 _rtree.load(entities.map(entityBBox));
58912 entities.forEach(function (entity) {
58913 if (entity.segments) {
58914 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
58916 _segmentsByWayId[entity.id] = entitySegments;
58917 segments = segments.concat(entitySegments);
58920 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
58923 function updateParents(entity, insertions, memo) {
58924 head.parentWays(entity).forEach(function (way) {
58925 if (_bboxes[way.id]) {
58927 insertions[way.id] = way;
58930 updateParents(way, insertions, memo);
58932 head.parentRelations(entity).forEach(function (relation) {
58933 if (memo[entity.id]) return;
58934 memo[entity.id] = true;
58936 if (_bboxes[relation.id]) {
58937 removeEntity(relation);
58938 insertions[relation.id] = relation;
58941 updateParents(relation, insertions, memo);
58945 tree.rebase = function (entities, force) {
58946 var insertions = {};
58948 for (var i = 0; i < entities.length; i++) {
58949 var entity = entities[i];
58950 if (!entity.visible) continue;
58952 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
58955 } else if (_bboxes[entity.id]) {
58956 removeEntity(entity);
58960 insertions[entity.id] = entity;
58961 updateParents(entity, insertions, {});
58964 loadEntities(Object.values(insertions));
58968 function updateToGraph(graph) {
58969 if (graph === head) return;
58970 var diff = coreDifference(head, graph);
58972 var changed = diff.didChange;
58973 if (!changed.addition && !changed.deletion && !changed.geometry) return;
58974 var insertions = {};
58976 if (changed.deletion) {
58977 diff.deleted().forEach(function (entity) {
58978 removeEntity(entity);
58982 if (changed.geometry) {
58983 diff.modified().forEach(function (entity) {
58984 removeEntity(entity);
58985 insertions[entity.id] = entity;
58986 updateParents(entity, insertions, {});
58990 if (changed.addition) {
58991 diff.created().forEach(function (entity) {
58992 insertions[entity.id] = entity;
58996 loadEntities(Object.values(insertions));
58997 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
59000 tree.intersects = function (extent, graph) {
59001 updateToGraph(graph);
59002 return _rtree.search(extent.bbox()).map(function (bbox) {
59003 return graph.entity(bbox.id);
59005 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
59008 tree.waySegments = function (extent, graph) {
59009 updateToGraph(graph);
59010 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
59011 return bbox.segment;
59018 function uiModal(selection, blocking) {
59021 var keybinding = utilKeybinding('modal');
59022 var previous = selection.select('div.modal');
59023 var animate = previous.empty();
59024 previous.transition().duration(200).style('opacity', 0).remove();
59025 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
59027 shaded.close = function () {
59028 shaded.transition().duration(200).style('opacity', 0).remove();
59029 modal.transition().duration(200).style('top', '0px');
59030 select(document).call(keybinding.unbind);
59033 var modal = shaded.append('div').attr('class', 'modal fillL');
59034 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
59037 shaded.on('click.remove-modal', function (d3_event) {
59038 if (d3_event.target === _this) {
59042 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
59043 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
59044 select(document).call(keybinding);
59047 modal.append('div').attr('class', 'content');
59048 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
59051 shaded.transition().style('opacity', 1);
59053 shaded.style('opacity', 1);
59058 function moveFocusToFirst() {
59059 var node = modal // there are additional rules about what's focusable, but this suits our purposes
59060 .select('a, button, input:not(.keytrap), select, textarea').node();
59065 select(this).node().blur();
59069 function moveFocusToLast() {
59070 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
59072 if (nodes.length) {
59073 nodes[nodes.length - 1].focus();
59075 select(this).node().blur();
59080 function uiLoading(context) {
59081 var _modalSelection = select(null);
59084 var _blocking = false;
59086 var loading = function loading(selection) {
59087 _modalSelection = uiModal(selection, _blocking);
59089 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
59091 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
59092 loadertext.append('h3').html(_message);
59094 _modalSelection.select('button.close').attr('class', 'hide');
59099 loading.message = function (val) {
59100 if (!arguments.length) return _message;
59105 loading.blocking = function (val) {
59106 if (!arguments.length) return _blocking;
59111 loading.close = function () {
59112 _modalSelection.remove();
59115 loading.isShown = function () {
59116 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
59122 function coreHistory(context) {
59123 var dispatch$1 = dispatch('reset', 'change', 'merge', 'restore', 'undone', 'redone');
59125 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
59128 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
59130 var duration = 150;
59131 var _imageryUsed = [];
59132 var _photoOverlaysUsed = [];
59133 var _checkpoints = {};
59141 var _tree; // internal _act, accepts list of actions and eased time
59144 function _act(actions, t) {
59145 actions = Array.prototype.slice.call(actions);
59148 if (typeof actions[actions.length - 1] !== 'function') {
59149 annotation = actions.pop();
59152 var graph = _stack[_index].graph;
59154 for (var i = 0; i < actions.length; i++) {
59155 graph = actions[i](graph, t);
59160 annotation: annotation,
59161 imageryUsed: _imageryUsed,
59162 photoOverlaysUsed: _photoOverlaysUsed,
59163 transform: context.projection.transform(),
59164 selectedIDs: context.selectedIDs()
59166 } // internal _perform with eased time
59169 function _perform(args, t) {
59170 var previous = _stack[_index].graph;
59171 _stack = _stack.slice(0, _index + 1);
59173 var actionResult = _act(args, t);
59175 _stack.push(actionResult);
59178 return change(previous);
59179 } // internal _replace with eased time
59182 function _replace(args, t) {
59183 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
59185 var actionResult = _act(args, t);
59187 _stack[_index] = actionResult;
59188 return change(previous);
59189 } // internal _overwrite with eased time
59192 function _overwrite(args, t) {
59193 var previous = _stack[_index].graph;
59201 _stack = _stack.slice(0, _index + 1);
59203 var actionResult = _act(args, t);
59205 _stack.push(actionResult);
59208 return change(previous);
59209 } // determine difference and dispatch a change event
59212 function change(previous) {
59213 var difference = coreDifference(previous, history.graph());
59215 if (!_pausedGraph) {
59216 dispatch$1.call('change', this, difference);
59220 } // iD uses namespaced keys so multiple installations do not conflict
59223 function getKey(n) {
59224 return 'iD_' + window.location.origin + '_' + n;
59228 graph: function graph() {
59229 return _stack[_index].graph;
59231 tree: function tree() {
59234 base: function base() {
59235 return _stack[0].graph;
59237 merge: function merge(entities
59240 var stack = _stack.map(function (state) {
59241 return state.graph;
59244 _stack[0].graph.rebase(entities, stack, false);
59246 _tree.rebase(entities, false);
59248 dispatch$1.call('merge', this, entities);
59250 perform: function perform() {
59251 // complete any transition already in progress
59252 select(document).interrupt('history.perform');
59253 var transitionable = false;
59254 var action0 = arguments[0];
59256 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
59257 transitionable = !!action0.transitionable;
59260 if (transitionable) {
59261 var origArguments = arguments;
59262 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
59263 return function (t) {
59264 if (t < 1) _overwrite([action0], t);
59266 }).on('start', function () {
59267 _perform([action0], 0);
59268 }).on('end interrupt', function () {
59269 _overwrite(origArguments, 1);
59272 return _perform(arguments);
59275 replace: function replace() {
59276 select(document).interrupt('history.perform');
59277 return _replace(arguments, 1);
59279 // Same as calling pop and then perform
59280 overwrite: function overwrite() {
59281 select(document).interrupt('history.perform');
59282 return _overwrite(arguments, 1);
59284 pop: function pop(n) {
59285 select(document).interrupt('history.perform');
59286 var previous = _stack[_index].graph;
59288 if (isNaN(+n) || +n < 0) {
59292 while (n-- > 0 && _index > 0) {
59298 return change(previous);
59300 // Back to the previous annotated state or _index = 0.
59301 undo: function undo() {
59302 select(document).interrupt('history.perform');
59303 var previousStack = _stack[_index];
59304 var previous = previousStack.graph;
59306 while (_index > 0) {
59308 if (_stack[_index].annotation) break;
59311 dispatch$1.call('undone', this, _stack[_index], previousStack);
59312 return change(previous);
59314 // Forward to the next annotated state.
59315 redo: function redo() {
59316 select(document).interrupt('history.perform');
59317 var previousStack = _stack[_index];
59318 var previous = previousStack.graph;
59319 var tryIndex = _index;
59321 while (tryIndex < _stack.length - 1) {
59324 if (_stack[tryIndex].annotation) {
59326 dispatch$1.call('redone', this, _stack[_index], previousStack);
59331 return change(previous);
59333 pauseChangeDispatch: function pauseChangeDispatch() {
59334 if (!_pausedGraph) {
59335 _pausedGraph = _stack[_index].graph;
59338 resumeChangeDispatch: function resumeChangeDispatch() {
59339 if (_pausedGraph) {
59340 var previous = _pausedGraph;
59341 _pausedGraph = null;
59342 return change(previous);
59345 undoAnnotation: function undoAnnotation() {
59349 if (_stack[i].annotation) return _stack[i].annotation;
59353 redoAnnotation: function redoAnnotation() {
59354 var i = _index + 1;
59356 while (i <= _stack.length - 1) {
59357 if (_stack[i].annotation) return _stack[i].annotation;
59361 // Returns the entities from the active graph with bounding boxes
59362 // overlapping the given `extent`.
59363 intersects: function intersects(extent) {
59364 return _tree.intersects(extent, _stack[_index].graph);
59366 difference: function difference() {
59367 var base = _stack[0].graph;
59368 var head = _stack[_index].graph;
59369 return coreDifference(base, head);
59371 changes: function changes(action) {
59372 var base = _stack[0].graph;
59373 var head = _stack[_index].graph;
59376 head = action(head);
59379 var difference = coreDifference(base, head);
59381 modified: difference.modified(),
59382 created: difference.created(),
59383 deleted: difference.deleted()
59386 hasChanges: function hasChanges() {
59387 return this.difference().length() > 0;
59389 imageryUsed: function imageryUsed(sources) {
59391 _imageryUsed = sources;
59396 _stack.slice(1, _index + 1).forEach(function (state) {
59397 state.imageryUsed.forEach(function (source) {
59398 if (source !== 'Custom') {
59404 return Array.from(s);
59407 photoOverlaysUsed: function photoOverlaysUsed(sources) {
59409 _photoOverlaysUsed = sources;
59414 _stack.slice(1, _index + 1).forEach(function (state) {
59415 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
59416 state.photoOverlaysUsed.forEach(function (photoOverlay) {
59417 s.add(photoOverlay);
59422 return Array.from(s);
59425 // save the current history state
59426 checkpoint: function checkpoint(key) {
59427 _checkpoints[key] = {
59433 // restore history state to a given checkpoint or reset completely
59434 reset: function reset(key) {
59435 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
59436 _stack = _checkpoints[key].stack;
59437 _index = _checkpoints[key].index;
59443 _tree = coreTree(_stack[0].graph);
59447 dispatch$1.call('reset');
59448 dispatch$1.call('change');
59451 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
59454 // 1. Start the walkthrough.
59455 // 2. Get to a "free editing" tutorial step
59456 // 3. Make your edits to the walkthrough map
59457 // 4. In your browser dev console run:
59458 // `id.history().toIntroGraph()`
59459 // 5. This outputs stringified JSON to the browser console
59460 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
59461 toIntroGraph: function toIntroGraph() {
59468 var graph = this.graph();
59469 var baseEntities = {}; // clone base entities..
59471 Object.values(graph.base().entities).forEach(function (entity) {
59472 var copy = copyIntroEntity(entity);
59473 baseEntities[copy.id] = copy;
59474 }); // replace base entities with head entities..
59476 Object.keys(graph.entities).forEach(function (id) {
59477 var entity = graph.entities[id];
59480 var copy = copyIntroEntity(entity);
59481 baseEntities[copy.id] = copy;
59483 delete baseEntities[id];
59485 }); // swap temporary for permanent ids..
59487 Object.values(baseEntities).forEach(function (entity) {
59488 if (Array.isArray(entity.nodes)) {
59489 entity.nodes = entity.nodes.map(function (node) {
59490 return permIDs[node] || node;
59494 if (Array.isArray(entity.members)) {
59495 entity.members = entity.members.map(function (member) {
59496 member.id = permIDs[member.id] || member.id;
59501 return JSON.stringify({
59502 dataIntroGraph: baseEntities
59505 function copyIntroEntity(source) {
59506 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
59508 if (copy.tags && !Object.keys(copy.tags)) {
59512 if (Array.isArray(copy.loc)) {
59513 copy.loc[0] = +copy.loc[0].toFixed(6);
59514 copy.loc[1] = +copy.loc[1].toFixed(6);
59517 var match = source.id.match(/([nrw])-\d*/); // temporary id
59519 if (match !== null) {
59520 var nrw = match[1];
59524 permID = nrw + ++nextID[nrw];
59525 } while (baseEntities.hasOwnProperty(permID));
59527 copy.id = permIDs[source.id] = permID;
59533 toJSON: function toJSON() {
59534 if (!this.hasChanges()) return;
59535 var allEntities = {};
59536 var baseEntities = {};
59537 var base = _stack[0];
59539 var s = _stack.map(function (i) {
59542 Object.keys(i.graph.entities).forEach(function (id) {
59543 var entity = i.graph.entities[id];
59546 var key = osmEntity.key(entity);
59547 allEntities[key] = entity;
59548 modified.push(key);
59551 } // make sure that the originals of changed or deleted entities get merged
59552 // into the base of the _stack after restoring the data from JSON.
59555 if (id in base.graph.entities) {
59556 baseEntities[id] = base.graph.entities[id];
59559 if (entity && entity.nodes) {
59560 // get originals of pre-existing child nodes
59561 entity.nodes.forEach(function (nodeID) {
59562 if (nodeID in base.graph.entities) {
59563 baseEntities[nodeID] = base.graph.entities[nodeID];
59566 } // get originals of parent entities too
59569 var baseParents = base.graph._parentWays[id];
59572 baseParents.forEach(function (parentID) {
59573 if (parentID in base.graph.entities) {
59574 baseEntities[parentID] = base.graph.entities[parentID];
59580 if (modified.length) x.modified = modified;
59581 if (deleted.length) x.deleted = deleted;
59582 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
59583 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
59584 if (i.annotation) x.annotation = i.annotation;
59585 if (i.transform) x.transform = i.transform;
59586 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
59590 return JSON.stringify({
59592 entities: Object.values(allEntities),
59593 baseEntities: Object.values(baseEntities),
59595 nextIDs: osmEntity.id.next,
59597 // note the time the changes were saved
59598 timestamp: new Date().getTime()
59601 fromJSON: function fromJSON(json, loadChildNodes) {
59602 var h = JSON.parse(json);
59603 var loadComplete = true;
59604 osmEntity.id.next = h.nextIDs;
59607 if (h.version === 2 || h.version === 3) {
59608 var allEntities = {};
59609 h.entities.forEach(function (entity) {
59610 allEntities[osmEntity.key(entity)] = osmEntity(entity);
59613 if (h.version === 3) {
59614 // This merges originals for changed entities into the base of
59615 // the _stack even if the current _stack doesn't have them (for
59616 // example when iD has been restarted in a different region)
59617 var baseEntities = h.baseEntities.map(function (d) {
59618 return osmEntity(d);
59621 var stack = _stack.map(function (state) {
59622 return state.graph;
59625 _stack[0].graph.rebase(baseEntities, stack, true);
59627 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
59628 // childnodes that would normally have been downloaded with it.. #2142
59631 if (loadChildNodes) {
59632 var osm = context.connection();
59633 var baseWays = baseEntities.filter(function (e) {
59634 return e.type === 'way';
59636 var nodeIDs = baseWays.reduce(function (acc, way) {
59637 return utilArrayUnion(acc, way.nodes);
59639 var missing = nodeIDs.filter(function (n) {
59640 return !_stack[0].graph.hasEntity(n);
59643 if (missing.length && osm) {
59644 loadComplete = false;
59645 context.map().redrawEnable(false);
59646 var loading = uiLoading(context).blocking(true);
59647 context.container().call(loading);
59649 var childNodesLoaded = function childNodesLoaded(err, result) {
59651 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
59652 var visibles = visibleGroups["true"] || []; // alive nodes
59654 var invisibles = visibleGroups["false"] || []; // deleted nodes
59656 if (visibles.length) {
59657 var visibleIDs = visibles.map(function (entity) {
59661 var stack = _stack.map(function (state) {
59662 return state.graph;
59665 missing = utilArrayDifference(missing, visibleIDs);
59667 _stack[0].graph.rebase(visibles, stack, true);
59669 _tree.rebase(visibles, true);
59670 } // fetch older versions of nodes that were deleted..
59673 invisibles.forEach(function (entity) {
59674 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
59678 if (err || !missing.length) {
59680 context.map().redrawEnable(true);
59681 dispatch$1.call('change');
59682 dispatch$1.call('restore', this);
59686 osm.loadMultiple(missing, childNodesLoaded);
59691 _stack = h.stack.map(function (d) {
59696 d.modified.forEach(function (key) {
59697 entity = allEntities[key];
59698 entities[entity.id] = entity;
59703 d.deleted.forEach(function (id) {
59704 entities[id] = undefined;
59709 graph: coreGraph(_stack[0].graph).load(entities),
59710 annotation: d.annotation,
59711 imageryUsed: d.imageryUsed,
59712 photoOverlaysUsed: d.photoOverlaysUsed,
59713 transform: d.transform,
59714 selectedIDs: d.selectedIDs
59718 // original version
59719 _stack = h.stack.map(function (d) {
59722 for (var i in d.entities) {
59723 var entity = d.entities[i];
59724 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
59727 d.graph = coreGraph(_stack[0].graph).load(entities);
59732 var transform = _stack[_index].transform;
59735 context.map().transformEase(transform, 0); // 0 = immediate, no easing
59738 if (loadComplete) {
59739 dispatch$1.call('change');
59740 dispatch$1.call('restore', this);
59745 lock: function lock() {
59746 return _lock.lock();
59748 unlock: function unlock() {
59751 save: function save() {
59752 if (_lock.locked() && // don't overwrite existing, unresolved changes
59753 !_hasUnresolvedRestorableChanges) {
59754 corePreferences(getKey('saved_history'), history.toJSON() || null);
59759 // delete the history version saved in localStorage
59760 clearSaved: function clearSaved() {
59761 context.debouncedSave.cancel();
59763 if (_lock.locked()) {
59764 _hasUnresolvedRestorableChanges = false;
59765 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
59767 corePreferences('comment', null);
59768 corePreferences('hashtags', null);
59769 corePreferences('source', null);
59774 savedHistoryJSON: function savedHistoryJSON() {
59775 return corePreferences(getKey('saved_history'));
59777 hasRestorableChanges: function hasRestorableChanges() {
59778 return _hasUnresolvedRestorableChanges;
59780 // load history from a version stored in localStorage
59781 restore: function restore() {
59782 if (_lock.locked()) {
59783 _hasUnresolvedRestorableChanges = false;
59784 var json = this.savedHistoryJSON();
59785 if (json) history.fromJSON(json, true);
59791 return utilRebind(history, dispatch$1, 'on');
59795 * Look for roads that can be connected to other roads with a short extension
59798 function validationAlmostJunction(context) {
59799 var type = 'almost_junction';
59800 var EXTEND_TH_METERS = 5;
59801 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
59803 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
59805 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
59807 function isHighway(entity) {
59808 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
59811 function isTaggedAsNotContinuing(node) {
59812 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
59815 var validation = function checkAlmostJunction(entity, graph) {
59816 if (!isHighway(entity)) return [];
59817 if (entity.isDegenerate()) return [];
59818 var tree = context.history().tree();
59819 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
59821 extendableNodeInfos.forEach(function (extendableNodeInfo) {
59822 issues.push(new validationIssue({
59824 subtype: 'highway-highway',
59825 severity: 'warning',
59826 message: function message(context) {
59827 var entity1 = context.hasEntity(this.entityIds[0]);
59829 if (this.entityIds[0] === this.entityIds[2]) {
59830 return entity1 ? _t.html('issues.almost_junction.self.message', {
59831 feature: utilDisplayLabel(entity1, context.graph())
59834 var entity2 = context.hasEntity(this.entityIds[2]);
59835 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
59836 feature: utilDisplayLabel(entity1, context.graph()),
59837 feature2: utilDisplayLabel(entity2, context.graph())
59841 reference: showReference,
59842 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
59843 loc: extendableNodeInfo.node.loc,
59844 hash: JSON.stringify(extendableNodeInfo.node.loc),
59846 midId: extendableNodeInfo.mid.id,
59847 edge: extendableNodeInfo.edge,
59848 cross_loc: extendableNodeInfo.cross_loc
59850 dynamicFixes: makeFixes
59855 function makeFixes(context) {
59856 var fixes = [new validationIssueFix({
59857 icon: 'iD-icon-abutment',
59858 title: _t.html('issues.fix.connect_features.title'),
59859 onClick: function onClick(context) {
59860 var annotation = _t('issues.fix.connect_almost_junction.annotation');
59862 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
59863 endNodeId = _this$issue$entityIds[1],
59864 crossWayId = _this$issue$entityIds[2];
59866 var midNode = context.entity(this.issue.data.midId);
59867 var endNode = context.entity(endNodeId);
59868 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
59870 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
59872 if (nearEndNodes.length > 0) {
59873 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
59876 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
59881 var targetEdge = this.issue.data.edge;
59882 var crossLoc = this.issue.data.cross_loc;
59883 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
59884 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
59886 if (closestNodeInfo.distance < WELD_TH_METERS) {
59887 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
59889 context.perform(actionAddMidpoint({
59892 }, endNode), annotation);
59896 var node = context.hasEntity(this.entityIds[1]);
59898 if (node && !node.hasInterestingTags()) {
59899 // node has no descriptive tags, suggest noexit fix
59900 fixes.push(new validationIssueFix({
59901 icon: 'maki-barrier',
59902 title: _t.html('issues.fix.tag_as_disconnected.title'),
59903 onClick: function onClick(context) {
59904 var nodeID = this.issue.entityIds[1];
59905 var tags = Object.assign({}, context.entity(nodeID).tags);
59906 tags.noexit = 'yes';
59907 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
59915 function showReference(selection) {
59916 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
59919 function isExtendableCandidate(node, way) {
59920 // can not accurately test vertices on tiles not downloaded from osm - #5938
59921 var osm = services.osm;
59923 if (osm && !osm.isDataLoaded(node.loc)) {
59927 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
59931 var occurrences = 0;
59933 for (var index in way.nodes) {
59934 if (way.nodes[index] === node.id) {
59937 if (occurrences > 1) {
59946 function findConnectableEndNodesByExtension(way) {
59948 if (way.isClosed()) return results;
59950 var indices = [0, way.nodes.length - 1];
59951 indices.forEach(function (nodeIndex) {
59952 var nodeID = way.nodes[nodeIndex];
59953 var node = graph.entity(nodeID);
59954 if (!isExtendableCandidate(node, way)) return;
59955 var connectionInfo = canConnectByExtend(way, nodeIndex);
59956 if (!connectionInfo) return;
59957 testNodes = graph.childNodes(way).slice(); // shallow copy
59959 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
59961 if (geoHasSelfIntersections(testNodes, nodeID)) return;
59962 results.push(connectionInfo);
59967 function findNearbyEndNodes(node, way) {
59968 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
59969 return graph.entity(d);
59970 }).filter(function (d) {
59971 // Node cannot be near to itself, but other endnode of same way could be
59972 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
59976 function findSmallJoinAngle(midNode, tipNode, endNodes) {
59977 // Both nodes could be close, so want to join whichever is closest to collinear
59979 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
59981 endNodes.forEach(function (endNode) {
59982 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
59983 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
59984 var diff = Math.max(a1, a2) - Math.min(a1, a2);
59986 if (diff < minAngle) {
59991 /* Threshold set by considering right angle triangle
59992 based on node joining threshold and extension distance */
59994 if (minAngle <= SIG_ANGLE_TH) return joinTo;
59998 function hasTag(tags, key) {
59999 return tags[key] !== undefined && tags[key] !== 'no';
60002 function canConnectWays(way, way2) {
60003 // allow self-connections
60004 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
60006 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
60007 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
60009 var layer1 = way.tags.layer || '0',
60010 layer2 = way2.tags.layer || '0';
60011 if (layer1 !== layer2) return false;
60012 var level1 = way.tags.level || '0',
60013 level2 = way2.tags.level || '0';
60014 if (level1 !== level2) return false;
60018 function canConnectByExtend(way, endNodeIdx) {
60019 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
60021 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
60023 var tipNode = graph.entity(tipNid);
60024 var midNode = graph.entity(midNid);
60025 var lon = tipNode.loc[0];
60026 var lat = tipNode.loc[1];
60027 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
60028 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
60029 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
60031 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
60032 var t = EXTEND_TH_METERS / edgeLen + 1.0;
60033 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
60035 var segmentInfos = tree.waySegments(queryExtent, graph);
60037 for (var i = 0; i < segmentInfos.length; i++) {
60038 var segmentInfo = segmentInfos[i];
60039 var way2 = graph.entity(segmentInfo.wayId);
60040 if (!isHighway(way2)) continue;
60041 if (!canConnectWays(way, way2)) continue;
60042 var nAid = segmentInfo.nodes[0],
60043 nBid = segmentInfo.nodes[1];
60044 if (nAid === tipNid || nBid === tipNid) continue;
60045 var nA = graph.entity(nAid),
60046 nB = graph.entity(nBid);
60047 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
60054 edge: [nA.id, nB.id],
60055 cross_loc: crossLoc
60064 validation.type = type;
60068 function validationCloseNodes(context) {
60069 var type = 'close_nodes';
60070 var pointThresholdMeters = 0.2;
60072 var validation = function validation(entity, graph) {
60073 if (entity.type === 'node') {
60074 return getIssuesForNode(entity);
60075 } else if (entity.type === 'way') {
60076 return getIssuesForWay(entity);
60081 function getIssuesForNode(node) {
60082 var parentWays = graph.parentWays(node);
60084 if (parentWays.length) {
60085 return getIssuesForVertex(node, parentWays);
60087 return getIssuesForDetachedPoint(node);
60091 function wayTypeFor(way) {
60092 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
60093 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
60094 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
60095 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
60096 var parentRelations = graph.parentRelations(way);
60098 for (var i in parentRelations) {
60099 var relation = parentRelations[i];
60100 if (relation.tags.type === 'boundary') return 'boundary';
60102 if (relation.isMultipolygon()) {
60103 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
60104 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
60111 function shouldCheckWay(way) {
60112 // don't flag issues where merging would create degenerate ways
60113 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
60114 var bbox = way.extent(graph).bbox();
60115 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
60117 if (hypotenuseMeters < 1.5) return false;
60121 function getIssuesForWay(way) {
60122 if (!shouldCheckWay(way)) return [];
60124 nodes = graph.childNodes(way);
60126 for (var i = 0; i < nodes.length - 1; i++) {
60127 var node1 = nodes[i];
60128 var node2 = nodes[i + 1];
60129 var issue = getWayIssueIfAny(node1, node2, way);
60130 if (issue) issues.push(issue);
60136 function getIssuesForVertex(node, parentWays) {
60139 function checkForCloseness(node1, node2, way) {
60140 var issue = getWayIssueIfAny(node1, node2, way);
60141 if (issue) issues.push(issue);
60144 for (var i = 0; i < parentWays.length; i++) {
60145 var parentWay = parentWays[i];
60146 if (!shouldCheckWay(parentWay)) continue;
60147 var lastIndex = parentWay.nodes.length - 1;
60149 for (var j = 0; j < parentWay.nodes.length; j++) {
60151 if (parentWay.nodes[j - 1] === node.id) {
60152 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
60156 if (j !== lastIndex) {
60157 if (parentWay.nodes[j + 1] === node.id) {
60158 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
60167 function thresholdMetersForWay(way) {
60168 if (!shouldCheckWay(way)) return 0;
60169 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
60171 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
60173 if (wayType === 'indoor') return 0.01;
60174 if (wayType === 'building') return 0.05;
60175 if (wayType === 'path') return 0.1;
60179 function getIssuesForDetachedPoint(node) {
60181 var lon = node.loc[0];
60182 var lat = node.loc[1];
60183 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
60184 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
60185 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
60186 var intersected = context.history().tree().intersects(queryExtent, graph);
60188 for (var j = 0; j < intersected.length; j++) {
60189 var nearby = intersected[j];
60190 if (nearby.id === node.id) continue;
60191 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
60193 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
60194 // allow very close points if tags indicate the z-axis might vary
60198 'addr:housenumber': true,
60201 var zAxisDifferentiates = false;
60203 for (var key in zAxisKeys) {
60204 var nodeValue = node.tags[key] || '0';
60205 var nearbyValue = nearby.tags[key] || '0';
60207 if (nodeValue !== nearbyValue) {
60208 zAxisDifferentiates = true;
60213 if (zAxisDifferentiates) continue;
60214 issues.push(new validationIssue({
60216 subtype: 'detached',
60217 severity: 'warning',
60218 message: function message(context) {
60219 var entity = context.hasEntity(this.entityIds[0]),
60220 entity2 = context.hasEntity(this.entityIds[1]);
60221 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
60222 feature: utilDisplayLabel(entity, context.graph()),
60223 feature2: utilDisplayLabel(entity2, context.graph())
60226 reference: showReference,
60227 entityIds: [node.id, nearby.id],
60228 dynamicFixes: function dynamicFixes() {
60229 return [new validationIssueFix({
60230 icon: 'iD-operation-disconnect',
60231 title: _t.html('issues.fix.move_points_apart.title')
60232 }), new validationIssueFix({
60233 icon: 'iD-icon-layers',
60234 title: _t.html('issues.fix.use_different_layers_or_levels.title')
60243 function showReference(selection) {
60244 var referenceText = _t('issues.close_nodes.detached.reference');
60245 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60249 function getWayIssueIfAny(node1, node2, way) {
60250 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
60254 if (node1.loc !== node2.loc) {
60255 var parentWays1 = graph.parentWays(node1);
60256 var parentWays2 = new Set(graph.parentWays(node2));
60257 var sharedWays = parentWays1.filter(function (parentWay) {
60258 return parentWays2.has(parentWay);
60260 var thresholds = sharedWays.map(function (parentWay) {
60261 return thresholdMetersForWay(parentWay);
60263 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
60264 var distance = geoSphericalDistance(node1.loc, node2.loc);
60265 if (distance > threshold) return null;
60268 return new validationIssue({
60270 subtype: 'vertices',
60271 severity: 'warning',
60272 message: function message(context) {
60273 var entity = context.hasEntity(this.entityIds[0]);
60274 return entity ? _t.html('issues.close_nodes.message', {
60275 way: utilDisplayLabel(entity, context.graph())
60278 reference: showReference,
60279 entityIds: [way.id, node1.id, node2.id],
60281 dynamicFixes: function dynamicFixes() {
60282 return [new validationIssueFix({
60283 icon: 'iD-icon-plus',
60284 title: _t.html('issues.fix.merge_points.title'),
60285 onClick: function onClick(context) {
60286 var entityIds = this.issue.entityIds;
60287 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
60288 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
60290 }), new validationIssueFix({
60291 icon: 'iD-operation-disconnect',
60292 title: _t.html('issues.fix.move_points_apart.title')
60297 function showReference(selection) {
60298 var referenceText = _t('issues.close_nodes.reference');
60299 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60304 validation.type = type;
60308 function validationCrossingWays(context) {
60309 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
60311 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
60312 if (getFeatureType(way, graph) === null) {
60313 // if the way doesn't match a feature type, check its parent relations
60314 var parentRels = graph.parentRelations(way);
60316 for (var i = 0; i < parentRels.length; i++) {
60317 var rel = parentRels[i];
60319 if (getFeatureType(rel, graph) !== null) {
60328 function hasTag(tags, key) {
60329 return tags[key] !== undefined && tags[key] !== 'no';
60332 function taggedAsIndoor(tags) {
60333 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
60336 function allowsBridge(featureType) {
60337 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60340 function allowsTunnel(featureType) {
60341 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60345 var ignoredBuildings = {
60352 function getFeatureType(entity, graph) {
60353 var geometry = entity.geometry(graph);
60354 if (geometry !== 'line' && geometry !== 'area') return null;
60355 var tags = entity.tags;
60356 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
60357 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
60359 if (geometry !== 'line') return null;
60360 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
60361 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
60365 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
60366 // assume 0 by default
60367 var level1 = tags1.level || '0';
60368 var level2 = tags2.level || '0';
60370 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
60371 // assume features don't interact if they're indoor on different levels
60373 } // assume 0 by default; don't use way.layer() since we account for structures here
60376 var layer1 = tags1.layer || '0';
60377 var layer2 = tags2.layer || '0';
60379 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
60380 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
60381 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
60383 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
60384 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
60386 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
60387 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
60388 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
60390 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
60391 } 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
60394 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
60395 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
60397 if (featureType1 === 'building' || featureType2 === 'building') {
60398 // for building crossings, different layers are enough
60399 if (layer1 !== layer2) return true;
60403 } // highway values for which we shouldn't recommend connecting to waterways
60406 var highwaysDisallowingFords = {
60408 motorway_link: true,
60412 primary_link: true,
60414 secondary_link: true
60416 var nonCrossingHighways = {
60420 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
60421 var featureType1 = getFeatureType(entity1, graph);
60422 var featureType2 = getFeatureType(entity2, graph);
60423 var geometry1 = entity1.geometry(graph);
60424 var geometry2 = entity2.geometry(graph);
60425 var bothLines = geometry1 === 'line' && geometry2 === 'line';
60427 if (featureType1 === featureType2) {
60428 if (featureType1 === 'highway') {
60429 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
60430 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
60432 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
60433 // one feature is a path but not both
60434 var roadFeature = entity1IsPath ? entity2 : entity1;
60436 if (nonCrossingHighways[roadFeature.tags.highway]) {
60437 // don't mark path connections with certain roads as crossings
60441 var pathFeature = entity1IsPath ? entity1 : entity2;
60443 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
60444 // if the path is a crossing, match the crossing type
60445 return bothLines ? {
60446 highway: 'crossing',
60447 crossing: pathFeature.tags.crossing
60449 } // don't add a `crossing` subtag to ambiguous crossings
60452 return bothLines ? {
60453 highway: 'crossing'
60460 if (featureType1 === 'waterway') return {};
60461 if (featureType1 === 'railway') return {};
60463 var featureTypes = [featureType1, featureType2];
60465 if (featureTypes.indexOf('highway') !== -1) {
60466 if (featureTypes.indexOf('railway') !== -1) {
60467 if (!bothLines) return {};
60468 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
60470 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
60471 // path-tram connections use this tag
60472 if (isTram) return {
60473 railway: 'tram_crossing'
60474 }; // other path-rail connections use this tag
60477 railway: 'crossing'
60480 // path-tram connections use this tag
60481 if (isTram) return {
60482 railway: 'tram_level_crossing'
60483 }; // other road-rail connections use this tag
60486 railway: 'level_crossing'
60491 if (featureTypes.indexOf('waterway') !== -1) {
60492 // do not allow fords on structures
60493 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
60494 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
60496 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
60497 // do not allow fords on major highways
60501 return bothLines ? {
60511 function findCrossingsByWay(way1, graph, tree) {
60512 var edgeCrossInfos = [];
60513 if (way1.type !== 'way') return edgeCrossInfos;
60514 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
60515 var way1FeatureType = getFeatureType(taggedFeature1, graph);
60516 if (way1FeatureType === null) return edgeCrossInfos;
60517 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
60521 var n1, n2, nA, nB, nAId, nBId;
60522 var segment1, segment2;
60524 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
60525 var way1Nodes = graph.childNodes(way1);
60526 var comparedWays = {};
60528 for (i = 0; i < way1Nodes.length - 1; i++) {
60530 n2 = way1Nodes[i + 1];
60531 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
60532 // of overlapping ways
60534 segmentInfos = tree.waySegments(extent, graph);
60536 for (j = 0; j < segmentInfos.length; j++) {
60537 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
60539 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
60541 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
60543 comparedWays[segment2Info.wayId] = true;
60544 way2 = graph.hasEntity(segment2Info.wayId);
60545 if (!way2) continue;
60546 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
60548 way2FeatureType = getFeatureType(taggedFeature2, graph);
60550 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
60552 } // create only one issue for building crossings
60555 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
60556 nAId = segment2Info.nodes[0];
60557 nBId = segment2Info.nodes[1];
60559 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
60560 // n1 or n2 is a connection node; skip
60564 nA = graph.hasEntity(nAId);
60566 nB = graph.hasEntity(nBId);
60568 segment1 = [n1.loc, n2.loc];
60569 segment2 = [nA.loc, nB.loc];
60570 var point = geoLineIntersection(segment1, segment2);
60573 edgeCrossInfos.push({
60576 featureType: way1FeatureType,
60577 edge: [n1.id, n2.id]
60580 featureType: way2FeatureType,
60581 edge: [nA.id, nB.id]
60587 checkedSingleCrossingWays[way2.id] = true;
60594 return edgeCrossInfos;
60597 function waysToCheck(entity, graph) {
60598 var featureType = getFeatureType(entity, graph);
60599 if (!featureType) return [];
60601 if (entity.type === 'way') {
60603 } else if (entity.type === 'relation') {
60604 return entity.members.reduce(function (array, member) {
60605 if (member.type === 'way' && ( // only look at geometry ways
60606 !member.role || member.role === 'outer' || member.role === 'inner')) {
60607 var entity = graph.hasEntity(member.id); // don't add duplicates
60609 if (entity && array.indexOf(entity) === -1) {
60610 array.push(entity);
60621 var validation = function checkCrossingWays(entity, graph) {
60622 var tree = context.history().tree();
60623 var ways = waysToCheck(entity, graph);
60624 var issues = []; // declare these here to reduce garbage collection
60626 var wayIndex, crossingIndex, crossings;
60628 for (wayIndex in ways) {
60629 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
60631 for (crossingIndex in crossings) {
60632 issues.push(createIssue(crossings[crossingIndex], graph));
60639 function createIssue(crossing, graph) {
60640 // use the entities with the tags that define the feature type
60641 crossing.wayInfos.sort(function (way1Info, way2Info) {
60642 var type1 = way1Info.featureType;
60643 var type2 = way2Info.featureType;
60645 if (type1 === type2) {
60646 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
60647 } else if (type1 === 'waterway') {
60649 } else if (type2 === 'waterway') {
60653 return type1 < type2;
60655 var entities = crossing.wayInfos.map(function (wayInfo) {
60656 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
60658 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
60659 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
60660 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
60661 var featureType1 = crossing.wayInfos[0].featureType;
60662 var featureType2 = crossing.wayInfos[1].featureType;
60663 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
60664 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
60665 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
60666 var subtype = [featureType1, featureType2].sort().join('-');
60667 var crossingTypeID = subtype;
60669 if (isCrossingIndoors) {
60670 crossingTypeID = 'indoor-indoor';
60671 } else if (isCrossingTunnels) {
60672 crossingTypeID = 'tunnel-tunnel';
60673 } else if (isCrossingBridges) {
60674 crossingTypeID = 'bridge-bridge';
60677 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
60678 crossingTypeID += '_connectable';
60681 return new validationIssue({
60684 severity: 'warning',
60685 message: function message(context) {
60686 var graph = context.graph();
60687 var entity1 = graph.hasEntity(this.entityIds[0]),
60688 entity2 = graph.hasEntity(this.entityIds[1]);
60689 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
60690 feature: utilDisplayLabel(entity1, graph),
60691 feature2: utilDisplayLabel(entity2, graph)
60694 reference: showReference,
60695 entityIds: entities.map(function (entity) {
60700 featureTypes: featureTypes,
60701 connectionTags: connectionTags
60703 // differentiate based on the loc since two ways can cross multiple times
60704 hash: crossing.crossPoint.toString() + // if the edges change then so does the fix
60705 edges.slice().sort(function (edge1, edge2) {
60706 // order to assure hash is deterministic
60707 return edge1[0] < edge2[0] ? -1 : 1;
60708 }).toString() + // ensure the correct connection tags are added in the fix
60709 JSON.stringify(connectionTags),
60710 loc: crossing.crossPoint,
60711 dynamicFixes: function dynamicFixes(context) {
60712 var mode = context.mode();
60713 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
60714 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
60715 var selectedFeatureType = this.data.featureTypes[selectedIndex];
60716 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
60719 if (connectionTags) {
60720 fixes.push(makeConnectWaysFix(this.data.connectionTags));
60723 if (isCrossingIndoors) {
60724 fixes.push(new validationIssueFix({
60725 icon: 'iD-icon-layers',
60726 title: _t.html('issues.fix.use_different_levels.title')
60728 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
60729 fixes.push(makeChangeLayerFix('higher'));
60730 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
60731 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
60732 // don't recommend adding bridges to waterways since they're uncommon
60733 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
60734 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
60735 } // don't recommend adding tunnels under waterways since they're uncommon
60738 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
60740 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
60741 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
60743 } // repositioning the features is always an option
60746 fixes.push(new validationIssueFix({
60747 icon: 'iD-operation-move',
60748 title: _t.html('issues.fix.reposition_features.title')
60754 function showReference(selection) {
60755 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
60759 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
60760 return new validationIssueFix({
60762 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60763 onClick: function onClick(context) {
60764 var mode = context.mode();
60765 if (!mode || mode.id !== 'select') return;
60766 var selectedIDs = mode.selectedIDs();
60767 if (selectedIDs.length !== 1) return;
60768 var selectedWayID = selectedIDs[0];
60769 if (!context.hasEntity(selectedWayID)) return;
60770 var resultWayIDs = [selectedWayID];
60771 var edge, crossedEdge, crossedWayID;
60773 if (this.issue.entityIds[0] === selectedWayID) {
60774 edge = this.issue.data.edges[0];
60775 crossedEdge = this.issue.data.edges[1];
60776 crossedWayID = this.issue.entityIds[1];
60778 edge = this.issue.data.edges[1];
60779 crossedEdge = this.issue.data.edges[0];
60780 crossedWayID = this.issue.entityIds[0];
60783 var crossingLoc = this.issue.loc;
60784 var projection = context.projection;
60786 var action = function actionAddStructure(graph) {
60787 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60788 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
60790 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
60792 if (!structLengthMeters) {
60793 // if no explicit width is set, approximate the width based on the tags
60794 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
60797 if (structLengthMeters) {
60798 if (getFeatureType(crossedWay, graph) === 'railway') {
60799 // bridges over railways are generally much longer than the rail bed itself, compensate
60800 structLengthMeters *= 2;
60803 // should ideally never land here since all rail/water/road tags should have an implied width
60804 structLengthMeters = 8;
60807 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
60808 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
60809 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
60810 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
60812 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
60814 structLengthMeters += 4; // clamp the length to a reasonable range
60816 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
60818 function geomToProj(geoPoint) {
60819 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
60822 function projToGeom(projPoint) {
60823 var lat = geoMetersToLat(projPoint[1]);
60824 return [geoMetersToLon(projPoint[0], lat), lat];
60827 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
60828 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
60829 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
60830 var projectedCrossingLoc = geomToProj(crossingLoc);
60831 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
60833 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
60834 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
60835 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
60838 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
60839 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
60842 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
60843 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
60844 }; // avoid creating very short edges from splitting too close to another node
60847 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
60849 function determineEndpoint(edge, endNode, locGetter) {
60851 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
60852 // the maximum length of this side of the structure
60854 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
60856 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
60857 // the edge is long enough to insert a new node
60858 // the loc that would result in the full expected length
60859 var idealNodeLoc = locGetter(idealLengthMeters);
60860 newNode = osmNode();
60861 graph = actionAddMidpoint({
60864 }, newNode)(graph);
60867 endNode.parentIntersectionWays(graph).forEach(function (way) {
60868 way.nodes.forEach(function (nodeID) {
60869 if (nodeID === endNode.id) {
60870 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
60879 if (edgeCount >= 3) {
60880 // the end node is a junction, try to leave a segment
60881 // between it and the structure - #7202
60882 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
60884 if (insetLength > minEdgeLengthMeters) {
60885 var insetNodeLoc = locGetter(insetLength);
60886 newNode = osmNode();
60887 graph = actionAddMidpoint({
60890 }, newNode)(graph);
60893 } // if the edge is too short to subdivide as desired, then
60894 // just bound the structure at the existing end node
60897 if (!newNode) newNode = endNode;
60898 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
60901 graph = splitAction(graph);
60903 if (splitAction.getCreatedWayIDs().length) {
60904 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
60910 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
60911 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
60912 var structureWay = resultWayIDs.map(function (id) {
60913 return graph.entity(id);
60914 }).find(function (way) {
60915 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
60917 var tags = Object.assign({}, structureWay.tags); // copy tags
60919 if (bridgeOrTunnel === 'bridge') {
60920 tags.bridge = 'yes';
60923 var tunnelValue = 'yes';
60925 if (getFeatureType(structureWay, graph) === 'waterway') {
60926 // use `tunnel=culvert` for waterways by default
60927 tunnelValue = 'culvert';
60930 tags.tunnel = tunnelValue;
60932 } // apply the structure tags to the way
60935 graph = actionChangeTags(structureWay.id, tags)(graph);
60939 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
60940 context.enter(modeSelect(context, resultWayIDs));
60945 function makeConnectWaysFix(connectionTags) {
60946 var fixTitleID = 'connect_features';
60948 if (connectionTags.ford) {
60949 fixTitleID = 'connect_using_ford';
60952 return new validationIssueFix({
60953 icon: 'iD-icon-crossing',
60954 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60955 onClick: function onClick(context) {
60956 var loc = this.issue.loc;
60957 var connectionTags = this.issue.data.connectionTags;
60958 var edges = this.issue.data.edges;
60959 context.perform(function actionConnectCrossingWays(graph) {
60960 // create the new node for the points
60961 var node = osmNode({
60963 tags: connectionTags
60965 graph = graph.replace(node);
60966 var nodesToMerge = [node.id];
60967 var mergeThresholdInMeters = 0.75;
60968 edges.forEach(function (edge) {
60969 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60970 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc); // if there is already a point nearby, use that
60972 if (closestNodeInfo.distance < mergeThresholdInMeters) {
60973 nodesToMerge.push(closestNodeInfo.node.id); // else add the new node to the way
60975 graph = actionAddMidpoint({
60982 if (nodesToMerge.length > 1) {
60983 // if we're using nearby nodes, merge them with the new node
60984 graph = actionMergeNodes(nodesToMerge, loc)(graph);
60988 }, _t('issues.fix.connect_crossing_features.annotation'));
60993 function makeChangeLayerFix(higherOrLower) {
60994 return new validationIssueFix({
60995 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
60996 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
60997 onClick: function onClick(context) {
60998 var mode = context.mode();
60999 if (!mode || mode.id !== 'select') return;
61000 var selectedIDs = mode.selectedIDs();
61001 if (selectedIDs.length !== 1) return;
61002 var selectedID = selectedIDs[0];
61003 if (!this.issue.entityIds.some(function (entityId) {
61004 return entityId === selectedID;
61006 var entity = context.hasEntity(selectedID);
61007 if (!entity) return;
61008 var tags = Object.assign({}, entity.tags); // shallow copy
61010 var layer = tags.layer && Number(tags.layer);
61012 if (layer && !isNaN(layer)) {
61013 if (higherOrLower === 'higher') {
61019 if (higherOrLower === 'higher') {
61026 tags.layer = layer.toString();
61027 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
61032 validation.type = type;
61036 function validationDisconnectedWay() {
61037 var type = 'disconnected_way';
61039 function isTaggedAsHighway(entity) {
61040 return osmRoutableHighwayTagValues[entity.tags.highway];
61043 var validation = function checkDisconnectedWay(entity, graph) {
61044 var routingIslandWays = routingIslandForEntity(entity);
61045 if (!routingIslandWays) return [];
61046 return [new validationIssue({
61048 subtype: 'highway',
61049 severity: 'warning',
61050 message: function message(context) {
61051 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
61052 var label = entity && utilDisplayLabel(entity, context.graph());
61053 return _t.html('issues.disconnected_way.routable.message', {
61054 count: this.entityIds.length,
61058 reference: showReference,
61059 entityIds: Array.from(routingIslandWays).map(function (way) {
61062 dynamicFixes: makeFixes
61065 function makeFixes(context) {
61067 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
61069 if (singleEntity) {
61070 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
61071 var textDirection = _mainLocalizer.textDirection();
61072 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
61073 if (startFix) fixes.push(startFix);
61074 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
61075 if (endFix) fixes.push(endFix);
61078 if (!fixes.length) {
61079 fixes.push(new validationIssueFix({
61080 title: _t.html('issues.fix.connect_feature.title')
61084 fixes.push(new validationIssueFix({
61085 icon: 'iD-operation-delete',
61086 title: _t.html('issues.fix.delete_feature.title'),
61087 entityIds: [singleEntity.id],
61088 onClick: function onClick(context) {
61089 var id = this.issue.entityIds[0];
61090 var operation = operationDelete(context, [id]);
61092 if (!operation.disabled()) {
61098 fixes.push(new validationIssueFix({
61099 title: _t.html('issues.fix.connect_features.title')
61106 function showReference(selection) {
61107 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
61110 function routingIslandForEntity(entity) {
61111 var routingIsland = new Set(); // the interconnected routable features
61113 var waysToCheck = []; // the queue of remaining routable ways to traverse
61115 function queueParentWays(node) {
61116 graph.parentWays(node).forEach(function (parentWay) {
61117 if (!routingIsland.has(parentWay) && // only check each feature once
61118 isRoutableWay(parentWay, false)) {
61119 // only check routable features
61120 routingIsland.add(parentWay);
61121 waysToCheck.push(parentWay);
61126 if (entity.type === 'way' && isRoutableWay(entity, true)) {
61127 routingIsland.add(entity);
61128 waysToCheck.push(entity);
61129 } else if (entity.type === 'node' && isRoutableNode(entity)) {
61130 routingIsland.add(entity);
61131 queueParentWays(entity);
61133 // this feature isn't routable, cannot be a routing island
61137 while (waysToCheck.length) {
61138 var wayToCheck = waysToCheck.pop();
61139 var childNodes = graph.childNodes(wayToCheck);
61141 for (var i in childNodes) {
61142 var vertex = childNodes[i];
61144 if (isConnectedVertex(vertex)) {
61145 // found a link to the wider network, not a routing island
61149 if (isRoutableNode(vertex)) {
61150 routingIsland.add(vertex);
61153 queueParentWays(vertex);
61155 } // no network link found, this is a routing island, return its members
61158 return routingIsland;
61161 function isConnectedVertex(vertex) {
61162 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
61163 var osm = services.osm;
61164 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
61166 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
61167 if (vertex.tags.amenity === 'parking_entrance') return true;
61171 function isRoutableNode(node) {
61172 // treat elevators as distinct features in the highway network
61173 if (node.tags.highway === 'elevator') return true;
61177 function isRoutableWay(way, ignoreInnerWays) {
61178 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
61179 return graph.parentRelations(way).some(function (parentRelation) {
61180 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
61181 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
61186 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
61187 var vertex = graph.hasEntity(vertexID);
61188 if (!vertex || vertex.tags.noexit === 'yes') return null;
61189 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
61190 return new validationIssueFix({
61191 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61192 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
61193 entityIds: [vertexID],
61194 onClick: function onClick(context) {
61195 var wayId = this.issue.entityIds[0];
61196 var way = context.hasEntity(wayId);
61197 var vertexId = this.entityIds[0];
61198 var vertex = context.hasEntity(vertexId);
61199 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
61201 var map = context.map();
61203 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61204 map.zoomToEase(vertex);
61207 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
61213 validation.type = type;
61217 function validationFormatting() {
61218 var type = 'invalid_format';
61220 var validation = function validation(entity) {
61223 function isValidEmail(email) {
61224 // Emails in OSM are going to be official so they should be pretty simple
61225 // Using negated lists to better support all possible unicode characters (#6494)
61226 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
61228 return !email || valid_email.test(email);
61231 function isSchemePresent(url) {
61232 var valid_scheme = /^https?:\/\//i;
61233 return (!url || valid_scheme.test(url));
61238 function showReferenceEmail(selection) {
61239 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
61242 function showReferenceWebsite(selection) {
61243 selection.selectAll('.issue-reference')
61247 .attr('class', 'issue-reference')
61248 .html(t.html('issues.invalid_format.website.reference'));
61250 if (entity.tags.website) {
61251 // Multiple websites are possible
61252 // If ever we support ES6, arrow functions make this nicer
61253 var websites = entity.tags.website
61255 .map(function(s) { return s.trim(); })
61256 .filter(function(x) { return !isSchemePresent(x); });
61257 if (websites.length) {
61258 issues.push(new validationIssue({
61260 subtype: 'website',
61261 severity: 'warning',
61262 message: function(context) {
61263 var entity = context.hasEntity(this.entityIds[0]);
61264 return entity ? t.html('issues.invalid_format.website.message' + this.data,
61265 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
61267 reference: showReferenceWebsite,
61268 entityIds: [entity.id],
61269 hash: websites.join(),
61270 data: (websites.length > 1) ? '_multi' : ''
61277 if (entity.tags.email) {
61278 // Multiple emails are possible
61279 var emails = entity.tags.email.split(';').map(function (s) {
61281 }).filter(function (x) {
61282 return !isValidEmail(x);
61285 if (emails.length) {
61286 issues.push(new validationIssue({
61289 severity: 'warning',
61290 message: function message(context) {
61291 var entity = context.hasEntity(this.entityIds[0]);
61292 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
61293 feature: utilDisplayLabel(entity, context.graph()),
61294 email: emails.join(', ')
61297 reference: showReferenceEmail,
61298 entityIds: [entity.id],
61299 hash: emails.join(),
61300 data: emails.length > 1 ? '_multi' : ''
61308 validation.type = type;
61312 function validationHelpRequest(context) {
61313 var type = 'help_request';
61315 var validation = function checkFixmeTag(entity) {
61316 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
61318 if (entity.version === undefined) return [];
61320 if (entity.v !== undefined) {
61321 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
61323 if (!baseEntity || !baseEntity.tags.fixme) return [];
61326 return [new validationIssue({
61328 subtype: 'fixme_tag',
61329 severity: 'warning',
61330 message: function message(context) {
61331 var entity = context.hasEntity(this.entityIds[0]);
61332 return entity ? _t.html('issues.fixme_tag.message', {
61333 feature: utilDisplayLabel(entity, context.graph())
61336 dynamicFixes: function dynamicFixes() {
61337 return [new validationIssueFix({
61338 title: _t.html('issues.fix.address_the_concern.title')
61341 reference: showReference,
61342 entityIds: [entity.id]
61345 function showReference(selection) {
61346 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
61350 validation.type = type;
61354 function validationImpossibleOneway() {
61355 var type = 'impossible_oneway';
61357 var validation = function checkImpossibleOneway(entity, graph) {
61358 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
61359 if (entity.isClosed()) return [];
61360 if (!typeForWay(entity)) return [];
61361 if (!isOneway(entity)) return [];
61362 var firstIssues = issuesForNode(entity, entity.first());
61363 var lastIssues = issuesForNode(entity, entity.last());
61364 return firstIssues.concat(lastIssues);
61366 function typeForWay(way) {
61367 if (way.geometry(graph) !== 'line') return null;
61368 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
61369 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
61373 function isOneway(way) {
61374 if (way.tags.oneway === 'yes') return true;
61375 if (way.tags.oneway) return false;
61377 for (var key in way.tags) {
61378 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
61386 function nodeOccursMoreThanOnce(way, nodeID) {
61387 var occurrences = 0;
61389 for (var index in way.nodes) {
61390 if (way.nodes[index] === nodeID) {
61392 if (occurrences > 1) return true;
61399 function isConnectedViaOtherTypes(way, node) {
61400 var wayType = typeForWay(way);
61402 if (wayType === 'highway') {
61403 // entrances are considered connected
61404 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
61405 if (node.tags.amenity === 'parking_entrance') return true;
61406 } else if (wayType === 'waterway') {
61407 if (node.id === way.first()) {
61408 // multiple waterways may start at the same spring
61409 if (node.tags.natural === 'spring') return true;
61411 // multiple waterways may end at the same drain
61412 if (node.tags.manhole === 'drain') return true;
61416 return graph.parentWays(node).some(function (parentWay) {
61417 if (parentWay.id === way.id) return false;
61419 if (wayType === 'highway') {
61420 // allow connections to highway areas
61421 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
61423 if (parentWay.tags.route === 'ferry') return true;
61424 return graph.parentRelations(parentWay).some(function (parentRelation) {
61425 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
61427 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
61429 } else if (wayType === 'waterway') {
61430 // multiple waterways may start or end at a water body at the same node
61431 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
61438 function issuesForNode(way, nodeID) {
61439 var isFirst = nodeID === way.first();
61440 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
61442 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
61443 var osm = services.osm;
61444 if (!osm) return [];
61445 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
61447 if (!node || !osm.isDataLoaded(node.loc)) return [];
61448 if (isConnectedViaOtherTypes(way, node)) return [];
61449 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
61450 if (parentWay.id === way.id) return false;
61451 return typeForWay(parentWay) === wayType;
61452 }); // assume it's okay for waterways to start or end disconnected for now
61454 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
61455 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
61456 return isOneway(attachedWay);
61457 }); // ignore if the way is connected to some non-oneway features
61459 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
61461 if (attachedOneways.length) {
61462 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
61463 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
61464 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
61467 if (connectedEndpointsOkay) return [];
61470 var placement = isFirst ? 'start' : 'end',
61471 messageID = wayType + '.',
61472 referenceID = wayType + '.';
61474 if (wayType === 'waterway') {
61475 messageID += 'connected.' + placement;
61476 referenceID += 'connected';
61478 messageID += placement;
61479 referenceID += placement;
61482 return [new validationIssue({
61485 severity: 'warning',
61486 message: function message(context) {
61487 var entity = context.hasEntity(this.entityIds[0]);
61488 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
61489 feature: utilDisplayLabel(entity, context.graph())
61492 reference: getReference(referenceID),
61493 entityIds: [way.id, node.id],
61494 dynamicFixes: function dynamicFixes() {
61497 if (attachedOneways.length) {
61498 fixes.push(new validationIssueFix({
61499 icon: 'iD-operation-reverse',
61500 title: _t.html('issues.fix.reverse_feature.title'),
61501 entityIds: [way.id],
61502 onClick: function onClick(context) {
61503 var id = this.issue.entityIds[0];
61504 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
61511 if (node.tags.noexit !== 'yes') {
61512 var textDirection = _mainLocalizer.textDirection();
61513 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
61514 fixes.push(new validationIssueFix({
61515 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61516 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
61517 onClick: function onClick(context) {
61518 var entityID = this.issue.entityIds[0];
61519 var vertexID = this.issue.entityIds[1];
61520 var way = context.entity(entityID);
61521 var vertex = context.entity(vertexID);
61522 continueDrawing(way, vertex, context);
61532 function getReference(referenceID) {
61533 return function showReference(selection) {
61534 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
61540 function continueDrawing(way, vertex, context) {
61541 // make sure the vertex is actually visible and editable
61542 var map = context.map();
61544 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61545 map.zoomToEase(vertex);
61548 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
61551 validation.type = type;
61555 function validationIncompatibleSource() {
61556 var type = 'incompatible_source';
61557 var invalidSources = [{
61560 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
61563 var validation = function checkIncompatibleSource(entity) {
61564 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
61565 if (!entitySources) return [];
61567 invalidSources.forEach(function (invalidSource) {
61568 var hasInvalidSource = entitySources.some(function (source) {
61569 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
61570 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
61573 if (!hasInvalidSource) return;
61574 issues.push(new validationIssue({
61576 severity: 'warning',
61577 message: function message(context) {
61578 var entity = context.hasEntity(this.entityIds[0]);
61579 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
61580 feature: utilDisplayLabel(entity, context.graph())
61583 reference: getReference(invalidSource.id),
61584 entityIds: [entity.id],
61585 dynamicFixes: function dynamicFixes() {
61586 return [new validationIssueFix({
61587 title: _t.html('issues.fix.remove_proprietary_data.title')
61594 function getReference(id) {
61595 return function showReference(selection) {
61596 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
61601 validation.type = type;
61605 function validationMaprules() {
61606 var type = 'maprules';
61608 var validation = function checkMaprules(entity, graph) {
61609 if (!services.maprules) return [];
61610 var rules = services.maprules.validationRules();
61613 for (var i = 0; i < rules.length; i++) {
61614 var rule = rules[i];
61615 rule.findIssues(entity, graph, issues);
61621 validation.type = type;
61625 function validationMismatchedGeometry() {
61626 var type = 'mismatched_geometry';
61628 function tagSuggestingLineIsArea(entity) {
61629 if (entity.type !== 'way' || entity.isClosed()) return null;
61630 var tagSuggestingArea = entity.tagSuggestingArea();
61632 if (!tagSuggestingArea) {
61636 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
61637 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
61639 if (asLine && asArea && asLine === asArea) {
61640 // these tags also allow lines and making this an area wouldn't matter
61644 return tagSuggestingArea;
61647 function makeConnectEndpointsFixOnClick(way, graph) {
61648 // must have at least three nodes to close this automatically
61649 if (way.nodes.length < 3) return null;
61650 var nodes = graph.childNodes(way),
61652 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
61654 if (firstToLastDistanceMeters < 0.75) {
61655 testNodes = nodes.slice(); // shallow copy
61658 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61660 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61661 return function (context) {
61662 var way = context.entity(this.issue.entityIds[0]);
61663 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
61666 } // if the points were not merged, attempt to close the way
61669 testNodes = nodes.slice(); // shallow copy
61671 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61673 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61674 return function (context) {
61675 var wayId = this.issue.entityIds[0];
61676 var way = context.entity(wayId);
61677 var nodeId = way.nodes[0];
61678 var index = way.nodes.length;
61679 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
61684 function lineTaggedAsAreaIssue(entity) {
61685 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
61686 if (!tagSuggestingArea) return null;
61687 return new validationIssue({
61689 subtype: 'area_as_line',
61690 severity: 'warning',
61691 message: function message(context) {
61692 var entity = context.hasEntity(this.entityIds[0]);
61693 return entity ? _t.html('issues.tag_suggests_area.message', {
61694 feature: utilDisplayLabel(entity, 'area'),
61696 tags: tagSuggestingArea
61700 reference: showReference,
61701 entityIds: [entity.id],
61702 hash: JSON.stringify(tagSuggestingArea),
61703 dynamicFixes: function dynamicFixes(context) {
61705 var entity = context.entity(this.entityIds[0]);
61706 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
61707 fixes.push(new validationIssueFix({
61708 title: _t.html('issues.fix.connect_endpoints.title'),
61709 onClick: connectEndsOnClick
61711 fixes.push(new validationIssueFix({
61712 icon: 'iD-operation-delete',
61713 title: _t.html('issues.fix.remove_tag.title'),
61714 onClick: function onClick(context) {
61715 var entityId = this.issue.entityIds[0];
61716 var entity = context.entity(entityId);
61717 var tags = Object.assign({}, entity.tags); // shallow copy
61719 for (var key in tagSuggestingArea) {
61723 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
61730 function showReference(selection) {
61731 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
61735 function vertexTaggedAsPointIssue(entity, graph) {
61736 // we only care about nodes
61737 if (entity.type !== 'node') return null; // ignore tagless points
61739 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
61741 if (entity.isOnAddressLine(graph)) return null;
61742 var geometry = entity.geometry(graph);
61743 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
61745 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
61746 return new validationIssue({
61748 subtype: 'vertex_as_point',
61749 severity: 'warning',
61750 message: function message(context) {
61751 var entity = context.hasEntity(this.entityIds[0]);
61752 return entity ? _t.html('issues.vertex_as_point.message', {
61753 feature: utilDisplayLabel(entity, 'vertex')
61756 reference: function showReference(selection) {
61757 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
61759 entityIds: [entity.id]
61761 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
61762 return new validationIssue({
61764 subtype: 'point_as_vertex',
61765 severity: 'warning',
61766 message: function message(context) {
61767 var entity = context.hasEntity(this.entityIds[0]);
61768 return entity ? _t.html('issues.point_as_vertex.message', {
61769 feature: utilDisplayLabel(entity, 'point')
61772 reference: function showReference(selection) {
61773 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
61775 entityIds: [entity.id],
61776 dynamicFixes: function dynamicFixes(context) {
61777 var entityId = this.entityIds[0];
61778 var extractOnClick = null;
61780 if (!context.hasHiddenConnections(entityId)) {
61781 extractOnClick = function extractOnClick(context) {
61782 var entityId = this.issue.entityIds[0];
61783 var action = actionExtract(entityId);
61784 context.perform(action, _t('operations.extract.annotation', {
61786 })); // re-enter mode to trigger updates
61788 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
61792 return [new validationIssueFix({
61793 icon: 'iD-operation-extract',
61794 title: _t.html('issues.fix.extract_point.title'),
61795 onClick: extractOnClick
61804 function unclosedMultipolygonPartIssues(entity, graph) {
61805 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
61806 !entity.isComplete(graph)) return null;
61807 var sequences = osmJoinWays(entity.members, graph);
61810 for (var i in sequences) {
61811 var sequence = sequences[i];
61812 if (!sequence.nodes) continue;
61813 var firstNode = sequence.nodes[0];
61814 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
61816 if (firstNode === lastNode) continue;
61817 var issue = new validationIssue({
61819 subtype: 'unclosed_multipolygon_part',
61820 severity: 'warning',
61821 message: function message(context) {
61822 var entity = context.hasEntity(this.entityIds[0]);
61823 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
61824 feature: utilDisplayLabel(entity, context.graph())
61827 reference: showReference,
61828 loc: sequence.nodes[0].loc,
61829 entityIds: [entity.id],
61830 hash: sequence.map(function (way) {
61834 issues.push(issue);
61839 function showReference(selection) {
61840 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
61844 var validation = function checkMismatchedGeometry(entity, graph) {
61845 var issues = [vertexTaggedAsPointIssue(entity, graph), lineTaggedAsAreaIssue(entity)];
61846 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
61847 return issues.filter(Boolean);
61850 validation.type = type;
61854 function validationMissingRole() {
61855 var type = 'missing_role';
61857 var validation = function checkMissingRole(entity, graph) {
61860 if (entity.type === 'way') {
61861 graph.parentRelations(entity).forEach(function (relation) {
61862 if (!relation.isMultipolygon()) return;
61863 var member = relation.memberById(entity.id);
61865 if (member && isMissingRole(member)) {
61866 issues.push(makeIssue(entity, relation, member));
61869 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
61870 entity.indexedMembers().forEach(function (member) {
61871 var way = graph.hasEntity(member.id);
61873 if (way && isMissingRole(member)) {
61874 issues.push(makeIssue(way, entity, member));
61882 function isMissingRole(member) {
61883 return !member.role || !member.role.trim().length;
61886 function makeIssue(way, relation, member) {
61887 return new validationIssue({
61889 severity: 'warning',
61890 message: function message(context) {
61891 var member = context.hasEntity(this.entityIds[1]),
61892 relation = context.hasEntity(this.entityIds[0]);
61893 return member && relation ? _t.html('issues.missing_role.message', {
61894 member: utilDisplayLabel(member, context.graph()),
61895 relation: utilDisplayLabel(relation, context.graph())
61898 reference: showReference,
61899 entityIds: [relation.id, way.id],
61903 hash: member.index.toString(),
61904 dynamicFixes: function dynamicFixes() {
61905 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
61906 icon: 'iD-operation-delete',
61907 title: _t.html('issues.fix.remove_from_relation.title'),
61908 onClick: function onClick(context) {
61909 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
61917 function showReference(selection) {
61918 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
61922 function makeAddRoleFix(role) {
61923 return new validationIssueFix({
61924 title: _t.html('issues.fix.set_as_' + role + '.title'),
61925 onClick: function onClick(context) {
61926 var oldMember = this.issue.data.member;
61928 id: this.issue.entityIds[1],
61929 type: oldMember.type,
61932 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
61939 validation.type = type;
61943 function validationMissingTag(context) {
61944 var type = 'missing_tag';
61946 function hasDescriptiveTags(entity, graph) {
61947 var keys = Object.keys(entity.tags).filter(function (k) {
61948 if (k === 'area' || k === 'name') {
61951 return osmIsInterestingTag(k);
61955 if (entity.type === 'relation' && keys.length === 1 && entity.tags.type === 'multipolygon') {
61956 // this relation's only interesting tag just says its a multipolygon,
61957 // which is not descriptive enough
61958 // It's okay for a simple multipolygon to have no descriptive tags
61959 // if its outer way has them (old model, see `outdated_tags.js`)
61960 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
61963 return keys.length > 0;
61966 function isUnknownRoad(entity) {
61967 return entity.type === 'way' && entity.tags.highway === 'road';
61970 function isUntypedRelation(entity) {
61971 return entity.type === 'relation' && !entity.tags.type;
61974 var validation = function checkMissingTag(entity, graph) {
61976 var osm = context.connection();
61977 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
61979 if (!isUnloadedNode && // allow untagged nodes that are part of ways
61980 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
61981 !entity.hasParentRelations(graph)) {
61982 if (Object.keys(entity.tags).length === 0) {
61984 } else if (!hasDescriptiveTags(entity, graph)) {
61985 subtype = 'descriptive';
61986 } else if (isUntypedRelation(entity)) {
61987 subtype = 'relation_type';
61989 } // flag an unknown road even if it's a member of a relation
61992 if (!subtype && isUnknownRoad(entity)) {
61993 subtype = 'highway_classification';
61996 if (!subtype) return [];
61997 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
61998 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
62000 var canDelete = entity.version === undefined || entity.v !== undefined;
62001 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
62002 return [new validationIssue({
62005 severity: severity,
62006 message: function message(context) {
62007 var entity = context.hasEntity(this.entityIds[0]);
62008 return entity ? _t.html('issues.' + messageID + '.message', {
62009 feature: utilDisplayLabel(entity, context.graph())
62012 reference: showReference,
62013 entityIds: [entity.id],
62014 dynamicFixes: function dynamicFixes(context) {
62016 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
62017 fixes.push(new validationIssueFix({
62018 icon: 'iD-icon-search',
62019 title: _t.html('issues.fix.' + selectFixType + '.title'),
62020 onClick: function onClick(context) {
62021 context.ui().sidebar.showPresetList();
62025 var id = this.entityIds[0];
62026 var operation = operationDelete(context, [id]);
62027 var disabledReasonID = operation.disabled();
62029 if (!disabledReasonID) {
62030 deleteOnClick = function deleteOnClick(context) {
62031 var id = this.issue.entityIds[0];
62032 var operation = operationDelete(context, [id]);
62034 if (!operation.disabled()) {
62040 fixes.push(new validationIssueFix({
62041 icon: 'iD-operation-delete',
62042 title: _t.html('issues.fix.delete_feature.title'),
62043 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
62044 onClick: deleteOnClick
62050 function showReference(selection) {
62051 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
62055 validation.type = type;
62059 var simplify = function simplify(str) {
62060 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());
62064 // kvnd: "amenity/fast_food|Thaï Express~(North America)",
62065 // kvn: "amenity/fast_food|Thaï Express",
62066 // kv: "amenity/fast_food",
62069 // n: "Thaï Express",
62070 // d: "(North America)",
62071 // nsimple: "thaiexpress",
62072 // kvnnsimple: "amenity/fast_food|thaiexpress"
62075 var to_parts = function to_parts(kvnd) {
62078 var kvndparts = kvnd.split('~', 2);
62079 if (kvndparts.length > 1) parts.d = kvndparts[1];
62080 parts.kvn = kvndparts[0];
62081 var kvnparts = parts.kvn.split('|', 2);
62082 if (kvnparts.length > 1) parts.n = kvnparts[1];
62083 parts.kv = kvnparts[0];
62084 var kvparts = parts.kv.split('/', 2);
62085 parts.k = kvparts[0];
62086 parts.v = kvparts[1];
62087 parts.nsimple = simplify(parts.n);
62088 parts.kvnsimple = parts.kv + '|' + parts.nsimple;
62092 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"]};
62094 matchGroups: matchGroups
62097 var matchGroups$1 = require$$0.matchGroups;
62099 var matcher$1 = function matcher() {
62100 var _warnings = []; // array of match conflict pairs
62102 var _ambiguous = {};
62103 var _matchIndex = {};
62104 var matcher = {}; // Create an index of all the keys/simplenames for fast matching
62106 matcher.buildMatchIndex = function (brands) {
62107 // two passes - once for primary names, once for secondary/alternate names
62108 Object.keys(brands).forEach(function (kvnd) {
62109 return insertNames(kvnd, 'primary');
62111 Object.keys(brands).forEach(function (kvnd) {
62112 return insertNames(kvnd, 'secondary');
62115 function insertNames(kvnd, which) {
62116 var obj = brands[kvnd];
62117 var parts = to_parts(kvnd); // Exit early for ambiguous names in the second pass.
62118 // They were collected in the first pass and we don't gather alt names for them.
62120 if (which === 'secondary' && parts.d) return;
62122 if (obj.countryCodes) {
62123 parts.countryCodes = obj.countryCodes.slice(); // copy
62126 var nomatches = obj.nomatch || [];
62128 if (nomatches.some(function (s) {
62131 console.log("WARNING match/nomatch conflict for ".concat(kvnd));
62135 var match_kv = [parts.kv].concat(obj.matchTags || []).concat(["".concat(parts.k, "/yes"), "building/yes"]) // #3454 - match some generic tags
62136 .map(function (s) {
62137 return s.toLowerCase();
62139 var match_nsimple = [];
62141 if (which === 'primary') {
62142 match_nsimple = [parts.n].concat(obj.matchNames || []).concat(obj.tags.official_name || []) // #2732 - match alternate names
62144 } else if (which === 'secondary') {
62145 match_nsimple = [].concat(obj.tags.alt_name || []) // #2732 - match alternate names
62146 .concat(obj.tags.short_name || []) // #2732 - match alternate names
62150 if (!match_nsimple.length) return; // nothing to do
62152 match_kv.forEach(function (kv) {
62153 match_nsimple.forEach(function (nsimple) {
62155 // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
62156 // FIXME: Name collisions will overwrite the initial entry (ok for now)
62157 if (!_ambiguous[kv]) _ambiguous[kv] = {};
62158 _ambiguous[kv][nsimple] = parts;
62160 // Names we mostly expect to be unique..
62161 if (!_matchIndex[kv]) _matchIndex[kv] = {};
62162 var m = _matchIndex[kv][nsimple];
62165 // There already is a match for this name, skip it
62166 // Warn if we detect collisions in a primary name.
62167 // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
62168 if (which === 'primary' && !/\/yes$/.test(kv)) {
62169 _warnings.push([m.kvnd, "".concat(kvnd, " (").concat(kv, "/").concat(nsimple, ")")]);
62172 _matchIndex[kv][nsimple] = parts; // insert
62178 }; // pass a `key`, `value`, `name` and return the best match,
62179 // `countryCode` optional (if supplied, must match that too)
62182 matcher.matchKVN = function (key, value, name, countryCode) {
62183 return matcher.matchParts(to_parts("".concat(key, "/").concat(value, "|").concat(name)), countryCode);
62184 }; // pass a parts object and return the best match,
62185 // `countryCode` optional (if supplied, must match that too)
62188 matcher.matchParts = function (parts, countryCode) {
62190 var inGroup = false; // fixme: we currently return a single match for ambiguous
62192 match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
62193 if (match && matchesCountryCode(match)) return match; // try to return an exact match
62195 match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
62196 if (match && matchesCountryCode(match)) return match; // look in match groups
62198 for (var mg in matchGroups$1) {
62199 var matchGroup = matchGroups$1[mg];
62203 for (var i = 0; i < matchGroup.length; i++) {
62204 var otherkv = matchGroup[i].toLowerCase();
62207 inGroup = otherkv === parts.kv;
62211 // fixme: we currently return a single match for ambiguous
62212 match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
62216 match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
62219 if (match && !matchesCountryCode(match)) {
62223 if (inGroup && match) {
62231 function matchesCountryCode(match) {
62232 if (!countryCode) return true;
62233 if (!match.countryCodes) return true;
62234 return match.countryCodes.indexOf(countryCode) !== -1;
62238 matcher.getWarnings = function () {
62245 var fromCharCode = String.fromCharCode;
62246 var nativeFromCodePoint = String.fromCodePoint;
62248 // length should be 1, old FF problem
62249 var INCORRECT_LENGTH = !!nativeFromCodePoint && nativeFromCodePoint.length != 1;
62251 // `String.fromCodePoint` method
62252 // https://tc39.github.io/ecma262/#sec-string.fromcodepoint
62253 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
62254 fromCodePoint: function fromCodePoint(x) { // eslint-disable-line no-unused-vars
62256 var length = arguments.length;
62259 while (length > i) {
62260 code = +arguments[i++];
62261 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
62262 elements.push(code < 0x10000
62263 ? fromCharCode(code)
62264 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
62266 } return elements.join('');
62270 var quickselect$2 = createCommonjsModule(function (module, exports) {
62271 (function (global, factory) {
62272 module.exports = factory() ;
62273 })(commonjsGlobal, function () {
62275 function quickselect(arr, k, left, right, compare) {
62276 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
62279 function quickselectStep(arr, k, left, right, compare) {
62280 while (right > left) {
62281 if (right - left > 600) {
62282 var n = right - left + 1;
62283 var m = k - left + 1;
62284 var z = Math.log(n);
62285 var s = 0.5 * Math.exp(2 * z / 3);
62286 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
62287 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
62288 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
62289 quickselectStep(arr, k, newLeft, newRight, compare);
62295 swap(arr, left, k);
62296 if (compare(arr[right], t) > 0) swap(arr, left, right);
62303 while (compare(arr[i], t) < 0) {
62307 while (compare(arr[j], t) > 0) {
62312 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
62314 swap(arr, j, right);
62316 if (j <= k) left = j + 1;
62317 if (k <= j) right = j - 1;
62321 function swap(arr, i, j) {
62327 function defaultCompare(a, b) {
62328 return a < b ? -1 : a > b ? 1 : 0;
62331 return quickselect;
62335 var rbush_1 = rbush;
62336 var _default$2 = rbush;
62338 function rbush(maxEntries, format) {
62339 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
62341 this._maxEntries = Math.max(4, maxEntries || 9);
62342 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
62345 this._initFormat(format);
62351 rbush.prototype = {
62352 all: function all() {
62353 return this._all(this.data, []);
62355 search: function search(bbox) {
62356 var node = this.data,
62358 toBBox = this.toBBox;
62359 if (!intersects$1(bbox, node)) return result;
62360 var nodesToSearch = [],
62367 for (i = 0, len = node.children.length; i < len; i++) {
62368 child = node.children[i];
62369 childBBox = node.leaf ? toBBox(child) : child;
62371 if (intersects$1(bbox, childBBox)) {
62372 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
62376 node = nodesToSearch.pop();
62381 collides: function collides(bbox) {
62382 var node = this.data,
62383 toBBox = this.toBBox;
62384 if (!intersects$1(bbox, node)) return false;
62385 var nodesToSearch = [],
62392 for (i = 0, len = node.children.length; i < len; i++) {
62393 child = node.children[i];
62394 childBBox = node.leaf ? toBBox(child) : child;
62396 if (intersects$1(bbox, childBBox)) {
62397 if (node.leaf || contains$1(bbox, childBBox)) return true;
62398 nodesToSearch.push(child);
62402 node = nodesToSearch.pop();
62407 load: function load(data) {
62408 if (!(data && data.length)) return this;
62410 if (data.length < this._minEntries) {
62411 for (var i = 0, len = data.length; i < len; i++) {
62412 this.insert(data[i]);
62416 } // recursively build the tree with the given data from scratch using OMT algorithm
62419 var node = this._build(data.slice(), 0, data.length - 1, 0);
62421 if (!this.data.children.length) {
62422 // save as is if tree is empty
62424 } else if (this.data.height === node.height) {
62425 // split root if trees have the same height
62426 this._splitRoot(this.data, node);
62428 if (this.data.height < node.height) {
62429 // swap trees if inserted one is bigger
62430 var tmpNode = this.data;
62433 } // insert the small tree into the large tree at appropriate level
62436 this._insert(node, this.data.height - node.height - 1, true);
62441 insert: function insert(item) {
62442 if (item) this._insert(item, this.data.height - 1);
62445 clear: function clear() {
62446 this.data = createNode$1([]);
62449 remove: function remove(item, equalsFn) {
62450 if (!item) return this;
62451 var node = this.data,
62452 bbox = this.toBBox(item),
62458 goingUp; // depth-first iterative tree traversal
62460 while (node || path.length) {
62464 parent = path[path.length - 1];
62470 // check current node
62471 index = findItem$1(item, node.children, equalsFn);
62473 if (index !== -1) {
62474 // item found, remove the item and condense tree upwards
62475 node.children.splice(index, 1);
62478 this._condense(path);
62484 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
62490 node = node.children[0];
62491 } else if (parent) {
62494 node = parent.children[i];
62496 } else node = null; // nothing found
62502 toBBox: function toBBox(item) {
62505 compareMinX: compareNodeMinX$1,
62506 compareMinY: compareNodeMinY$1,
62507 toJSON: function toJSON() {
62510 fromJSON: function fromJSON(data) {
62514 _all: function _all(node, result) {
62515 var nodesToSearch = [];
62518 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
62519 node = nodesToSearch.pop();
62524 _build: function _build(items, left, right, height) {
62525 var N = right - left + 1,
62526 M = this._maxEntries,
62530 // reached leaf level; return leaf
62531 node = createNode$1(items.slice(left, right + 1));
62532 calcBBox$1(node, this.toBBox);
62537 // target height of the bulk-loaded tree
62538 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
62540 M = Math.ceil(N / Math.pow(M, height - 1));
62543 node = createNode$1([]);
62545 node.height = height; // split the items into M mostly square tiles
62547 var N2 = Math.ceil(N / M),
62548 N1 = N2 * Math.ceil(Math.sqrt(M)),
62553 multiSelect$1(items, left, right, N1, this.compareMinX);
62555 for (i = left; i <= right; i += N1) {
62556 right2 = Math.min(i + N1 - 1, right);
62557 multiSelect$1(items, i, right2, N2, this.compareMinY);
62559 for (j = i; j <= right2; j += N2) {
62560 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
62562 node.children.push(this._build(items, j, right3, height - 1));
62566 calcBBox$1(node, this.toBBox);
62569 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
62570 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
62574 if (node.leaf || path.length - 1 === level) break;
62575 minArea = minEnlargement = Infinity;
62577 for (i = 0, len = node.children.length; i < len; i++) {
62578 child = node.children[i];
62579 area = bboxArea$1(child);
62580 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
62582 if (enlargement < minEnlargement) {
62583 minEnlargement = enlargement;
62584 minArea = area < minArea ? area : minArea;
62585 targetNode = child;
62586 } else if (enlargement === minEnlargement) {
62587 // otherwise choose one with the smallest area
62588 if (area < minArea) {
62590 targetNode = child;
62595 node = targetNode || node.children[0];
62600 _insert: function _insert(item, level, isNode) {
62601 var toBBox = this.toBBox,
62602 bbox = isNode ? item : toBBox(item),
62603 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
62605 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
62608 node.children.push(item);
62609 extend$3(node, bbox); // split on node overflow; propagate upwards if necessary
62611 while (level >= 0) {
62612 if (insertPath[level].children.length > this._maxEntries) {
62613 this._split(insertPath, level);
62617 } // adjust bboxes along the insertion path
62620 this._adjustParentBBoxes(bbox, insertPath, level);
62622 // split overflowed node into two
62623 _split: function _split(insertPath, level) {
62624 var node = insertPath[level],
62625 M = node.children.length,
62626 m = this._minEntries;
62628 this._chooseSplitAxis(node, m, M);
62630 var splitIndex = this._chooseSplitIndex(node, m, M);
62632 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
62633 newNode.height = node.height;
62634 newNode.leaf = node.leaf;
62635 calcBBox$1(node, this.toBBox);
62636 calcBBox$1(newNode, this.toBBox);
62637 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
62639 _splitRoot: function _splitRoot(node, newNode) {
62641 this.data = createNode$1([node, newNode]);
62642 this.data.height = node.height + 1;
62643 this.data.leaf = false;
62644 calcBBox$1(this.data, this.toBBox);
62646 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
62647 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
62648 minOverlap = minArea = Infinity;
62650 for (i = m; i <= M - m; i++) {
62651 bbox1 = distBBox$1(node, 0, i, this.toBBox);
62652 bbox2 = distBBox$1(node, i, M, this.toBBox);
62653 overlap = intersectionArea$1(bbox1, bbox2);
62654 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
62656 if (overlap < minOverlap) {
62657 minOverlap = overlap;
62659 minArea = area < minArea ? area : minArea;
62660 } else if (overlap === minOverlap) {
62661 // otherwise choose distribution with minimum area
62662 if (area < minArea) {
62671 // sorts node children by the best axis for split
62672 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
62673 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
62674 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
62675 xMargin = this._allDistMargin(node, m, M, compareMinX),
62676 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
62677 // otherwise it's already sorted by minY
62680 if (xMargin < yMargin) node.children.sort(compareMinX);
62682 // total margin of all possible split distributions where each node is at least m full
62683 _allDistMargin: function _allDistMargin(node, m, M, compare) {
62684 node.children.sort(compare);
62685 var toBBox = this.toBBox,
62686 leftBBox = distBBox$1(node, 0, m, toBBox),
62687 rightBBox = distBBox$1(node, M - m, M, toBBox),
62688 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
62692 for (i = m; i < M - m; i++) {
62693 child = node.children[i];
62694 extend$3(leftBBox, node.leaf ? toBBox(child) : child);
62695 margin += bboxMargin$1(leftBBox);
62698 for (i = M - m - 1; i >= m; i--) {
62699 child = node.children[i];
62700 extend$3(rightBBox, node.leaf ? toBBox(child) : child);
62701 margin += bboxMargin$1(rightBBox);
62706 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
62707 // adjust bboxes along the given tree path
62708 for (var i = level; i >= 0; i--) {
62709 extend$3(path[i], bbox);
62712 _condense: function _condense(path) {
62713 // go through the path, removing empty nodes and updating bboxes
62714 for (var i = path.length - 1, siblings; i >= 0; i--) {
62715 if (path[i].children.length === 0) {
62717 siblings = path[i - 1].children;
62718 siblings.splice(siblings.indexOf(path[i]), 1);
62719 } else this.clear();
62720 } else calcBBox$1(path[i], this.toBBox);
62723 _initFormat: function _initFormat(format) {
62724 // data format (minX, minY, maxX, maxY accessors)
62725 // uses eval-type function compilation instead of just accepting a toBBox function
62726 // because the algorithms are very sensitive to sorting functions performance,
62727 // so they should be dead simple and without inner calls
62728 var compareArr = ['return a', ' - b', ';'];
62729 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
62730 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
62731 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
62735 function findItem$1(item, items, equalsFn) {
62736 if (!equalsFn) return items.indexOf(item);
62738 for (var i = 0; i < items.length; i++) {
62739 if (equalsFn(item, items[i])) return i;
62743 } // calculate node's bbox from bboxes of its children
62746 function calcBBox$1(node, toBBox) {
62747 distBBox$1(node, 0, node.children.length, toBBox, node);
62748 } // min bounding rectangle of node children from k to p-1
62751 function distBBox$1(node, k, p, toBBox, destNode) {
62752 if (!destNode) destNode = createNode$1(null);
62753 destNode.minX = Infinity;
62754 destNode.minY = Infinity;
62755 destNode.maxX = -Infinity;
62756 destNode.maxY = -Infinity;
62758 for (var i = k, child; i < p; i++) {
62759 child = node.children[i];
62760 extend$3(destNode, node.leaf ? toBBox(child) : child);
62766 function extend$3(a, b) {
62767 a.minX = Math.min(a.minX, b.minX);
62768 a.minY = Math.min(a.minY, b.minY);
62769 a.maxX = Math.max(a.maxX, b.maxX);
62770 a.maxY = Math.max(a.maxY, b.maxY);
62774 function compareNodeMinX$1(a, b) {
62775 return a.minX - b.minX;
62778 function compareNodeMinY$1(a, b) {
62779 return a.minY - b.minY;
62782 function bboxArea$1(a) {
62783 return (a.maxX - a.minX) * (a.maxY - a.minY);
62786 function bboxMargin$1(a) {
62787 return a.maxX - a.minX + (a.maxY - a.minY);
62790 function enlargedArea$1(a, b) {
62791 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));
62794 function intersectionArea$1(a, b) {
62795 var minX = Math.max(a.minX, b.minX),
62796 minY = Math.max(a.minY, b.minY),
62797 maxX = Math.min(a.maxX, b.maxX),
62798 maxY = Math.min(a.maxY, b.maxY);
62799 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
62802 function contains$1(a, b) {
62803 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
62806 function intersects$1(a, b) {
62807 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
62810 function createNode$1(children) {
62812 children: children,
62820 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
62821 // combines selection algorithm with binary divide & conquer approach
62824 function multiSelect$1(arr, left, right, n, compare) {
62825 var stack = [left, right],
62828 while (stack.length) {
62829 right = stack.pop();
62830 left = stack.pop();
62831 if (right - left <= n) continue;
62832 mid = left + Math.ceil((right - left) / n / 2) * n;
62833 quickselect$2(arr, mid, left, right, compare);
62834 stack.push(left, mid, mid, right);
62837 rbush_1["default"] = _default$2;
62839 var lineclip_1$1 = lineclip$1;
62840 lineclip$1.polyline = lineclip$1;
62841 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
62842 // handle polylines rather than just segments
62844 function lineclip$1(points, bbox, result) {
62845 var len = points.length,
62846 codeA = bitCode$1(points[0], bbox),
62853 if (!result) result = [];
62855 for (i = 1; i < len; i++) {
62858 codeB = lastCode = bitCode$1(b, bbox);
62861 if (!(codeA | codeB)) {
62865 if (codeB !== lastCode) {
62866 // segment went outside
62870 // start a new line
62874 } else if (i === len - 1) {
62879 } else if (codeA & codeB) {
62882 } else if (codeA) {
62883 // a outside, intersect with clip edge
62884 a = intersect$1(a, b, codeA, bbox);
62885 codeA = bitCode$1(a, bbox);
62888 b = intersect$1(a, b, codeB, bbox);
62889 codeB = bitCode$1(b, bbox);
62896 if (part.length) result.push(part);
62898 } // Sutherland-Hodgeman polygon clipping algorithm
62901 function polygonclip$1(points, bbox) {
62902 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
62904 for (edge = 1; edge <= 8; edge *= 2) {
62906 prev = points[points.length - 1];
62907 prevInside = !(bitCode$1(prev, bbox) & edge);
62909 for (i = 0; i < points.length; i++) {
62911 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
62913 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
62914 if (inside) result.push(p); // add a point if it's inside
62917 prevInside = inside;
62921 if (!points.length) break;
62925 } // intersect a segment against one of the 4 lines that make up the bbox
62928 function intersect$1(a, b, edge, bbox) {
62929 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
62930 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
62931 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
62932 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
62934 } // bit code reflects the point position relative to the bbox:
62936 // top 1001 1000 1010
62937 // mid 0001 0000 0010
62938 // bottom 0101 0100 0110
62941 function bitCode$1(p, bbox) {
62943 if (p[0] < bbox[0]) code |= 1; // left
62944 else if (p[0] > bbox[2]) code |= 2; // right
62946 if (p[1] < bbox[1]) code |= 4; // bottom
62947 else if (p[1] > bbox[3]) code |= 8; // top
62952 var whichPolygon_1 = whichPolygon;
62954 function whichPolygon(data) {
62957 for (var i = 0; i < data.features.length; i++) {
62958 var feature = data.features[i];
62959 var coords = feature.geometry.coordinates;
62961 if (feature.geometry.type === 'Polygon') {
62962 bboxes.push(treeItem(coords, feature.properties));
62963 } else if (feature.geometry.type === 'MultiPolygon') {
62964 for (var j = 0; j < coords.length; j++) {
62965 bboxes.push(treeItem(coords[j], feature.properties));
62970 var tree = rbush_1().load(bboxes);
62972 function query(p, multi) {
62974 result = tree.search({
62981 for (var i = 0; i < result.length; i++) {
62982 if (insidePolygon(result[i].coords, p)) {
62983 if (multi) output.push(result[i].props);else return result[i].props;
62987 return multi && output.length ? output : null;
62992 query.bbox = function queryBBox(bbox) {
62994 var result = tree.search({
63001 for (var i = 0; i < result.length; i++) {
63002 if (polygonIntersectsBBox(result[i].coords, bbox)) {
63003 output.push(result[i].props);
63013 function polygonIntersectsBBox(polygon, bbox) {
63014 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
63015 if (insidePolygon(polygon, bboxCenter)) return true;
63017 for (var i = 0; i < polygon.length; i++) {
63018 if (lineclip_1$1(polygon[i], bbox).length > 0) return true;
63022 } // ray casting algorithm for detecting if point is in polygon
63025 function insidePolygon(rings, p) {
63026 var inside = false;
63028 for (var i = 0, len = rings.length; i < len; i++) {
63029 var ring = rings[i];
63031 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
63032 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
63039 function rayIntersect(p, p1, p2) {
63040 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];
63043 function treeItem(coords, props) {
63053 for (var i = 0; i < coords[0].length; i++) {
63054 var p = coords[0][i];
63055 item.minX = Math.min(item.minX, p[0]);
63056 item.minY = Math.min(item.minY, p[1]);
63057 item.maxX = Math.max(item.maxX, p[0]);
63058 item.maxY = Math.max(item.maxY, p[1]);
63064 var type = "FeatureCollection";
63065 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]]]]}}];
63071 var borders = rawBorders;
63072 var whichPolygonGetter = {};
63073 var featuresByCode = {};
63074 var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
63075 var levels = ['subterritory', 'territory', 'country', 'intermediateRegion', 'subregion', 'region', 'union', 'world'];
63076 loadDerivedDataAndCaches(borders);
63078 function loadDerivedDataAndCaches(borders) {
63079 var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
63080 var geometryFeatures = [];
63082 for (var i in borders.features) {
63083 var _feature = borders.features[i];
63084 _feature.properties.id = _feature.properties.iso1A2 || _feature.properties.m49;
63086 loadIsoStatus(_feature);
63087 loadLevel(_feature);
63088 loadGroups(_feature);
63089 loadRoadSpeedUnit(_feature);
63090 loadDriveSide(_feature);
63091 loadFlag(_feature);
63092 cacheFeatureByIDs(_feature);
63093 if (_feature.geometry) geometryFeatures.push(_feature);
63096 for (var _i in borders.features) {
63097 var _feature2 = borders.features[_i];
63099 _feature2.properties.groups.sort(function (groupID1, groupID2) {
63100 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
63103 loadMembersForGroupsOf(_feature2);
63106 var geometryOnlyCollection = {
63107 type: 'RegionFeatureCollection',
63108 features: geometryFeatures
63110 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
63112 function loadGroups(feature) {
63113 var props = feature.properties;
63115 if (!props.groups) {
63119 if (props.country) {
63120 props.groups.push(props.country);
63123 if (props.m49 !== '001') {
63124 props.groups.push('001');
63128 function loadM49(feature) {
63129 var props = feature.properties;
63131 if (!props.m49 && props.iso1N3) {
63132 props.m49 = props.iso1N3;
63136 function loadIsoStatus(feature) {
63137 var props = feature.properties;
63139 if (!props.isoStatus && props.iso1A2) {
63140 props.isoStatus = 'official';
63144 function loadLevel(feature) {
63145 var props = feature.properties;
63146 if (props.level) return;
63148 if (!props.country) {
63149 props.level = 'country';
63150 } else if (props.isoStatus === 'official') {
63151 props.level = 'territory';
63153 props.level = 'subterritory';
63157 function loadRoadSpeedUnit(feature) {
63158 var props = feature.properties;
63160 if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63161 props.roadSpeedUnit = 'km/h';
63165 function loadDriveSide(feature) {
63166 var props = feature.properties;
63168 if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63169 props.driveSide = 'right';
63173 function loadFlag(feature) {
63174 if (!feature.properties.iso1A2) return;
63175 var flag = feature.properties.iso1A2.replace(/./g, function (_char) {
63176 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
63178 feature.properties.emojiFlag = flag;
63181 function loadMembersForGroupsOf(feature) {
63182 var featureID = feature.properties.id;
63183 var standardizedGroupIDs = [];
63185 for (var j in feature.properties.groups) {
63186 var groupID = feature.properties.groups[j];
63187 var groupFeature = featuresByCode[groupID];
63188 standardizedGroupIDs.push(groupFeature.properties.id);
63190 if (groupFeature.properties.members) {
63191 groupFeature.properties.members.push(featureID);
63193 groupFeature.properties.members = [featureID];
63197 feature.properties.groups = standardizedGroupIDs;
63200 function cacheFeatureByIDs(feature) {
63201 for (var k in identifierProps) {
63202 var prop = identifierProps[k];
63203 var id = prop && feature.properties[prop];
63206 id = id.replace(idFilterRegex, '').toUpperCase();
63207 featuresByCode[id] = feature;
63211 if (feature.properties.aliases) {
63212 for (var j in feature.properties.aliases) {
63213 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
63214 featuresByCode[alias] = feature;
63220 function locArray(loc) {
63221 if (Array.isArray(loc)) {
63223 } else if (loc.coordinates) {
63224 return loc.coordinates;
63227 return loc.geometry.coordinates;
63230 function smallestFeature(loc) {
63231 var query = locArray(loc);
63232 var featureProperties = whichPolygonGetter(query);
63233 if (!featureProperties) return null;
63234 return featuresByCode[featureProperties.id];
63237 function countryFeature(loc) {
63238 var feature = smallestFeature(loc);
63239 if (!feature) return null;
63240 var countryCode = feature.properties.country || feature.properties.iso1A2;
63241 return featuresByCode[countryCode];
63244 function featureForLoc(loc, opts) {
63245 if (opts && opts.level && opts.level !== 'country') {
63246 var features = featuresContaining(loc);
63247 var targetLevel = opts.level;
63248 var targetLevelIndex = levels.indexOf(targetLevel);
63249 if (targetLevelIndex === -1) return null;
63251 for (var i in features) {
63252 var _feature3 = features[i];
63254 if (_feature3.properties.level === targetLevel || levels.indexOf(_feature3.properties.level) > targetLevelIndex) {
63262 return countryFeature(loc);
63265 function featureForID(id) {
63268 if (typeof id === 'number') {
63269 stringID = id.toString();
63271 if (stringID.length === 1) {
63272 stringID = '00' + stringID;
63273 } else if (stringID.length === 2) {
63274 stringID = '0' + stringID;
63277 stringID = id.replace(idFilterRegex, '').toUpperCase();
63280 return featuresByCode[stringID] || null;
63283 function smallestOrMatchingFeature(query) {
63284 if (_typeof(query) === 'object') {
63285 return smallestFeature(query);
63288 return featureForID(query);
63291 function feature(query, opts) {
63292 if (_typeof(query) === 'object') {
63293 return featureForLoc(query, opts);
63296 return featureForID(query);
63298 function iso1A2Code(query, opts) {
63299 var match = feature(query, opts);
63300 if (!match) return null;
63301 return match.properties.iso1A2 || null;
63303 function featuresContaining(query, strict) {
63304 var feature = smallestOrMatchingFeature(query);
63305 if (!feature) return [];
63308 if (!strict || _typeof(query) === 'object') {
63309 features.push(feature);
63312 var properties = feature.properties;
63314 for (var i in properties.groups) {
63315 var groupID = properties.groups[i];
63316 features.push(featuresByCode[groupID]);
63321 function roadSpeedUnit(query) {
63322 var feature = smallestOrMatchingFeature(query);
63323 return feature && feature.properties.roadSpeedUnit || null;
63326 var _dataDeprecated;
63330 function validationOutdatedTags() {
63331 var type = 'outdated_tags';
63332 var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office']; // A concern here in switching to async data means that `_dataDeprecated`
63333 // and `_nsi` will not be available at first, so the data on early tiles
63334 // may not have tags validated fully.
63335 // initialize deprecated tags array
63337 _mainFileFetcher.get('deprecated').then(function (d) {
63338 return _dataDeprecated = d;
63339 })["catch"](function () {
63342 _mainFileFetcher.get('nsi_brands').then(function (d) {
63345 matcher: matcher$1(),
63348 }; // initialize name-suggestion-index matcher
63350 _nsi.matcher.buildMatchIndex(d.brands); // index all known wikipedia and wikidata tags
63353 Object.keys(d.brands).forEach(function (kvnd) {
63354 var brand = d.brands[kvnd];
63355 var wd = brand.tags['brand:wikidata'];
63356 var wp = brand.tags['brand:wikipedia'];
63359 _nsi.wikidata[wd] = kvnd;
63363 _nsi.wikipedia[wp] = kvnd;
63367 })["catch"](function () {
63371 function oldTagIssues(entity, graph) {
63372 var oldTags = Object.assign({}, entity.tags); // shallow copy
63374 var preset = _mainPresetIndex.match(entity, graph);
63375 var subtype = 'deprecated_tags';
63376 if (!preset) return []; // upgrade preset..
63378 if (preset.replacement) {
63379 var newPreset = _mainPresetIndex.item(preset.replacement);
63380 graph = actionChangePreset(entity.id, preset, newPreset, true
63381 /* skip field defaults */
63383 entity = graph.entity(entity.id);
63384 preset = newPreset;
63385 } // upgrade tags..
63388 if (_dataDeprecated) {
63389 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
63391 if (deprecatedTags.length) {
63392 deprecatedTags.forEach(function (tag) {
63393 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
63395 entity = graph.entity(entity.id);
63397 } // add missing addTags..
63400 var newTags = Object.assign({}, entity.tags); // shallow copy
63402 if (preset.tags !== preset.addTags) {
63403 Object.keys(preset.addTags).forEach(function (k) {
63405 if (preset.addTags[k] === '*') {
63406 newTags[k] = 'yes';
63408 newTags[k] = preset.addTags[k];
63415 // Do `wikidata` or `wikipedia` identify this entity as a brand? #6416
63416 // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
63419 if (newTags.wikidata) {
63420 // try matching `wikidata`
63421 isBrand = _nsi.wikidata[newTags.wikidata];
63424 if (!isBrand && newTags.wikipedia) {
63425 // fallback to `wikipedia`
63426 isBrand = _nsi.wikipedia[newTags.wikipedia];
63429 if (isBrand && !newTags.office) {
63430 // but avoid doing this for corporate offices
63431 if (newTags.wikidata) {
63432 newTags['brand:wikidata'] = newTags.wikidata;
63433 delete newTags.wikidata;
63436 if (newTags.wikipedia) {
63437 newTags['brand:wikipedia'] = newTags.wikipedia;
63438 delete newTags.wikipedia;
63439 } // I considered setting `name` and other tags here, but they aren't unique per wikidata
63440 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
63441 // So users will really need to use a preset or assign `name` themselves.
63443 } // try key/value|name match against name-suggestion-index
63446 if (newTags.name) {
63447 for (var i = 0; i < nsiKeys.length; i++) {
63448 var k = nsiKeys[i];
63449 if (!newTags[k]) continue;
63450 var center = entity.extent(graph).center();
63451 var countryCode = iso1A2Code(center);
63453 var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
63455 if (!match) continue; // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
63457 if (match.d) continue;
63458 var brand = _nsi.brands[match.kvnd];
63460 if (brand && brand.tags['brand:wikidata'] && brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
63461 subtype = 'noncanonical_brand';
63462 var keepTags = ['takeaway'].reduce(function (acc, k) {
63464 acc[k] = newTags[k];
63469 nsiKeys.forEach(function (k) {
63470 return delete newTags[k];
63472 Object.assign(newTags, brand.tags, keepTags);
63477 } // determine diff
63480 var tagDiff = utilTagDiff(oldTags, newTags);
63481 if (!tagDiff.length) return [];
63482 var isOnlyAddingTags = tagDiff.every(function (d) {
63483 return d.type === '+';
63487 if (subtype === 'noncanonical_brand') {
63488 prefix = 'noncanonical_brand.';
63489 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
63490 subtype = 'incomplete_tags';
63491 prefix = 'incomplete.';
63492 } // don't allow autofixing brand tags
63495 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
63496 return [new validationIssue({
63499 severity: 'warning',
63500 message: showMessage,
63501 reference: showReference,
63502 entityIds: [entity.id],
63503 hash: JSON.stringify(tagDiff),
63504 dynamicFixes: function dynamicFixes() {
63505 return [new validationIssueFix({
63506 autoArgs: autoArgs,
63507 title: _t.html('issues.fix.upgrade_tags.title'),
63508 onClick: function onClick(context) {
63509 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63515 function doUpgrade(graph) {
63516 var currEntity = graph.hasEntity(entity.id);
63517 if (!currEntity) return graph;
63518 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63520 tagDiff.forEach(function (diff) {
63521 if (diff.type === '-') {
63522 delete newTags[diff.key];
63523 } else if (diff.type === '+') {
63524 newTags[diff.key] = diff.newVal;
63527 return actionChangeTags(currEntity.id, newTags)(graph);
63530 function showMessage(context) {
63531 var currEntity = context.hasEntity(entity.id);
63532 if (!currEntity) return '';
63533 var messageID = "issues.outdated_tags.".concat(prefix, "message");
63535 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
63536 messageID += '_incomplete';
63539 return _t.html(messageID, {
63540 feature: utilDisplayLabel(currEntity, context.graph())
63544 function showReference(selection) {
63545 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63546 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
63547 enter.append('strong').html(_t.html('issues.suggested'));
63548 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) {
63549 var klass = d.type === '+' ? 'add' : 'remove';
63550 return "tagDiff-cell tagDiff-cell-".concat(klass);
63551 }).html(function (d) {
63557 function oldMultipolygonIssues(entity, graph) {
63558 var multipolygon, outerWay;
63560 if (entity.type === 'relation') {
63561 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
63562 multipolygon = entity;
63563 } else if (entity.type === 'way') {
63564 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
63570 if (!multipolygon || !outerWay) return [];
63571 return [new validationIssue({
63573 subtype: 'old_multipolygon',
63574 severity: 'warning',
63575 message: showMessage,
63576 reference: showReference,
63577 entityIds: [outerWay.id, multipolygon.id],
63578 dynamicFixes: function dynamicFixes() {
63579 return [new validationIssueFix({
63580 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
63581 title: _t.html('issues.fix.move_tags.title'),
63582 onClick: function onClick(context) {
63583 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
63589 function doUpgrade(graph) {
63590 var currMultipolygon = graph.hasEntity(multipolygon.id);
63591 var currOuterWay = graph.hasEntity(outerWay.id);
63592 if (!currMultipolygon || !currOuterWay) return graph;
63593 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
63594 graph = graph.replace(currMultipolygon);
63595 return actionChangeTags(currOuterWay.id, {})(graph);
63598 function showMessage(context) {
63599 var currMultipolygon = context.hasEntity(multipolygon.id);
63600 if (!currMultipolygon) return '';
63601 return _t.html('issues.old_multipolygon.message', {
63602 multipolygon: utilDisplayLabel(currMultipolygon, context.graph())
63606 function showReference(selection) {
63607 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
63611 var validation = function checkOutdatedTags(entity, graph) {
63612 var issues = oldMultipolygonIssues(entity, graph);
63613 if (!issues.length) issues = oldTagIssues(entity, graph);
63617 validation.type = type;
63621 function validationPrivateData() {
63622 var type = 'private_data'; // assume that some buildings are private
63624 var privateBuildingValues = {
63630 semidetached_house: true,
63631 static_caravan: true
63632 }; // but they might be public if they have one of these other tags
63642 }; // these tags may contain personally identifying info
63644 var personalTags = {
63645 'contact:email': true,
63646 'contact:fax': true,
63647 'contact:phone': true,
63653 var validation = function checkPrivateData(entity) {
63654 var tags = entity.tags;
63655 if (!tags.building || !privateBuildingValues[tags.building]) return [];
63658 for (var k in tags) {
63659 if (publicKeys[k]) return []; // probably a public feature
63661 if (!personalTags[k]) {
63662 keepTags[k] = tags[k];
63666 var tagDiff = utilTagDiff(tags, keepTags);
63667 if (!tagDiff.length) return [];
63668 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
63669 return [new validationIssue({
63671 severity: 'warning',
63672 message: showMessage,
63673 reference: showReference,
63674 entityIds: [entity.id],
63675 dynamicFixes: function dynamicFixes() {
63676 return [new validationIssueFix({
63677 icon: 'iD-operation-delete',
63678 title: _t.html('issues.fix.' + fixID + '.title'),
63679 onClick: function onClick(context) {
63680 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63686 function doUpgrade(graph) {
63687 var currEntity = graph.hasEntity(entity.id);
63688 if (!currEntity) return graph;
63689 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63691 tagDiff.forEach(function (diff) {
63692 if (diff.type === '-') {
63693 delete newTags[diff.key];
63694 } else if (diff.type === '+') {
63695 newTags[diff.key] = diff.newVal;
63698 return actionChangeTags(currEntity.id, newTags)(graph);
63701 function showMessage(context) {
63702 var currEntity = context.hasEntity(this.entityIds[0]);
63703 if (!currEntity) return '';
63704 return _t.html('issues.private_data.contact.message', {
63705 feature: utilDisplayLabel(currEntity, context.graph())
63709 function showReference(selection) {
63710 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63711 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
63712 enter.append('strong').html(_t.html('issues.suggested'));
63713 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) {
63714 var klass = d.type === '+' ? 'add' : 'remove';
63715 return 'tagDiff-cell tagDiff-cell-' + klass;
63716 }).html(function (d) {
63722 validation.type = type;
63726 var _discardNameRegexes = [];
63727 function validationSuspiciousName() {
63728 var type = 'suspicious_name';
63729 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
63730 // be available at first, so the data on early tiles may not have tags validated fully.
63732 _mainFileFetcher.get('nsi_filters').then(function (filters) {
63733 // known list of generic names (e.g. "bar")
63734 _discardNameRegexes = filters.discardNames.map(function (discardName) {
63735 return new RegExp(discardName, 'i');
63737 })["catch"](function () {
63741 function isDiscardedSuggestionName(lowercaseName) {
63742 return _discardNameRegexes.some(function (regex) {
63743 return regex.test(lowercaseName);
63745 } // test if the name is just the key or tag value (e.g. "park")
63748 function nameMatchesRawTag(lowercaseName, tags) {
63749 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
63750 var key = keysToTestForGenericValues[i];
63751 var val = tags[key];
63754 val = val.toLowerCase();
63756 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
63765 function isGenericName(name, tags) {
63766 name = name.toLowerCase();
63767 return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
63770 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
63771 return new validationIssue({
63773 subtype: 'generic_name',
63774 severity: 'warning',
63775 message: function message(context) {
63776 var entity = context.hasEntity(this.entityIds[0]);
63777 if (!entity) return '';
63778 var preset = _mainPresetIndex.match(entity, context.graph());
63779 var langName = langCode && _mainLocalizer.languageName(langCode);
63780 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
63781 feature: preset.name(),
63786 reference: showReference,
63787 entityIds: [entityId],
63788 hash: nameKey + '=' + genericName,
63789 dynamicFixes: function dynamicFixes() {
63790 return [new validationIssueFix({
63791 icon: 'iD-operation-delete',
63792 title: _t.html('issues.fix.remove_the_name.title'),
63793 onClick: function onClick(context) {
63794 var entityId = this.issue.entityIds[0];
63795 var entity = context.entity(entityId);
63796 var tags = Object.assign({}, entity.tags); // shallow copy
63798 delete tags[nameKey];
63799 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
63805 function showReference(selection) {
63806 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63810 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
63811 return new validationIssue({
63813 subtype: 'not_name',
63814 severity: 'warning',
63815 message: function message(context) {
63816 var entity = context.hasEntity(this.entityIds[0]);
63817 if (!entity) return '';
63818 var preset = _mainPresetIndex.match(entity, context.graph());
63819 var langName = langCode && _mainLocalizer.languageName(langCode);
63820 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
63821 feature: preset.name(),
63822 name: incorrectName,
63826 reference: showReference,
63827 entityIds: [entityId],
63828 hash: nameKey + '=' + incorrectName,
63829 dynamicFixes: function dynamicFixes() {
63830 return [new validationIssueFix({
63831 icon: 'iD-operation-delete',
63832 title: _t.html('issues.fix.remove_the_name.title'),
63833 onClick: function onClick(context) {
63834 var entityId = this.issue.entityIds[0];
63835 var entity = context.entity(entityId);
63836 var tags = Object.assign({}, entity.tags); // shallow copy
63838 delete tags[nameKey];
63839 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
63845 function showReference(selection) {
63846 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63850 var validation = function checkGenericName(entity) {
63851 // a generic name is okay if it's a known brand or entity
63852 if (entity.hasWikidata()) return [];
63854 var notNames = (entity.tags['not:name'] || '').split(';');
63856 for (var key in entity.tags) {
63857 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
63859 var langCode = m.length >= 2 ? m[1] : null;
63860 var value = entity.tags[key];
63862 if (notNames.length) {
63863 for (var i in notNames) {
63864 var notName = notNames[i];
63866 if (notName && value === notName) {
63867 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
63873 if (isGenericName(value, entity.tags)) {
63874 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
63881 validation.type = type;
63885 function validationUnsquareWay(context) {
63886 var type = 'unsquare_way';
63887 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
63888 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
63890 var epsilon = 0.05;
63891 var nodeThreshold = 10;
63893 function isBuilding(entity, graph) {
63894 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
63895 return entity.tags.building && entity.tags.building !== 'no';
63898 var validation = function checkUnsquareWay(entity, graph) {
63899 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
63901 if (entity.tags.nonsquare === 'yes') return [];
63902 var isClosed = entity.isClosed();
63903 if (!isClosed) return []; // this building has bigger problems
63904 // don't flag ways with lots of nodes since they are likely detail-mapped
63906 var nodes = graph.childNodes(entity).slice(); // shallow copy
63908 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
63909 // ignore if not all nodes are fully downloaded
63911 var osm = services.osm;
63912 if (!osm || nodes.some(function (node) {
63913 return !osm.isDataLoaded(node.loc);
63914 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
63916 var hasConnectedSquarableWays = nodes.some(function (node) {
63917 return graph.parentWays(node).some(function (way) {
63918 if (way.id === entity.id) return false;
63919 if (isBuilding(way, graph)) return true;
63920 return graph.parentRelations(way).some(function (parentRelation) {
63921 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
63925 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
63927 var storedDegreeThreshold = corePreferences('validate-square-degrees');
63928 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
63929 var points = nodes.map(function (node) {
63930 return context.projection(node.loc);
63932 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
63933 var autoArgs; // don't allow autosquaring features linked to wikidata
63935 if (!entity.tags.wikidata) {
63936 // use same degree threshold as for detection
63937 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
63938 autoAction.transitionable = false; // when autofixing, do it instantly
63940 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
63945 return [new validationIssue({
63947 subtype: 'building',
63948 severity: 'warning',
63949 message: function message(context) {
63950 var entity = context.hasEntity(this.entityIds[0]);
63951 return entity ? _t.html('issues.unsquare_way.message', {
63952 feature: utilDisplayLabel(entity, context.graph())
63955 reference: showReference,
63956 entityIds: [entity.id],
63957 hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
63958 dynamicFixes: function dynamicFixes() {
63959 return [new validationIssueFix({
63960 icon: 'iD-operation-orthogonalize',
63961 title: _t.html('issues.fix.square_feature.title'),
63962 autoArgs: autoArgs,
63963 onClick: function onClick(context, completionHandler) {
63964 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
63966 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
63968 })); // run after the squaring transition (currently 150ms)
63970 window.setTimeout(function () {
63971 completionHandler();
63976 new validationIssueFix({
63977 title: t.html('issues.fix.tag_as_unsquare.title'),
63978 onClick: function(context) {
63979 var entityId = this.issue.entityIds[0];
63980 var entity = context.entity(entityId);
63981 var tags = Object.assign({}, entity.tags); // shallow copy
63982 tags.nonsquare = 'yes';
63984 actionChangeTags(entityId, tags),
63985 t('issues.fix.tag_as_unsquare.annotation')
63994 function showReference(selection) {
63995 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
63999 validation.type = type;
64003 var Validations = /*#__PURE__*/Object.freeze({
64005 validationAlmostJunction: validationAlmostJunction,
64006 validationCloseNodes: validationCloseNodes,
64007 validationCrossingWays: validationCrossingWays,
64008 validationDisconnectedWay: validationDisconnectedWay,
64009 validationFormatting: validationFormatting,
64010 validationHelpRequest: validationHelpRequest,
64011 validationImpossibleOneway: validationImpossibleOneway,
64012 validationIncompatibleSource: validationIncompatibleSource,
64013 validationMaprules: validationMaprules,
64014 validationMismatchedGeometry: validationMismatchedGeometry,
64015 validationMissingRole: validationMissingRole,
64016 validationMissingTag: validationMissingTag,
64017 validationOutdatedTags: validationOutdatedTags,
64018 validationPrivateData: validationPrivateData,
64019 validationSuspiciousName: validationSuspiciousName,
64020 validationUnsquareWay: validationUnsquareWay
64023 function coreValidator(context) {
64024 var dispatch$1 = dispatch('validated', 'focusedIssue');
64025 var validator = utilRebind({}, dispatch$1, 'on');
64027 var _disabledRules = {};
64028 var _ignoredIssueIDs = {}; // issue.id -> true
64030 var _baseCache = validationCache(); // issues before any user edits
64033 var _headCache = validationCache(); // issues after all user edits
64036 var _validatedGraph = null;
64038 var _deferred = new Set(); //
64039 // initialize the validator rulesets
64043 validator.init = function () {
64044 Object.values(Validations).forEach(function (validation) {
64045 if (typeof validation !== 'function') return;
64046 var fn = validation(context);
64050 var disabledRules = corePreferences('validate-disabledRules');
64052 if (disabledRules) {
64053 disabledRules.split(',').forEach(function (key) {
64054 _disabledRules[key] = true;
64059 function reset(resetIgnored) {
64060 Array.from(_deferred).forEach(function (handle) {
64061 window.cancelIdleCallback(handle);
64063 _deferred["delete"](handle);
64064 }); // clear caches
64066 if (resetIgnored) _ignoredIssueIDs = {};
64067 _baseCache = validationCache();
64068 _headCache = validationCache();
64069 _validatedGraph = null;
64071 // clear caches, called whenever iD resets after a save
64075 validator.reset = function () {
64079 validator.resetIgnoredIssues = function () {
64080 _ignoredIssueIDs = {}; // reload UI
64082 dispatch$1.call('validated');
64083 }; // must update issues when the user changes the unsquare thereshold
64086 validator.reloadUnsquareIssues = function () {
64087 reloadUnsquareIssues(_headCache, context.graph());
64088 reloadUnsquareIssues(_baseCache, context.history().base());
64089 dispatch$1.call('validated');
64092 function reloadUnsquareIssues(cache, graph) {
64093 var checkUnsquareWay = _rules.unsquare_way;
64094 if (typeof checkUnsquareWay !== 'function') return; // uncache existing
64096 cache.uncacheIssuesOfType('unsquare_way');
64097 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), graph) // everywhere
64098 .filter(function (entity) {
64099 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
64100 }); // rerun for all buildings
64102 buildings.forEach(function (entity) {
64103 var detected = checkUnsquareWay(entity, graph);
64104 if (detected.length !== 1) return;
64105 var issue = detected[0];
64107 if (!cache.issuesByEntityID[entity.id]) {
64108 cache.issuesByEntityID[entity.id] = new Set();
64111 cache.issuesByEntityID[entity.id].add(issue.id);
64112 cache.issuesByIssueID[issue.id] = issue;
64115 // what: 'all', // 'all' or 'edited'
64116 // where: 'all', // 'all' or 'visible'
64117 // includeIgnored: false // true, false, or 'only'
64118 // includeDisabledRules: false // true, false, or 'only'
64122 validator.getIssues = function (options) {
64123 var opts = Object.assign({
64126 includeIgnored: false,
64127 includeDisabledRules: false
64129 var issues = Object.values(_headCache.issuesByIssueID);
64130 var view = context.map().extent();
64131 return issues.filter(function (issue) {
64132 if (!issue) return false;
64133 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64134 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64135 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64136 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; // Sanity check: This issue may be for an entity that not longer exists.
64137 // If we detect this, uncache and return false so it is not included..
64139 var entityIds = issue.entityIds || [];
64141 for (var i = 0; i < entityIds.length; i++) {
64142 var entityId = entityIds[i];
64144 if (!context.hasEntity(entityId)) {
64145 delete _headCache.issuesByEntityID[entityId];
64146 delete _headCache.issuesByIssueID[issue.id];
64151 if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
64153 if (opts.where === 'visible') {
64154 var extent = issue.extent(context.graph());
64155 if (!view.intersects(extent)) return false;
64162 validator.getResolvedIssues = function () {
64163 var baseIssues = Object.values(_baseCache.issuesByIssueID);
64164 return baseIssues.filter(function (issue) {
64165 return !_headCache.issuesByIssueID[issue.id];
64169 validator.focusIssue = function (issue) {
64170 var extent = issue.extent(context.graph());
64173 var setZoom = Math.max(context.map().zoom(), 19);
64174 context.map().unobscuredCenterZoomEase(extent.center(), setZoom); // select the first entity
64176 if (issue.entityIds && issue.entityIds.length) {
64177 window.setTimeout(function () {
64178 var ids = issue.entityIds;
64179 context.enter(modeSelect(context, [ids[0]]));
64180 dispatch$1.call('focusedIssue', this, issue);
64181 }, 250); // after ease
64186 validator.getIssuesBySeverity = function (options) {
64187 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
64188 groups.error = groups.error || [];
64189 groups.warning = groups.warning || [];
64191 }; // show some issue types in a particular order
64194 var orderedIssueTypes = [// flag missing data first
64195 'missing_tag', 'missing_role', // then flag identity issues
64196 'outdated_tags', 'mismatched_geometry', // flag geometry issues where fixing them might solve connectivity issues
64197 'crossing_ways', 'almost_junction', // then flag connectivity issues
64198 'disconnected_way', 'impossible_oneway']; // returns the issues that the given entity IDs have in common, matching the given options
64200 validator.getSharedEntityIssues = function (entityIDs, options) {
64201 var cache = _headCache; // gather the issues that are common to all the entities
64203 var issueIDs = entityIDs.reduce(function (acc, entityID) {
64204 var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
64207 return new Set(entityIssueIDs);
64210 return new Set(_toConsumableArray(acc).filter(function (elem) {
64211 return entityIssueIDs.has(elem);
64214 var opts = options || {};
64215 return Array.from(issueIDs).map(function (id) {
64216 return cache.issuesByIssueID[id];
64217 }).filter(function (issue) {
64218 if (!issue) return false;
64219 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64220 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64221 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64222 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
64224 }).sort(function (issue1, issue2) {
64225 if (issue1.type === issue2.type) {
64226 // issues of the same type, sort deterministically
64227 return issue1.id < issue2.id ? -1 : 1;
64230 var index1 = orderedIssueTypes.indexOf(issue1.type);
64231 var index2 = orderedIssueTypes.indexOf(issue2.type);
64233 if (index1 !== -1 && index2 !== -1) {
64234 // both issue types have explicit sort orders
64235 return index1 - index2;
64236 } else if (index1 === -1 && index2 === -1) {
64237 // neither issue type has an explicit sort order, sort by type
64238 return issue1.type < issue2.type ? -1 : 1;
64240 // order explicit types before everything else
64241 return index1 !== -1 ? -1 : 1;
64246 validator.getEntityIssues = function (entityID, options) {
64247 return validator.getSharedEntityIssues([entityID], options);
64250 validator.getRuleKeys = function () {
64251 return Object.keys(_rules);
64254 validator.isRuleEnabled = function (key) {
64255 return !_disabledRules[key];
64258 validator.toggleRule = function (key) {
64259 if (_disabledRules[key]) {
64260 delete _disabledRules[key];
64262 _disabledRules[key] = true;
64265 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64266 validator.validate();
64269 validator.disableRules = function (keys) {
64270 _disabledRules = {};
64271 keys.forEach(function (k) {
64272 _disabledRules[k] = true;
64274 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64275 validator.validate();
64278 validator.ignoreIssue = function (id) {
64279 _ignoredIssueIDs[id] = true;
64281 // Run validation on a single entity for the given graph
64285 function validateEntity(entity, graph) {
64286 var entityIssues = []; // runs validation and appends resulting issues
64288 function runValidation(key) {
64289 var fn = _rules[key];
64291 if (typeof fn !== 'function') {
64292 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
64297 var detected = fn(entity, graph);
64298 entityIssues = entityIssues.concat(detected);
64302 Object.keys(_rules).forEach(runValidation);
64303 return entityIssues;
64306 function entityIDsToValidate(entityIDs, graph) {
64307 var processedIDs = new Set();
64308 return entityIDs.reduce(function (acc, entityID) {
64309 // keep redundancy check separate from `acc` because an `entityID`
64310 // could have been added to `acc` as a related entity through an earlier pass
64311 if (processedIDs.has(entityID)) return acc;
64312 processedIDs.add(entityID);
64313 var entity = graph.hasEntity(entityID);
64314 if (!entity) return acc;
64316 var checkParentRels = [entity];
64318 if (entity.type === 'node') {
64319 graph.parentWays(entity).forEach(function (parentWay) {
64320 acc.add(parentWay.id); // include parent ways
64322 checkParentRels.push(parentWay);
64324 } else if (entity.type === 'relation') {
64325 entity.members.forEach(function (member) {
64326 acc.add(member.id); // include members
64328 } else if (entity.type === 'way') {
64329 entity.nodes.forEach(function (nodeID) {
64330 acc.add(nodeID); // include child nodes
64332 graph._parentWays[nodeID].forEach(function (wayID) {
64333 acc.add(wayID); // include connected ways
64338 checkParentRels.forEach(function (entity) {
64339 // include parent relations
64340 if (entity.type !== 'relation') {
64341 // but not super-relations
64342 graph.parentRelations(entity).forEach(function (parentRelation) {
64343 acc.add(parentRelation.id);
64350 // Run validation for several entities, supplied `entityIDs`,
64351 // against `graph` for the given `cache`
64355 function validateEntities(entityIDs, graph, cache) {
64356 // clear caches for existing issues related to these entities
64357 entityIDs.forEach(cache.uncacheEntityID); // detect new issues and update caches
64359 entityIDs.forEach(function (entityID) {
64360 var entity = graph.hasEntity(entityID); // don't validate deleted entities
64362 if (!entity) return;
64363 var issues = validateEntity(entity, graph);
64364 cache.cacheIssues(issues);
64367 // Validates anything that has changed since the last time it was run.
64368 // Also updates the "validatedGraph" to be the current graph
64369 // and dispatches a `validated` event when finished.
64373 validator.validate = function () {
64374 var currGraph = context.graph();
64375 _validatedGraph = _validatedGraph || context.history().base();
64377 if (currGraph === _validatedGraph) {
64378 dispatch$1.call('validated');
64382 var oldGraph = _validatedGraph;
64383 var difference = coreDifference(oldGraph, currGraph);
64384 _validatedGraph = currGraph;
64385 var createdAndModifiedEntityIDs = difference.extantIDs(true); // created/modified (true = w/relation members)
64387 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph); // check modified and deleted entities against the old graph in order to update their related entities
64388 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
64390 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified()).map(function (entity) {
64393 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph); // concat the sets
64395 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
64396 validateEntities(entityIDsToCheck, context.graph(), _headCache);
64397 dispatch$1.call('validated');
64400 context.history().on('reset.validator', function () {
64401 // cached issues aren't valid any longer if the history has been reset
64403 validator.validate();
64404 }); // WHEN TO RUN VALIDATION:
64405 // When graph changes:
64407 context.history().on('restore.validator', validator.validate) // restore saved history
64408 .on('undone.validator', validator.validate) // undo
64409 .on('redone.validator', validator.validate); // redo
64410 // but not on 'change' (e.g. while drawing)
64411 // When user changes editing modes:
64413 context.on('exit.validator', validator.validate); // When merging fetched data:
64415 context.history().on('merge.validator', function (entities) {
64416 if (!entities) return;
64417 var handle = window.requestIdleCallback(function () {
64418 var entityIDs = entities.map(function (entity) {
64421 var headGraph = context.graph();
64422 validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
64423 var baseGraph = context.history().base();
64424 validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
64425 dispatch$1.call('validated');
64428 _deferred.add(handle);
64433 function validationCache() {
64435 issuesByIssueID: {},
64436 // issue.id -> issue
64437 issuesByEntityID: {} // entity.id -> set(issue.id)
64441 cache.cacheIssues = function (issues) {
64442 issues.forEach(function (issue) {
64443 var entityIds = issue.entityIds || [];
64444 entityIds.forEach(function (entityId) {
64445 if (!cache.issuesByEntityID[entityId]) {
64446 cache.issuesByEntityID[entityId] = new Set();
64449 cache.issuesByEntityID[entityId].add(issue.id);
64451 cache.issuesByIssueID[issue.id] = issue;
64455 cache.uncacheIssue = function (issue) {
64456 // When multiple entities are involved (e.g. crossing_ways),
64457 // remove this issue from the other entity caches too..
64458 var entityIds = issue.entityIds || [];
64459 entityIds.forEach(function (entityId) {
64460 if (cache.issuesByEntityID[entityId]) {
64461 cache.issuesByEntityID[entityId]["delete"](issue.id);
64464 delete cache.issuesByIssueID[issue.id];
64467 cache.uncacheIssues = function (issues) {
64468 issues.forEach(cache.uncacheIssue);
64471 cache.uncacheIssuesOfType = function (type) {
64472 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
64473 return issue.type === type;
64475 cache.uncacheIssues(issuesOfType);
64477 // Remove a single entity and all its related issues from the caches
64481 cache.uncacheEntityID = function (entityID) {
64482 var issueIDs = cache.issuesByEntityID[entityID];
64483 if (!issueIDs) return;
64484 issueIDs.forEach(function (issueID) {
64485 var issue = cache.issuesByIssueID[issueID];
64488 cache.uncacheIssue(issue);
64490 delete cache.issuesByIssueID[issueID];
64493 delete cache.issuesByEntityID[entityID];
64499 function coreUploader(context) {
64500 var dispatch$1 = dispatch( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
64501 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
64502 'saveEnded', // dispatched after the result event has been dispatched
64503 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
64504 'progressChanged', // Each save results in one of these outcomes:
64505 'resultNoChanges', // upload wasn't attempted since there were no edits
64506 'resultErrors', // upload failed due to errors
64507 'resultConflicts', // upload failed due to data conflicts
64508 'resultSuccess' // upload completed without errors
64510 var _isSaving = false;
64511 var _conflicts = [];
64516 var _discardTags = {};
64517 _mainFileFetcher.get('discarded').then(function (d) {
64519 })["catch"](function () {
64522 var uploader = utilRebind({}, dispatch$1, 'on');
64524 uploader.isSaving = function () {
64528 uploader.save = function (changeset, tryAgain, checkConflicts) {
64529 // Guard against accidentally entering save code twice - #4641
64530 if (_isSaving && !tryAgain) {
64534 var osm = context.connection();
64535 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
64536 // This can happen if they were logged in from before, but the tokens are no longer valid.
64538 if (!osm.authenticated()) {
64539 osm.authenticate(function (err) {
64541 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
64549 dispatch$1.call('saveStarted', this);
64552 var history = context.history();
64554 _errors = []; // Store original changes, in case user wants to download them as an .osc file
64556 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
64557 // Any conflict resolutions will be done as `history.replace`
64558 // Remember to pop this later if needed
64561 history.perform(actionNoop());
64562 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
64565 if (!checkConflicts) {
64566 upload(changeset); // Do the full (slow) conflict check..
64568 performFullConflictCheck(changeset);
64572 function performFullConflictCheck(changeset) {
64573 var osm = context.connection();
64575 var history = context.history();
64576 var localGraph = context.graph();
64577 var remoteGraph = coreGraph(history.base(), true);
64578 var summary = history.difference().summary();
64581 for (var i = 0; i < summary.length; i++) {
64582 var item = summary[i];
64584 if (item.changeType === 'modified') {
64585 _toCheck.push(item.entity.id);
64589 var _toLoad = withChildNodes(_toCheck, localGraph);
64592 var _toLoadCount = 0;
64593 var _toLoadTotal = _toLoad.length;
64595 if (_toCheck.length) {
64596 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64598 _toLoad.forEach(function (id) {
64599 _loaded[id] = false;
64602 osm.loadMultiple(_toLoad, loaded);
64609 function withChildNodes(ids, graph) {
64610 var s = new Set(ids);
64611 ids.forEach(function (id) {
64612 var entity = graph.entity(id);
64613 if (entity.type !== 'way') return;
64614 graph.childNodes(entity).forEach(function (child) {
64615 if (child.version !== undefined) {
64620 return Array.from(s);
64621 } // Reload modified entities into an alternate graph and check for conflicts..
64624 function loaded(err, result) {
64625 if (_errors.length) return;
64629 msg: err.message || err.responseText,
64630 details: [_t('save.status_code', {
64635 didResultInErrors();
64638 result.data.forEach(function (entity) {
64639 remoteGraph.replace(entity);
64640 _loaded[entity.id] = true;
64641 _toLoad = _toLoad.filter(function (val) {
64642 return val !== entity.id;
64644 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
64645 // need to also load children that aren't already being checked..
64649 if (entity.type === 'way') {
64650 for (i = 0; i < entity.nodes.length; i++) {
64651 id = entity.nodes[i];
64653 if (_loaded[id] === undefined) {
64654 _loaded[id] = false;
64658 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
64659 for (i = 0; i < entity.members.length; i++) {
64660 id = entity.members[i].id;
64662 if (_loaded[id] === undefined) {
64663 _loaded[id] = false;
64669 _toLoadCount += result.data.length;
64670 _toLoadTotal += loadMore.length;
64671 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64673 if (loadMore.length) {
64674 _toLoad.push.apply(_toLoad, loadMore);
64676 osm.loadMultiple(loadMore, loaded);
64679 if (!_toLoad.length) {
64686 function detectConflicts() {
64687 function choice(id, text, _action) {
64691 action: function action() {
64692 history.replace(_action);
64697 function formatUser(d) {
64698 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
64701 function entityName(entity) {
64702 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
64705 function sameVersions(local, remote) {
64706 if (local.version !== remote.version) return false;
64708 if (local.type === 'way') {
64709 var children = utilArrayUnion(local.nodes, remote.nodes);
64711 for (var i = 0; i < children.length; i++) {
64712 var a = localGraph.hasEntity(children[i]);
64713 var b = remoteGraph.hasEntity(children[i]);
64714 if (a && b && a.version !== b.version) return false;
64721 _toCheck.forEach(function (id) {
64722 var local = localGraph.entity(id);
64723 var remote = remoteGraph.entity(id);
64724 if (sameVersions(local, remote)) return;
64725 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
64726 history.replace(merge);
64727 var mergeConflicts = merge.conflicts();
64728 if (!mergeConflicts.length) return; // merged safely
64730 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
64731 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
64732 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
64733 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
64737 name: entityName(local),
64738 details: mergeConflicts,
64740 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
64746 function upload(changeset) {
64747 var osm = context.connection();
64751 msg: 'No OSM Service'
64755 if (_conflicts.length) {
64756 didResultInConflicts(changeset);
64757 } else if (_errors.length) {
64758 didResultInErrors();
64760 var history = context.history();
64761 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
64763 if (changes.modified.length || changes.created.length || changes.deleted.length) {
64764 dispatch$1.call('willAttemptUpload', this);
64765 osm.putChangeset(changeset, changes, uploadCallback);
64767 // changes were insignificant or reverted by user
64768 didResultInNoChanges();
64773 function uploadCallback(err, changeset) {
64775 if (err.status === 409) {
64777 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
64780 msg: err.message || err.responseText,
64781 details: [_t('save.status_code', {
64786 didResultInErrors();
64789 didResultInSuccess(changeset);
64793 function didResultInNoChanges() {
64794 dispatch$1.call('resultNoChanges', this);
64796 context.flush(); // reset iD
64799 function didResultInErrors() {
64800 context.history().pop();
64801 dispatch$1.call('resultErrors', this, _errors);
64805 function didResultInConflicts(changeset) {
64806 _conflicts.sort(function (a, b) {
64807 return b.id.localeCompare(a.id);
64810 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
64814 function didResultInSuccess(changeset) {
64815 // delete the edit stack cached to local storage
64816 context.history().clearSaved();
64817 dispatch$1.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
64819 window.setTimeout(function () {
64821 context.flush(); // reset iD
64825 function endSave() {
64827 dispatch$1.call('saveEnded', this);
64830 uploader.cancelConflictResolution = function () {
64831 context.history().pop();
64834 uploader.processResolvedConflicts = function (changeset) {
64835 var history = context.history();
64837 for (var i = 0; i < _conflicts.length; i++) {
64838 if (_conflicts[i].chosen === 1) {
64839 // user chose "use theirs"
64840 var entity = context.hasEntity(_conflicts[i].id);
64842 if (entity && entity.type === 'way') {
64843 var children = utilArrayUniq(entity.nodes);
64845 for (var j = 0; j < children.length; j++) {
64846 history.replace(actionRevert(children[j]));
64850 history.replace(actionRevert(_conflicts[i].id));
64854 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
64857 uploader.reset = function () {};
64862 var abs$4 = Math.abs;
64863 var exp$2 = Math.exp;
64866 var FORCED$g = fails(function () {
64867 return Math.sinh(-2e-17) != -2e-17;
64870 // `Math.sinh` method
64871 // https://tc39.github.io/ecma262/#sec-math.sinh
64872 // V8 near Chromium 38 has a problem with very small numbers
64873 _export({ target: 'Math', stat: true, forced: FORCED$g }, {
64874 sinh: function sinh(x) {
64875 return abs$4(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp$2(x - 1) - exp$2(-x - 1)) * (E / 2);
64879 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
64881 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
64882 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
64885 function localeDateString(s) {
64886 if (!s) return null;
64892 var d = new Date(s);
64893 if (isNaN(d.getTime())) return null;
64894 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
64897 function vintageRange(vintage) {
64900 if (vintage.start || vintage.end) {
64901 s = vintage.start || '?';
64903 if (vintage.start !== vintage.end) {
64904 s += ' - ' + (vintage.end || '?');
64911 function rendererBackgroundSource(data) {
64912 var source = Object.assign({}, data); // shallow copy
64914 var _offset = [0, 0];
64915 var _name = source.name;
64916 var _description = source.description;
64918 var _best = !!source.best;
64920 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
64922 source.tileSize = data.tileSize || 256;
64923 source.zoomExtent = data.zoomExtent || [0, 22];
64924 source.overzoom = data.overzoom !== false;
64926 source.offset = function (val) {
64927 if (!arguments.length) return _offset;
64932 source.nudge = function (val, zoomlevel) {
64933 _offset[0] += val[0] / Math.pow(2, zoomlevel);
64934 _offset[1] += val[1] / Math.pow(2, zoomlevel);
64938 source.name = function () {
64939 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64940 return _t('imagery.' + id_safe + '.name', {
64945 source.label = function () {
64946 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64947 return _t.html('imagery.' + id_safe + '.name', {
64952 source.description = function () {
64953 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64954 return _t.html('imagery.' + id_safe + '.description', {
64955 "default": _description
64959 source.best = function () {
64963 source.area = function () {
64964 if (!data.polygon) return Number.MAX_VALUE; // worldwide
64966 var area = d3_geoArea({
64967 type: 'MultiPolygon',
64968 coordinates: [data.polygon]
64970 return isNaN(area) ? 0 : area;
64973 source.imageryUsed = function () {
64974 return _name || source.id;
64977 source.template = function (val) {
64978 if (!arguments.length) return _template;
64980 if (source.id === 'custom') {
64987 source.url = function (coord) {
64988 var result = _template;
64989 if (result === '') return result; // source 'none'
64990 // Guess a type based on the tokens present in the template
64991 // (This is for 'custom' source, where we don't know)
64993 if (!source.type) {
64994 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
64995 source.type = 'wms';
64996 source.projection = 'EPSG:3857'; // guess
64997 } else if (/\{(x|y)\}/.test(_template)) {
64998 source.type = 'tms';
64999 } else if (/\{u\}/.test(_template)) {
65000 source.type = 'bing';
65004 if (source.type === 'wms') {
65005 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
65006 //polyfill for IE11, PhantomJS
65007 var sinh = Math.sinh || function (x) {
65008 var y = Math.exp(x);
65009 return (y - 1 / y) / 2;
65012 var zoomSize = Math.pow(2, z);
65013 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
65014 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
65016 switch (source.projection) {
65019 x: lon * 180 / Math.PI,
65020 y: lat * 180 / Math.PI
65024 // EPSG:3857 and synonyms
65025 var mercCoords = mercatorRaw(lon, lat);
65027 x: 20037508.34 / Math.PI * mercCoords[0],
65028 y: 20037508.34 / Math.PI * mercCoords[1]
65033 var tileSize = source.tileSize;
65034 var projection = source.projection;
65035 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
65036 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
65037 result = result.replace(/\{(\w+)\}/g, function (token, key) {
65047 return projection.replace(/^EPSG:/, '');
65050 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
65051 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
65052 /VERSION=1.3|CRS={proj}/.test(source.template())) {
65053 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
65055 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
65074 } else if (source.type === 'tms') {
65075 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
65076 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
65077 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
65078 } else if (source.type === 'bing') {
65079 result = result.replace('{u}', function () {
65082 for (var zoom = coord[2]; zoom > 0; zoom--) {
65084 var mask = 1 << zoom - 1;
65085 if ((coord[0] & mask) !== 0) b++;
65086 if ((coord[1] & mask) !== 0) b += 2;
65092 } // these apply to any type..
65095 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
65096 var subdomains = r.split(',');
65097 return subdomains[(coord[0] + coord[1]) % subdomains.length];
65102 source.validZoom = function (z) {
65103 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
65106 source.isLocatorOverlay = function () {
65107 return source.id === 'mapbox_locator_overlay';
65109 /* hides a source from the list, but leaves it available for use */
65112 source.isHidden = function () {
65113 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
65116 source.copyrightNotices = function () {};
65118 source.getMetadata = function (center, tileCoord, callback) {
65120 start: localeDateString(source.startDate),
65121 end: localeDateString(source.endDate)
65123 vintage.range = vintageRange(vintage);
65127 callback(null, metadata);
65133 rendererBackgroundSource.Bing = function (data, dispatch) {
65134 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
65135 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
65136 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
65137 var bing = rendererBackgroundSource(data); // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
65139 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
65141 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
65144 var providers = [];
65145 d3_json(url).then(function (json) {
65146 providers = json.resourceSets[0].resources[0].imageryProviders.map(function (provider) {
65148 attribution: provider.attribution,
65149 areas: provider.coverageAreas.map(function (area) {
65151 zoom: [area.zoomMin, area.zoomMax],
65152 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
65157 dispatch.call('change');
65158 })["catch"](function () {
65162 bing.copyrightNotices = function (zoom, extent) {
65163 zoom = Math.min(zoom, 21);
65164 return providers.filter(function (provider) {
65165 return provider.areas.some(function (area) {
65166 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
65168 }).map(function (provider) {
65169 return provider.attribution;
65173 bing.getMetadata = function (center, tileCoord, callback) {
65174 var tileID = tileCoord.slice(0, 3).join('/');
65175 var zoom = Math.min(tileCoord[2], 21);
65176 var centerPoint = center[1] + ',' + center[0]; // lat,lng
65178 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
65179 if (inflight[tileID]) return;
65181 if (!cache[tileID]) {
65182 cache[tileID] = {};
65185 if (cache[tileID] && cache[tileID].metadata) {
65186 return callback(null, cache[tileID].metadata);
65189 inflight[tileID] = true;
65190 d3_json(url).then(function (result) {
65191 delete inflight[tileID];
65194 throw new Error('Unknown Error');
65198 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
65199 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
65201 vintage.range = vintageRange(vintage);
65205 cache[tileID].metadata = metadata;
65206 if (callback) callback(null, metadata);
65207 })["catch"](function (err) {
65208 delete inflight[tileID];
65209 if (callback) callback(err.message);
65213 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
65217 rendererBackgroundSource.Esri = function (data) {
65218 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
65219 if (data.template.match(/blankTile/) === null) {
65220 data.template = data.template + '?blankTile=false';
65223 var esri = rendererBackgroundSource(data);
65227 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
65228 // https://developers.arcgis.com/documentation/tiled-elevation-service/
65231 esri.fetchTilemap = function (center) {
65232 // skip if we have already fetched a tilemap within 5km
65233 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
65234 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
65236 var z = 20; // first generate a random url using the template
65238 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
65240 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
65241 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
65243 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
65245 d3_json(tilemapUrl).then(function (tilemap) {
65247 throw new Error('Unknown Error');
65250 var hasTiles = true;
65252 for (var i = 0; i < tilemap.data.length; i++) {
65253 // 0 means an individual tile in the grid doesn't exist
65254 if (!tilemap.data[i]) {
65258 } // if any tiles are missing at level 20 we restrict maxZoom to 19
65261 esri.zoomExtent[1] = hasTiles ? 22 : 19;
65262 })["catch"](function () {
65267 esri.getMetadata = function (center, tileCoord, callback) {
65268 var tileID = tileCoord.slice(0, 3).join('/');
65269 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
65270 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
65272 var unknown = _t('info_panels.background.unknown');
65276 if (inflight[tileID]) return;
65279 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
65296 metadataLayer = 99;
65299 var url; // build up query using the layer appropriate to the current zoom
65301 if (esri.id === 'EsriWorldImagery') {
65302 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
65303 } else if (esri.id === 'EsriWorldImageryClarity') {
65304 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
65307 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
65309 if (!cache[tileID]) {
65310 cache[tileID] = {};
65313 if (cache[tileID] && cache[tileID].metadata) {
65314 return callback(null, cache[tileID].metadata);
65315 } // accurate metadata is only available >= 13
65318 if (metadataLayer === 99) {
65327 description: unknown,
65328 resolution: unknown,
65331 callback(null, metadata);
65333 inflight[tileID] = true;
65334 d3_json(url).then(function (result) {
65335 delete inflight[tileID];
65338 throw new Error('Unknown Error');
65339 } else if (result.features && result.features.length < 1) {
65340 throw new Error('No Results');
65341 } else if (result.error && result.error.message) {
65342 throw new Error(result.error.message);
65343 } // pass through the discrete capture date from metadata
65346 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
65348 start: captureDate,
65354 source: clean(result.features[0].attributes.NICE_NAME),
65355 description: clean(result.features[0].attributes.NICE_DESC),
65356 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
65357 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
65358 }; // append units - meters
65360 if (isFinite(metadata.resolution)) {
65361 metadata.resolution += ' m';
65364 if (isFinite(metadata.accuracy)) {
65365 metadata.accuracy += ' m';
65368 cache[tileID].metadata = metadata;
65369 if (callback) callback(null, metadata);
65370 })["catch"](function (err) {
65371 delete inflight[tileID];
65372 if (callback) callback(err.message);
65376 function clean(val) {
65377 return String(val).trim() || unknown;
65384 rendererBackgroundSource.None = function () {
65385 var source = rendererBackgroundSource({
65390 source.name = function () {
65391 return _t('background.none');
65394 source.label = function () {
65395 return _t.html('background.none');
65398 source.imageryUsed = function () {
65402 source.area = function () {
65403 return -1; // sources in background pane are sorted by area
65409 rendererBackgroundSource.Custom = function (template) {
65410 var source = rendererBackgroundSource({
65415 source.name = function () {
65416 return _t('background.custom');
65419 source.label = function () {
65420 return _t.html('background.custom');
65423 source.imageryUsed = function () {
65424 // sanitize personal connection tokens - #6801
65425 var cleaned = source.template(); // from query string parameters
65427 if (cleaned.indexOf('?') !== -1) {
65428 var parts = cleaned.split('?', 2);
65429 var qs = utilStringQs(parts[1]);
65430 ['access_token', 'connectId', 'token'].forEach(function (param) {
65432 qs[param] = '{apikey}';
65435 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
65436 } // from wms/wmts api path parameters
65439 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
65440 return 'Custom (' + cleaned + ' )';
65443 source.area = function () {
65444 return -2; // sources in background pane are sorted by area
65450 function rendererTileLayer(context) {
65451 var transformProp = utilPrefixCSSProperty('Transform');
65452 var tiler = utilTiler();
65453 var _tileSize = 256;
65465 function tileSizeAtZoom(d, z) {
65466 var EPSILON = 0.002; // close seams
65468 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
65471 function atZoom(t, distance) {
65472 var power = Math.pow(2, distance);
65473 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
65476 function lookUp(d) {
65477 for (var up = -1; up > -d[2]; up--) {
65478 var tile = atZoom(d, up);
65480 if (_cache[_source.url(tile)] !== false) {
65486 function uniqueBy(a, n) {
65490 for (var i = 0; i < a.length; i++) {
65491 if (seen[a[i][n]] === undefined) {
65493 seen[a[i][n]] = true;
65500 function addSource(d) {
65501 d.push(_source.url(d));
65503 } // Update tiles based on current state of `projection`.
65506 function background(selection) {
65507 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
65511 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
65513 pixelOffset = [0, 0];
65516 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
65517 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
65518 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
65520 } // Derive the tiles onscreen, remove those offscreen and position them.
65521 // Important that this part not depend on `_projection` because it's
65522 // rentered when tiles load/error (see #644).
65525 function render(selection) {
65526 if (!_source) return;
65528 var showDebug = context.getDebug('tile') && !_source.overlay;
65530 if (_source.validZoom(_zoom)) {
65531 tiler.skipNullIsland(!!_source.overlay);
65532 tiler().forEach(function (d) {
65534 if (d[3] === '') return;
65535 if (typeof d[3] !== 'string') return; // Workaround for #2295
65539 if (_cache[d[3]] === false && lookUp(d)) {
65540 requests.push(addSource(lookUp(d)));
65543 requests = uniqueBy(requests, 3).filter(function (r) {
65544 // don't re-request tiles which have failed in the past
65545 return _cache[r[3]] !== false;
65549 function load(d3_event, d) {
65550 _cache[d[3]] = true;
65551 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
65555 function error(d3_event, d) {
65556 _cache[d[3]] = false;
65557 select(this).on('error', null).on('load', null).remove();
65561 function imageTransform(d) {
65562 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65564 var scale = tileSizeAtZoom(d, _zoom);
65565 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
65568 function tileCenter(d) {
65569 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65571 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
65574 function debugTransform(d) {
65575 var coord = tileCenter(d);
65576 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
65577 } // Pick a representative tile near the center of the viewport
65578 // (This is useful for sampling the imagery vintage)
65581 var dims = tiler.size();
65582 var mapCenter = [dims[0] / 2, dims[1] / 2];
65583 var minDist = Math.max(dims[0], dims[1]);
65585 requests.forEach(function (d) {
65586 var c = tileCenter(d);
65587 var dist = geoVecLength(c, mapCenter);
65589 if (dist < minDist) {
65594 var image = selection.selectAll('img').data(requests, function (d) {
65597 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
65598 var tile = select(this);
65599 window.setTimeout(function () {
65600 if (tile.classed('tile-removing')) {
65605 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
65607 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
65608 return d === nearCenter;
65610 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
65613 debug.exit().remove();
65616 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
65617 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
65618 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
65619 debug = debug.merge(debugEnter);
65620 debug.style(transformProp, debugTransform);
65621 debug.selectAll('.tile-label-debug-coord').html(function (d) {
65622 return d[2] + ' / ' + d[0] + ' / ' + d[1];
65624 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
65625 var span = select(this);
65626 var center = context.projection.invert(tileCenter(d));
65628 _source.getMetadata(center, d, function (err, result) {
65629 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
65635 background.projection = function (val) {
65636 if (!arguments.length) return _projection;
65641 background.dimensions = function (val) {
65642 if (!arguments.length) return tiler.size();
65647 background.source = function (val) {
65648 if (!arguments.length) return _source;
65650 _tileSize = _source.tileSize;
65652 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
65659 var _imageryIndex = null;
65660 function rendererBackground(context) {
65661 var dispatch$1 = dispatch('change');
65662 var detected = utilDetect();
65663 var baseLayer = rendererTileLayer(context).projection(context.projection);
65664 var _isValid = true;
65665 var _overlayLayers = [];
65666 var _brightness = 1;
65668 var _saturation = 1;
65669 var _sharpness = 1;
65671 function ensureImageryIndex() {
65672 return _mainFileFetcher.get('imagery').then(function (sources) {
65673 if (_imageryIndex) return _imageryIndex;
65677 }; // use which-polygon to support efficient index and querying for imagery
65679 var features = sources.map(function (source) {
65680 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
65681 // Add an extra array nest to each element in `source.polygon`
65682 // so the rings are not treated as a bunch of holes:
65683 // what we have: [ [[outer],[hole],[hole]] ]
65684 // what we want: [ [[outer]],[[outer]],[[outer]] ]
65686 var rings = source.polygon.map(function (ring) {
65695 type: 'MultiPolygon',
65699 _imageryIndex.features[source.id] = feature;
65701 }).filter(Boolean);
65702 _imageryIndex.query = whichPolygon_1({
65703 type: 'FeatureCollection',
65705 }); // Instantiate `rendererBackgroundSource` objects for each source
65707 _imageryIndex.backgrounds = sources.map(function (source) {
65708 if (source.type === 'bing') {
65709 return rendererBackgroundSource.Bing(source, dispatch$1);
65710 } else if (/^EsriWorldImagery/.test(source.id)) {
65711 return rendererBackgroundSource.Esri(source);
65713 return rendererBackgroundSource(source);
65717 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
65720 var template = corePreferences('background-custom-template') || '';
65721 var custom = rendererBackgroundSource.Custom(template);
65723 _imageryIndex.backgrounds.unshift(custom);
65725 return _imageryIndex;
65729 function background(selection) {
65730 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
65731 // check its tilemap to see how high the zoom can go
65733 if (context.map().zoom() > 18) {
65734 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
65735 var center = context.map().center();
65736 currSource.fetchTilemap(center);
65738 } // Is the imagery valid here? - #4827
65741 var sources = background.sources(context.map().extent());
65742 var wasValid = _isValid;
65743 _isValid = !!sources.filter(function (d) {
65744 return d === currSource;
65747 if (wasValid !== _isValid) {
65748 // change in valid status
65749 background.updateImagery();
65752 var baseFilter = '';
65754 if (detected.cssfilters) {
65755 if (_brightness !== 1) {
65756 baseFilter += " brightness(".concat(_brightness, ")");
65759 if (_contrast !== 1) {
65760 baseFilter += " contrast(".concat(_contrast, ")");
65763 if (_saturation !== 1) {
65764 baseFilter += " saturate(".concat(_saturation, ")");
65767 if (_sharpness < 1) {
65769 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
65770 baseFilter += " blur(".concat(blur, "px)");
65774 var base = selection.selectAll('.layer-background').data([0]);
65775 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
65777 if (detected.cssfilters) {
65778 base.style('filter', baseFilter || null);
65780 base.style('opacity', _brightness);
65783 var imagery = base.selectAll('.layer-imagery').data([0]);
65784 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
65785 var maskFilter = '';
65786 var mixBlendMode = '';
65788 if (detected.cssfilters && _sharpness > 1) {
65789 // apply unsharp mask
65790 mixBlendMode = 'overlay';
65791 maskFilter = 'saturate(0) blur(3px) invert(1)';
65792 var contrast = _sharpness - 1;
65793 maskFilter += " contrast(".concat(contrast, ")");
65794 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
65795 maskFilter += " brightness(".concat(brightness, ")");
65798 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
65799 mask.exit().remove();
65800 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);
65801 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
65802 return d.source().name();
65804 overlays.exit().remove();
65805 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
65806 return select(nodes[i]).call(layer);
65810 background.updateImagery = function () {
65811 var currSource = baseLayer.source();
65812 if (context.inIntro() || !currSource) return;
65814 var o = _overlayLayers.filter(function (d) {
65815 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65816 }).map(function (d) {
65817 return d.source().id;
65820 var meters = geoOffsetToMeters(currSource.offset());
65821 var EPSILON = 0.01;
65822 var x = +meters[0].toFixed(2);
65823 var y = +meters[1].toFixed(2);
65824 var hash = utilStringQs(window.location.hash);
65825 var id = currSource.id;
65827 if (id === 'custom') {
65828 id = "custom:".concat(currSource.template());
65832 hash.background = id;
65834 delete hash.background;
65840 delete hash.overlays;
65843 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
65844 hash.offset = "".concat(x, ",").concat(y);
65846 delete hash.offset;
65849 if (!window.mocha) {
65850 window.location.replace('#' + utilQsString(hash, true));
65853 var imageryUsed = [];
65854 var photoOverlaysUsed = [];
65855 var currUsed = currSource.imageryUsed();
65857 if (currUsed && _isValid) {
65858 imageryUsed.push(currUsed);
65861 _overlayLayers.filter(function (d) {
65862 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65863 }).forEach(function (d) {
65864 return imageryUsed.push(d.source().imageryUsed());
65867 var dataLayer = context.layers().layer('data');
65869 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
65870 imageryUsed.push(dataLayer.getSrc());
65873 var photoOverlayLayers = {
65874 streetside: 'Bing Streetside',
65875 mapillary: 'Mapillary Images',
65876 'mapillary-map-features': 'Mapillary Map Features',
65877 'mapillary-signs': 'Mapillary Signs',
65878 openstreetcam: 'OpenStreetCam Images'
65881 for (var layerID in photoOverlayLayers) {
65882 var layer = context.layers().layer(layerID);
65884 if (layer && layer.enabled()) {
65885 photoOverlaysUsed.push(layerID);
65886 imageryUsed.push(photoOverlayLayers[layerID]);
65890 context.history().imageryUsed(imageryUsed);
65891 context.history().photoOverlaysUsed(photoOverlaysUsed);
65894 var _checkedBlocklists;
65896 background.sources = function (extent, zoom, includeCurrent) {
65897 if (!_imageryIndex) return []; // called before init()?
65900 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
65901 return visible[d.id] = true;
65903 var currSource = baseLayer.source();
65904 var osm = context.connection();
65905 var blocklists = osm && osm.imageryBlocklists();
65907 if (blocklists && blocklists !== _checkedBlocklists) {
65908 _imageryIndex.backgrounds.forEach(function (source) {
65909 source.isBlocked = blocklists.some(function (blocklist) {
65910 return blocklist.test(source.template());
65914 _checkedBlocklists = blocklists;
65917 return _imageryIndex.backgrounds.filter(function (source) {
65918 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
65920 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
65922 if (!source.polygon) return true; // always include imagery with worldwide coverage
65924 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
65926 return visible[source.id]; // include imagery visible in given extent
65930 background.dimensions = function (val) {
65932 baseLayer.dimensions(val);
65934 _overlayLayers.forEach(function (layer) {
65935 return layer.dimensions(val);
65939 background.baseLayerSource = function (d) {
65940 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
65942 var osm = context.connection();
65943 if (!osm) return background;
65944 var blocklists = osm.imageryBlocklists();
65945 var template = d.template();
65950 for (var i = 0; i < blocklists.length; i++) {
65951 regex = blocklists[i];
65952 fail = regex.test(template);
65955 } // ensure at least one test was run.
65959 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
65960 fail = regex.test(template);
65963 baseLayer.source(!fail ? d : background.findSource('none'));
65964 dispatch$1.call('change');
65965 background.updateImagery();
65969 background.findSource = function (id) {
65970 if (!id || !_imageryIndex) return null; // called before init()?
65972 return _imageryIndex.backgrounds.find(function (d) {
65973 return d.id && d.id === id;
65977 background.bing = function () {
65978 background.baseLayerSource(background.findSource('Bing'));
65981 background.showsLayer = function (d) {
65982 var currSource = baseLayer.source();
65983 if (!d || !currSource) return false;
65984 return d.id === currSource.id || _overlayLayers.some(function (layer) {
65985 return d.id === layer.source().id;
65989 background.overlayLayerSources = function () {
65990 return _overlayLayers.map(function (layer) {
65991 return layer.source();
65995 background.toggleOverlayLayer = function (d) {
65998 for (var i = 0; i < _overlayLayers.length; i++) {
65999 layer = _overlayLayers[i];
66001 if (layer.source() === d) {
66002 _overlayLayers.splice(i, 1);
66004 dispatch$1.call('change');
66005 background.updateImagery();
66010 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
66012 _overlayLayers.push(layer);
66014 dispatch$1.call('change');
66015 background.updateImagery();
66018 background.nudge = function (d, zoom) {
66019 var currSource = baseLayer.source();
66022 currSource.nudge(d, zoom);
66023 dispatch$1.call('change');
66024 background.updateImagery();
66030 background.offset = function (d) {
66031 var currSource = baseLayer.source();
66033 if (!arguments.length) {
66034 return currSource && currSource.offset() || [0, 0];
66038 currSource.offset(d);
66039 dispatch$1.call('change');
66040 background.updateImagery();
66046 background.brightness = function (d) {
66047 if (!arguments.length) return _brightness;
66049 if (context.mode()) dispatch$1.call('change');
66053 background.contrast = function (d) {
66054 if (!arguments.length) return _contrast;
66056 if (context.mode()) dispatch$1.call('change');
66060 background.saturation = function (d) {
66061 if (!arguments.length) return _saturation;
66063 if (context.mode()) dispatch$1.call('change');
66067 background.sharpness = function (d) {
66068 if (!arguments.length) return _sharpness;
66070 if (context.mode()) dispatch$1.call('change');
66076 background.ensureLoaded = function () {
66077 if (_loadPromise) return _loadPromise;
66079 function parseMapParams(qmap) {
66080 if (!qmap) return false;
66081 var params = qmap.split('/').map(Number);
66082 if (params.length < 3 || params.some(isNaN)) return false;
66083 return geoExtent([params[2], params[1]]); // lon,lat
66086 var hash = utilStringQs(window.location.hash);
66087 var requested = hash.background || hash.layer;
66088 var extent = parseMapParams(hash.map);
66089 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
66090 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
66093 if (!requested && extent) {
66094 best = background.sources(extent).find(function (s) {
66097 } // Decide which background layer to display
66100 if (requested && requested.indexOf('custom:') === 0) {
66101 var template = requested.replace(/^custom:/, '');
66102 var custom = background.findSource('custom');
66103 background.baseLayerSource(custom.template(template));
66104 corePreferences('background-custom-template', template);
66106 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
66109 var locator = imageryIndex.backgrounds.find(function (d) {
66110 return d.overlay && d["default"];
66114 background.toggleOverlayLayer(locator);
66117 var overlays = (hash.overlays || '').split(',');
66118 overlays.forEach(function (overlay) {
66119 overlay = background.findSource(overlay);
66122 background.toggleOverlayLayer(overlay);
66127 var gpx = context.layers().layer('data');
66130 gpx.url(hash.gpx, '.gpx');
66135 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
66136 return !isNaN(n) && n;
66139 if (offset.length === 2) {
66140 background.offset(geoMetersToOffset(offset));
66143 })["catch"](function () {
66148 return utilRebind(background, dispatch$1, 'on');
66151 function rendererFeatures(context) {
66152 var dispatch$1 = dispatch('change', 'redraw');
66153 var features = utilRebind({}, dispatch$1, 'on');
66155 var _deferred = new Set();
66157 var traffic_roads = {
66159 'motorway_link': true,
66161 'trunk_link': true,
66163 'primary_link': true,
66165 'secondary_link': true,
66167 'tertiary_link': true,
66168 'residential': true,
66169 'unclassified': true,
66170 'living_street': true
66172 var service_roads = {
66185 var past_futures = {
66187 'construction': true,
66189 'dismantled': true,
66192 'demolished': true,
66193 'obliterated': true
66195 var _cullFactor = 1;
66201 var _forceVisible = {};
66203 function update() {
66204 if (!window.mocha) {
66205 var hash = utilStringQs(window.location.hash);
66206 var disabled = features.disabled();
66208 if (disabled.length) {
66209 hash.disable_features = disabled.join(',');
66211 delete hash.disable_features;
66214 window.location.replace('#' + utilQsString(hash, true));
66215 corePreferences('disabled-features', disabled.join(','));
66218 _hidden = features.hidden();
66219 dispatch$1.call('change');
66220 dispatch$1.call('redraw');
66223 function defineRule(k, filter, max) {
66224 var isEnabled = true;
66230 enabled: isEnabled,
66231 // whether the user wants it enabled..
66233 currentMax: max || Infinity,
66234 defaultMax: max || Infinity,
66235 enable: function enable() {
66236 this.enabled = true;
66237 this.currentMax = this.defaultMax;
66239 disable: function disable() {
66240 this.enabled = false;
66241 this.currentMax = 0;
66243 hidden: function hidden() {
66244 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
66246 autoHidden: function autoHidden() {
66247 return this.hidden() && this.currentMax > 0;
66252 defineRule('points', function isPoint(tags, geometry) {
66253 return geometry === 'point';
66255 defineRule('traffic_roads', function isTrafficRoad(tags) {
66256 return traffic_roads[tags.highway];
66258 defineRule('service_roads', function isServiceRoad(tags) {
66259 return service_roads[tags.highway];
66261 defineRule('paths', function isPath(tags) {
66262 return paths[tags.highway];
66264 defineRule('buildings', function isBuilding(tags) {
66265 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
66267 defineRule('building_parts', function isBuildingPart(tags) {
66268 return tags['building:part'];
66270 defineRule('indoor', function isIndoor(tags) {
66271 return tags.indoor;
66273 defineRule('landuse', function isLanduse(tags, geometry) {
66274 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
66276 defineRule('boundaries', function isBoundary(tags) {
66277 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);
66279 defineRule('water', function isWater(tags) {
66280 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';
66282 defineRule('rail', function isRail(tags) {
66283 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
66285 defineRule('pistes', function isPiste(tags) {
66286 return tags['piste:type'];
66288 defineRule('aerialways', function isPiste(tags) {
66289 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
66291 defineRule('power', function isPower(tags) {
66292 return !!tags.power;
66293 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
66295 defineRule('past_future', function isPastFuture(tags) {
66296 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
66300 var strings = Object.keys(tags);
66302 for (var i = 0; i < strings.length; i++) {
66303 var s = strings[i];
66305 if (past_futures[s] || past_futures[tags[s]]) {
66311 }); // Lines or areas that don't match another feature filter.
66312 // IMPORTANT: The 'others' feature must be the last one defined,
66313 // so that code in getMatches can skip this test if `hasMatch = true`
66315 defineRule('others', function isOther(tags, geometry) {
66316 return geometry === 'line' || geometry === 'area';
66319 features.features = function () {
66323 features.keys = function () {
66327 features.enabled = function (k) {
66328 if (!arguments.length) {
66329 return _keys.filter(function (k) {
66330 return _rules[k].enabled;
66334 return _rules[k] && _rules[k].enabled;
66337 features.disabled = function (k) {
66338 if (!arguments.length) {
66339 return _keys.filter(function (k) {
66340 return !_rules[k].enabled;
66344 return _rules[k] && !_rules[k].enabled;
66347 features.hidden = function (k) {
66348 if (!arguments.length) {
66349 return _keys.filter(function (k) {
66350 return _rules[k].hidden();
66354 return _rules[k] && _rules[k].hidden();
66357 features.autoHidden = function (k) {
66358 if (!arguments.length) {
66359 return _keys.filter(function (k) {
66360 return _rules[k].autoHidden();
66364 return _rules[k] && _rules[k].autoHidden();
66367 features.enable = function (k) {
66368 if (_rules[k] && !_rules[k].enabled) {
66369 _rules[k].enable();
66375 features.enableAll = function () {
66376 var didEnable = false;
66378 for (var k in _rules) {
66379 if (!_rules[k].enabled) {
66382 _rules[k].enable();
66386 if (didEnable) update();
66389 features.disable = function (k) {
66390 if (_rules[k] && _rules[k].enabled) {
66391 _rules[k].disable();
66397 features.disableAll = function () {
66398 var didDisable = false;
66400 for (var k in _rules) {
66401 if (_rules[k].enabled) {
66404 _rules[k].disable();
66408 if (didDisable) update();
66411 features.toggle = function (k) {
66414 return f.enabled ? f.disable() : f.enable();
66421 features.resetStats = function () {
66422 for (var i = 0; i < _keys.length; i++) {
66423 _rules[_keys[i]].count = 0;
66426 dispatch$1.call('change');
66429 features.gatherStats = function (d, resolver, dimensions) {
66430 var needsRedraw = false;
66431 var types = utilArrayGroupBy(d, 'type');
66432 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66433 var currHidden, geometry, matches, i, j;
66435 for (i = 0; i < _keys.length; i++) {
66436 _rules[_keys[i]].count = 0;
66437 } // adjust the threshold for point/building culling based on viewport size..
66438 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
66441 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
66443 for (i = 0; i < entities.length; i++) {
66444 geometry = entities[i].geometry(resolver);
66445 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
66447 for (j = 0; j < matches.length; j++) {
66448 _rules[matches[j]].count++;
66452 currHidden = features.hidden();
66454 if (currHidden !== _hidden) {
66455 _hidden = currHidden;
66456 needsRedraw = true;
66457 dispatch$1.call('change');
66460 return needsRedraw;
66463 features.stats = function () {
66464 for (var i = 0; i < _keys.length; i++) {
66465 _stats[_keys[i]] = _rules[_keys[i]].count;
66471 features.clear = function (d) {
66472 for (var i = 0; i < d.length; i++) {
66473 features.clearEntity(d[i]);
66477 features.clearEntity = function (entity) {
66478 delete _cache[osmEntity.key(entity)];
66481 features.reset = function () {
66482 Array.from(_deferred).forEach(function (handle) {
66483 window.cancelIdleCallback(handle);
66485 _deferred["delete"](handle);
66488 }; // only certain relations are worth checking
66491 function relationShouldBeChecked(relation) {
66492 // multipolygon features have `area` geometry and aren't checked here
66493 return relation.tags.type === 'boundary';
66496 features.getMatches = function (entity, resolver, geometry) {
66497 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
66498 var ent = osmEntity.key(entity);
66500 if (!_cache[ent]) {
66504 if (!_cache[ent].matches) {
66506 var hasMatch = false;
66508 for (var i = 0; i < _keys.length; i++) {
66509 if (_keys[i] === 'others') {
66510 if (hasMatch) continue; // If an entity...
66511 // 1. is a way that hasn't matched other 'interesting' feature rules,
66513 if (entity.type === 'way') {
66514 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
66516 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
66517 parents.length > 0 && parents.every(function (parent) {
66518 return parent.tags.type === 'boundary';
66520 // ...then match whatever feature rules the parent relation has matched.
66521 // see #2548, #2887
66524 // For this to work, getMatches must be called on relations before ways.
66526 var pkey = osmEntity.key(parents[0]);
66528 if (_cache[pkey] && _cache[pkey].matches) {
66529 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
66537 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
66538 matches[_keys[i]] = hasMatch = true;
66542 _cache[ent].matches = matches;
66545 return _cache[ent].matches;
66548 features.getParents = function (entity, resolver, geometry) {
66549 if (geometry === 'point') return [];
66550 var ent = osmEntity.key(entity);
66552 if (!_cache[ent]) {
66556 if (!_cache[ent].parents) {
66559 if (geometry === 'vertex') {
66560 parents = resolver.parentWays(entity);
66562 // 'line', 'area', 'relation'
66563 parents = resolver.parentRelations(entity);
66566 _cache[ent].parents = parents;
66569 return _cache[ent].parents;
66572 features.isHiddenPreset = function (preset, geometry) {
66573 if (!_hidden.length) return false;
66574 if (!preset.tags) return false;
66575 var test = preset.setTags({}, geometry);
66577 for (var key in _rules) {
66578 if (_rules[key].filter(test, geometry)) {
66579 if (_hidden.indexOf(key) !== -1) {
66590 features.isHiddenFeature = function (entity, resolver, geometry) {
66591 if (!_hidden.length) return false;
66592 if (!entity.version) return false;
66593 if (_forceVisible[entity.id]) return false;
66594 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
66595 return matches.length && matches.every(function (k) {
66596 return features.hidden(k);
66600 features.isHiddenChild = function (entity, resolver, geometry) {
66601 if (!_hidden.length) return false;
66602 if (!entity.version || geometry === 'point') return false;
66603 if (_forceVisible[entity.id]) return false;
66604 var parents = features.getParents(entity, resolver, geometry);
66605 if (!parents.length) return false;
66607 for (var i = 0; i < parents.length; i++) {
66608 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
66616 features.hasHiddenConnections = function (entity, resolver) {
66617 if (!_hidden.length) return false;
66618 var childNodes, connections;
66620 if (entity.type === 'midpoint') {
66621 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
66624 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
66625 connections = features.getParents(entity, resolver, entity.geometry(resolver));
66626 } // gather ways connected to child nodes..
66629 connections = childNodes.reduce(function (result, e) {
66630 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
66632 return connections.some(function (e) {
66633 return features.isHidden(e, resolver, e.geometry(resolver));
66637 features.isHidden = function (entity, resolver, geometry) {
66638 if (!_hidden.length) return false;
66639 if (!entity.version) return false;
66640 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
66641 return fn(entity, resolver, geometry);
66644 features.filter = function (d, resolver) {
66645 if (!_hidden.length) return d;
66648 for (var i = 0; i < d.length; i++) {
66651 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
66652 result.push(entity);
66659 features.forceVisible = function (entityIDs) {
66660 if (!arguments.length) return Object.keys(_forceVisible);
66661 _forceVisible = {};
66663 for (var i = 0; i < entityIDs.length; i++) {
66664 _forceVisible[entityIDs[i]] = true;
66665 var entity = context.hasEntity(entityIDs[i]);
66667 if (entity && entity.type === 'relation') {
66668 // also show relation members (one level deep)
66669 for (var j in entity.members) {
66670 _forceVisible[entity.members[j].id] = true;
66678 features.init = function () {
66679 var storage = corePreferences('disabled-features');
66682 var storageDisabled = storage.replace(/;/g, ',').split(',');
66683 storageDisabled.forEach(features.disable);
66686 var hash = utilStringQs(window.location.hash);
66688 if (hash.disable_features) {
66689 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
66690 hashDisabled.forEach(features.disable);
66692 }; // warm up the feature matching cache upon merging fetched data
66695 context.history().on('merge.features', function (newEntities) {
66696 if (!newEntities) return;
66697 var handle = window.requestIdleCallback(function () {
66698 var graph = context.graph();
66699 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
66701 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66703 for (var i = 0; i < entities.length; i++) {
66704 var geometry = entities[i].geometry(graph);
66705 features.getMatches(entities[i], graph, geometry);
66709 _deferred.add(handle);
66715 // - the activeID - nope
66716 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
66717 // - 2 away from the activeID - nope (would create a self intersecting segment)
66718 // - all others on a linear way - yes
66719 // - all others on a closed way - nope (would create a self intersecting polygon)
66722 // 0 = active vertex - no touch/connect
66723 // 1 = passive vertex - yes touch/connect
66724 // 2 = adjacent vertex - yes but pay attention segmenting a line here
66727 function svgPassiveVertex(node, graph, activeID) {
66728 if (!activeID) return 1;
66729 if (activeID === node.id) return 0;
66730 var parents = graph.parentWays(node);
66731 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
66733 for (i = 0; i < parents.length; i++) {
66734 nodes = parents[i].nodes;
66735 isClosed = parents[i].isClosed();
66737 for (j = 0; j < nodes.length; j++) {
66738 // find this vertex, look nearby
66739 if (nodes[j] === node.id) {
66746 // wraparound if needed
66747 max = nodes.length - 1;
66748 if (ix1 < 0) ix1 = max + ix1;
66749 if (ix2 < 0) ix2 = max + ix2;
66750 if (ix3 > max) ix3 = ix3 - max;
66751 if (ix4 > max) ix4 = ix4 - max;
66754 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
66755 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
66756 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
66757 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
66758 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
66765 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
66766 return function (entity) {
66770 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
66771 var coordinates = graph.childNodes(entity).map(function (n) {
66776 if (shouldReverse(entity)) {
66777 coordinates.reverse();
66781 type: 'LineString',
66782 coordinates: coordinates
66783 }, projection.stream(clip({
66784 lineStart: function lineStart() {},
66785 lineEnd: function lineEnd() {
66788 point: function point(x, y) {
66792 var span = geoVecLength(a, b) - offset;
66795 var heading = geoVecAngle(a, b);
66796 var dx = dt * Math.cos(heading);
66797 var dy = dt * Math.sin(heading);
66798 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
66800 var coord = [a, p];
66802 for (span -= dt; span >= 0; span -= dt) {
66803 p = geoVecAdd(p, [dx, dy]);
66807 coord.push(b); // generate svg paths
66812 for (j = 0; j < coord.length; j++) {
66813 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66822 if (bothDirections(entity)) {
66825 for (j = coord.length - 1; j >= 0; j--) {
66826 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66846 function svgPath(projection, graph, isArea) {
66847 // Explanation of magic numbers:
66848 // "padding" here allows space for strokes to extend beyond the viewport,
66849 // so that the stroke isn't drawn along the edge of the viewport when
66850 // the shape is clipped.
66852 // When drawing lines, pad viewport by 5px.
66853 // When drawing areas, pad viewport by 65px in each direction to allow
66854 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
66856 var padding = isArea ? 65 : 5;
66857 var viewport = projection.clipExtent();
66858 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
66859 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
66860 var project = projection.stream;
66861 var path = d3_geoPath().projection({
66862 stream: function stream(output) {
66863 return project(clip(output));
66867 var svgpath = function svgpath(entity) {
66868 if (entity.id in cache) {
66869 return cache[entity.id];
66871 return cache[entity.id] = path(entity.asGeoJSON(graph));
66875 svgpath.geojson = function (d) {
66876 if (d.__featurehash__ !== undefined) {
66877 if (d.__featurehash__ in cache) {
66878 return cache[d.__featurehash__];
66880 return cache[d.__featurehash__] = path(d);
66889 function svgPointTransform(projection) {
66890 var svgpoint = function svgpoint(entity) {
66891 // http://jsperf.com/short-array-join
66892 var pt = projection(entity.loc);
66893 return 'translate(' + pt[0] + ',' + pt[1] + ')';
66896 svgpoint.geojson = function (d) {
66897 return svgpoint(d.properties.entity);
66902 function svgRelationMemberTags(graph) {
66903 return function (entity) {
66904 var tags = entity.tags;
66905 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
66906 graph.parentRelations(entity).forEach(function (relation) {
66907 var type = relation.tags.type;
66909 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
66910 tags = Object.assign({}, relation.tags, tags);
66916 function svgSegmentWay(way, graph, activeID) {
66917 // When there is no activeID, we can memoize this expensive computation
66918 if (activeID === undefined) {
66919 return graph["transient"](way, 'waySegments', getWaySegments);
66921 return getWaySegments();
66924 function getWaySegments() {
66925 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
66934 for (var i = 0; i < way.nodes.length; i++) {
66935 node = graph.entity(way.nodes[i]);
66936 type = svgPassiveVertex(node, graph, activeID);
66942 if (start.type !== undefined) {
66943 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
66944 // one adjacent vertex
66945 pushActive(start, end, i);
66946 } else if (start.type === 0 && end.type === 0) {
66947 // both active vertices
66948 pushActive(start, end, i);
66950 pushPassive(start, end, i);
66959 function pushActive(start, end, index) {
66960 features.active.push({
66962 id: way.id + '-' + index + '-nope',
66967 nodes: [start.node, end.node],
66971 type: 'LineString',
66972 coordinates: [start.node.loc, end.node.loc]
66977 function pushPassive(start, end, index) {
66978 features.passive.push({
66980 id: way.id + '-' + index,
66984 nodes: [start.node, end.node],
66988 type: 'LineString',
66989 coordinates: [start.node.loc, end.node.loc]
66996 function svgTagClasses() {
66997 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'];
66998 var statuses = [// nonexistent, might be built
66999 'proposed', 'planned', // under maintentance or between groundbreaking and opening
67000 'construction', // existent but not functional
67001 'disused', // dilapidated to nonexistent
67002 'abandoned', // nonexistent, still may appear in imagery
67003 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
67005 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
67007 var _tags = function _tags(entity) {
67008 return entity.tags;
67011 var tagClasses = function tagClasses(selection) {
67012 selection.each(function tagClassesEach(entity) {
67013 var value = this.className;
67015 if (value.baseVal !== undefined) {
67016 value = value.baseVal;
67019 var t = _tags(entity);
67021 var computed = tagClasses.getClassesString(t, value);
67023 if (computed !== value) {
67024 select(this).attr('class', computed);
67029 tagClasses.getClassesString = function (t, value) {
67030 var primary, status;
67031 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
67033 var overrideGeometry;
67035 if (/\bstroke\b/.test(value)) {
67036 if (!!t.barrier && t.barrier !== 'no') {
67037 overrideGeometry = 'line';
67039 } // preserve base classes (nothing with `tag-`)
67042 var classes = value.trim().split(/\s+/).filter(function (klass) {
67043 return klass.length && !/^tag-/.test(klass);
67044 }).map(function (klass) {
67045 // special overrides for some perimeter strokes
67046 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
67047 }); // pick at most one primary classification tag..
67049 for (i = 0; i < primaries.length; i++) {
67052 if (!v || v === 'no') continue;
67054 if (k === 'piste:type') {
67055 // avoid a ':' in the class name
67057 } else if (k === 'building:part') {
67058 // avoid a ':' in the class name
67059 k = 'building_part';
67064 if (statuses.indexOf(v) !== -1) {
67065 // e.g. `railway=abandoned`
67067 classes.push('tag-' + k);
67069 classes.push('tag-' + k);
67070 classes.push('tag-' + k + '-' + v);
67077 for (i = 0; i < statuses.length; i++) {
67078 for (j = 0; j < primaries.length; j++) {
67079 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
67082 if (!v || v === 'no') continue;
67083 status = statuses[i];
67087 } // add at most one status tag, only if relates to primary tag..
67091 for (i = 0; i < statuses.length; i++) {
67094 if (!v || v === 'no') continue;
67097 // e.g. `railway=rail + abandoned=yes`
67099 } else if (primary && primary === v) {
67100 // e.g. `railway=rail + abandoned=railway`
67102 } else if (!primary && primaries.indexOf(v) !== -1) {
67103 // e.g. `abandoned=railway`
67106 classes.push('tag-' + v);
67107 } // else ignore e.g. `highway=path + abandoned=railway`
67115 classes.push('tag-status');
67116 classes.push('tag-status-' + status);
67117 } // add any secondary tags
67120 for (i = 0; i < secondaries.length; i++) {
67121 k = secondaries[i];
67123 if (!v || v === 'no' || k === primary) continue;
67124 classes.push('tag-' + k);
67125 classes.push('tag-' + k + '-' + v);
67126 } // For highways, look for surface tagging..
67129 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
67130 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
67135 if (k in osmPavedTags) {
67136 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
67139 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
67140 surface = 'semipaved';
67144 classes.push('tag-' + surface);
67145 } // If this is a wikidata-tagged item, add a class for that..
67148 if (t.wikidata || t['brand:wikidata']) {
67149 classes.push('tag-wikidata');
67152 return classes.join(' ').trim();
67155 tagClasses.tags = function (val) {
67156 if (!arguments.length) return _tags;
67164 // Patterns only work in Firefox when set directly on element.
67165 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
67167 // tag - pattern name
67169 // tag - value - pattern name
67171 // tag - value - rules (optional tag-values, pattern name)
67172 // (matches earlier rules first, so fallback should be last entry)
67174 grave_yard: 'cemetery',
67175 fountain: 'water_standing'
67179 religion: 'christian',
67180 pattern: 'cemetery_christian'
67182 religion: 'buddhist',
67183 pattern: 'cemetery_buddhist'
67185 religion: 'muslim',
67186 pattern: 'cemetery_muslim'
67188 religion: 'jewish',
67189 pattern: 'cemetery_jewish'
67191 pattern: 'cemetery'
67193 construction: 'construction',
67194 farmland: 'farmland',
67195 farmyard: 'farmyard',
67197 leaf_type: 'broadleaved',
67198 pattern: 'forest_broadleaved'
67200 leaf_type: 'needleleaved',
67201 pattern: 'forest_needleleaved'
67203 leaf_type: 'leafless',
67204 pattern: 'forest_leafless'
67207 } // same as 'leaf_type:mixed'
67209 grave_yard: 'cemetery',
67212 pattern: 'golf_green'
67216 landfill: 'landfill',
67218 military: 'construction',
67219 orchard: 'orchard',
67221 vineyard: 'vineyard'
67225 grassland: 'grass',
67232 water: 'reservoir',
67233 pattern: 'water_standing'
67239 pattern: 'wetland_marsh'
67242 pattern: 'wetland_swamp'
67245 pattern: 'wetland_bog'
67247 wetland: 'reedbed',
67248 pattern: 'wetland_reedbed'
67253 leaf_type: 'broadleaved',
67254 pattern: 'forest_broadleaved'
67256 leaf_type: 'needleleaved',
67257 pattern: 'forest_needleleaved'
67259 leaf_type: 'leafless',
67260 pattern: 'forest_leafless'
67263 } // same as 'leaf_type:mixed'
67281 function svgTagPattern(tags) {
67282 // Skip pattern filling if this is a building (buildings don't get patterns applied)
67283 if (tags.building && tags.building !== 'no') {
67287 for (var tag in patterns) {
67288 var entityValue = tags[tag];
67289 if (!entityValue) continue;
67291 if (typeof patterns[tag] === 'string') {
67292 // extra short syntax (just tag) - pattern name
67293 return 'pattern-' + patterns[tag];
67295 var values = patterns[tag];
67297 for (var value in values) {
67298 if (entityValue !== value) continue;
67299 var rules = values[value];
67301 if (typeof rules === 'string') {
67302 // short syntax - pattern name
67303 return 'pattern-' + rules;
67304 } // long syntax - rule array
67307 for (var ruleKey in rules) {
67308 var rule = rules[ruleKey];
67311 for (var criterion in rule) {
67312 if (criterion !== 'pattern') {
67313 // reserved for pattern name
67314 // The only rule is a required tag-value pair
67315 var v = tags[criterion];
67317 if (!v || v !== rule[criterion]) {
67325 return 'pattern-' + rule.pattern;
67335 function svgAreas(projection, context) {
67336 function getPatternStyle(tags) {
67337 var imageID = svgTagPattern(tags);
67340 return 'url("#ideditor-' + imageID + '")';
67346 function drawTargets(selection, graph, entities, filter) {
67347 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
67348 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
67349 var getPath = svgPath(projection).geojson;
67350 var activeID = context.activeID();
67351 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
67357 entities.forEach(function (way) {
67358 var features = svgSegmentWay(way, graph, activeID);
67359 data.targets.push.apply(data.targets, features.passive);
67360 data.nopes.push.apply(data.nopes, features.active);
67361 }); // Targets allow hover and vertex snapping
67363 var targetData = data.targets.filter(getPath);
67364 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
67365 return filter(d.properties.entity);
67366 }).data(targetData, function key(d) {
67370 targets.exit().remove();
67372 var segmentWasEdited = function segmentWasEdited(d) {
67373 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
67375 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
67379 return d.properties.nodes.some(function (n) {
67380 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
67385 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
67386 return 'way area target target-allowed ' + targetClass + d.id;
67387 }).classed('segment-edited', segmentWasEdited); // NOPE
67389 var nopeData = data.nopes.filter(getPath);
67390 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
67391 return filter(d.properties.entity);
67392 }).data(nopeData, function key(d) {
67396 nopes.exit().remove(); // enter/update
67398 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
67399 return 'way area target target-nope ' + nopeClass + d.id;
67400 }).classed('segment-edited', segmentWasEdited);
67403 function drawAreas(selection, graph, entities, filter) {
67404 var path = svgPath(projection, graph, true);
67407 var base = context.history().base();
67409 for (var i = 0; i < entities.length; i++) {
67410 var entity = entities[i];
67411 if (entity.geometry(graph) !== 'area') continue;
67412 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
67414 if (multipolygon) {
67415 areas[multipolygon.id] = {
67416 entity: multipolygon.mergeTags(entity.tags),
67417 area: Math.abs(entity.area(graph))
67419 } else if (!areas[entity.id]) {
67420 areas[entity.id] = {
67422 area: Math.abs(entity.area(graph))
67427 var fills = Object.values(areas).filter(function hasPath(a) {
67428 return path(a.entity);
67430 fills.sort(function areaSort(a, b) {
67431 return b.area - a.area;
67433 fills = fills.map(function (a) {
67436 var strokes = fills.filter(function (area) {
67437 return area.type === 'way';
67445 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
67446 clipPaths.exit().remove();
67447 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
67448 return 'ideditor-' + entity.id + '-clippath';
67450 clipPathsEnter.append('path');
67451 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
67452 var drawLayer = selection.selectAll('.layer-osm.areas');
67453 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
67455 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
67456 areagroup = areagroup.enter().append('g').attr('class', function (d) {
67457 return 'areagroup area-' + d;
67458 }).merge(areagroup);
67459 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
67460 return data[layer];
67462 paths.exit().remove();
67463 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
67464 var bisect = d3_bisector(function (node) {
67465 return -node.__data__.area(graph);
67468 function sortedByArea(entity) {
67469 if (this._parent.__data__ === 'fill') {
67470 return fillpaths[bisect(fillpaths, -entity.area(graph))];
67474 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
67475 var layer = this.parentNode.__data__;
67476 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
67478 if (layer === 'fill') {
67479 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
67480 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
67482 }).classed('added', function (d) {
67483 return !base.entities[d.id];
67484 }).classed('geometry-edited', function (d) {
67485 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
67486 }).classed('retagged', function (d) {
67487 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
67488 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
67490 touchLayer.call(drawTargets, graph, data.stroke, filter);
67496 //[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]
67497 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
67498 //[5] Name ::= NameStartChar (NameChar)*
67499 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
67501 var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
67502 var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
67503 //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(',')
67504 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67505 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67507 var S_TAG = 0; //tag name offerring
67509 var S_ATTR = 1; //attr name offerring
67511 var S_ATTR_SPACE = 2; //attr name end and space offer
67513 var S_EQ = 3; //=space?
67515 var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
67517 var S_ATTR_END = 5; //attr value end and no space(quot end)
67519 var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
67521 var S_TAG_CLOSE = 7; //closed el<el />
67523 function XMLReader() {}
67525 XMLReader.prototype = {
67526 parse: function parse(source, defaultNSMap, entityMap) {
67527 var domBuilder = this.domBuilder;
67528 domBuilder.startDocument();
67530 _copy(defaultNSMap, defaultNSMap = {});
67532 _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
67534 domBuilder.endDocument();
67538 function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
67539 function fixedFromCharCode(code) {
67540 // String.prototype.fromCharCode does not supports
67541 // > 2 bytes unicode chars directly
67542 if (code > 0xffff) {
67544 var surrogate1 = 0xd800 + (code >> 10),
67545 surrogate2 = 0xdc00 + (code & 0x3ff);
67546 return String.fromCharCode(surrogate1, surrogate2);
67548 return String.fromCharCode(code);
67552 function entityReplacer(a) {
67553 var k = a.slice(1, -1);
67555 if (k in entityMap) {
67556 return entityMap[k];
67557 } else if (k.charAt(0) === '#') {
67558 return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
67560 errorHandler.error('entity not found:' + a);
67565 function appendText(end) {
67568 var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
67569 locator && position(start);
67570 domBuilder.characters(xt, 0, end - start);
67575 function position(p, m) {
67576 while (p >= lineEnd && (m = linePattern.exec(source))) {
67577 lineStart = m.index;
67578 lineEnd = lineStart + m[0].length;
67579 locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
67582 locator.columnNumber = p - lineStart + 1;
67587 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
67588 var locator = domBuilder.locator;
67589 var parseStack = [{
67590 currentNSMap: defaultNSMapCopy
67597 var tagStart = source.indexOf('<', start);
67599 if (tagStart < 0) {
67600 if (!source.substr(start).match(/^\s*$/)) {
67601 var doc = domBuilder.doc;
67602 var text = doc.createTextNode(source.substr(start));
67603 doc.appendChild(text);
67604 domBuilder.currentElement = text;
67610 if (tagStart > start) {
67611 appendText(tagStart);
67614 switch (source.charAt(tagStart + 1)) {
67616 var end = source.indexOf('>', tagStart + 3);
67617 var tagName = source.substring(tagStart + 2, end);
67618 var config = parseStack.pop();
67621 tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
67623 errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
67624 end = tagStart + 1 + tagName.length;
67625 } else if (tagName.match(/\s</)) {
67626 tagName = tagName.replace(/[\s<].*/, '');
67627 errorHandler.error("end tag name: " + tagName + ' maybe not complete');
67628 end = tagStart + 1 + tagName.length;
67629 } //console.error(parseStack.length,parseStack)
67630 //console.error(config);
67633 var localNSMap = config.localNSMap;
67634 var endMatch = config.tagName == tagName;
67635 var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
67637 if (endIgnoreCaseMach) {
67638 domBuilder.endElement(config.uri, config.localName, tagName);
67641 for (var prefix in localNSMap) {
67642 domBuilder.endPrefixMapping(prefix);
67647 errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
67650 parseStack.push(config);
67659 locator && position(tagStart);
67660 end = parseInstruction(source, tagStart, domBuilder);
67664 // <!doctype,<![CDATA,<!--
67665 locator && position(tagStart);
67666 end = parseDCC(source, tagStart, domBuilder, errorHandler);
67670 locator && position(tagStart);
67671 var el = new ElementAttributes();
67672 var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
67674 var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
67675 var len = el.length;
67677 if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
67680 if (!entityMap.nbsp) {
67681 errorHandler.warning('unclosed xml attribute');
67685 if (locator && len) {
67686 var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
67688 for (var i = 0; i < len; i++) {
67690 position(a.offset);
67691 a.locator = copyLocator(locator, {});
67692 } //}catch(e){console.error('@@@@@'+e)}
67695 domBuilder.locator = locator2;
67697 if (appendElement(el, domBuilder, currentNSMap)) {
67698 parseStack.push(el);
67701 domBuilder.locator = locator;
67703 if (appendElement(el, domBuilder, currentNSMap)) {
67704 parseStack.push(el);
67708 if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
67709 end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
67716 errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
67718 end = -1; //throw e;
67724 //TODO: 这里有可能sax回退,有位置错误风险
67725 appendText(Math.max(tagStart, start) + 1);
67730 function copyLocator(f, t) {
67731 t.lineNumber = f.lineNumber;
67732 t.columnNumber = f.columnNumber;
67736 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
67737 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
67741 function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
67745 var s = S_TAG; //status
67748 var c = source.charAt(p);
67752 if (s === S_ATTR) {
67754 attrName = source.slice(start, p);
67756 } else if (s === S_ATTR_SPACE) {
67759 //fatalError: equal must after attrName or space after attrName
67760 throw new Error('attribute equal must after attrName');
67767 if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
67770 if (s === S_ATTR) {
67771 errorHandler.warning('attribute value must after "="');
67772 attrName = source.slice(start, p);
67776 p = source.indexOf(c, start);
67779 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67780 el.add(attrName, value, start - 1);
67783 //fatalError: no end quot match
67784 throw new Error('attribute value no end \'' + c + '\' match');
67786 } else if (s == S_ATTR_NOQUOT_VALUE) {
67787 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
67789 el.add(attrName, value, start); //console.dir(el)
67791 errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
67795 //fatalError: no equal before
67796 throw new Error('attribute value must after "="');
67804 el.setTagName(source.slice(start, p));
67812 case S_ATTR_NOQUOT_VALUE:
67819 throw new Error("attribute invalid close char('/')");
67826 //throw new Error('unexpected end of input')
67827 errorHandler.error('unexpected end of input');
67830 el.setTagName(source.slice(start, p));
67838 el.setTagName(source.slice(start, p));
67846 case S_ATTR_NOQUOT_VALUE: //Compatible state
67849 value = source.slice(start, p);
67851 if (value.slice(-1) === '/') {
67853 value = value.slice(0, -1);
67857 if (s === S_ATTR_SPACE) {
67861 if (s == S_ATTR_NOQUOT_VALUE) {
67862 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67863 el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
67865 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
67866 errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
67869 el.add(value, value, start);
67875 throw new Error('attribute value missed!!');
67876 } // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
67881 /*xml space '\x20' | #x9 | #xD | #xA; */
67891 el.setTagName(source.slice(start, p)); //tagName
67897 attrName = source.slice(start, p);
67901 case S_ATTR_NOQUOT_VALUE:
67902 var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67903 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67904 el.add(attrName, value, start);
67909 //case S_TAG_SPACE:
67911 //case S_ATTR_SPACE:
67913 //case S_TAG_CLOSE:
67918 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67919 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67921 //case S_TAG:void();break;
67922 //case S_ATTR:void();break;
67923 //case S_ATTR_NOQUOT_VALUE:void();break;
67925 var tagName = el.tagName;
67927 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
67928 errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
67931 el.add(attrName, attrName, start);
67937 errorHandler.warning('attribute space is required"' + attrName + '"!!');
67945 s = S_ATTR_NOQUOT_VALUE;
67950 throw new Error("elements closed character '/' and '>' must be connected to");
67954 } //end outer switch
67955 //console.log('p++',p)
67962 * @return true if has new namespace define
67966 function appendElement(el, domBuilder, currentNSMap) {
67967 var tagName = el.tagName;
67968 var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
67974 var qName = a.qName;
67975 var value = a.value;
67976 var nsp = qName.indexOf(':');
67979 var prefix = a.prefix = qName.slice(0, nsp);
67980 var localName = qName.slice(nsp + 1);
67981 var nsPrefix = prefix === 'xmlns' && localName;
67985 nsPrefix = qName === 'xmlns' && '';
67986 } //can not set prefix,because prefix !== ''
67989 a.localName = localName; //prefix == null for no ns prefix attribute
67991 if (nsPrefix !== false) {
67993 if (localNSMap == null) {
67994 localNSMap = {}; //console.log(currentNSMap,0)
67996 _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
68000 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
68001 a.uri = 'http://www.w3.org/2000/xmlns/';
68002 domBuilder.startPrefixMapping(nsPrefix, value);
68010 var prefix = a.prefix;
68013 //no prefix attribute has no namespace
68014 if (prefix === 'xml') {
68015 a.uri = 'http://www.w3.org/XML/1998/namespace';
68018 if (prefix !== 'xmlns') {
68019 a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
68024 var nsp = tagName.indexOf(':');
68027 prefix = el.prefix = tagName.slice(0, nsp);
68028 localName = el.localName = tagName.slice(nsp + 1);
68030 prefix = null; //important!!
68032 localName = el.localName = tagName;
68033 } //no prefix element has default namespace
68036 var ns = el.uri = currentNSMap[prefix || ''];
68037 domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
68038 //localNSMap = null
68041 domBuilder.endElement(ns, localName, tagName);
68044 for (prefix in localNSMap) {
68045 domBuilder.endPrefixMapping(prefix);
68049 el.currentNSMap = currentNSMap;
68050 el.localNSMap = localNSMap; //parseStack.push(el);
68056 function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
68057 if (/^(?:script|textarea)$/i.test(tagName)) {
68058 var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
68059 var text = source.substring(elStartEnd + 1, elEndStart);
68061 if (/[&<]/.test(text)) {
68062 if (/^script$/i.test(tagName)) {
68063 //if(!/\]\]>/.test(text)){
68064 //lexHandler.startCDATA();
68065 domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
68067 return elEndStart; //}
68068 } //}else{//text area
68071 text = text.replace(/&#?\w+;/g, entityReplacer);
68072 domBuilder.characters(text, 0, text.length);
68073 return elEndStart; //}
68077 return elStartEnd + 1;
68080 function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
68081 //if(tagName in closeMap){
68082 var pos = closeMap[tagName];
68085 //console.log(tagName)
68086 pos = source.lastIndexOf('</' + tagName + '>');
68088 if (pos < elStartEnd) {
68090 pos = source.lastIndexOf('</' + tagName);
68093 closeMap[tagName] = pos;
68096 return pos < elStartEnd; //}
68099 function _copy(source, target) {
68100 for (var n in source) {
68101 target[n] = source[n];
68105 function parseDCC(source, start, domBuilder, errorHandler) {
68106 //sure start with '<!'
68107 var next = source.charAt(start + 2);
68111 if (source.charAt(start + 3) === '-') {
68112 var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
68115 domBuilder.comment(source, start + 4, end - start - 4);
68118 errorHandler.error("Unclosed comment");
68127 if (source.substr(start + 3, 6) == 'CDATA[') {
68128 var end = source.indexOf(']]>', start + 9);
68129 domBuilder.startCDATA();
68130 domBuilder.characters(source, start + 9, end - start - 9);
68131 domBuilder.endCDATA();
68134 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
68137 var matchs = split$1(source, start);
68138 var len = matchs.length;
68140 if (len > 1 && /!doctype/i.test(matchs[0][0])) {
68141 var name = matchs[1][0];
68142 var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
68143 var sysid = len > 4 && matchs[4][0];
68144 var lastMatch = matchs[len - 1];
68145 domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
68146 domBuilder.endDTD();
68147 return lastMatch.index + lastMatch[0].length;
68155 function parseInstruction(source, start, domBuilder) {
68156 var end = source.indexOf('?>', start);
68159 var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
68162 var len = match[0].length;
68163 domBuilder.processingInstruction(match[1], match[2]);
68178 function ElementAttributes(source) {}
68180 ElementAttributes.prototype = {
68181 setTagName: function setTagName(tagName) {
68182 if (!tagNamePattern.test(tagName)) {
68183 throw new Error('invalid tagName:' + tagName);
68186 this.tagName = tagName;
68188 add: function add(qName, value, offset) {
68189 if (!tagNamePattern.test(qName)) {
68190 throw new Error('invalid attribute:' + qName);
68193 this[this.length++] = {
68200 getLocalName: function getLocalName(i) {
68201 return this[i].localName;
68203 getLocator: function getLocator(i) {
68204 return this[i].locator;
68206 getQName: function getQName(i) {
68207 return this[i].qName;
68209 getURI: function getURI(i) {
68210 return this[i].uri;
68212 getValue: function getValue(i) {
68213 return this[i].value;
68214 } // ,getIndex:function(uri, localName)){
68221 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
68222 // getType:function(uri,localName){}
68223 // getType:function(i){},
68227 function _set_proto_(thiz, parent) {
68228 thiz.__proto__ = parent;
68232 if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
68233 _set_proto_ = function _set_proto_(thiz, parent) {
68235 p.prototype = parent;
68238 for (parent in thiz) {
68239 p[parent] = thiz[parent];
68246 function split$1(source, start) {
68249 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
68250 reg.lastIndex = start;
68251 reg.exec(source); //skip <
68253 while (match = reg.exec(source)) {
68255 if (match[1]) return buf;
68259 var XMLReader_1 = XMLReader;
68261 XMLReader: XMLReader_1
68266 * Object DOMException
68267 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
68268 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
68270 function copy$1(src, dest) {
68271 for (var p in src) {
68276 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
68277 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
68281 function _extends(Class, Super) {
68282 var pt = Class.prototype;
68284 if (Object.create) {
68285 var ppt = Object.create(Super.prototype);
68286 pt.__proto__ = ppt;
68289 if (!(pt instanceof Super)) {
68290 var t = function t() {};
68291 t.prototype = Super.prototype;
68294 Class.prototype = pt = t;
68297 if (pt.constructor != Class) {
68298 if (typeof Class != 'function') {
68299 console.error("unknow Class:" + Class);
68302 pt.constructor = Class;
68306 var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
68309 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
68310 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
68311 var TEXT_NODE = NodeType.TEXT_NODE = 3;
68312 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
68313 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
68314 var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
68315 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
68316 var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
68317 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
68318 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
68319 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
68320 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
68322 var ExceptionCode = {};
68323 var ExceptionMessage = {};
68324 var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
68325 var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
68326 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
68327 var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
68328 var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
68329 var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
68330 var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
68331 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
68332 var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
68333 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
68335 var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
68336 var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
68337 var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
68338 var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
68339 var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
68341 function DOMException$2(code, message) {
68342 if (message instanceof Error) {
68343 var error = message;
68346 Error.call(this, ExceptionMessage[code]);
68347 this.message = ExceptionMessage[code];
68348 if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
68352 if (message) this.message = this.message + ": " + message;
68355 DOMException$2.prototype = Error.prototype;
68356 copy$1(ExceptionCode, DOMException$2);
68358 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
68359 * 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.
68360 * The items in the NodeList are accessible via an integral index, starting from 0.
68363 function NodeList() {}
68364 NodeList.prototype = {
68366 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
68372 * 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.
68374 * @param index unsigned long
68375 * Index into the collection.
68377 * The node at the indexth position in the NodeList, or null if that is not a valid index.
68379 item: function item(index) {
68380 return this[index] || null;
68382 toString: function toString(isHTML, nodeFilter) {
68383 for (var buf = [], i = 0; i < this.length; i++) {
68384 serializeToString(this[i], buf, isHTML, nodeFilter);
68387 return buf.join('');
68391 function LiveNodeList(node, refresh) {
68393 this._refresh = refresh;
68395 _updateLiveList(this);
68398 function _updateLiveList(list) {
68399 var inc = list._node._inc || list._node.ownerDocument._inc;
68401 if (list._inc != inc) {
68402 var ls = list._refresh(list._node); //console.log(ls.length)
68405 __set__(list, 'length', ls.length);
68412 LiveNodeList.prototype.item = function (i) {
68413 _updateLiveList(this);
68418 _extends(LiveNodeList, NodeList);
68421 * 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.
68422 * NamedNodeMap objects in the DOM are live.
68423 * used for attributes or DocumentType entities
68427 function NamedNodeMap() {}
68429 function _findNodeIndex(list, node) {
68430 var i = list.length;
68433 if (list[i] === node) {
68439 function _addNamedNode(el, list, newAttr, oldAttr) {
68441 list[_findNodeIndex(list, oldAttr)] = newAttr;
68443 list[list.length++] = newAttr;
68447 newAttr.ownerElement = el;
68448 var doc = el.ownerDocument;
68451 oldAttr && _onRemoveAttribute(doc, el, oldAttr);
68453 _onAddAttribute(doc, el, newAttr);
68458 function _removeNamedNode(el, list, attr) {
68459 //console.log('remove attr:'+attr)
68460 var i = _findNodeIndex(list, attr);
68463 var lastIndex = list.length - 1;
68465 while (i < lastIndex) {
68466 list[i] = list[++i];
68469 list.length = lastIndex;
68472 var doc = el.ownerDocument;
68475 _onRemoveAttribute(doc, el, attr);
68477 attr.ownerElement = null;
68481 throw DOMException$2(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
68485 NamedNodeMap.prototype = {
68487 item: NodeList.prototype.item,
68488 getNamedItem: function getNamedItem(key) {
68489 // if(key.indexOf(':')>0 || key == 'xmlns'){
68493 var i = this.length;
68496 var attr = this[i]; //console.log(attr.nodeName,key)
68498 if (attr.nodeName == key) {
68503 setNamedItem: function setNamedItem(attr) {
68504 var el = attr.ownerElement;
68506 if (el && el != this._ownerElement) {
68507 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68510 var oldAttr = this.getNamedItem(attr.nodeName);
68512 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68518 setNamedItemNS: function setNamedItemNS(attr) {
68519 // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
68520 var el = attr.ownerElement,
68523 if (el && el != this._ownerElement) {
68524 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68527 oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
68529 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68535 removeNamedItem: function removeNamedItem(key) {
68536 var attr = this.getNamedItem(key);
68538 _removeNamedNode(this._ownerElement, this, attr);
68542 // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
68544 removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
68545 var attr = this.getNamedItemNS(namespaceURI, localName);
68547 _removeNamedNode(this._ownerElement, this, attr);
68551 getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
68552 var i = this.length;
68555 var node = this[i];
68557 if (node.localName == localName && node.namespaceURI == namespaceURI) {
68566 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
68569 function DOMImplementation(
68572 this._features = {};
68575 for (var feature in features) {
68576 this._features = features[feature];
68580 DOMImplementation.prototype = {
68581 hasFeature: function hasFeature(
68586 var versions = this._features[feature.toLowerCase()];
68588 if (versions && (!version || version in versions)) {
68594 // Introduced in DOM Level 2:
68595 createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
68596 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
68597 var doc = new Document();
68598 doc.implementation = this;
68599 doc.childNodes = new NodeList();
68600 doc.doctype = doctype;
68603 doc.appendChild(doctype);
68606 if (qualifiedName) {
68607 var root = doc.createElementNS(namespaceURI, qualifiedName);
68608 doc.appendChild(root);
68613 // Introduced in DOM Level 2:
68614 createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
68615 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
68616 var node = new DocumentType();
68617 node.name = qualifiedName;
68618 node.nodeName = qualifiedName;
68619 node.publicId = publicId;
68620 node.systemId = systemId; // Introduced in DOM Level 2:
68621 //readonly attribute DOMString internalSubset;
68623 // readonly attribute NamedNodeMap entities;
68624 // readonly attribute NamedNodeMap notations;
68630 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
68637 previousSibling: null,
68642 ownerDocument: null,
68644 namespaceURI: null,
68647 // Modified in DOM Level 2:
68648 insertBefore: function insertBefore(newChild, refChild) {
68650 return _insertBefore(this, newChild, refChild);
68652 replaceChild: function replaceChild(newChild, oldChild) {
68654 this.insertBefore(newChild, oldChild);
68657 this.removeChild(oldChild);
68660 removeChild: function removeChild(oldChild) {
68661 return _removeChild(this, oldChild);
68663 appendChild: function appendChild(newChild) {
68664 return this.insertBefore(newChild, null);
68666 hasChildNodes: function hasChildNodes() {
68667 return this.firstChild != null;
68669 cloneNode: function cloneNode(deep) {
68670 return _cloneNode(this.ownerDocument || this, this, deep);
68672 // Modified in DOM Level 2:
68673 normalize: function normalize() {
68674 var child = this.firstChild;
68677 var next = child.nextSibling;
68679 if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
68680 this.removeChild(next);
68681 child.appendData(next.data);
68688 // Introduced in DOM Level 2:
68689 isSupported: function isSupported(feature, version) {
68690 return this.ownerDocument.implementation.hasFeature(feature, version);
68692 // Introduced in DOM Level 2:
68693 hasAttributes: function hasAttributes() {
68694 return this.attributes.length > 0;
68696 lookupPrefix: function lookupPrefix(namespaceURI) {
68700 var map = el._nsMap; //console.dir(map)
68703 for (var n in map) {
68704 if (map[n] == namespaceURI) {
68710 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68715 // Introduced in DOM Level 3:
68716 lookupNamespaceURI: function lookupNamespaceURI(prefix) {
68720 var map = el._nsMap; //console.dir(map)
68723 if (prefix in map) {
68724 return map[prefix];
68728 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68733 // Introduced in DOM Level 3:
68734 isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
68735 var prefix = this.lookupPrefix(namespaceURI);
68736 return prefix == null;
68740 function _xmlEncoder(c) {
68741 return c == '<' && '<' || c == '>' && '>' || c == '&' && '&' || c == '"' && '"' || '&#' + c.charCodeAt() + ';';
68744 copy$1(NodeType, Node);
68745 copy$1(NodeType, Node.prototype);
68747 * @param callback return true for continue,false for break
68748 * @return boolean true: break visit;
68751 function _visitNode(node, callback) {
68752 if (callback(node)) {
68756 if (node = node.firstChild) {
68758 if (_visitNode(node, callback)) {
68761 } while (node = node.nextSibling);
68765 function Document() {}
68767 function _onAddAttribute(doc, el, newAttr) {
68769 var ns = newAttr.namespaceURI;
68771 if (ns == 'http://www.w3.org/2000/xmlns/') {
68773 el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
68777 function _onRemoveAttribute(doc, el, newAttr, remove) {
68779 var ns = newAttr.namespaceURI;
68781 if (ns == 'http://www.w3.org/2000/xmlns/') {
68783 delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
68787 function _onUpdateChild(doc, el, newChild) {
68788 if (doc && doc._inc) {
68789 doc._inc++; //update childNodes
68791 var cs = el.childNodes;
68794 cs[cs.length++] = newChild;
68797 var child = el.firstChild;
68802 child = child.nextSibling;
68813 * writeable properties:
68814 * nodeValue,Attr:value,CharacterData:data
68819 function _removeChild(parentNode, child) {
68820 var previous = child.previousSibling;
68821 var next = child.nextSibling;
68824 previous.nextSibling = next;
68826 parentNode.firstChild = next;
68830 next.previousSibling = previous;
68832 parentNode.lastChild = previous;
68835 _onUpdateChild(parentNode.ownerDocument, parentNode);
68840 * preformance key(refChild == null)
68844 function _insertBefore(parentNode, newChild, nextChild) {
68845 var cp = newChild.parentNode;
68848 cp.removeChild(newChild); //remove and update
68851 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
68852 var newFirst = newChild.firstChild;
68854 if (newFirst == null) {
68858 var newLast = newChild.lastChild;
68860 newFirst = newLast = newChild;
68863 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
68864 newFirst.previousSibling = pre;
68865 newLast.nextSibling = nextChild;
68868 pre.nextSibling = newFirst;
68870 parentNode.firstChild = newFirst;
68873 if (nextChild == null) {
68874 parentNode.lastChild = newLast;
68876 nextChild.previousSibling = newLast;
68880 newFirst.parentNode = parentNode;
68881 } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
68883 _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
68886 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68887 newChild.firstChild = newChild.lastChild = null;
68893 function _appendSingleChild(parentNode, newChild) {
68894 var cp = newChild.parentNode;
68897 var pre = parentNode.lastChild;
68898 cp.removeChild(newChild); //remove and update
68900 var pre = parentNode.lastChild;
68903 var pre = parentNode.lastChild;
68904 newChild.parentNode = parentNode;
68905 newChild.previousSibling = pre;
68906 newChild.nextSibling = null;
68909 pre.nextSibling = newChild;
68911 parentNode.firstChild = newChild;
68914 parentNode.lastChild = newChild;
68916 _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
68918 return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
68921 Document.prototype = {
68922 //implementation : null,
68923 nodeName: '#document',
68924 nodeType: DOCUMENT_NODE,
68926 documentElement: null,
68928 insertBefore: function insertBefore(newChild, refChild) {
68930 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68931 var child = newChild.firstChild;
68934 var next = child.nextSibling;
68935 this.insertBefore(child, refChild);
68942 if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
68943 this.documentElement = newChild;
68946 return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
68948 removeChild: function removeChild(oldChild) {
68949 if (this.documentElement == oldChild) {
68950 this.documentElement = null;
68953 return _removeChild(this, oldChild);
68955 // Introduced in DOM Level 2:
68956 importNode: function importNode(importedNode, deep) {
68957 return _importNode(this, importedNode, deep);
68959 // Introduced in DOM Level 2:
68960 getElementById: function getElementById(id) {
68963 _visitNode(this.documentElement, function (node) {
68964 if (node.nodeType == ELEMENT_NODE) {
68965 if (node.getAttribute('id') == id) {
68974 //document factory method:
68975 createElement: function createElement(tagName) {
68976 var node = new Element();
68977 node.ownerDocument = this;
68978 node.nodeName = tagName;
68979 node.tagName = tagName;
68980 node.childNodes = new NodeList();
68981 var attrs = node.attributes = new NamedNodeMap();
68982 attrs._ownerElement = node;
68985 createDocumentFragment: function createDocumentFragment() {
68986 var node = new DocumentFragment();
68987 node.ownerDocument = this;
68988 node.childNodes = new NodeList();
68991 createTextNode: function createTextNode(data) {
68992 var node = new Text();
68993 node.ownerDocument = this;
68994 node.appendData(data);
68997 createComment: function createComment(data) {
68998 var node = new Comment();
68999 node.ownerDocument = this;
69000 node.appendData(data);
69003 createCDATASection: function createCDATASection(data) {
69004 var node = new CDATASection();
69005 node.ownerDocument = this;
69006 node.appendData(data);
69009 createProcessingInstruction: function createProcessingInstruction(target, data) {
69010 var node = new ProcessingInstruction();
69011 node.ownerDocument = this;
69012 node.tagName = node.target = target;
69013 node.nodeValue = node.data = data;
69016 createAttribute: function createAttribute(name) {
69017 var node = new Attr();
69018 node.ownerDocument = this;
69020 node.nodeName = name;
69021 node.localName = name;
69022 node.specified = true;
69025 createEntityReference: function createEntityReference(name) {
69026 var node = new EntityReference();
69027 node.ownerDocument = this;
69028 node.nodeName = name;
69031 // Introduced in DOM Level 2:
69032 createElementNS: function createElementNS(namespaceURI, qualifiedName) {
69033 var node = new Element();
69034 var pl = qualifiedName.split(':');
69035 var attrs = node.attributes = new NamedNodeMap();
69036 node.childNodes = new NodeList();
69037 node.ownerDocument = this;
69038 node.nodeName = qualifiedName;
69039 node.tagName = qualifiedName;
69040 node.namespaceURI = namespaceURI;
69042 if (pl.length == 2) {
69043 node.prefix = pl[0];
69044 node.localName = pl[1];
69046 //el.prefix = null;
69047 node.localName = qualifiedName;
69050 attrs._ownerElement = node;
69053 // Introduced in DOM Level 2:
69054 createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
69055 var node = new Attr();
69056 var pl = qualifiedName.split(':');
69057 node.ownerDocument = this;
69058 node.nodeName = qualifiedName;
69059 node.name = qualifiedName;
69060 node.namespaceURI = namespaceURI;
69061 node.specified = true;
69063 if (pl.length == 2) {
69064 node.prefix = pl[0];
69065 node.localName = pl[1];
69067 //el.prefix = null;
69068 node.localName = qualifiedName;
69075 _extends(Document, Node);
69077 function Element() {
69080 Element.prototype = {
69081 nodeType: ELEMENT_NODE,
69082 hasAttribute: function hasAttribute(name) {
69083 return this.getAttributeNode(name) != null;
69085 getAttribute: function getAttribute(name) {
69086 var attr = this.getAttributeNode(name);
69087 return attr && attr.value || '';
69089 getAttributeNode: function getAttributeNode(name) {
69090 return this.attributes.getNamedItem(name);
69092 setAttribute: function setAttribute(name, value) {
69093 var attr = this.ownerDocument.createAttribute(name);
69094 attr.value = attr.nodeValue = "" + value;
69095 this.setAttributeNode(attr);
69097 removeAttribute: function removeAttribute(name) {
69098 var attr = this.getAttributeNode(name);
69099 attr && this.removeAttributeNode(attr);
69101 //four real opeartion method
69102 appendChild: function appendChild(newChild) {
69103 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
69104 return this.insertBefore(newChild, null);
69106 return _appendSingleChild(this, newChild);
69109 setAttributeNode: function setAttributeNode(newAttr) {
69110 return this.attributes.setNamedItem(newAttr);
69112 setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
69113 return this.attributes.setNamedItemNS(newAttr);
69115 removeAttributeNode: function removeAttributeNode(oldAttr) {
69116 //console.log(this == oldAttr.ownerElement)
69117 return this.attributes.removeNamedItem(oldAttr.nodeName);
69119 //get real attribute name,and remove it by removeAttributeNode
69120 removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
69121 var old = this.getAttributeNodeNS(namespaceURI, localName);
69122 old && this.removeAttributeNode(old);
69124 hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
69125 return this.getAttributeNodeNS(namespaceURI, localName) != null;
69127 getAttributeNS: function getAttributeNS(namespaceURI, localName) {
69128 var attr = this.getAttributeNodeNS(namespaceURI, localName);
69129 return attr && attr.value || '';
69131 setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
69132 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
69133 attr.value = attr.nodeValue = "" + value;
69134 this.setAttributeNode(attr);
69136 getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
69137 return this.attributes.getNamedItemNS(namespaceURI, localName);
69139 getElementsByTagName: function getElementsByTagName(tagName) {
69140 return new LiveNodeList(this, function (base) {
69143 _visitNode(base, function (node) {
69144 if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
69152 getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
69153 return new LiveNodeList(this, function (base) {
69156 _visitNode(base, function (node) {
69157 if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
69166 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
69167 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
69169 _extends(Element, Node);
69172 Attr.prototype.nodeType = ATTRIBUTE_NODE;
69174 _extends(Attr, Node);
69176 function CharacterData() {}
69177 CharacterData.prototype = {
69179 substringData: function substringData(offset, count) {
69180 return this.data.substring(offset, offset + count);
69182 appendData: function appendData(text) {
69183 text = this.data + text;
69184 this.nodeValue = this.data = text;
69185 this.length = text.length;
69187 insertData: function insertData(offset, text) {
69188 this.replaceData(offset, 0, text);
69190 appendChild: function appendChild(newChild) {
69191 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
69193 deleteData: function deleteData(offset, count) {
69194 this.replaceData(offset, count, "");
69196 replaceData: function replaceData(offset, count, text) {
69197 var start = this.data.substring(0, offset);
69198 var end = this.data.substring(offset + count);
69199 text = start + text + end;
69200 this.nodeValue = this.data = text;
69201 this.length = text.length;
69205 _extends(CharacterData, Node);
69210 nodeType: TEXT_NODE,
69211 splitText: function splitText(offset) {
69212 var text = this.data;
69213 var newText = text.substring(offset);
69214 text = text.substring(0, offset);
69215 this.data = this.nodeValue = text;
69216 this.length = text.length;
69217 var newNode = this.ownerDocument.createTextNode(newText);
69219 if (this.parentNode) {
69220 this.parentNode.insertBefore(newNode, this.nextSibling);
69227 _extends(Text, CharacterData);
69229 function Comment() {}
69230 Comment.prototype = {
69231 nodeName: "#comment",
69232 nodeType: COMMENT_NODE
69235 _extends(Comment, CharacterData);
69237 function CDATASection() {}
69238 CDATASection.prototype = {
69239 nodeName: "#cdata-section",
69240 nodeType: CDATA_SECTION_NODE
69243 _extends(CDATASection, CharacterData);
69245 function DocumentType() {}
69246 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
69248 _extends(DocumentType, Node);
69250 function Notation() {}
69251 Notation.prototype.nodeType = NOTATION_NODE;
69253 _extends(Notation, Node);
69255 function Entity() {}
69256 Entity.prototype.nodeType = ENTITY_NODE;
69258 _extends(Entity, Node);
69260 function EntityReference() {}
69261 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
69263 _extends(EntityReference, Node);
69265 function DocumentFragment() {}
69266 DocumentFragment.prototype.nodeName = "#document-fragment";
69267 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
69269 _extends(DocumentFragment, Node);
69271 function ProcessingInstruction() {}
69273 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
69275 _extends(ProcessingInstruction, Node);
69277 function XMLSerializer$1() {}
69279 XMLSerializer$1.prototype.serializeToString = function (node, isHtml, nodeFilter) {
69280 return nodeSerializeToString.call(node, isHtml, nodeFilter);
69283 Node.prototype.toString = nodeSerializeToString;
69285 function nodeSerializeToString(isHtml, nodeFilter) {
69287 var refNode = this.nodeType == 9 ? this.documentElement : this;
69288 var prefix = refNode.prefix;
69289 var uri = refNode.namespaceURI;
69291 if (uri && prefix == null) {
69292 //console.log(prefix)
69293 var prefix = refNode.lookupPrefix(uri);
69295 if (prefix == null) {
69297 var visibleNamespaces = [{
69300 } //{namespace:uri,prefix:''}
69305 serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
69307 return buf.join('');
69310 function needNamespaceDefine(node, isHTML, visibleNamespaces) {
69311 var prefix = node.prefix || '';
69312 var uri = node.namespaceURI;
69314 if (!prefix && !uri) {
69318 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
69322 var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
69325 var ns = visibleNamespaces[i]; // get namespace prefix
69326 //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
69328 if (ns.prefix == prefix) {
69329 return ns.namespace != uri;
69331 } //console.log(isHTML,uri,prefix=='')
69332 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
69335 //node.flag = '11111'
69336 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
69342 function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
69344 node = nodeFilter(node);
69347 if (typeof node == 'string') {
69353 } //buf.sort.apply(attrs, attributeSorter);
69357 switch (node.nodeType) {
69359 if (!visibleNamespaces) visibleNamespaces = [];
69360 var startVisibleNamespaces = visibleNamespaces.length;
69361 var attrs = node.attributes;
69362 var len = attrs.length;
69363 var child = node.firstChild;
69364 var nodeName = node.tagName;
69365 isHTML = htmlns === node.namespaceURI || isHTML;
69366 buf.push('<', nodeName);
69368 for (var i = 0; i < len; i++) {
69369 // add namespaces for attributes
69370 var attr = attrs.item(i);
69372 if (attr.prefix == 'xmlns') {
69373 visibleNamespaces.push({
69374 prefix: attr.localName,
69375 namespace: attr.value
69377 } else if (attr.nodeName == 'xmlns') {
69378 visibleNamespaces.push({
69380 namespace: attr.value
69385 for (var i = 0; i < len; i++) {
69386 var attr = attrs.item(i);
69388 if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
69389 var prefix = attr.prefix || '';
69390 var uri = attr.namespaceURI;
69391 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69392 buf.push(ns, '="', uri, '"');
69393 visibleNamespaces.push({
69399 serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
69400 } // add namespace for current node
69403 if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
69404 var prefix = node.prefix || '';
69405 var uri = node.namespaceURI;
69406 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69407 buf.push(ns, '="', uri, '"');
69408 visibleNamespaces.push({
69414 if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
69415 buf.push('>'); //if is cdata child node
69417 if (isHTML && /^script$/i.test(nodeName)) {
69420 buf.push(child.data);
69422 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69425 child = child.nextSibling;
69429 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69430 child = child.nextSibling;
69434 buf.push('</', nodeName, '>');
69437 } // remove added visible namespaces
69438 //visibleNamespaces.length = startVisibleNamespaces;
69443 case DOCUMENT_NODE:
69444 case DOCUMENT_FRAGMENT_NODE:
69445 var child = node.firstChild;
69448 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69449 child = child.nextSibling;
69454 case ATTRIBUTE_NODE:
69455 return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
69458 return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
69460 case CDATA_SECTION_NODE:
69461 return buf.push('<![CDATA[', node.data, ']]>');
69464 return buf.push("<!--", node.data, "-->");
69466 case DOCUMENT_TYPE_NODE:
69467 var pubid = node.publicId;
69468 var sysid = node.systemId;
69469 buf.push('<!DOCTYPE ', node.name);
69472 buf.push(' PUBLIC "', pubid);
69474 if (sysid && sysid != '.') {
69475 buf.push('" "', sysid);
69479 } else if (sysid && sysid != '.') {
69480 buf.push(' SYSTEM "', sysid, '">');
69482 var sub = node.internalSubset;
69485 buf.push(" [", sub, "]");
69493 case PROCESSING_INSTRUCTION_NODE:
69494 return buf.push("<?", node.target, " ", node.data, "?>");
69496 case ENTITY_REFERENCE_NODE:
69497 return buf.push('&', node.nodeName, ';');
69498 //case ENTITY_NODE:
69499 //case NOTATION_NODE:
69502 buf.push('??', node.nodeName);
69506 function _importNode(doc, node, deep) {
69509 switch (node.nodeType) {
69511 node2 = node.cloneNode(false);
69512 node2.ownerDocument = doc;
69513 //var attrs = node2.attributes;
69514 //var len = attrs.length;
69515 //for(var i=0;i<len;i++){
69516 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
69519 case DOCUMENT_FRAGMENT_NODE:
69522 case ATTRIBUTE_NODE:
69525 //case ENTITY_REFERENCE_NODE:
69526 //case PROCESSING_INSTRUCTION_NODE:
69527 ////case TEXT_NODE:
69528 //case CDATA_SECTION_NODE:
69529 //case COMMENT_NODE:
69532 //case DOCUMENT_NODE:
69533 //case DOCUMENT_TYPE_NODE:
69534 //cannot be imported.
69535 //case ENTITY_NODE:
69536 //case NOTATION_NODE:
69537 //can not hit in level3
69542 node2 = node.cloneNode(false); //false
69545 node2.ownerDocument = doc;
69546 node2.parentNode = null;
69549 var child = node.firstChild;
69552 node2.appendChild(_importNode(doc, child, deep));
69553 child = child.nextSibling;
69559 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
69560 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
69563 function _cloneNode(doc, node, deep) {
69564 var node2 = new node.constructor();
69566 for (var n in node) {
69569 if (_typeof(v) != 'object') {
69570 if (v != node2[n]) {
69576 if (node.childNodes) {
69577 node2.childNodes = new NodeList();
69580 node2.ownerDocument = doc;
69582 switch (node2.nodeType) {
69584 var attrs = node.attributes;
69585 var attrs2 = node2.attributes = new NamedNodeMap();
69586 var len = attrs.length;
69587 attrs2._ownerElement = node2;
69589 for (var i = 0; i < len; i++) {
69590 node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
69595 case ATTRIBUTE_NODE:
69600 var child = node.firstChild;
69603 node2.appendChild(_cloneNode(doc, child, deep));
69604 child = child.nextSibling;
69611 function __set__(object, key, value) {
69612 object[key] = value;
69617 if (Object.defineProperty) {
69618 var getTextContent = function getTextContent(node) {
69619 switch (node.nodeType) {
69621 case DOCUMENT_FRAGMENT_NODE:
69623 node = node.firstChild;
69626 if (node.nodeType !== 7 && node.nodeType !== 8) {
69627 buf.push(getTextContent(node));
69630 node = node.nextSibling;
69633 return buf.join('');
69636 return node.nodeValue;
69640 Object.defineProperty(LiveNodeList.prototype, 'length', {
69641 get: function get() {
69642 _updateLiveList(this);
69644 return this.$$length;
69647 Object.defineProperty(Node.prototype, 'textContent', {
69648 get: function get() {
69649 return getTextContent(this);
69651 set: function set(data) {
69652 switch (this.nodeType) {
69654 case DOCUMENT_FRAGMENT_NODE:
69655 while (this.firstChild) {
69656 this.removeChild(this.firstChild);
69659 if (data || String(data)) {
69660 this.appendChild(this.ownerDocument.createTextNode(data));
69669 this.nodeValue = data;
69674 __set__ = function __set__(object, key, value) {
69675 //console.log(value)
69676 object['$$' + key] = value;
69680 } //if(typeof require == 'function'){
69683 var DOMImplementation_1 = DOMImplementation;
69684 var XMLSerializer_1 = XMLSerializer$1; //}
69687 DOMImplementation: DOMImplementation_1,
69688 XMLSerializer: XMLSerializer_1
69691 var domParser = createCommonjsModule(function (module, exports) {
69692 function DOMParser(options) {
69693 this.options = options || {
69698 DOMParser.prototype.parseFromString = function (source, mimeType) {
69699 var options = this.options;
69700 var sax = new XMLReader();
69701 var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
69703 var errorHandler = options.errorHandler;
69704 var locator = options.locator;
69705 var defaultNSMap = options.xmlns || {};
69715 domBuilder.setDocumentLocator(locator);
69718 sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
69719 sax.domBuilder = options.domBuilder || domBuilder;
69721 if (/\/x?html?$/.test(mimeType)) {
69722 entityMap.nbsp = '\xa0';
69723 entityMap.copy = '\xa9';
69724 defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
69727 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
69730 sax.parse(source, defaultNSMap, entityMap);
69732 sax.errorHandler.error("invalid doc source");
69735 return domBuilder.doc;
69738 function buildErrorHandler(errorImpl, domBuilder, locator) {
69740 if (domBuilder instanceof DOMHandler) {
69744 errorImpl = domBuilder;
69747 var errorHandler = {};
69748 var isCallback = errorImpl instanceof Function;
69749 locator = locator || {};
69751 function build(key) {
69752 var fn = errorImpl[key];
69754 if (!fn && isCallback) {
69755 fn = errorImpl.length == 2 ? function (msg) {
69756 errorImpl(key, msg);
69760 errorHandler[key] = fn && function (msg) {
69761 fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
69762 } || function () {};
69767 build('fatalError');
69768 return errorHandler;
69769 } //console.log('#\n\n\n\n\n\n\n####')
69772 * +ContentHandler+ErrorHandler
69773 * +LexicalHandler+EntityResolver2
69774 * -DeclHandler-DTDHandler
69776 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
69777 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
69778 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
69782 function DOMHandler() {
69783 this.cdata = false;
69786 function position(locator, node) {
69787 node.lineNumber = locator.lineNumber;
69788 node.columnNumber = locator.columnNumber;
69791 * @see org.xml.sax.ContentHandler#startDocument
69792 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
69796 DOMHandler.prototype = {
69797 startDocument: function startDocument() {
69798 this.doc = new DOMImplementation().createDocument(null, null, null);
69800 if (this.locator) {
69801 this.doc.documentURI = this.locator.systemId;
69804 startElement: function startElement(namespaceURI, localName, qName, attrs) {
69805 var doc = this.doc;
69806 var el = doc.createElementNS(namespaceURI, qName || localName);
69807 var len = attrs.length;
69808 appendElement(this, el);
69809 this.currentElement = el;
69810 this.locator && position(this.locator, el);
69812 for (var i = 0; i < len; i++) {
69813 var namespaceURI = attrs.getURI(i);
69814 var value = attrs.getValue(i);
69815 var qName = attrs.getQName(i);
69816 var attr = doc.createAttributeNS(namespaceURI, qName);
69817 this.locator && position(attrs.getLocator(i), attr);
69818 attr.value = attr.nodeValue = value;
69819 el.setAttributeNode(attr);
69822 endElement: function endElement(namespaceURI, localName, qName) {
69823 var current = this.currentElement;
69824 var tagName = current.tagName;
69825 this.currentElement = current.parentNode;
69827 startPrefixMapping: function startPrefixMapping(prefix, uri) {},
69828 endPrefixMapping: function endPrefixMapping(prefix) {},
69829 processingInstruction: function processingInstruction(target, data) {
69830 var ins = this.doc.createProcessingInstruction(target, data);
69831 this.locator && position(this.locator, ins);
69832 appendElement(this, ins);
69834 ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
69835 characters: function characters(chars, start, length) {
69836 chars = _toString.apply(this, arguments); //console.log(chars)
69840 var charNode = this.doc.createCDATASection(chars);
69842 var charNode = this.doc.createTextNode(chars);
69845 if (this.currentElement) {
69846 this.currentElement.appendChild(charNode);
69847 } else if (/^\s*$/.test(chars)) {
69848 this.doc.appendChild(charNode); //process xml
69851 this.locator && position(this.locator, charNode);
69854 skippedEntity: function skippedEntity(name) {},
69855 endDocument: function endDocument() {
69856 this.doc.normalize();
69858 setDocumentLocator: function setDocumentLocator(locator) {
69859 if (this.locator = locator) {
69860 // && !('lineNumber' in locator)){
69861 locator.lineNumber = 0;
69865 comment: function comment(chars, start, length) {
69866 chars = _toString.apply(this, arguments);
69867 var comm = this.doc.createComment(chars);
69868 this.locator && position(this.locator, comm);
69869 appendElement(this, comm);
69871 startCDATA: function startCDATA() {
69872 //used in characters() methods
69875 endCDATA: function endCDATA() {
69876 this.cdata = false;
69878 startDTD: function startDTD(name, publicId, systemId) {
69879 var impl = this.doc.implementation;
69881 if (impl && impl.createDocumentType) {
69882 var dt = impl.createDocumentType(name, publicId, systemId);
69883 this.locator && position(this.locator, dt);
69884 appendElement(this, dt);
69889 * @see org.xml.sax.ErrorHandler
69890 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
69892 warning: function warning(error) {
69893 console.warn('[xmldom warning]\t' + error, _locator(this.locator));
69895 error: function error(_error) {
69896 console.error('[xmldom error]\t' + _error, _locator(this.locator));
69898 fatalError: function fatalError(error) {
69899 console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
69904 function _locator(l) {
69906 return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
69910 function _toString(chars, start, length) {
69911 if (typeof chars == 'string') {
69912 return chars.substr(start, length);
69914 //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
69915 if (chars.length >= start + length || start) {
69916 return new java.lang.String(chars, start, length) + '';
69923 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
69924 * used method of org.xml.sax.ext.LexicalHandler:
69925 * #comment(chars, start, length)
69928 * #startDTD(name, publicId, systemId)
69931 * IGNORED method of org.xml.sax.ext.LexicalHandler:
69933 * #startEntity(name)
69937 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
69938 * IGNORED method of org.xml.sax.ext.DeclHandler
69939 * #attributeDecl(eName, aName, type, mode, value)
69940 * #elementDecl(name, model)
69941 * #externalEntityDecl(name, publicId, systemId)
69942 * #internalEntityDecl(name, value)
69943 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
69944 * IGNORED method of org.xml.sax.EntityResolver2
69945 * #resolveEntity(String name,String publicId,String baseURI,String systemId)
69946 * #resolveEntity(publicId, systemId)
69947 * #getExternalSubset(name, baseURI)
69948 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
69949 * IGNORED method of org.xml.sax.DTDHandler
69950 * #notationDecl(name, publicId, systemId) {};
69951 * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
69955 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
69956 DOMHandler.prototype[key] = function () {
69960 /* 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 */
69962 function appendElement(hander, node) {
69963 if (!hander.currentElement) {
69964 hander.doc.appendChild(node);
69966 hander.currentElement.appendChild(node);
69968 } //appendChild and setAttributeNS are preformance key
69969 //if(typeof require == 'function'){
69972 var XMLReader = sax.XMLReader;
69973 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
69974 exports.XMLSerializer = dom.XMLSerializer;
69975 exports.DOMParser = DOMParser; //}
69978 var togeojson = createCommonjsModule(function (module, exports) {
69979 var toGeoJSON = function () {
69981 var removeSpace = /\s*/g,
69982 trimSpace = /^\s*|\s*$/g,
69983 splitSpace = /\s+/; // generate a short, numeric hash of a string
69985 function okhash(x) {
69986 if (!x || !x.length) return 0;
69988 for (var i = 0, h = 0; i < x.length; i++) {
69989 h = (h << 5) - h + x.charCodeAt(i) | 0;
69993 } // all Y children of X
69996 function get(x, y) {
69997 return x.getElementsByTagName(y);
70000 function attr(x, y) {
70001 return x.getAttribute(y);
70004 function attrf(x, y) {
70005 return parseFloat(attr(x, y));
70006 } // one Y child of X, if any, otherwise null
70009 function get1(x, y) {
70011 return n.length ? n[0] : null;
70012 } // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
70015 function norm(el) {
70016 if (el.normalize) {
70021 } // cast array x into numbers
70024 function numarray(x) {
70025 for (var j = 0, o = []; j < x.length; j++) {
70026 o[j] = parseFloat(x[j]);
70030 } // get the content of a text node, if any
70033 function nodeVal(x) {
70038 return x && x.textContent || '';
70039 } // get the contents of multiple text nodes, if present
70042 function getMulti(x, ys) {
70047 for (k = 0; k < ys.length; k++) {
70048 n = get1(x, ys[k]);
70049 if (n) o[ys[k]] = nodeVal(n);
70053 } // add properties of Y to X, overwriting if present in both
70056 function extend(x, y) {
70060 } // get one coordinate from a coordinate array, if any
70063 function coord1(v) {
70064 return numarray(v.replace(removeSpace, '').split(','));
70065 } // get all coordinates from a coordinate array as [[],[]]
70068 function coord(v) {
70069 var coords = v.replace(trimSpace, '').split(splitSpace),
70072 for (var i = 0; i < coords.length; i++) {
70073 o.push(coord1(coords[i]));
70079 function coordPair(x) {
70080 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
70081 ele = get1(x, 'ele'),
70082 // handle namespaced attribute in browser
70083 heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
70084 time = get1(x, 'time'),
70088 e = parseFloat(nodeVal(ele));
70097 time: time ? nodeVal(time) : null,
70098 heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
70100 } // create a new feature collection parent object
70105 type: 'FeatureCollection',
70112 if (typeof XMLSerializer !== 'undefined') {
70113 /* istanbul ignore next */
70114 serializer = new XMLSerializer(); // only require xmldom in a node environment
70115 } else if ( (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && !process.browser) {
70116 serializer = new domParser.XMLSerializer();
70119 function xml2str(str) {
70120 // IE9 will create a new XMLSerializer but it'll crash immediately.
70121 // This line is ignored because we don't run coverage tests in IE9
70123 /* istanbul ignore next */
70124 if (str.xml !== undefined) return str.xml;
70125 return serializer.serializeToString(str);
70129 kml: function kml(doc) {
70131 // styleindex keeps track of hashed styles in order to match features
70134 // stylemapindex keeps track of style maps to expose in properties
70135 styleMapIndex = {},
70136 // atomic geospatial types supported by KML - MultiGeometry is
70137 // handled separately
70138 geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
70139 // all root placemarks in the file
70140 placemarks = get(doc, 'Placemark'),
70141 styles = get(doc, 'Style'),
70142 styleMaps = get(doc, 'StyleMap');
70144 for (var k = 0; k < styles.length; k++) {
70145 var hash = okhash(xml2str(styles[k])).toString(16);
70146 styleIndex['#' + attr(styles[k], 'id')] = hash;
70147 styleByHash[hash] = styles[k];
70150 for (var l = 0; l < styleMaps.length; l++) {
70151 styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
70152 var pairs = get(styleMaps[l], 'Pair');
70155 for (var m = 0; m < pairs.length; m++) {
70156 pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
70159 styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
70162 for (var j = 0; j < placemarks.length; j++) {
70163 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
70166 function kmlColor(v) {
70167 var color, opacity;
70170 if (v.substr(0, 1) === '#') {
70174 if (v.length === 6 || v.length === 3) {
70178 if (v.length === 8) {
70179 opacity = parseInt(v.substr(0, 2), 16) / 255;
70180 color = '#' + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
70183 return [color, isNaN(opacity) ? undefined : opacity];
70186 function gxCoord(v) {
70187 return numarray(v.split(' '));
70190 function gxCoords(root) {
70191 var elems = get(root, 'coord'),
70194 if (elems.length === 0) elems = get(root, 'gx:coord');
70196 for (var i = 0; i < elems.length; i++) {
70197 coords.push(gxCoord(nodeVal(elems[i])));
70200 var timeElems = get(root, 'when');
70202 for (var j = 0; j < timeElems.length; j++) {
70203 times.push(nodeVal(timeElems[j]));
70212 function getGeometry(root) {
70221 if (get1(root, 'MultiGeometry')) {
70222 return getGeometry(get1(root, 'MultiGeometry'));
70225 if (get1(root, 'MultiTrack')) {
70226 return getGeometry(get1(root, 'MultiTrack'));
70229 if (get1(root, 'gx:MultiTrack')) {
70230 return getGeometry(get1(root, 'gx:MultiTrack'));
70233 for (i = 0; i < geotypes.length; i++) {
70234 geomNodes = get(root, geotypes[i]);
70237 for (j = 0; j < geomNodes.length; j++) {
70238 geomNode = geomNodes[j];
70240 if (geotypes[i] === 'Point') {
70243 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
70245 } else if (geotypes[i] === 'LineString') {
70247 type: 'LineString',
70248 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
70250 } else if (geotypes[i] === 'Polygon') {
70251 var rings = get(geomNode, 'LinearRing'),
70254 for (k = 0; k < rings.length; k++) {
70255 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
70260 coordinates: coords
70262 } else if (geotypes[i] === 'Track' || geotypes[i] === 'gx:Track') {
70263 var track = gxCoords(geomNode);
70265 type: 'LineString',
70266 coordinates: track.coords
70268 if (track.times.length) coordTimes.push(track.times);
70276 coordTimes: coordTimes
70280 function getPlacemark(root) {
70281 var geomsAndTimes = getGeometry(root),
70284 name = nodeVal(get1(root, 'name')),
70285 address = nodeVal(get1(root, 'address')),
70286 styleUrl = nodeVal(get1(root, 'styleUrl')),
70287 description = nodeVal(get1(root, 'description')),
70288 timeSpan = get1(root, 'TimeSpan'),
70289 timeStamp = get1(root, 'TimeStamp'),
70290 extendedData = get1(root, 'ExtendedData'),
70291 lineStyle = get1(root, 'LineStyle'),
70292 polyStyle = get1(root, 'PolyStyle'),
70293 visibility = get1(root, 'visibility');
70294 if (!geomsAndTimes.geoms.length) return [];
70295 if (name) properties.name = name;
70296 if (address) properties.address = address;
70299 if (styleUrl[0] !== '#') {
70300 styleUrl = '#' + styleUrl;
70303 properties.styleUrl = styleUrl;
70305 if (styleIndex[styleUrl]) {
70306 properties.styleHash = styleIndex[styleUrl];
70309 if (styleMapIndex[styleUrl]) {
70310 properties.styleMapHash = styleMapIndex[styleUrl];
70311 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
70312 } // Try to populate the lineStyle or polyStyle since we got the style hash
70315 var style = styleByHash[properties.styleHash];
70318 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
70319 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
70323 if (description) properties.description = description;
70326 var begin = nodeVal(get1(timeSpan, 'begin'));
70327 var end = nodeVal(get1(timeSpan, 'end'));
70328 properties.timespan = {
70335 properties.timestamp = nodeVal(get1(timeStamp, 'when'));
70339 var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
70340 color = linestyles[0],
70341 opacity = linestyles[1],
70342 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70343 if (color) properties.stroke = color;
70344 if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
70345 if (!isNaN(width)) properties['stroke-width'] = width;
70349 var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
70350 pcolor = polystyles[0],
70351 popacity = polystyles[1],
70352 fill = nodeVal(get1(polyStyle, 'fill')),
70353 outline = nodeVal(get1(polyStyle, 'outline'));
70354 if (pcolor) properties.fill = pcolor;
70355 if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
70356 if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
70357 if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
70360 if (extendedData) {
70361 var datas = get(extendedData, 'Data'),
70362 simpleDatas = get(extendedData, 'SimpleData');
70364 for (i = 0; i < datas.length; i++) {
70365 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
70368 for (i = 0; i < simpleDatas.length; i++) {
70369 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
70374 properties.visibility = nodeVal(visibility);
70377 if (geomsAndTimes.coordTimes.length) {
70378 properties.coordTimes = geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
70383 geometry: geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
70384 type: 'GeometryCollection',
70385 geometries: geomsAndTimes.geoms
70387 properties: properties
70389 if (attr(root, 'id')) feature.id = attr(root, 'id');
70395 gpx: function gpx(doc) {
70397 tracks = get(doc, 'trk'),
70398 routes = get(doc, 'rte'),
70399 waypoints = get(doc, 'wpt'),
70400 // a feature collection
70404 for (i = 0; i < tracks.length; i++) {
70405 feature = getTrack(tracks[i]);
70406 if (feature) gj.features.push(feature);
70409 for (i = 0; i < routes.length; i++) {
70410 feature = getRoute(routes[i]);
70411 if (feature) gj.features.push(feature);
70414 for (i = 0; i < waypoints.length; i++) {
70415 gj.features.push(getPoint(waypoints[i]));
70418 function getPoints(node, pointname) {
70419 var pts = get(node, pointname),
70424 if (l < 2) return {}; // Invalid line in GeoJSON
70426 for (var i = 0; i < l; i++) {
70427 var c = coordPair(pts[i]);
70428 line.push(c.coordinates);
70429 if (c.time) times.push(c.time);
70430 if (c.heartRate) heartRates.push(c.heartRate);
70436 heartRates: heartRates
70440 function getTrack(node) {
70441 var segments = get(node, 'trkseg'),
70447 for (var i = 0; i < segments.length; i++) {
70448 line = getPoints(segments[i], 'trkpt');
70451 if (line.line) track.push(line.line);
70452 if (line.times && line.times.length) times.push(line.times);
70453 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
70457 if (track.length === 0) return;
70458 var properties = getProperties(node);
70459 extend(properties, getLineStyle(get1(node, 'extensions')));
70460 if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
70461 if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
70464 properties: properties,
70466 type: track.length === 1 ? 'LineString' : 'MultiLineString',
70467 coordinates: track.length === 1 ? track[0] : track
70472 function getRoute(node) {
70473 var line = getPoints(node, 'rtept');
70474 if (!line.line) return;
70475 var prop = getProperties(node);
70476 extend(prop, getLineStyle(get1(node, 'extensions')));
70481 type: 'LineString',
70482 coordinates: line.line
70488 function getPoint(node) {
70489 var prop = getProperties(node);
70490 extend(prop, getMulti(node, ['sym']));
70496 coordinates: coordPair(node).coordinates
70501 function getLineStyle(extensions) {
70505 var lineStyle = get1(extensions, 'line');
70508 var color = nodeVal(get1(lineStyle, 'color')),
70509 opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
70510 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70511 if (color) style.stroke = color;
70512 if (!isNaN(opacity)) style['stroke-opacity'] = opacity; // GPX width is in mm, convert to px with 96 px per inch
70514 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
70521 function getProperties(node) {
70522 var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
70523 links = get(node, 'link');
70524 if (links.length) prop.links = [];
70526 for (var i = 0, link; i < links.length; i++) {
70528 href: attr(links[i], 'href')
70530 extend(link, getMulti(links[i], ['text', 'type']));
70531 prop.links.push(link);
70543 module.exports = toGeoJSON;
70546 var _initialized = false;
70547 var _enabled = false;
70551 function svgData(projection, context, dispatch) {
70552 var throttledRedraw = throttle(function () {
70553 dispatch.call('change');
70556 var _showLabels = true;
70557 var detected = utilDetect();
70558 var layer = select(null);
70569 if (_initialized) return; // run once
70574 function over(d3_event) {
70575 d3_event.stopPropagation();
70576 d3_event.preventDefault();
70577 d3_event.dataTransfer.dropEffect = 'copy';
70580 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
70581 d3_event.stopPropagation();
70582 d3_event.preventDefault();
70583 if (!detected.filedrop) return;
70584 drawData.fileList(d3_event.dataTransfer.files);
70585 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
70586 _initialized = true;
70589 function getService() {
70590 if (services.vectorTile && !_vtService) {
70591 _vtService = services.vectorTile;
70593 _vtService.event.on('loadedData', throttledRedraw);
70594 } else if (!services.vectorTile && _vtService) {
70601 function showLayer() {
70603 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
70604 dispatch.call('change');
70608 function hideLayer() {
70609 throttledRedraw.cancel();
70610 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
70613 function layerOn() {
70614 layer.style('display', 'block');
70617 function layerOff() {
70618 layer.selectAll('.viewfield-group').remove();
70619 layer.style('display', 'none');
70620 } // ensure that all geojson features in a collection have IDs
70623 function ensureIDs(gj) {
70624 if (!gj) return null;
70626 if (gj.type === 'FeatureCollection') {
70627 for (var i = 0; i < gj.features.length; i++) {
70628 ensureFeatureID(gj.features[i]);
70631 ensureFeatureID(gj);
70635 } // ensure that each single Feature object has a unique ID
70638 function ensureFeatureID(feature) {
70639 if (!feature) return;
70640 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
70642 } // Prefer an array of Features instead of a FeatureCollection
70645 function getFeatures(gj) {
70646 if (!gj) return [];
70648 if (gj.type === 'FeatureCollection') {
70649 return gj.features;
70655 function featureKey(d) {
70656 return d.__featurehash__;
70659 function isPolygon(d) {
70660 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
70663 function clipPathID(d) {
70664 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
70667 function featureClasses(d) {
70668 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
70671 function drawData(selection) {
70672 var vtService = getService();
70673 var getPath = svgPath(projection).geojson;
70674 var getAreaPath = svgPath(projection, null, true).geojson;
70675 var hasData = drawData.hasData();
70676 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
70677 layer.exit().remove();
70678 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
70679 var surface = context.surface();
70680 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
70683 var geoData, polygonData;
70685 if (_template && vtService) {
70686 // fetch data from vector tile service
70687 var sourceID = _template;
70688 vtService.loadTiles(sourceID, _template, projection);
70689 geoData = vtService.data(sourceID, projection);
70691 geoData = getFeatures(_geojson);
70694 geoData = geoData.filter(getPath);
70695 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
70697 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
70698 clipPaths.exit().remove();
70699 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
70700 clipPathsEnter.append('path');
70701 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
70703 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
70704 datagroups = datagroups.enter().append('g').attr('class', function (d) {
70705 return 'datagroup datagroup-' + d;
70706 }).merge(datagroups); // Draw paths
70713 var paths = datagroups.selectAll('path').data(function (layer) {
70714 return pathData[layer];
70715 }, featureKey); // exit
70717 paths.exit().remove(); // enter/update
70719 paths = paths.enter().append('path').attr('class', function (d) {
70720 var datagroup = this.parentNode.__data__;
70721 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
70722 }).attr('clip-path', function (d) {
70723 var datagroup = this.parentNode.__data__;
70724 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
70725 }).merge(paths).attr('d', function (d) {
70726 var datagroup = this.parentNode.__data__;
70727 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
70730 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
70732 function drawLabels(selection, textClass, data) {
70733 var labelPath = d3_geoPath(projection);
70734 var labelData = data.filter(function (d) {
70735 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
70737 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
70739 labels.exit().remove(); // enter/update
70741 labels = labels.enter().append('text').attr('class', function (d) {
70742 return textClass + ' ' + featureClasses(d);
70743 }).merge(labels).text(function (d) {
70744 return d.properties.desc || d.properties.name;
70745 }).attr('x', function (d) {
70746 var centroid = labelPath.centroid(d);
70747 return centroid[0] + 11;
70748 }).attr('y', function (d) {
70749 var centroid = labelPath.centroid(d);
70750 return centroid[1];
70755 function getExtension(fileName) {
70756 if (!fileName) return;
70757 var re = /\.(gpx|kml|(geo)?json)$/i;
70758 var match = fileName.toLowerCase().match(re);
70759 return match && match.length && match[0];
70762 function xmlToDom(textdata) {
70763 return new DOMParser().parseFromString(textdata, 'text/xml');
70766 drawData.setFile = function (extension, data) {
70773 switch (extension) {
70775 gj = togeojson.gpx(xmlToDom(data));
70779 gj = togeojson.kml(xmlToDom(data));
70784 gj = JSON.parse(data);
70790 if (Object.keys(gj).length) {
70791 _geojson = ensureIDs(gj);
70792 _src = extension + ' data file';
70796 dispatch.call('change');
70800 drawData.showLabels = function (val) {
70801 if (!arguments.length) return _showLabels;
70806 drawData.enabled = function (val) {
70807 if (!arguments.length) return _enabled;
70816 dispatch.call('change');
70820 drawData.hasData = function () {
70821 var gj = _geojson || {};
70822 return !!(_template || Object.keys(gj).length);
70825 drawData.template = function (val, src) {
70826 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
70828 var osm = context.connection();
70831 var blocklists = osm.imageryBlocklists();
70836 for (var i = 0; i < blocklists.length; i++) {
70837 regex = blocklists[i];
70838 fail = regex.test(val);
70841 } // ensure at least one test was run.
70845 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
70846 fail = regex.test(val);
70852 _geojson = null; // strip off the querystring/hash from the template,
70853 // it often includes the access token
70855 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
70856 dispatch.call('change');
70860 drawData.geojson = function (gj, src) {
70861 if (!arguments.length) return _geojson;
70868 if (Object.keys(gj).length) {
70869 _geojson = ensureIDs(gj);
70870 _src = src || 'unknown.geojson';
70873 dispatch.call('change');
70877 drawData.fileList = function (fileList) {
70878 if (!arguments.length) return _fileList;
70880 _fileList = fileList;
70883 if (!fileList || !fileList.length) return this;
70884 var f = fileList[0];
70885 var extension = getExtension(f.name);
70886 var reader = new FileReader();
70888 reader.onload = function () {
70889 return function (e) {
70890 drawData.setFile(extension, e.target.result);
70894 reader.readAsText(f);
70898 drawData.url = function (url, defaultExtension) {
70902 _src = null; // strip off any querystring/hash from the url before checking extension
70904 var testUrl = url.split(/[?#]/)[0];
70905 var extension = getExtension(testUrl) || defaultExtension;
70909 d3_text(url).then(function (data) {
70910 drawData.setFile(extension, data);
70911 })["catch"](function () {
70915 drawData.template(url);
70921 drawData.getSrc = function () {
70925 drawData.fitZoom = function () {
70926 var features = getFeatures(_geojson);
70927 if (!features.length) return;
70928 var map = context.map();
70929 var viewport = map.trimmedExtent().polygon();
70930 var coords = features.reduce(function (coords, feature) {
70931 var geom = feature.geometry;
70932 if (!geom) return coords;
70933 var c = geom.coordinates;
70934 /* eslint-disable no-fallthrough */
70936 switch (geom.type) {
70944 case 'MultiPolygon':
70945 c = utilArrayFlatten(c);
70948 case 'MultiLineString':
70949 c = utilArrayFlatten(c);
70952 /* eslint-enable no-fallthrough */
70955 return utilArrayUnion(coords, c);
70958 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
70959 var extent = geoExtent(d3_geoBounds({
70960 type: 'LineString',
70961 coordinates: coords
70963 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
70973 function svgDebug(projection, context) {
70974 function drawDebug(selection) {
70975 var showTile = context.getDebug('tile');
70976 var showCollision = context.getDebug('collision');
70977 var showImagery = context.getDebug('imagery');
70978 var showTouchTargets = context.getDebug('target');
70979 var showDownloaded = context.getDebug('downloaded');
70980 var debugData = [];
70989 if (showCollision) {
71003 if (showTouchTargets) {
71006 label: 'touchTargets'
71010 if (showDownloaded) {
71013 label: 'downloaded'
71017 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
71018 legend.exit().remove();
71019 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
71020 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
71023 legendItems.exit().remove();
71024 legendItems.enter().append('span').attr('class', function (d) {
71025 return "debug-legend-item ".concat(d["class"]);
71026 }).text(function (d) {
71029 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
71030 layer.exit().remove();
71031 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
71033 var extent = context.map().extent();
71034 _mainFileFetcher.get('imagery').then(function (d) {
71035 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
71036 var features = hits.map(function (d) {
71037 return d.features[d.id];
71039 var imagery = layer.selectAll('path.debug-imagery').data(features);
71040 imagery.exit().remove();
71041 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
71042 })["catch"](function () {
71046 var osm = context.connection();
71047 var dataDownloaded = [];
71049 if (osm && showDownloaded) {
71050 var rtree = osm.caches('get').tile.rtree;
71051 dataDownloaded = rtree.all().map(function (bbox) {
71059 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
71065 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
71066 downloaded.exit().remove();
71067 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
71069 layer.selectAll('path').attr('d', svgPath(projection).geojson);
71070 } // This looks strange because `enabled` methods on other layers are
71071 // chainable getter/setters, and this one is just a getter.
71074 drawDebug.enabled = function () {
71075 if (!arguments.length) {
71076 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
71086 A standalone SVG element that contains only a `defs` sub-element. To be
71087 used once globally, since defs IDs must be unique within a document.
71090 function svgDefs(context) {
71091 var _defsSelection = select(null);
71093 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
71095 function drawDefs(selection) {
71096 _defsSelection = selection.append('defs'); // add markers
71098 _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
71099 // (they can't inherit it from the line they're attached to),
71100 // so we need to manually define markers for each color of tag
71101 // (also, it's slightly nicer if we can control the
71102 // positioning for different tags)
71105 function addSidedMarker(name, color, offset) {
71106 _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);
71109 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
71110 // the water side, so let's color them blue (with a gap) to
71111 // give a stronger indication
71113 addSidedMarker('coastline', '#77dede', 1);
71114 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
71115 // from the line visually suits that
71117 addSidedMarker('barrier', '#ddd', 1);
71118 addSidedMarker('man_made', '#fff', 0);
71120 _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');
71122 _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
71125 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
71126 ['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) {
71127 return 'ideditor-pattern-' + d[0];
71128 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
71130 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
71131 return 'pattern-color-' + d[0];
71133 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
71134 return context.imagePath('pattern/' + d[1] + '.png');
71135 }); // add clip paths
71137 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
71138 return 'ideditor-clip-square-' + d;
71139 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
71141 }).attr('height', function (d) {
71143 }); // add symbol spritesheets
71146 addSprites(_spritesheetIds, true);
71149 function addSprites(ids, overrideColors) {
71150 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
71152 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
71154 spritesheets.enter().append('g').attr('class', function (d) {
71155 return 'spritesheet spritesheet-' + d;
71156 }).each(function (d) {
71157 var url = context.imagePath(d + '.svg');
71158 var node = select(this).node();
71159 svg(url).then(function (svg) {
71160 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
71162 if (overrideColors && d !== 'iD-sprite') {
71163 // allow icon colors to be overridden..
71164 select(node).selectAll('path').attr('fill', 'currentColor');
71166 })["catch"](function () {
71170 spritesheets.exit().remove();
71173 drawDefs.addSprites = addSprites;
71177 var _layerEnabled = false;
71181 function svgKeepRight(projection, context, dispatch) {
71182 var throttledRedraw = throttle(function () {
71183 return dispatch.call('change');
71187 var touchLayer = select(null);
71188 var drawLayer = select(null);
71189 var layerVisible = false;
71191 function markerPath(selection, klass) {
71192 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');
71193 } // Loosely-coupled keepRight service for fetching issues.
71196 function getService() {
71197 if (services.keepRight && !_qaService) {
71198 _qaService = services.keepRight;
71200 _qaService.on('loaded', throttledRedraw);
71201 } else if (!services.keepRight && _qaService) {
71206 } // Show the markers
71209 function editOn() {
71210 if (!layerVisible) {
71211 layerVisible = true;
71212 drawLayer.style('display', 'block');
71214 } // Immediately remove the markers and their touch targets
71217 function editOff() {
71218 if (layerVisible) {
71219 layerVisible = false;
71220 drawLayer.style('display', 'none');
71221 drawLayer.selectAll('.qaItem.keepRight').remove();
71222 touchLayer.selectAll('.qaItem.keepRight').remove();
71224 } // Enable the layer. This shows the markers and transitions them to visible.
71227 function layerOn() {
71229 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71230 return dispatch.call('change');
71232 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71235 function layerOff() {
71236 throttledRedraw.cancel();
71237 drawLayer.interrupt();
71238 touchLayer.selectAll('.qaItem.keepRight').remove();
71239 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
71241 dispatch.call('change');
71243 } // Update the issue markers
71246 function updateMarkers() {
71247 if (!layerVisible || !_layerEnabled) return;
71248 var service = getService();
71249 var selectedID = context.selectedErrorID();
71250 var data = service ? service.getItems(projection) : [];
71251 var getTransform = svgPointTransform(projection); // Draw markers..
71253 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71257 markers.exit().remove(); // enter
71259 var markersEnter = markers.enter().append('g').attr('class', function (d) {
71260 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
71262 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
71263 markersEnter.append('path').call(markerPath, 'shadow');
71264 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
71266 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
71267 return d.id === selectedID;
71268 }).attr('transform', getTransform); // Draw targets..
71270 if (touchLayer.empty()) return;
71271 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71272 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71276 targets.exit().remove(); // enter/update
71278 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
71279 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
71280 }).attr('transform', getTransform);
71282 function sortY(a, b) {
71283 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];
71285 } // Draw the keepRight layer and schedule loading issues and updating markers.
71288 function drawKeepRight(selection) {
71289 var service = getService();
71290 var surface = context.surface();
71292 if (surface && !surface.empty()) {
71293 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71296 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
71297 drawLayer.exit().remove();
71298 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
71300 if (_layerEnabled) {
71301 if (service && ~~context.map().zoom() >= minZoom) {
71303 service.loadIssues(projection);
71309 } // Toggles the layer on and off
71312 drawKeepRight.enabled = function (val) {
71313 if (!arguments.length) return _layerEnabled;
71314 _layerEnabled = val;
71316 if (_layerEnabled) {
71321 if (context.selectedErrorID()) {
71322 context.enter(modeBrowse(context));
71326 dispatch.call('change');
71330 drawKeepRight.supported = function () {
71331 return !!getService();
71334 return drawKeepRight;
71337 function svgGeolocate(projection) {
71338 var layer = select(null);
71343 if (svgGeolocate.initialized) return; // run once
71345 svgGeolocate.enabled = false;
71346 svgGeolocate.initialized = true;
71349 function showLayer() {
71350 layer.style('display', 'block');
71353 function hideLayer() {
71354 layer.transition().duration(250).style('opacity', 0);
71357 function layerOn() {
71358 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
71361 function layerOff() {
71362 layer.style('display', 'none');
71365 function transform(d) {
71366 return svgPointTransform(projection)(d);
71369 function accuracy(accuracy, loc) {
71370 // converts accuracy to pixels...
71371 var degreesRadius = geoMetersToLat(accuracy),
71372 tangentLoc = [loc[0], loc[1] + degreesRadius],
71373 projectedTangent = projection(tangentLoc),
71374 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
71376 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
71379 function update() {
71380 var geolocation = {
71381 loc: [_position.coords.longitude, _position.coords.latitude]
71383 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
71384 groups.exit().remove();
71385 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
71386 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');
71387 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');
71388 groups.merge(pointsEnter).attr('transform', transform);
71389 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
71392 function drawLocation(selection) {
71393 var enabled = svgGeolocate.enabled;
71394 layer = selection.selectAll('.layer-geolocate').data([0]);
71395 layer.exit().remove();
71396 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
71397 layerEnter.append('g').attr('class', 'geolocations');
71398 layer = layerEnter.merge(layer);
71407 drawLocation.enabled = function (position, enabled) {
71408 if (!arguments.length) return svgGeolocate.enabled;
71409 _position = position;
71410 svgGeolocate.enabled = enabled;
71412 if (svgGeolocate.enabled) {
71423 return drawLocation;
71426 function svgLabels(projection, context) {
71427 var path = d3_geoPath(projection);
71428 var detected = utilDetect();
71429 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
71431 var _rdrawn = new RBush();
71433 var _rskipped = new RBush();
71435 var _textWidthCache = {};
71436 var _entitybboxes = {}; // Listed from highest to lowest priority
71438 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]];
71440 function shouldSkipIcon(preset) {
71441 var noIcons = ['building', 'landuse', 'natural'];
71442 return noIcons.some(function (s) {
71443 return preset.id.indexOf(s) >= 0;
71447 function get(array, prop) {
71448 return function (d, i) {
71449 return array[i][prop];
71453 function textWidth(text, size, elem) {
71454 var c = _textWidthCache[size];
71455 if (!c) c = _textWidthCache[size] = {};
71460 c[text] = elem.getComputedTextLength();
71463 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
71465 if (str === null) {
71466 return size / 3 * 2 * text.length;
71468 return size / 3 * (2 * text.length + str.length);
71473 function drawLinePaths(selection, entities, filter, classes, labels) {
71474 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
71476 paths.exit().remove(); // enter/update
71478 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
71479 return 'ideditor-labelpath-' + d.id;
71480 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
71483 function drawLineLabels(selection, entities, filter, classes, labels) {
71484 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71486 texts.exit().remove(); // enter
71488 texts.enter().append('text').attr('class', function (d, i) {
71489 return classes + ' ' + labels[i].classes + ' ' + d.id;
71490 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
71492 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
71493 return '#ideditor-labelpath-' + d.id;
71494 }).text(utilDisplayNameForPath);
71497 function drawPointLabels(selection, entities, filter, classes, labels) {
71498 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71500 texts.exit().remove(); // enter/update
71502 texts.enter().append('text').attr('class', function (d, i) {
71503 return classes + ' ' + labels[i].classes + ' ' + d.id;
71504 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
71505 textWidth(utilDisplayName(d), labels[i].height, this);
71509 function drawAreaLabels(selection, entities, filter, classes, labels) {
71510 entities = entities.filter(hasText);
71511 labels = labels.filter(hasText);
71512 drawPointLabels(selection, entities, filter, classes, labels);
71514 function hasText(d, i) {
71515 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
71519 function drawAreaIcons(selection, entities, filter, classes, labels) {
71520 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71522 icons.exit().remove(); // enter/update
71524 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) {
71525 var preset = _mainPresetIndex.match(d, context.graph());
71526 var picon = preset && preset.icon;
71531 var isMaki = /^maki-/.test(picon);
71532 return '#' + picon + (isMaki ? '-15' : '');
71537 function drawCollisionBoxes(selection, rtree, which) {
71538 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
71541 if (context.getDebug('collision')) {
71542 gj = rtree.all().map(function (d) {
71545 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
71550 var boxes = selection.selectAll('.' + which).data(gj); // exit
71552 boxes.exit().remove(); // enter/update
71554 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
71557 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
71558 var wireframe = context.surface().classed('fill-wireframe');
71559 var zoom = geoScaleToZoom(projection.scale());
71560 var labelable = [];
71561 var renderNodeAs = {};
71562 var i, j, k, entity, geometry;
71564 for (i = 0; i < labelStack.length; i++) {
71565 labelable.push([]);
71573 _entitybboxes = {};
71575 for (i = 0; i < entities.length; i++) {
71576 entity = entities[i];
71577 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
71579 for (j = 0; j < toRemove.length; j++) {
71580 _rdrawn.remove(toRemove[j]);
71582 _rskipped.remove(toRemove[j]);
71585 } // Loop through all the entities to do some preprocessing
71588 for (i = 0; i < entities.length; i++) {
71589 entity = entities[i];
71590 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
71592 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
71593 var hasDirections = entity.directions(graph, projection).length;
71596 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
71597 renderNodeAs[entity.id] = 'point';
71598 markerPadding = 20; // extra y for marker height
71600 renderNodeAs[entity.id] = 'vertex';
71604 var coord = projection(entity.loc);
71605 var nodePadding = 10;
71607 minX: coord[0] - nodePadding,
71608 minY: coord[1] - nodePadding - markerPadding,
71609 maxX: coord[0] + nodePadding,
71610 maxY: coord[1] + nodePadding
71612 doInsert(bbox, entity.id + 'P');
71613 } // From here on, treat vertices like points
71616 if (geometry === 'vertex') {
71617 geometry = 'point';
71618 } // Determine which entities are label-able
71621 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
71622 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
71623 if (!icon && !utilDisplayName(entity)) continue;
71625 for (k = 0; k < labelStack.length; k++) {
71626 var matchGeom = labelStack[k][0];
71627 var matchKey = labelStack[k][1];
71628 var matchVal = labelStack[k][2];
71629 var hasVal = entity.tags[matchKey];
71631 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
71632 labelable[k].push(entity);
71647 }; // Try and find a valid label for labellable entities
71649 for (k = 0; k < labelable.length; k++) {
71650 var fontSize = labelStack[k][3];
71652 for (i = 0; i < labelable[k].length; i++) {
71653 entity = labelable[k][i];
71654 geometry = entity.geometry(graph);
71655 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
71656 var name = getName(entity);
71657 var width = name && textWidth(name, fontSize);
71660 if (geometry === 'point' || geometry === 'vertex') {
71661 // no point or vertex labels in wireframe mode
71662 // no vertex labels at low zooms (vertices have no icons)
71663 if (wireframe) continue;
71664 var renderAs = renderNodeAs[entity.id];
71665 if (renderAs === 'vertex' && zoom < 17) continue;
71666 p = getPointLabel(entity, width, fontSize, renderAs);
71667 } else if (geometry === 'line') {
71668 p = getLineLabel(entity, width, fontSize);
71669 } else if (geometry === 'area') {
71670 p = getAreaLabel(entity, width, fontSize);
71674 if (geometry === 'vertex') {
71675 geometry = 'point';
71676 } // treat vertex like point
71679 p.classes = geometry + ' tag-' + labelStack[k][1];
71680 positions[geometry].push(p);
71681 labelled[geometry].push(entity);
71686 function isInterestingVertex(entity) {
71687 var selectedIDs = context.selectedIDs();
71688 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
71689 return selectedIDs.indexOf(parent.id) !== -1;
71693 function getPointLabel(entity, width, height, geometry) {
71694 var y = geometry === 'point' ? -12 : 0;
71695 var pointOffsets = {
71696 ltr: [15, y, 'start'],
71697 rtl: [-15, y, 'end']
71699 var textDirection = _mainLocalizer.textDirection();
71700 var coord = projection(entity.loc);
71701 var textPadding = 2;
71702 var offset = pointOffsets[textDirection];
71706 x: coord[0] + offset[0],
71707 y: coord[1] + offset[1],
71708 textAnchor: offset[2]
71709 }; // insert a collision box for the text label..
71713 if (textDirection === 'rtl') {
71715 minX: p.x - width - textPadding,
71716 minY: p.y - height / 2 - textPadding,
71717 maxX: p.x + textPadding,
71718 maxY: p.y + height / 2 + textPadding
71722 minX: p.x - textPadding,
71723 minY: p.y - height / 2 - textPadding,
71724 maxX: p.x + width + textPadding,
71725 maxY: p.y + height / 2 + textPadding
71729 if (tryInsert([bbox], entity.id, true)) {
71734 function getLineLabel(entity, width, height) {
71735 var viewport = geoExtent(context.projection.clipExtent()).polygon();
71736 var points = graph.childNodes(entity).map(function (node) {
71737 return projection(node.loc);
71739 var length = geoPathLength(points);
71740 if (length < width + 20) return; // % along the line to attempt to place the label
71742 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
71745 for (var i = 0; i < lineOffsets.length; i++) {
71746 var offset = lineOffsets[i];
71747 var middle = offset / 100 * length;
71748 var start = middle - width / 2;
71749 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
71751 var sub = subpath(points, start, start + width);
71753 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
71757 var isReverse = reverse(sub);
71760 sub = sub.reverse();
71764 var boxsize = (height + 2) / 2;
71766 for (var j = 0; j < sub.length - 1; j++) {
71768 var b = sub[j + 1]; // split up the text into small collision boxes
71770 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
71772 for (var box = 0; box < num; box++) {
71773 var p = geoVecInterp(a, b, box / num);
71774 var x0 = p[0] - boxsize - padding;
71775 var y0 = p[1] - boxsize - padding;
71776 var x1 = p[0] + boxsize + padding;
71777 var y1 = p[1] + boxsize + padding;
71779 minX: Math.min(x0, x1),
71780 minY: Math.min(y0, y1),
71781 maxX: Math.max(x0, x1),
71782 maxY: Math.max(y0, y1)
71787 if (tryInsert(bboxes, entity.id, false)) {
71790 'font-size': height + 2,
71791 lineString: lineString(sub),
71792 startOffset: offset + '%'
71797 function reverse(p) {
71798 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
71799 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
71802 function lineString(points) {
71803 return 'M' + points.join('L');
71806 function subpath(points, from, to) {
71808 var start, end, i0, i1;
71810 for (var i = 0; i < points.length - 1; i++) {
71812 var b = points[i + 1];
71813 var current = geoVecLength(a, b);
71816 if (!start && sofar + current >= from) {
71817 portion = (from - sofar) / current;
71818 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71822 if (!end && sofar + current >= to) {
71823 portion = (to - sofar) / current;
71824 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71831 var result = points.slice(i0, i1);
71832 result.unshift(start);
71838 function getAreaLabel(entity, width, height) {
71839 var centroid = path.centroid(entity.asGeoJSON(graph, true));
71840 var extent = entity.extent(graph);
71841 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
71842 if (isNaN(centroid[0]) || areaWidth < 20) return;
71843 var preset = _mainPresetIndex.match(entity, context.graph());
71844 var picon = preset && preset.icon;
71850 // icon and label..
71852 addLabel(iconSize + padding);
71862 function addIcon() {
71863 var iconX = centroid[0] - iconSize / 2;
71864 var iconY = centroid[1] - iconSize / 2;
71868 maxX: iconX + iconSize,
71869 maxY: iconY + iconSize
71872 if (tryInsert([bbox], entity.id + 'I', true)) {
71873 p.transform = 'translate(' + iconX + ',' + iconY + ')';
71880 function addLabel(yOffset) {
71881 if (width && areaWidth >= width + 20) {
71882 var labelX = centroid[0];
71883 var labelY = centroid[1] + yOffset;
71885 minX: labelX - width / 2 - padding,
71886 minY: labelY - height / 2 - padding,
71887 maxX: labelX + width / 2 + padding,
71888 maxY: labelY + height / 2 + padding
71891 if (tryInsert([bbox], entity.id, true)) {
71894 p.textAnchor = 'middle';
71902 } // force insert a singular bounding box
71903 // singular box only, no array, id better be unique
71906 function doInsert(bbox, id) {
71908 var oldbox = _entitybboxes[id];
71911 _rdrawn.remove(oldbox);
71914 _entitybboxes[id] = bbox;
71916 _rdrawn.insert(bbox);
71919 function tryInsert(bboxes, id, saveSkipped) {
71920 var skipped = false;
71922 for (var i = 0; i < bboxes.length; i++) {
71923 var bbox = bboxes[i];
71924 bbox.id = id; // Check that label is visible
71926 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
71931 if (_rdrawn.collides(bbox)) {
71937 _entitybboxes[id] = bboxes;
71941 _rskipped.load(bboxes);
71944 _rdrawn.load(bboxes);
71950 var layer = selection.selectAll('.layer-osm.labels');
71951 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
71952 return 'labels-group ' + d;
71954 var halo = layer.selectAll('.labels-group.halo');
71955 var label = layer.selectAll('.labels-group.label');
71956 var debug = layer.selectAll('.labels-group.debug'); // points
71958 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
71959 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
71961 drawLinePaths(layer, labelled.line, filter, '', positions.line);
71962 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
71963 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
71965 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
71966 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
71967 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
71968 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
71970 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
71971 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
71972 layer.call(filterLabels);
71975 function filterLabels(selection) {
71976 var drawLayer = selection.selectAll('.layer-osm.labels');
71977 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
71978 layers.selectAll('.nolabel').classed('nolabel', false);
71979 var mouse = context.map().mouse();
71980 var graph = context.graph();
71981 var selectedIDs = context.selectedIDs();
71983 var pad, bbox; // hide labels near the mouse
71988 minX: mouse[0] - pad,
71989 minY: mouse[1] - pad,
71990 maxX: mouse[0] + pad,
71991 maxY: mouse[1] + pad
71994 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
71998 ids.push.apply(ids, nearMouse);
71999 } // hide labels on selected nodes (they look weird when dragging / haloed)
72002 for (var i = 0; i < selectedIDs.length; i++) {
72003 var entity = graph.hasEntity(selectedIDs[i]);
72005 if (entity && entity.type === 'node') {
72006 ids.push(selectedIDs[i]);
72010 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
72012 var debug = selection.selectAll('.labels-group.debug');
72015 if (context.getDebug('collision')) {
72018 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
72022 var box = debug.selectAll('.debug-mouse').data(gj); // exit
72024 box.exit().remove(); // enter/update
72026 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
72029 var throttleFilterLabels = throttle(filterLabels, 100);
72031 drawLabels.observe = function (selection) {
72032 var listener = function listener() {
72033 throttleFilterLabels(selection);
72036 selection.on('mousemove.hidelabels', listener);
72037 context.on('enter.hidelabels', listener);
72040 drawLabels.off = function (selection) {
72041 throttleFilterLabels.cancel();
72042 selection.on('mousemove.hidelabels', null);
72043 context.on('enter.hidelabels', null);
72049 var _layerEnabled$1 = false;
72053 function svgImproveOSM(projection, context, dispatch) {
72054 var throttledRedraw = throttle(function () {
72055 return dispatch.call('change');
72059 var touchLayer = select(null);
72060 var drawLayer = select(null);
72061 var layerVisible = false;
72063 function markerPath(selection, klass) {
72064 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');
72065 } // Loosely-coupled improveOSM service for fetching issues
72068 function getService() {
72069 if (services.improveOSM && !_qaService$1) {
72070 _qaService$1 = services.improveOSM;
72072 _qaService$1.on('loaded', throttledRedraw);
72073 } else if (!services.improveOSM && _qaService$1) {
72074 _qaService$1 = null;
72077 return _qaService$1;
72078 } // Show the markers
72081 function editOn() {
72082 if (!layerVisible) {
72083 layerVisible = true;
72084 drawLayer.style('display', 'block');
72086 } // Immediately remove the markers and their touch targets
72089 function editOff() {
72090 if (layerVisible) {
72091 layerVisible = false;
72092 drawLayer.style('display', 'none');
72093 drawLayer.selectAll('.qaItem.improveOSM').remove();
72094 touchLayer.selectAll('.qaItem.improveOSM').remove();
72096 } // Enable the layer. This shows the markers and transitions them to visible.
72099 function layerOn() {
72101 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72102 return dispatch.call('change');
72104 } // Disable the layer. This transitions the layer invisible and then hides the markers.
72107 function layerOff() {
72108 throttledRedraw.cancel();
72109 drawLayer.interrupt();
72110 touchLayer.selectAll('.qaItem.improveOSM').remove();
72111 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72113 dispatch.call('change');
72115 } // Update the issue markers
72118 function updateMarkers() {
72119 if (!layerVisible || !_layerEnabled$1) return;
72120 var service = getService();
72121 var selectedID = context.selectedErrorID();
72122 var data = service ? service.getItems(projection) : [];
72123 var getTransform = svgPointTransform(projection); // Draw markers..
72125 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72129 markers.exit().remove(); // enter
72131 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72132 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72134 markersEnter.append('polygon').call(markerPath, 'shadow');
72135 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72136 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
72137 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72138 var picon = d.icon;
72143 var isMaki = /^maki-/.test(picon);
72144 return "#".concat(picon).concat(isMaki ? '-11' : '');
72148 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72149 return d.id === selectedID;
72150 }).attr('transform', getTransform); // Draw targets..
72152 if (touchLayer.empty()) return;
72153 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72154 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72158 targets.exit().remove(); // enter/update
72160 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72161 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72162 }).attr('transform', getTransform);
72164 function sortY(a, b) {
72165 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72167 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
72170 function drawImproveOSM(selection) {
72171 var service = getService();
72172 var surface = context.surface();
72174 if (surface && !surface.empty()) {
72175 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72178 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
72179 drawLayer.exit().remove();
72180 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
72182 if (_layerEnabled$1) {
72183 if (service && ~~context.map().zoom() >= minZoom) {
72185 service.loadIssues(projection);
72191 } // Toggles the layer on and off
72194 drawImproveOSM.enabled = function (val) {
72195 if (!arguments.length) return _layerEnabled$1;
72196 _layerEnabled$1 = val;
72198 if (_layerEnabled$1) {
72203 if (context.selectedErrorID()) {
72204 context.enter(modeBrowse(context));
72208 dispatch.call('change');
72212 drawImproveOSM.supported = function () {
72213 return !!getService();
72216 return drawImproveOSM;
72219 var _layerEnabled$2 = false;
72223 function svgOsmose(projection, context, dispatch) {
72224 var throttledRedraw = throttle(function () {
72225 return dispatch.call('change');
72229 var touchLayer = select(null);
72230 var drawLayer = select(null);
72231 var layerVisible = false;
72233 function markerPath(selection, klass) {
72234 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');
72235 } // Loosely-coupled osmose service for fetching issues
72238 function getService() {
72239 if (services.osmose && !_qaService$2) {
72240 _qaService$2 = services.osmose;
72242 _qaService$2.on('loaded', throttledRedraw);
72243 } else if (!services.osmose && _qaService$2) {
72244 _qaService$2 = null;
72247 return _qaService$2;
72248 } // Show the markers
72251 function editOn() {
72252 if (!layerVisible) {
72253 layerVisible = true;
72254 drawLayer.style('display', 'block');
72256 } // Immediately remove the markers and their touch targets
72259 function editOff() {
72260 if (layerVisible) {
72261 layerVisible = false;
72262 drawLayer.style('display', 'none');
72263 drawLayer.selectAll('.qaItem.osmose').remove();
72264 touchLayer.selectAll('.qaItem.osmose').remove();
72266 } // Enable the layer. This shows the markers and transitions them to visible.
72269 function layerOn() {
72271 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72272 return dispatch.call('change');
72274 } // Disable the layer. This transitions the layer invisible and then hides the markers.
72277 function layerOff() {
72278 throttledRedraw.cancel();
72279 drawLayer.interrupt();
72280 touchLayer.selectAll('.qaItem.osmose').remove();
72281 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72283 dispatch.call('change');
72285 } // Update the issue markers
72288 function updateMarkers() {
72289 if (!layerVisible || !_layerEnabled$2) return;
72290 var service = getService();
72291 var selectedID = context.selectedErrorID();
72292 var data = service ? service.getItems(projection) : [];
72293 var getTransform = svgPointTransform(projection); // Draw markers..
72295 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72299 markers.exit().remove(); // enter
72301 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72302 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72304 markersEnter.append('polygon').call(markerPath, 'shadow');
72305 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72306 markersEnter.append('polygon').attr('fill', function (d) {
72307 return service.getColor(d.item);
72308 }).call(markerPath, 'qaItem-fill');
72309 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72310 var picon = d.icon;
72315 var isMaki = /^maki-/.test(picon);
72316 return "#".concat(picon).concat(isMaki ? '-11' : '');
72320 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72321 return d.id === selectedID;
72322 }).attr('transform', getTransform); // Draw targets..
72324 if (touchLayer.empty()) return;
72325 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
72326 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72330 targets.exit().remove(); // enter/update
72332 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72333 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72334 }).attr('transform', getTransform);
72336 function sortY(a, b) {
72337 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72339 } // Draw the Osmose layer and schedule loading issues and updating markers.
72342 function drawOsmose(selection) {
72343 var service = getService();
72344 var surface = context.surface();
72346 if (surface && !surface.empty()) {
72347 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72350 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
72351 drawLayer.exit().remove();
72352 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
72354 if (_layerEnabled$2) {
72355 if (service && ~~context.map().zoom() >= minZoom) {
72357 service.loadIssues(projection);
72363 } // Toggles the layer on and off
72366 drawOsmose.enabled = function (val) {
72367 if (!arguments.length) return _layerEnabled$2;
72368 _layerEnabled$2 = val;
72370 if (_layerEnabled$2) {
72371 // Strings supplied by Osmose fetched before showing layer for first time
72372 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
72373 // Also, If layer is toggled quickly multiple requests are sent
72374 getService().loadStrings().then(layerOn)["catch"](function (err) {
72375 console.log(err); // eslint-disable-line no-console
72380 if (context.selectedErrorID()) {
72381 context.enter(modeBrowse(context));
72385 dispatch.call('change');
72389 drawOsmose.supported = function () {
72390 return !!getService();
72396 function svgStreetside(projection, context, dispatch) {
72397 var throttledRedraw = throttle(function () {
72398 dispatch.call('change');
72402 var minMarkerZoom = 16;
72403 var minViewfieldZoom = 18;
72404 var layer = select(null);
72405 var _viewerYaw = 0;
72406 var _selectedSequence = null;
72415 if (svgStreetside.initialized) return; // run once
72417 svgStreetside.enabled = false;
72418 svgStreetside.initialized = true;
72425 function getService() {
72426 if (services.streetside && !_streetside) {
72427 _streetside = services.streetside;
72429 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
72430 } else if (!services.streetside && _streetside) {
72431 _streetside = null;
72434 return _streetside;
72441 function showLayer() {
72442 var service = getService();
72443 if (!service) return;
72445 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72446 dispatch.call('change');
72454 function hideLayer() {
72455 throttledRedraw.cancel();
72456 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72463 function editOn() {
72464 layer.style('display', 'block');
72471 function editOff() {
72472 layer.selectAll('.viewfield-group').remove();
72473 layer.style('display', 'none');
72476 * click() Handles 'bubble' point click event.
72480 function click(d3_event, d) {
72481 var service = getService();
72482 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
72484 if (d.sequenceKey !== _selectedSequence) {
72485 _viewerYaw = 0; // reset
72488 _selectedSequence = d.sequenceKey;
72489 service.ensureViewerLoaded(context).then(function () {
72490 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
72492 context.map().centerEase(d.loc);
72499 function mouseover(d3_event, d) {
72500 var service = getService();
72501 if (service) service.setStyles(context, d);
72508 function mouseout() {
72509 var service = getService();
72510 if (service) service.setStyles(context, null);
72517 function transform(d) {
72518 var t = svgPointTransform(projection)(d);
72519 var rot = d.ca + _viewerYaw;
72522 t += ' rotate(' + Math.floor(rot) + ',0,0)';
72528 function viewerChanged() {
72529 var service = getService();
72530 if (!service) return;
72531 var viewer = service.viewer();
72532 if (!viewer) return; // update viewfield rotation
72534 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
72535 // e.g. during drags or easing.
72537 if (context.map().isTransformed()) return;
72538 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
72541 function filterBubbles(bubbles) {
72542 var fromDate = context.photos().fromDate();
72543 var toDate = context.photos().toDate();
72544 var usernames = context.photos().usernames();
72547 var fromTimestamp = new Date(fromDate).getTime();
72548 bubbles = bubbles.filter(function (bubble) {
72549 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
72554 var toTimestamp = new Date(toDate).getTime();
72555 bubbles = bubbles.filter(function (bubble) {
72556 return new Date(bubble.captured_at).getTime() <= toTimestamp;
72561 bubbles = bubbles.filter(function (bubble) {
72562 return usernames.indexOf(bubble.captured_by) !== -1;
72569 function filterSequences(sequences) {
72570 var fromDate = context.photos().fromDate();
72571 var toDate = context.photos().toDate();
72572 var usernames = context.photos().usernames();
72575 var fromTimestamp = new Date(fromDate).getTime();
72576 sequences = sequences.filter(function (sequences) {
72577 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
72582 var toTimestamp = new Date(toDate).getTime();
72583 sequences = sequences.filter(function (sequences) {
72584 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
72589 sequences = sequences.filter(function (sequences) {
72590 return usernames.indexOf(sequences.properties.captured_by) !== -1;
72601 function update() {
72602 var viewer = context.container().select('.photoviewer');
72603 var selected = viewer.empty() ? undefined : viewer.datum();
72604 var z = ~~context.map().zoom();
72605 var showMarkers = z >= minMarkerZoom;
72606 var showViewfields = z >= minViewfieldZoom;
72607 var service = getService();
72608 var sequences = [];
72611 if (context.photos().showsPanoramic()) {
72612 sequences = service ? service.sequences(projection) : [];
72613 bubbles = service && showMarkers ? service.bubbles(projection) : [];
72614 sequences = filterSequences(sequences);
72615 bubbles = filterBubbles(bubbles);
72618 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72619 return d.properties.key;
72622 traces.exit().remove(); // enter/update
72624 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72625 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
72626 // force reenter once bubbles are attached to a sequence
72627 return d.key + (d.sequenceKey ? 'v1' : 'v0');
72630 groups.exit().remove(); // enter
72632 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72633 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72635 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72636 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
72637 }).attr('transform', transform).select('.viewfield-scale');
72638 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72639 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72640 viewfields.exit().remove(); // viewfields may or may not be drawn...
72641 // but if they are, draw below the circles
72643 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72645 function viewfieldPath() {
72646 var d = this.parentNode.__data__;
72649 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72651 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72657 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
72658 * 'svgStreetside()' is called from index.js
72662 function drawImages(selection) {
72663 var enabled = svgStreetside.enabled;
72664 var service = getService();
72665 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
72666 layer.exit().remove();
72667 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
72668 layerEnter.append('g').attr('class', 'sequences');
72669 layerEnter.append('g').attr('class', 'markers');
72670 layer = layerEnter.merge(layer);
72673 if (service && ~~context.map().zoom() >= minZoom) {
72676 service.loadBubbles(projection);
72683 * drawImages.enabled().
72687 drawImages.enabled = function (_) {
72688 if (!arguments.length) return svgStreetside.enabled;
72689 svgStreetside.enabled = _;
72691 if (svgStreetside.enabled) {
72693 context.photos().on('change.streetside', update);
72696 context.photos().on('change.streetside', null);
72699 dispatch.call('change');
72703 * drawImages.supported().
72707 drawImages.supported = function () {
72708 return !!getService();
72715 function svgMapillaryImages(projection, context, dispatch) {
72716 var throttledRedraw = throttle(function () {
72717 dispatch.call('change');
72721 var minMarkerZoom = 16;
72722 var minViewfieldZoom = 18;
72723 var layer = select(null);
72727 var viewerCompassAngle;
72730 if (svgMapillaryImages.initialized) return; // run once
72732 svgMapillaryImages.enabled = false;
72733 svgMapillaryImages.initialized = true;
72736 function getService() {
72737 if (services.mapillary && !_mapillary) {
72738 _mapillary = services.mapillary;
72740 _mapillary.event.on('loadedImages', throttledRedraw);
72741 } else if (!services.mapillary && _mapillary) {
72748 function showLayer() {
72749 var service = getService();
72750 if (!service) return;
72752 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72753 dispatch.call('change');
72757 function hideLayer() {
72758 throttledRedraw.cancel();
72759 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72762 function editOn() {
72763 layer.style('display', 'block');
72766 function editOff() {
72767 layer.selectAll('.viewfield-group').remove();
72768 layer.style('display', 'none');
72771 function click(d3_event, d) {
72772 var service = getService();
72773 if (!service) return;
72774 service.ensureViewerLoaded(context).then(function () {
72775 service.selectImage(context, d.key).showViewer(context);
72777 context.map().centerEase(d.loc);
72780 function mouseover(d) {
72781 var service = getService();
72782 if (service) service.setStyles(context, d);
72785 function mouseout() {
72786 var service = getService();
72787 if (service) service.setStyles(context, null);
72790 function transform(d) {
72791 var t = svgPointTransform(projection)(d);
72793 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72794 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72796 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72802 function filterImages(images) {
72803 var showsPano = context.photos().showsPanoramic();
72804 var showsFlat = context.photos().showsFlat();
72805 var fromDate = context.photos().fromDate();
72806 var toDate = context.photos().toDate();
72807 var usernames = context.photos().usernames();
72809 if (!showsPano || !showsFlat) {
72810 images = images.filter(function (image) {
72811 if (image.pano) return showsPano;
72817 var fromTimestamp = new Date(fromDate).getTime();
72818 images = images.filter(function (image) {
72819 return new Date(image.captured_at).getTime() >= fromTimestamp;
72824 var toTimestamp = new Date(toDate).getTime();
72825 images = images.filter(function (image) {
72826 return new Date(image.captured_at).getTime() <= toTimestamp;
72831 images = images.filter(function (image) {
72832 return usernames.indexOf(image.captured_by) !== -1;
72839 function filterSequences(sequences, service) {
72840 var showsPano = context.photos().showsPanoramic();
72841 var showsFlat = context.photos().showsFlat();
72842 var fromDate = context.photos().fromDate();
72843 var toDate = context.photos().toDate();
72844 var usernames = context.photos().usernames();
72846 if (!showsPano || !showsFlat) {
72847 sequences = sequences.filter(function (sequence) {
72848 if (sequence.properties.hasOwnProperty('pano')) {
72849 if (sequence.properties.pano) return showsPano;
72852 // if the sequence doesn't specify pano or not, search its images
72853 var cProps = sequence.properties.coordinateProperties;
72855 if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
72856 for (var index in cProps.image_keys) {
72857 var imageKey = cProps.image_keys[index];
72858 var image = service.cachedImage(imageKey);
72860 if (image && image.hasOwnProperty('pano')) {
72861 if (image.pano) return showsPano;
72873 var fromTimestamp = new Date(fromDate).getTime();
72874 sequences = sequences.filter(function (sequence) {
72875 return new Date(sequence.properties.captured_at).getTime() >= fromTimestamp;
72880 var toTimestamp = new Date(toDate).getTime();
72881 sequences = sequences.filter(function (sequence) {
72882 return new Date(sequence.properties.captured_at).getTime() <= toTimestamp;
72887 sequences = sequences.filter(function (sequence) {
72888 return usernames.indexOf(sequence.properties.username) !== -1;
72895 function update() {
72896 var z = ~~context.map().zoom();
72897 var showMarkers = z >= minMarkerZoom;
72898 var showViewfields = z >= minViewfieldZoom;
72899 var service = getService();
72900 var sequences = service ? service.sequences(projection) : [];
72901 var images = service && showMarkers ? service.images(projection) : [];
72902 images = filterImages(images);
72903 sequences = filterSequences(sequences, service);
72904 service.filterViewer(context);
72905 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72906 return d.properties.key;
72909 traces.exit().remove(); // enter/update
72911 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72912 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
72916 groups.exit().remove(); // enter
72918 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72919 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72921 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72922 return b.loc[1] - a.loc[1]; // sort Y
72923 }).attr('transform', transform).select('.viewfield-scale');
72924 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72925 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72926 viewfields.exit().remove();
72927 viewfields.enter() // viewfields may or may not be drawn...
72928 .insert('path', 'circle') // but if they are, draw below the circles
72929 .attr('class', 'viewfield').classed('pano', function () {
72930 return this.parentNode.__data__.pano;
72931 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72933 function viewfieldPath() {
72934 var d = this.parentNode.__data__;
72937 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72939 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72944 function drawImages(selection) {
72945 var enabled = svgMapillaryImages.enabled;
72946 var service = getService();
72947 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
72948 layer.exit().remove();
72949 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
72950 layerEnter.append('g').attr('class', 'sequences');
72951 layerEnter.append('g').attr('class', 'markers');
72952 layer = layerEnter.merge(layer);
72955 if (service && ~~context.map().zoom() >= minZoom) {
72958 service.loadImages(projection);
72965 drawImages.enabled = function (_) {
72966 if (!arguments.length) return svgMapillaryImages.enabled;
72967 svgMapillaryImages.enabled = _;
72969 if (svgMapillaryImages.enabled) {
72971 context.photos().on('change.mapillary_images', update);
72974 context.photos().on('change.mapillary_images', null);
72977 dispatch.call('change');
72981 drawImages.supported = function () {
72982 return !!getService();
72989 function svgMapillaryPosition(projection, context) {
72990 var throttledRedraw = throttle(function () {
72995 var minViewfieldZoom = 18;
72996 var layer = select(null);
73000 var viewerCompassAngle;
73003 if (svgMapillaryPosition.initialized) return; // run once
73005 svgMapillaryPosition.initialized = true;
73008 function getService() {
73009 if (services.mapillary && !_mapillary) {
73010 _mapillary = services.mapillary;
73012 _mapillary.event.on('nodeChanged', throttledRedraw);
73014 _mapillary.event.on('bearingChanged', function (e) {
73015 viewerCompassAngle = e;
73016 if (context.map().isTransformed()) return;
73017 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
73019 }).attr('transform', transform);
73021 } else if (!services.mapillary && _mapillary) {
73028 function editOn() {
73029 layer.style('display', 'block');
73032 function editOff() {
73033 layer.selectAll('.viewfield-group').remove();
73034 layer.style('display', 'none');
73037 function transform(d) {
73038 var t = svgPointTransform(projection)(d);
73040 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
73041 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
73043 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
73049 function update() {
73050 var z = ~~context.map().zoom();
73051 var showViewfields = z >= minViewfieldZoom;
73052 var service = getService();
73053 var node = service && service.getActiveImage();
73054 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(node ? [node] : [], function (d) {
73058 groups.exit().remove(); // enter
73060 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
73061 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
73063 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
73064 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
73065 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
73066 viewfields.exit().remove();
73067 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').classed('pano', function () {
73068 return this.parentNode.__data__.pano;
73069 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
73071 function viewfieldPath() {
73072 var d = this.parentNode.__data__;
73075 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
73077 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
73082 function drawImages(selection) {
73083 var service = getService();
73084 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
73085 layer.exit().remove();
73086 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
73087 layerEnter.append('g').attr('class', 'markers');
73088 layer = layerEnter.merge(layer);
73090 if (service && ~~context.map().zoom() >= minZoom) {
73098 drawImages.enabled = function () {
73103 drawImages.supported = function () {
73104 return !!getService();
73111 function svgMapillarySigns(projection, context, dispatch) {
73112 var throttledRedraw = throttle(function () {
73113 dispatch.call('change');
73117 var layer = select(null);
73122 if (svgMapillarySigns.initialized) return; // run once
73124 svgMapillarySigns.enabled = false;
73125 svgMapillarySigns.initialized = true;
73128 function getService() {
73129 if (services.mapillary && !_mapillary) {
73130 _mapillary = services.mapillary;
73132 _mapillary.event.on('loadedSigns', throttledRedraw);
73133 } else if (!services.mapillary && _mapillary) {
73140 function showLayer() {
73141 var service = getService();
73142 if (!service) return;
73143 service.loadSignResources(context);
73147 function hideLayer() {
73148 throttledRedraw.cancel();
73152 function editOn() {
73153 layer.style('display', 'block');
73156 function editOff() {
73157 layer.selectAll('.icon-sign').remove();
73158 layer.style('display', 'none');
73161 function click(d3_event, d) {
73162 var service = getService();
73163 if (!service) return;
73164 context.map().centerEase(d.loc);
73165 var selectedImageKey = service.getSelectedImageKey();
73167 var highlightedDetection; // Pick one of the images the sign was detected in,
73168 // preference given to an image already selected.
73170 d.detections.forEach(function (detection) {
73171 if (!imageKey || selectedImageKey === detection.image_key) {
73172 imageKey = detection.image_key;
73173 highlightedDetection = detection;
73177 if (imageKey === selectedImageKey) {
73178 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73180 service.ensureViewerLoaded(context).then(function () {
73181 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73186 function filterData(detectedFeatures) {
73187 var service = getService();
73188 var fromDate = context.photos().fromDate();
73189 var toDate = context.photos().toDate();
73190 var usernames = context.photos().usernames();
73193 var fromTimestamp = new Date(fromDate).getTime();
73194 detectedFeatures = detectedFeatures.filter(function (feature) {
73195 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
73200 var toTimestamp = new Date(toDate).getTime();
73201 detectedFeatures = detectedFeatures.filter(function (feature) {
73202 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
73206 if (usernames && service) {
73207 detectedFeatures = detectedFeatures.filter(function (feature) {
73208 return feature.detections.some(function (detection) {
73209 var imageKey = detection.image_key;
73210 var image = service.cachedImage(imageKey);
73211 return image && usernames.indexOf(image.captured_by) !== -1;
73216 return detectedFeatures;
73219 function update() {
73220 var service = getService();
73221 var data = service ? service.signs(projection) : [];
73222 data = filterData(data);
73223 var selectedImageKey = service.getSelectedImageKey();
73224 var transform = svgPointTransform(projection);
73225 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
73229 signs.exit().remove(); // enter
73231 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
73232 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73233 return '#' + d.value;
73235 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73237 signs.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73238 return d.detections.some(function (detection) {
73239 return detection.image_key === selectedImageKey;
73241 }).sort(function (a, b) {
73242 var aSelected = a.detections.some(function (detection) {
73243 return detection.image_key === selectedImageKey;
73245 var bSelected = b.detections.some(function (detection) {
73246 return detection.image_key === selectedImageKey;
73249 if (aSelected === bSelected) {
73250 return b.loc[1] - a.loc[1]; // sort Y
73251 } else if (aSelected) {
73259 function drawSigns(selection) {
73260 var enabled = svgMapillarySigns.enabled;
73261 var service = getService();
73262 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
73263 layer.exit().remove();
73264 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73267 if (service && ~~context.map().zoom() >= minZoom) {
73270 service.loadSigns(projection);
73271 service.showSignDetections(true);
73275 } else if (service) {
73276 service.showSignDetections(false);
73280 drawSigns.enabled = function (_) {
73281 if (!arguments.length) return svgMapillarySigns.enabled;
73282 svgMapillarySigns.enabled = _;
73284 if (svgMapillarySigns.enabled) {
73286 context.photos().on('change.mapillary_signs', update);
73289 context.photos().on('change.mapillary_signs', null);
73292 dispatch.call('change');
73296 drawSigns.supported = function () {
73297 return !!getService();
73304 function svgMapillaryMapFeatures(projection, context, dispatch) {
73305 var throttledRedraw = throttle(function () {
73306 dispatch.call('change');
73310 var layer = select(null);
73315 if (svgMapillaryMapFeatures.initialized) return; // run once
73317 svgMapillaryMapFeatures.enabled = false;
73318 svgMapillaryMapFeatures.initialized = true;
73321 function getService() {
73322 if (services.mapillary && !_mapillary) {
73323 _mapillary = services.mapillary;
73325 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
73326 } else if (!services.mapillary && _mapillary) {
73333 function showLayer() {
73334 var service = getService();
73335 if (!service) return;
73336 service.loadObjectResources(context);
73340 function hideLayer() {
73341 throttledRedraw.cancel();
73345 function editOn() {
73346 layer.style('display', 'block');
73349 function editOff() {
73350 layer.selectAll('.icon-map-feature').remove();
73351 layer.style('display', 'none');
73354 function click(d3_event, d) {
73355 var service = getService();
73356 if (!service) return;
73357 context.map().centerEase(d.loc);
73358 var selectedImageKey = service.getSelectedImageKey();
73360 var highlightedDetection; // Pick one of the images the map feature was detected in,
73361 // preference given to an image already selected.
73363 d.detections.forEach(function (detection) {
73364 if (!imageKey || selectedImageKey === detection.image_key) {
73365 imageKey = detection.image_key;
73366 highlightedDetection = detection;
73370 if (imageKey === selectedImageKey) {
73371 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73373 service.ensureViewerLoaded(context).then(function () {
73374 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73379 function filterData(detectedFeatures) {
73380 var service = getService();
73381 var fromDate = context.photos().fromDate();
73382 var toDate = context.photos().toDate();
73383 var usernames = context.photos().usernames();
73386 var fromTimestamp = new Date(fromDate).getTime();
73387 detectedFeatures = detectedFeatures.filter(function (feature) {
73388 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
73393 var toTimestamp = new Date(toDate).getTime();
73394 detectedFeatures = detectedFeatures.filter(function (feature) {
73395 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
73399 if (usernames && service) {
73400 detectedFeatures = detectedFeatures.filter(function (feature) {
73401 return feature.detections.some(function (detection) {
73402 var imageKey = detection.image_key;
73403 var image = service.cachedImage(imageKey);
73404 return image && usernames.indexOf(image.captured_by) !== -1;
73409 return detectedFeatures;
73412 function update() {
73413 var service = getService();
73414 var data = service ? service.mapFeatures(projection) : [];
73415 data = filterData(data);
73416 var selectedImageKey = service && service.getSelectedImageKey();
73417 var transform = svgPointTransform(projection);
73418 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
73422 mapFeatures.exit().remove(); // enter
73424 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
73425 enter.append('title').text(function (d) {
73426 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
73427 return _t('mapillary_map_features.' + id);
73429 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73430 if (d.value === 'object--billboard') {
73431 // no billboard icon right now, so use the advertisement icon
73432 return '#object--sign--advertisement';
73435 return '#' + d.value;
73437 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73439 mapFeatures.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73440 return d.detections.some(function (detection) {
73441 return detection.image_key === selectedImageKey;
73443 }).sort(function (a, b) {
73444 var aSelected = a.detections.some(function (detection) {
73445 return detection.image_key === selectedImageKey;
73447 var bSelected = b.detections.some(function (detection) {
73448 return detection.image_key === selectedImageKey;
73451 if (aSelected === bSelected) {
73452 return b.loc[1] - a.loc[1]; // sort Y
73453 } else if (aSelected) {
73461 function drawMapFeatures(selection) {
73462 var enabled = svgMapillaryMapFeatures.enabled;
73463 var service = getService();
73464 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
73465 layer.exit().remove();
73466 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73469 if (service && ~~context.map().zoom() >= minZoom) {
73472 service.loadMapFeatures(projection);
73473 service.showFeatureDetections(true);
73477 } else if (service) {
73478 service.showFeatureDetections(false);
73482 drawMapFeatures.enabled = function (_) {
73483 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
73484 svgMapillaryMapFeatures.enabled = _;
73486 if (svgMapillaryMapFeatures.enabled) {
73488 context.photos().on('change.mapillary_map_features', update);
73491 context.photos().on('change.mapillary_map_features', null);
73494 dispatch.call('change');
73498 drawMapFeatures.supported = function () {
73499 return !!getService();
73503 return drawMapFeatures;
73506 function svgOpenstreetcamImages(projection, context, dispatch) {
73507 var throttledRedraw = throttle(function () {
73508 dispatch.call('change');
73512 var minMarkerZoom = 16;
73513 var minViewfieldZoom = 18;
73514 var layer = select(null);
73516 var _openstreetcam;
73519 if (svgOpenstreetcamImages.initialized) return; // run once
73521 svgOpenstreetcamImages.enabled = false;
73522 svgOpenstreetcamImages.initialized = true;
73525 function getService() {
73526 if (services.openstreetcam && !_openstreetcam) {
73527 _openstreetcam = services.openstreetcam;
73529 _openstreetcam.event.on('loadedImages', throttledRedraw);
73530 } else if (!services.openstreetcam && _openstreetcam) {
73531 _openstreetcam = null;
73534 return _openstreetcam;
73537 function showLayer() {
73538 var service = getService();
73539 if (!service) return;
73541 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
73542 dispatch.call('change');
73546 function hideLayer() {
73547 throttledRedraw.cancel();
73548 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
73551 function editOn() {
73552 layer.style('display', 'block');
73555 function editOff() {
73556 layer.selectAll('.viewfield-group').remove();
73557 layer.style('display', 'none');
73560 function click(d3_event, d) {
73561 var service = getService();
73562 if (!service) return;
73563 service.ensureViewerLoaded(context).then(function () {
73564 service.selectImage(context, d.key).showViewer(context);
73566 context.map().centerEase(d.loc);
73569 function mouseover(d3_event, d) {
73570 var service = getService();
73571 if (service) service.setStyles(context, d);
73574 function mouseout() {
73575 var service = getService();
73576 if (service) service.setStyles(context, null);
73579 function transform(d) {
73580 var t = svgPointTransform(projection)(d);
73583 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
73589 function filterImages(images) {
73590 var fromDate = context.photos().fromDate();
73591 var toDate = context.photos().toDate();
73592 var usernames = context.photos().usernames();
73595 var fromTimestamp = new Date(fromDate).getTime();
73596 images = images.filter(function (item) {
73597 return new Date(item.captured_at).getTime() >= fromTimestamp;
73602 var toTimestamp = new Date(toDate).getTime();
73603 images = images.filter(function (item) {
73604 return new Date(item.captured_at).getTime() <= toTimestamp;
73609 images = images.filter(function (item) {
73610 return usernames.indexOf(item.captured_by) !== -1;
73617 function filterSequences(sequences) {
73618 var fromDate = context.photos().fromDate();
73619 var toDate = context.photos().toDate();
73620 var usernames = context.photos().usernames();
73623 var fromTimestamp = new Date(fromDate).getTime();
73624 sequences = sequences.filter(function (image) {
73625 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
73630 var toTimestamp = new Date(toDate).getTime();
73631 sequences = sequences.filter(function (image) {
73632 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
73637 sequences = sequences.filter(function (image) {
73638 return usernames.indexOf(image.properties.captured_by) !== -1;
73645 function update() {
73646 var viewer = context.container().select('.photoviewer');
73647 var selected = viewer.empty() ? undefined : viewer.datum();
73648 var z = ~~context.map().zoom();
73649 var showMarkers = z >= minMarkerZoom;
73650 var showViewfields = z >= minViewfieldZoom;
73651 var service = getService();
73652 var sequences = [];
73655 if (context.photos().showsFlat()) {
73656 sequences = service ? service.sequences(projection) : [];
73657 images = service && showMarkers ? service.images(projection) : [];
73658 sequences = filterSequences(sequences);
73659 images = filterImages(images);
73662 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
73663 return d.properties.key;
73666 traces.exit().remove(); // enter/update
73668 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
73669 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
73673 groups.exit().remove(); // enter
73675 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
73676 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
73678 var markers = groups.merge(groupsEnter).sort(function (a, b) {
73679 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
73680 }).attr('transform', transform).select('.viewfield-scale');
73681 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
73682 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
73683 viewfields.exit().remove();
73684 viewfields.enter() // viewfields may or may not be drawn...
73685 .insert('path', 'circle') // but if they are, draw below the circles
73686 .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');
73689 function drawImages(selection) {
73690 var enabled = svgOpenstreetcamImages.enabled,
73691 service = getService();
73692 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
73693 layer.exit().remove();
73694 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
73695 layerEnter.append('g').attr('class', 'sequences');
73696 layerEnter.append('g').attr('class', 'markers');
73697 layer = layerEnter.merge(layer);
73700 if (service && ~~context.map().zoom() >= minZoom) {
73703 service.loadImages(projection);
73710 drawImages.enabled = function (_) {
73711 if (!arguments.length) return svgOpenstreetcamImages.enabled;
73712 svgOpenstreetcamImages.enabled = _;
73714 if (svgOpenstreetcamImages.enabled) {
73716 context.photos().on('change.openstreetcam_images', update);
73719 context.photos().on('change.openstreetcam_images', null);
73722 dispatch.call('change');
73726 drawImages.supported = function () {
73727 return !!getService();
73734 function svgOsm(projection, context, dispatch) {
73735 var enabled = true;
73737 function drawOsm(selection) {
73738 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
73739 return 'layer-osm ' + d;
73741 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
73742 return 'points-group ' + d;
73746 function showLayer() {
73747 var layer = context.surface().selectAll('.data-layer.osm');
73749 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73750 dispatch.call('change');
73754 function hideLayer() {
73755 var layer = context.surface().selectAll('.data-layer.osm');
73757 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73758 layer.classed('disabled', true);
73759 dispatch.call('change');
73763 drawOsm.enabled = function (val) {
73764 if (!arguments.length) return enabled;
73773 dispatch.call('change');
73780 var _notesEnabled = false;
73784 function svgNotes(projection, context, dispatch$1) {
73786 dispatch$1 = dispatch('change');
73789 var throttledRedraw = throttle(function () {
73790 dispatch$1.call('change');
73794 var touchLayer = select(null);
73795 var drawLayer = select(null);
73796 var _notesVisible = false;
73798 function markerPath(selection, klass) {
73799 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');
73800 } // Loosely-coupled osm service for fetching notes.
73803 function getService() {
73804 if (services.osm && !_osmService) {
73805 _osmService = services.osm;
73807 _osmService.on('loadedNotes', throttledRedraw);
73808 } else if (!services.osm && _osmService) {
73809 _osmService = null;
73812 return _osmService;
73813 } // Show the notes
73816 function editOn() {
73817 if (!_notesVisible) {
73818 _notesVisible = true;
73819 drawLayer.style('display', 'block');
73821 } // Immediately remove the notes and their touch targets
73824 function editOff() {
73825 if (_notesVisible) {
73826 _notesVisible = false;
73827 drawLayer.style('display', 'none');
73828 drawLayer.selectAll('.note').remove();
73829 touchLayer.selectAll('.note').remove();
73831 } // Enable the layer. This shows the notes and transitions them to visible.
73834 function layerOn() {
73836 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73837 dispatch$1.call('change');
73839 } // Disable the layer. This transitions the layer invisible and then hides the notes.
73842 function layerOff() {
73843 throttledRedraw.cancel();
73844 drawLayer.interrupt();
73845 touchLayer.selectAll('.note').remove();
73846 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73848 dispatch$1.call('change');
73850 } // Update the note markers
73853 function updateMarkers() {
73854 if (!_notesVisible || !_notesEnabled) return;
73855 var service = getService();
73856 var selectedID = context.selectedNoteID();
73857 var data = service ? service.notes(projection) : [];
73858 var getTransform = svgPointTransform(projection); // Draw markers..
73860 var notes = drawLayer.selectAll('.note').data(data, function (d) {
73861 return d.status + d.id;
73864 notes.exit().remove(); // enter
73866 var notesEnter = notes.enter().append('g').attr('class', function (d) {
73867 return 'note note-' + d.id + ' ' + d.status;
73868 }).classed('new', function (d) {
73871 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
73872 notesEnter.append('path').call(markerPath, 'shadow');
73873 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');
73874 notesEnter.selectAll('.icon-annotation').data(function (d) {
73876 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
73877 return '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
73880 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
73881 var mode = context.mode();
73882 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
73884 return !isMoving && d.id === selectedID;
73885 }).attr('transform', getTransform); // Draw targets..
73887 if (touchLayer.empty()) return;
73888 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73889 var targets = touchLayer.selectAll('.note').data(data, function (d) {
73893 targets.exit().remove(); // enter/update
73895 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
73896 var newClass = d.id < 0 ? 'new' : '';
73897 return 'note target note-' + d.id + ' ' + fillClass + newClass;
73898 }).attr('transform', getTransform);
73900 function sortY(a, b) {
73901 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
73903 } // Draw the notes layer and schedule loading notes and updating markers.
73906 function drawNotes(selection) {
73907 var service = getService();
73908 var surface = context.surface();
73910 if (surface && !surface.empty()) {
73911 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
73914 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
73915 drawLayer.exit().remove();
73916 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
73918 if (_notesEnabled) {
73919 if (service && ~~context.map().zoom() >= minZoom) {
73921 service.loadNotes(projection);
73927 } // Toggles the layer on and off
73930 drawNotes.enabled = function (val) {
73931 if (!arguments.length) return _notesEnabled;
73932 _notesEnabled = val;
73934 if (_notesEnabled) {
73939 if (context.selectedNoteID()) {
73940 context.enter(modeBrowse(context));
73944 dispatch$1.call('change');
73951 function svgTouch() {
73952 function drawTouch(selection) {
73953 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
73954 return 'layer-touch ' + d;
73961 function refresh(selection, node) {
73962 var cr = node.getBoundingClientRect();
73963 var prop = [cr.width, cr.height];
73964 selection.property('__dimensions__', prop);
73968 function utilGetDimensions(selection, force) {
73969 if (!selection || selection.empty()) {
73973 var node = selection.node(),
73974 cached = selection.property('__dimensions__');
73975 return !cached || force ? refresh(selection, node) : cached;
73977 function utilSetDimensions(selection, dimensions) {
73978 if (!selection || selection.empty()) {
73982 var node = selection.node();
73984 if (dimensions === null) {
73985 refresh(selection, node);
73989 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
73992 function svgLayers(projection, context) {
73993 var dispatch$1 = dispatch('change');
73994 var svg = select(null);
73997 layer: svgOsm(projection, context, dispatch$1)
74000 layer: svgNotes(projection, context, dispatch$1)
74003 layer: svgData(projection, context, dispatch$1)
74006 layer: svgKeepRight(projection, context, dispatch$1)
74009 layer: svgImproveOSM(projection, context, dispatch$1)
74012 layer: svgOsmose(projection, context, dispatch$1)
74015 layer: svgStreetside(projection, context, dispatch$1)
74018 layer: svgMapillaryImages(projection, context, dispatch$1)
74020 id: 'mapillary-position',
74021 layer: svgMapillaryPosition(projection, context)
74023 id: 'mapillary-map-features',
74024 layer: svgMapillaryMapFeatures(projection, context, dispatch$1)
74026 id: 'mapillary-signs',
74027 layer: svgMapillarySigns(projection, context, dispatch$1)
74029 id: 'openstreetcam',
74030 layer: svgOpenstreetcamImages(projection, context, dispatch$1)
74033 layer: svgDebug(projection, context)
74036 layer: svgGeolocate(projection)
74042 function drawLayers(selection) {
74043 svg = selection.selectAll('.surface').data([0]);
74044 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
74045 var defs = svg.selectAll('.surface-defs').data([0]);
74046 defs.enter().append('defs').attr('class', 'surface-defs');
74047 var groups = svg.selectAll('.data-layer').data(_layers);
74048 groups.exit().remove();
74049 groups.enter().append('g').attr('class', function (d) {
74050 return 'data-layer ' + d.id;
74051 }).merge(groups).each(function (d) {
74052 select(this).call(d.layer);
74056 drawLayers.all = function () {
74060 drawLayers.layer = function (id) {
74061 var obj = _layers.find(function (o) {
74062 return o.id === id;
74065 return obj && obj.layer;
74068 drawLayers.only = function (what) {
74069 var arr = [].concat(what);
74071 var all = _layers.map(function (layer) {
74075 return drawLayers.remove(utilArrayDifference(all, arr));
74078 drawLayers.remove = function (what) {
74079 var arr = [].concat(what);
74080 arr.forEach(function (id) {
74081 _layers = _layers.filter(function (o) {
74082 return o.id !== id;
74085 dispatch$1.call('change');
74089 drawLayers.add = function (what) {
74090 var arr = [].concat(what);
74091 arr.forEach(function (obj) {
74092 if ('id' in obj && 'layer' in obj) {
74096 dispatch$1.call('change');
74100 drawLayers.dimensions = function (val) {
74101 if (!arguments.length) return utilGetDimensions(svg);
74102 utilSetDimensions(svg, val);
74106 return utilRebind(drawLayers, dispatch$1, 'on');
74109 function svgLines(projection, context) {
74110 var detected = utilDetect();
74111 var highway_stack = {
74126 function drawTargets(selection, graph, entities, filter) {
74127 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74128 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
74129 var getPath = svgPath(projection).geojson;
74130 var activeID = context.activeID();
74131 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
74137 entities.forEach(function (way) {
74138 var features = svgSegmentWay(way, graph, activeID);
74139 data.targets.push.apply(data.targets, features.passive);
74140 data.nopes.push.apply(data.nopes, features.active);
74141 }); // Targets allow hover and vertex snapping
74143 var targetData = data.targets.filter(getPath);
74144 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
74145 return filter(d.properties.entity);
74146 }).data(targetData, function key(d) {
74150 targets.exit().remove();
74152 var segmentWasEdited = function segmentWasEdited(d) {
74153 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
74155 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
74159 return d.properties.nodes.some(function (n) {
74160 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
74165 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
74166 return 'way line target target-allowed ' + targetClass + d.id;
74167 }).classed('segment-edited', segmentWasEdited); // NOPE
74169 var nopeData = data.nopes.filter(getPath);
74170 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
74171 return filter(d.properties.entity);
74172 }).data(nopeData, function key(d) {
74176 nopes.exit().remove(); // enter/update
74178 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
74179 return 'way line target target-nope ' + nopeClass + d.id;
74180 }).classed('segment-edited', segmentWasEdited);
74183 function drawLines(selection, graph, entities, filter) {
74184 var base = context.history().base();
74186 function waystack(a, b) {
74187 var selected = context.selectedIDs();
74188 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
74189 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
74191 if (a.tags.highway) {
74192 scoreA -= highway_stack[a.tags.highway];
74195 if (b.tags.highway) {
74196 scoreB -= highway_stack[b.tags.highway];
74199 return scoreA - scoreB;
74202 function drawLineGroup(selection, klass, isSelected) {
74203 // Note: Don't add `.selected` class in draw modes
74204 var mode = context.mode();
74205 var isDrawing = mode && /^draw/.test(mode.id);
74206 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
74207 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
74208 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
74209 // works because osmEntity.key is defined to include the entity v attribute.
74211 lines.enter().append('path').attr('class', function (d) {
74212 var prefix = 'way line'; // if this line isn't styled by its own tags
74214 if (!d.hasInterestingTags()) {
74215 var parentRelations = graph.parentRelations(d);
74216 var parentMultipolygons = parentRelations.filter(function (relation) {
74217 return relation.isMultipolygon();
74218 }); // and if it's a member of at least one multipolygon relation
74220 if (parentMultipolygons.length > 0 && // and only multipolygon relations
74221 parentRelations.length === parentMultipolygons.length) {
74222 // then fudge the classes to style this as an area edge
74223 prefix = 'relation area';
74227 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
74228 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
74229 }).classed('added', function (d) {
74230 return !base.entities[d.id];
74231 }).classed('geometry-edited', function (d) {
74232 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
74233 }).classed('retagged', function (d) {
74234 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74235 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
74239 function getPathData(isSelected) {
74240 return function () {
74241 var layer = this.parentNode.__data__;
74242 var data = pathdata[layer] || [];
74243 return data.filter(function (d) {
74244 if (isSelected) return context.selectedIDs().indexOf(d.id) !== -1;else return context.selectedIDs().indexOf(d.id) === -1;
74249 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
74250 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
74251 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
74252 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
74253 return groupdata[this.parentNode.__data__] || [];
74254 }, function key(d) {
74255 return [d.id, d.index];
74257 markers.exit().remove();
74258 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
74263 markers.each(function () {
74264 this.parentNode.insertBefore(this, this);
74269 var getPath = svgPath(projection, graph);
74271 var onewaydata = {};
74272 var sideddata = {};
74273 var oldMultiPolygonOuters = {};
74275 for (var i = 0; i < entities.length; i++) {
74276 var entity = entities[i];
74277 var outer = osmOldMultipolygonOuterMember(entity, graph);
74280 ways.push(entity.mergeTags(outer.tags));
74281 oldMultiPolygonOuters[outer.id] = true;
74282 } else if (entity.geometry(graph) === 'line') {
74287 ways = ways.filter(getPath);
74288 var pathdata = utilArrayGroupBy(ways, function (way) {
74289 return way.layer();
74291 Object.keys(pathdata).forEach(function (k) {
74292 var v = pathdata[k];
74293 var onewayArr = v.filter(function (d) {
74294 return d.isOneWay();
74296 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
74297 return entity.tags.oneway === '-1';
74298 }, function bothDirections(entity) {
74299 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
74301 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
74302 var sidedArr = v.filter(function (d) {
74303 return d.isSided();
74305 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
74307 }, function bothDirections() {
74310 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
74312 var covered = selection.selectAll('.layer-osm.covered'); // under areas
74314 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
74316 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
74318 [covered, uncovered].forEach(function (selection) {
74319 var range$1 = selection === covered ? range(-10, 0) : range(0, 11);
74320 var layergroup = selection.selectAll('g.layergroup').data(range$1);
74321 layergroup = layergroup.enter().append('g').attr('class', function (d) {
74322 return 'layergroup layer' + String(d);
74323 }).merge(layergroup);
74324 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
74325 return 'linegroup line-' + d;
74327 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
74328 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
74329 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
74330 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
74331 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
74332 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
74333 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
74334 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
74335 var category = graph.entity(d.id).sidednessIdentifier();
74336 return 'url(#ideditor-sided-marker-' + category + ')';
74338 }); // Draw touch targets..
74340 touchLayer.call(drawTargets, graph, ways, filter);
74346 function svgMidpoints(projection, context) {
74347 var targetRadius = 8;
74349 function drawTargets(selection, graph, entities, filter) {
74350 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74351 var getTransform = svgPointTransform(projection).geojson;
74352 var data = entities.map(function (midpoint) {
74362 coordinates: midpoint.loc
74366 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
74367 return filter(d.properties.entity);
74368 }).data(data, function key(d) {
74372 targets.exit().remove(); // enter/update
74374 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
74375 return 'node midpoint target ' + fillClass + d.id;
74376 }).attr('transform', getTransform);
74379 function drawMidpoints(selection, graph, entities, filter, extent) {
74380 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
74381 var touchLayer = selection.selectAll('.layer-touch.points');
74382 var mode = context.mode();
74384 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
74385 drawLayer.selectAll('.midpoint').remove();
74386 touchLayer.selectAll('.midpoint.target').remove();
74390 var poly = extent.polygon();
74391 var midpoints = {};
74393 for (var i = 0; i < entities.length; i++) {
74394 var entity = entities[i];
74395 if (entity.type !== 'way') continue;
74396 if (!filter(entity)) continue;
74397 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
74398 var nodes = graph.childNodes(entity);
74400 for (var j = 0; j < nodes.length - 1; j++) {
74402 var b = nodes[j + 1];
74403 var id = [a.id, b.id].sort().join('-');
74405 if (midpoints[id]) {
74406 midpoints[id].parents.push(entity);
74407 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
74408 var point = geoVecInterp(a.loc, b.loc, 0.5);
74411 if (extent.intersects(point)) {
74414 for (var k = 0; k < 4; k++) {
74415 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
74417 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
74429 edge: [a.id, b.id],
74437 function midpointFilter(d) {
74438 if (midpoints[d.id]) return true;
74440 for (var i = 0; i < d.parents.length; i++) {
74441 if (filter(d.parents[i])) {
74449 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
74452 groups.exit().remove();
74453 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
74454 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
74455 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
74456 groups = groups.merge(enter).attr('transform', function (d) {
74457 var translate = svgPointTransform(projection);
74458 var a = graph.entity(d.edge[0]);
74459 var b = graph.entity(d.edge[1]);
74460 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
74461 return translate(d) + ' rotate(' + angle + ')';
74462 }).call(svgTagClasses().tags(function (d) {
74463 return d.parents[0].tags;
74464 })); // Propagate data bindings.
74466 groups.select('polygon.shadow');
74467 groups.select('polygon.fill'); // Draw touch targets..
74469 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
74472 return drawMidpoints;
74475 function svgPoints(projection, context) {
74476 function markerPath(selection, klass) {
74477 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');
74480 function sortY(a, b) {
74481 return b.loc[1] - a.loc[1];
74482 } // Avoid exit/enter if we're just moving stuff around.
74483 // The node will get a new version but we only need to run the update selection.
74486 function fastEntityKey(d) {
74487 var mode = context.mode();
74488 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74489 return isMoving ? d.id : osmEntity.key(d);
74492 function drawTargets(selection, graph, entities, filter) {
74493 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74494 var getTransform = svgPointTransform(projection).geojson;
74495 var activeID = context.activeID();
74497 entities.forEach(function (node) {
74498 if (activeID === node.id) return; // draw no target on the activeID
74507 geometry: node.asGeoJSON()
74510 var targets = selection.selectAll('.point.target').filter(function (d) {
74511 return filter(d.properties.entity);
74512 }).data(data, function key(d) {
74516 targets.exit().remove(); // enter/update
74518 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
74519 return 'node point target ' + fillClass + d.id;
74520 }).attr('transform', getTransform);
74523 function drawPoints(selection, graph, entities, filter) {
74524 var wireframe = context.surface().classed('fill-wireframe');
74525 var zoom = geoScaleToZoom(projection.scale());
74526 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
74528 function renderAsPoint(entity) {
74529 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
74530 } // All points will render as vertices in wireframe mode too..
74533 var points = wireframe ? [] : entities.filter(renderAsPoint);
74534 points.sort(sortY);
74535 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
74536 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
74538 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
74539 groups.exit().remove();
74540 var enter = groups.enter().append('g').attr('class', function (d) {
74541 return 'node point ' + d.id;
74543 enter.append('path').call(markerPath, 'shadow');
74544 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
74545 enter.append('path').call(markerPath, 'stroke');
74546 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
74547 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
74548 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74549 }).classed('moved', function (d) {
74550 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74551 }).classed('retagged', function (d) {
74552 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74553 }).call(svgTagClasses());
74554 groups.select('.shadow'); // propagate bound data
74556 groups.select('.stroke'); // propagate bound data
74558 groups.select('.icon') // propagate bound data
74559 .attr('xlink:href', function (entity) {
74560 var preset = _mainPresetIndex.match(entity, graph);
74561 var picon = preset && preset.icon;
74566 var isMaki = /^maki-/.test(picon);
74567 return '#' + picon + (isMaki ? '-11' : '');
74569 }); // Draw touch targets..
74571 touchLayer.call(drawTargets, graph, points, filter);
74577 function svgTurns(projection, context) {
74578 function icon(turn) {
74579 var u = turn.u ? '-u' : '';
74580 if (turn.no) return '#iD-turn-no' + u;
74581 if (turn.only) return '#iD-turn-only' + u;
74582 return '#iD-turn-yes' + u;
74585 function drawTurns(selection, graph, turns) {
74586 function turnTransform(d) {
74588 var toWay = graph.entity(d.to.way);
74589 var toPoints = graph.childNodes(toWay).map(function (n) {
74591 }).map(projection);
74592 var toLength = geoPathLength(toPoints);
74593 var mid = toLength / 2; // midpoint of destination way
74595 var toNode = graph.entity(d.to.node);
74596 var toVertex = graph.entity(d.to.vertex);
74597 var a = geoAngle(toVertex, toNode, projection);
74598 var o = projection(toVertex.loc);
74599 var r = d.u ? 0 // u-turn: no radius
74600 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
74601 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
74603 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
74606 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
74607 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
74609 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
74613 groups.exit().remove(); // enter
74615 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
74616 return 'turn ' + d.key;
74618 var turnsEnter = groupsEnter.filter(function (d) {
74621 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74622 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74623 var uEnter = groupsEnter.filter(function (d) {
74626 uEnter.append('circle').attr('r', '16');
74627 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
74629 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
74630 return d.direct === false ? '0.7' : null;
74631 }).attr('transform', turnTransform);
74632 groups.select('use').attr('xlink:href', icon);
74633 groups.select('rect'); // propagate bound data
74635 groups.select('circle'); // propagate bound data
74636 // Draw touch targets..
74638 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74639 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
74643 groups.exit().remove(); // enter
74645 groupsEnter = groups.enter().append('g').attr('class', function (d) {
74646 return 'turn ' + d.key;
74648 turnsEnter = groupsEnter.filter(function (d) {
74651 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74652 uEnter = groupsEnter.filter(function (d) {
74655 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
74657 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
74658 groups.select('rect'); // propagate bound data
74660 groups.select('circle'); // propagate bound data
74668 function svgVertices(projection, context) {
74670 // z16-, z17, z18+, w/icon
74671 shadow: [6, 7.5, 7.5, 12],
74672 stroke: [2.5, 3.5, 3.5, 8],
74673 fill: [1, 1.5, 1.5, 1.5]
74676 var _currHoverTarget;
74678 var _currPersistent = {};
74679 var _currHover = {};
74680 var _prevHover = {};
74681 var _currSelected = {};
74682 var _prevSelected = {};
74685 function sortY(a, b) {
74686 return b.loc[1] - a.loc[1];
74687 } // Avoid exit/enter if we're just moving stuff around.
74688 // The node will get a new version but we only need to run the update selection.
74691 function fastEntityKey(d) {
74692 var mode = context.mode();
74693 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74694 return isMoving ? d.id : osmEntity.key(d);
74697 function draw(selection, graph, vertices, sets, filter) {
74704 var directions = {};
74705 var wireframe = context.surface().classed('fill-wireframe');
74706 var zoom = geoScaleToZoom(projection.scale());
74707 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
74708 var activeID = context.activeID();
74709 var base = context.history().base();
74711 function getIcon(d) {
74712 // always check latest entity, as fastEntityKey avoids enter/exit now
74713 var entity = graph.entity(d.id);
74714 if (entity.id in icons) return icons[entity.id];
74715 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
74716 return icons[entity.id];
74717 } // memoize directions results, return false for empty arrays (for use in filter)
74720 function getDirections(entity) {
74721 if (entity.id in directions) return directions[entity.id];
74722 var angles = entity.directions(graph, projection);
74723 directions[entity.id] = angles.length ? angles : false;
74727 function updateAttributes(selection) {
74728 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
74729 var rads = radiuses[klass];
74730 selection.selectAll('.' + klass).each(function (entity) {
74731 var i = z && getIcon(entity);
74732 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
74734 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
74738 if (klass === 'shadow') {
74739 // remember this value, so we don't need to
74740 _radii[entity.id] = r; // recompute it when we draw the touch targets
74743 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
74748 vertices.sort(sortY);
74749 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
74751 groups.exit().remove(); // enter
74753 var enter = groups.enter().append('g').attr('class', function (d) {
74754 return 'node vertex ' + d.id;
74756 enter.append('circle').attr('class', 'shadow');
74757 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
74759 enter.filter(function (d) {
74760 return d.hasInterestingTags();
74761 }).append('circle').attr('class', 'fill'); // update
74763 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
74764 return d.id in sets.selected;
74765 }).classed('shared', function (d) {
74766 return graph.isShared(d);
74767 }).classed('endpoint', function (d) {
74768 return d.isEndpoint(graph);
74769 }).classed('added', function (d) {
74770 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74771 }).classed('moved', function (d) {
74772 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74773 }).classed('retagged', function (d) {
74774 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74775 }).call(updateAttributes); // Vertices with icons get a `use`.
74777 var iconUse = groups.selectAll('.icon').data(function data(d) {
74778 return zoom >= 17 && getIcon(d) ? [d] : [];
74779 }, fastEntityKey); // exit
74781 iconUse.exit().remove(); // enter
74783 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) {
74784 var picon = getIcon(d);
74785 var isMaki = /^maki-/.test(picon);
74786 return '#' + picon + (isMaki ? '-11' : '');
74787 }); // Vertices with directions get viewfields
74789 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
74790 return zoom >= 18 && getDirections(d) ? [d] : [];
74791 }, fastEntityKey); // exit
74793 dgroups.exit().remove(); // enter/update
74795 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
74796 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
74797 return osmEntity.key(d);
74800 viewfields.exit().remove(); // enter/update
74802 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) {
74803 return 'rotate(' + d + ')';
74807 function drawTargets(selection, graph, entities, filter) {
74808 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74809 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
74810 var getTransform = svgPointTransform(projection).geojson;
74811 var activeID = context.activeID();
74816 entities.forEach(function (node) {
74817 if (activeID === node.id) return; // draw no target on the activeID
74819 var vertexType = svgPassiveVertex(node, graph, activeID);
74821 if (vertexType !== 0) {
74822 // passive or adjacent - allow to connect
74823 data.targets.push({
74830 geometry: node.asGeoJSON()
74835 id: node.id + '-nope',
74841 geometry: node.asGeoJSON()
74844 }); // Targets allow hover and vertex snapping
74846 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
74847 return filter(d.properties.entity);
74848 }).data(data.targets, function key(d) {
74852 targets.exit().remove(); // enter/update
74854 targets.enter().append('circle').attr('r', function (d) {
74855 return _radii[d.id] || radiuses.shadow[3];
74856 }).merge(targets).attr('class', function (d) {
74857 return 'node vertex target target-allowed ' + targetClass + d.id;
74858 }).attr('transform', getTransform); // NOPE
74860 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
74861 return filter(d.properties.entity);
74862 }).data(data.nopes, function key(d) {
74866 nopes.exit().remove(); // enter/update
74868 nopes.enter().append('circle').attr('r', function (d) {
74869 return _radii[d.properties.entity.id] || radiuses.shadow[3];
74870 }).merge(nopes).attr('class', function (d) {
74871 return 'node vertex target target-nope ' + nopeClass + d.id;
74872 }).attr('transform', getTransform);
74873 } // Points can also render as vertices:
74874 // 1. in wireframe mode or
74875 // 2. at higher zooms if they have a direction
74878 function renderAsVertex(entity, graph, wireframe, zoom) {
74879 var geometry = entity.geometry(graph);
74880 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
74883 function isEditedNode(node, base, head) {
74884 var baseNode = base.entities[node.id];
74885 var headNode = head.entities[node.id];
74886 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
74889 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
74893 function addChildVertices(entity) {
74894 // avoid redundant work and infinite recursion of circular relations
74895 if (seenIds[entity.id]) return;
74896 seenIds[entity.id] = true;
74897 var geometry = entity.geometry(graph);
74899 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
74902 if (entity.type === 'way') {
74903 for (i = 0; i < entity.nodes.length; i++) {
74904 var child = graph.hasEntity(entity.nodes[i]);
74907 addChildVertices(child);
74910 } else if (entity.type === 'relation') {
74911 for (i = 0; i < entity.members.length; i++) {
74912 var member = graph.hasEntity(entity.members[i].id);
74915 addChildVertices(member);
74918 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
74919 results[entity.id] = entity;
74924 ids.forEach(function (id) {
74925 var entity = graph.hasEntity(id);
74926 if (!entity) return;
74928 if (entity.type === 'node') {
74929 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74930 results[entity.id] = entity;
74931 graph.parentWays(entity).forEach(function (entity) {
74932 addChildVertices(entity);
74937 addChildVertices(entity);
74943 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
74944 var wireframe = context.surface().classed('fill-wireframe');
74945 var visualDiff = context.surface().classed('highlight-edited');
74946 var zoom = geoScaleToZoom(projection.scale());
74947 var mode = context.mode();
74948 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74949 var base = context.history().base();
74950 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
74951 var touchLayer = selection.selectAll('.layer-touch.points');
74954 _currPersistent = {};
74956 } // Collect important vertices from the `entities` list..
74957 // (during a partial redraw, it will not contain everything)
74960 for (var i = 0; i < entities.length; i++) {
74961 var entity = entities[i];
74962 var geometry = entity.geometry(graph);
74963 var keep = false; // a point that looks like a vertex..
74965 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
74966 _currPersistent[entity.id] = entity;
74967 keep = true; // a vertex of some importance..
74968 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
74969 _currPersistent[entity.id] = entity;
74971 } // whatever this is, it's not a persistent vertex..
74974 if (!keep && !fullRedraw) {
74975 delete _currPersistent[entity.id];
74977 } // 3 sets of vertices to consider:
74981 persistent: _currPersistent,
74982 // persistent = important vertices (render always)
74983 selected: _currSelected,
74984 // selected + siblings of selected (render always)
74985 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
74988 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
74989 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
74990 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
74992 var filterRendered = function filterRendered(d) {
74993 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
74996 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
74997 // When drawing, render all targets (not just those affected by a partial redraw)
74999 var filterTouch = function filterTouch(d) {
75000 return isMoving ? true : filterRendered(d);
75003 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
75005 function currentVisible(which) {
75006 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
75007 .filter(function (entity) {
75008 return entity && entity.intersects(extent, graph);
75011 } // partial redraw - only update the selected items..
75014 drawVertices.drawSelected = function (selection, graph, extent) {
75015 var wireframe = context.surface().classed('fill-wireframe');
75016 var zoom = geoScaleToZoom(projection.scale());
75017 _prevSelected = _currSelected || {};
75019 if (context.map().isInWideSelection()) {
75020 _currSelected = {};
75021 context.selectedIDs().forEach(function (id) {
75022 var entity = graph.hasEntity(id);
75023 if (!entity) return;
75025 if (entity.type === 'node') {
75026 if (renderAsVertex(entity, graph, wireframe, zoom)) {
75027 _currSelected[entity.id] = entity;
75032 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
75033 } // note that drawVertices will add `_currSelected` automatically if needed..
75036 var filter = function filter(d) {
75037 return d.id in _prevSelected;
75040 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
75041 }; // partial redraw - only update the hovered items..
75044 drawVertices.drawHover = function (selection, graph, target, extent) {
75045 if (target === _currHoverTarget) return; // continue only if something changed
75047 var wireframe = context.surface().classed('fill-wireframe');
75048 var zoom = geoScaleToZoom(projection.scale());
75049 _prevHover = _currHover || {};
75050 _currHoverTarget = target;
75051 var entity = target && target.properties && target.properties.entity;
75054 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
75057 } // note that drawVertices will add `_currHover` automatically if needed..
75060 var filter = function filter(d) {
75061 return d.id in _prevHover;
75064 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
75067 return drawVertices;
75070 function utilBindOnce(target, type, listener, capture) {
75071 var typeOnce = type + '.once';
75074 target.on(typeOnce, null);
75075 listener.apply(this, arguments);
75078 target.on(typeOnce, one, capture);
75082 function defaultFilter$2(d3_event) {
75083 return !d3_event.ctrlKey && !d3_event.button;
75086 function defaultExtent$1() {
75089 if (e instanceof SVGElement) {
75090 e = e.ownerSVGElement || e;
75092 if (e.hasAttribute('viewBox')) {
75093 e = e.viewBox.baseVal;
75094 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
75097 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
75100 return [[0, 0], [e.clientWidth, e.clientHeight]];
75103 function defaultWheelDelta$1(d3_event) {
75104 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
75107 function defaultConstrain$1(transform, extent, translateExtent) {
75108 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
75109 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
75110 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
75111 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
75112 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));
75115 function utilZoomPan() {
75116 var filter = defaultFilter$2,
75117 extent = defaultExtent$1,
75118 constrain = defaultConstrain$1,
75119 wheelDelta = defaultWheelDelta$1,
75120 scaleExtent = [0, Infinity],
75121 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
75122 interpolate = interpolateZoom,
75123 dispatch$1 = dispatch('start', 'zoom', 'end'),
75125 _transform = identity$2,
75128 function zoom(selection) {
75129 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
75130 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
75133 zoom.transform = function (collection, transform, point) {
75134 var selection = collection.selection ? collection.selection() : collection;
75136 if (collection !== selection) {
75137 schedule(collection, transform, point);
75139 selection.interrupt().each(function () {
75140 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
75145 zoom.scaleBy = function (selection, k, p) {
75146 zoom.scaleTo(selection, function () {
75147 var k0 = _transform.k,
75148 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
75153 zoom.scaleTo = function (selection, k, p) {
75154 zoom.transform(selection, function () {
75155 var e = extent.apply(this, arguments),
75157 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
75158 p1 = t0.invert(p0),
75159 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
75160 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
75164 zoom.translateBy = function (selection, x, y) {
75165 zoom.transform(selection, function () {
75166 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);
75170 zoom.translateTo = function (selection, x, y, p) {
75171 zoom.transform(selection, function () {
75172 var e = extent.apply(this, arguments),
75174 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
75175 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);
75179 function scale(transform, k) {
75180 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
75181 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
75184 function translate(transform, p0, p1) {
75185 var x = p0[0] - p1[0] * transform.k,
75186 y = p0[1] - p1[1] * transform.k;
75187 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
75190 function centroid(extent) {
75191 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
75194 function schedule(transition, transform, point) {
75195 transition.on('start.zoom', function () {
75196 gesture(this, arguments).start(null);
75197 }).on('interrupt.zoom end.zoom', function () {
75198 gesture(this, arguments).end(null);
75199 }).tween('zoom', function () {
75202 g = gesture(that, args),
75203 e = extent.apply(that, args),
75204 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
75205 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
75207 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
75208 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
75209 return function (t) {
75210 if (t === 1) t = b; // Avoid rounding error on end.
75214 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
75216 g.zoom(null, null, t);
75221 function gesture(that, args, clean) {
75222 return !clean && _activeGesture || new Gesture(that, args);
75225 function Gesture(that, args) {
75229 this.extent = extent.apply(that, args);
75232 Gesture.prototype = {
75233 start: function start(d3_event) {
75234 if (++this.active === 1) {
75235 _activeGesture = this;
75236 dispatch$1.call('start', this, d3_event);
75241 zoom: function zoom(d3_event, key, transform) {
75242 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
75243 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
75244 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
75245 _transform = transform;
75246 dispatch$1.call('zoom', this, d3_event, key, transform);
75249 end: function end(d3_event) {
75250 if (--this.active === 0) {
75251 _activeGesture = null;
75252 dispatch$1.call('end', this, d3_event);
75259 function wheeled(d3_event) {
75260 if (!filter.apply(this, arguments)) return;
75261 var g = gesture(this, arguments),
75263 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
75264 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
75265 // If there were recent wheel events, reset the wheel idle timeout.
75268 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
75269 g.mouse[1] = t.invert(g.mouse[0] = p);
75272 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
75274 g.mouse = [p, t.invert(p)];
75279 d3_event.preventDefault();
75280 d3_event.stopImmediatePropagation();
75281 g.wheel = setTimeout(wheelidled, _wheelDelay);
75282 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
75284 function wheelidled() {
75290 var _downPointerIDs = new Set();
75292 var _pointerLocGetter;
75294 function pointerdown(d3_event) {
75295 _downPointerIDs.add(d3_event.pointerId);
75297 if (!filter.apply(this, arguments)) return;
75298 var g = gesture(this, arguments, _downPointerIDs.size === 1);
75300 d3_event.stopImmediatePropagation();
75301 _pointerLocGetter = utilFastMouse(this);
75303 var loc = _pointerLocGetter(d3_event);
75305 var p = [loc, _transform.invert(loc), d3_event.pointerId];
75310 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
75320 function pointermove(d3_event) {
75321 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75322 if (!_activeGesture || !_pointerLocGetter) return;
75323 var g = gesture(this, arguments);
75324 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
75325 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
75327 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
75328 // The pointer went up without ending the gesture somehow, e.g.
75329 // a down mouse was moved off the map and released. End it here.
75330 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
75331 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
75336 d3_event.preventDefault();
75337 d3_event.stopImmediatePropagation();
75339 var loc = _pointerLocGetter(d3_event);
75342 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
75346 var p0 = g.pointer0[0],
75347 l0 = g.pointer0[1],
75348 p1 = g.pointer1[0],
75349 l1 = g.pointer1[1],
75350 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
75351 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
75352 t = scale(t, Math.sqrt(dp / dl));
75353 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
75354 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
75355 } else if (g.pointer0) {
75360 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
75363 function pointerup(d3_event) {
75364 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75366 _downPointerIDs["delete"](d3_event.pointerId);
75368 if (!_activeGesture) return;
75369 var g = gesture(this, arguments);
75370 d3_event.stopImmediatePropagation();
75371 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;
75373 if (g.pointer1 && !g.pointer0) {
75374 g.pointer0 = g.pointer1;
75378 if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);else {
75383 zoom.wheelDelta = function (_) {
75384 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
75387 zoom.filter = function (_) {
75388 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
75391 zoom.extent = function (_) {
75392 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
75395 zoom.scaleExtent = function (_) {
75396 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
75399 zoom.translateExtent = function (_) {
75400 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]]];
75403 zoom.constrain = function (_) {
75404 return arguments.length ? (constrain = _, zoom) : constrain;
75407 zoom.interpolate = function (_) {
75408 return arguments.length ? (interpolate = _, zoom) : interpolate;
75411 zoom._transform = function (_) {
75412 return arguments.length ? (_transform = _, zoom) : _transform;
75415 return utilRebind(zoom, dispatch$1, 'on');
75418 // if pointer events are supported. Falls back to default `dblclick` event.
75420 function utilDoubleUp() {
75421 var dispatch$1 = dispatch('doubleUp');
75422 var _maxTimespan = 500; // milliseconds
75424 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
75426 var _pointer; // object representing the pointer that could trigger double up
75429 function pointerIsValidFor(loc) {
75430 // second pointerup must occur within a small timeframe after the first pointerdown
75431 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
75432 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
75435 function pointerdown(d3_event) {
75436 // ignore right-click
75437 if (d3_event.ctrlKey || d3_event.button === 2) return;
75438 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
75439 // events on touch devices
75441 if (_pointer && !pointerIsValidFor(loc)) {
75442 // if this pointer is no longer valid, clear it so another can be started
75443 _pointer = undefined;
75449 startTime: new Date().getTime(),
75451 pointerId: d3_event.pointerId
75455 _pointer.pointerId = d3_event.pointerId;
75459 function pointerup(d3_event) {
75460 // ignore right-click
75461 if (d3_event.ctrlKey || d3_event.button === 2) return;
75462 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
75463 _pointer.upCount += 1;
75465 if (_pointer.upCount === 2) {
75467 var loc = [d3_event.clientX, d3_event.clientY];
75469 if (pointerIsValidFor(loc)) {
75470 var locInThis = utilFastMouse(this)(d3_event);
75471 dispatch$1.call('doubleUp', this, d3_event, locInThis);
75472 } // clear the pointer info in any case
75475 _pointer = undefined;
75479 function doubleUp(selection) {
75480 if ('PointerEvent' in window) {
75481 // dblclick isn't well supported on touch devices so manually use
75482 // pointer events if they're available
75483 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
75485 // fallback to dblclick
75486 selection.on('dblclick.doubleUp', function (d3_event) {
75487 dispatch$1.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
75492 doubleUp.off = function (selection) {
75493 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
75496 return utilRebind(doubleUp, dispatch$1, 'on');
75499 var TILESIZE = 256;
75502 var kMin = geoZoomToScale(minZoom, TILESIZE);
75503 var kMax = geoZoomToScale(maxZoom, TILESIZE);
75505 function clamp(num, min, max) {
75506 return Math.max(min, Math.min(num, max));
75509 function rendererMap(context) {
75510 var dispatch$1 = dispatch('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
75511 var projection = context.projection;
75512 var curtainProjection = context.curtainProjection;
75521 var _selection = select(null);
75523 var supersurface = select(null);
75524 var wrapper = select(null);
75525 var surface = select(null);
75526 var _dimensions = [1, 1];
75527 var _dblClickZoomEnabled = true;
75528 var _redrawEnabled = true;
75530 var _gestureTransformStart;
75532 var _transformStart = projection.transform();
75534 var _transformLast;
75536 var _isTransformed = false;
75539 var _getMouseCoords;
75541 var _lastPointerEvent;
75543 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
75546 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
75548 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
75551 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
75553 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
75554 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
75555 }).on('end.map', function () {
75556 _pointerDown = false;
75559 var _doubleUpHandler = utilDoubleUp();
75561 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
75562 // var pendingRedrawCall;
75563 // function scheduleRedraw() {
75564 // // Only schedule the redraw if one has not already been set.
75565 // if (isRedrawScheduled) return;
75566 // isRedrawScheduled = true;
75567 // var that = this;
75568 // var args = arguments;
75569 // pendingRedrawCall = window.requestIdleCallback(function () {
75570 // // Reset the boolean so future redraws can be set.
75571 // isRedrawScheduled = false;
75572 // redraw.apply(that, args);
75573 // }, { timeout: 1400 });
75577 function cancelPendingRedraw() {
75578 scheduleRedraw.cancel(); // isRedrawScheduled = false;
75579 // window.cancelIdleCallback(pendingRedrawCall);
75582 function map(selection) {
75583 _selection = selection;
75584 context.on('change.map', immediateRedraw);
75585 var osm = context.connection();
75588 osm.on('change.map', immediateRedraw);
75591 function didUndoOrRedo(targetTransform) {
75592 var mode = context.mode().id;
75593 if (mode !== 'browse' && mode !== 'select') return;
75595 if (targetTransform) {
75596 map.transformEase(targetTransform);
75600 context.history().on('merge.map', function () {
75602 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
75603 didUndoOrRedo(fromStack.transform);
75604 }).on('redone.map', function (stack) {
75605 didUndoOrRedo(stack.transform);
75607 context.background().on('change.map', immediateRedraw);
75608 context.features().on('redraw.map', immediateRedraw);
75609 drawLayers.on('change.map', function () {
75610 context.background().updateImagery();
75613 selection.on('wheel.map mousewheel.map', function (d3_event) {
75614 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
75615 d3_event.preventDefault();
75616 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
75618 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
75619 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
75621 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
75622 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
75623 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
75624 _lastPointerEvent = d3_event;
75626 if (d3_event.button === 2) {
75627 d3_event.stopPropagation();
75629 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
75630 _lastPointerEvent = d3_event;
75632 if (resetTransform()) {
75635 }).on(_pointerPrefix + 'move.map', function (d3_event) {
75636 _lastPointerEvent = d3_event;
75637 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
75638 if (map.editableDataEnabled() && !_isTransformed) {
75639 var hover = d3_event.target.__data__;
75640 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75641 dispatch$1.call('drawn', this, {
75645 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
75646 if (map.editableDataEnabled() && !_isTransformed) {
75647 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
75648 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75649 dispatch$1.call('drawn', this, {
75654 var detected = utilDetect(); // only WebKit supports gesture events
75656 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
75657 // but we only need to do this on desktop Safari anyway. – #7694
75658 !detected.isMobileWebKit) {
75659 // Desktop Safari sends gesture events for multitouch trackpad pinches.
75660 // We can listen for these and translate them into map zooms.
75661 surface.on('gesturestart.surface', function (d3_event) {
75662 d3_event.preventDefault();
75663 _gestureTransformStart = projection.transform();
75664 }).on('gesturechange.surface', gestureChange);
75665 } // must call after surface init
75670 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
75671 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
75673 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
75674 !select(d3_event.target).classed('fill')) return;
75675 var zoomOut = d3_event.shiftKey;
75676 var t = projection.transform();
75677 var p1 = t.invert(p0);
75678 t = t.scale(zoomOut ? 0.5 : 2);
75679 t.x = p0[0] - p1[0] * t.k;
75680 t.y = p0[1] - p1[1] * t.k;
75681 map.transformEase(t);
75684 context.on('enter.map', function () {
75685 if (!map.editableDataEnabled(true
75686 /* skip zoom check */
75687 )) return; // redraw immediately any objects affected by a change in selectedIDs.
75689 var graph = context.graph();
75690 var selectedAndParents = {};
75691 context.selectedIDs().forEach(function (id) {
75692 var entity = graph.hasEntity(id);
75695 selectedAndParents[entity.id] = entity;
75697 if (entity.type === 'node') {
75698 graph.parentWays(entity).forEach(function (parent) {
75699 selectedAndParents[parent.id] = parent;
75704 var data = Object.values(selectedAndParents);
75706 var filter = function filter(d) {
75707 return d.id in selectedAndParents;
75710 data = context.features().filter(data, graph);
75711 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
75712 dispatch$1.call('drawn', this, {
75714 }); // redraw everything else later
75718 map.dimensions(utilGetDimensions(selection));
75721 function zoomEventFilter(d3_event) {
75722 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
75723 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
75724 // This can happen if a previous `mousedown` occurred without a `mouseup`.
75725 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
75726 // so that d3-zoom won't stop propagation of new `mousedown` events.
75727 if (d3_event.type === 'mousedown') {
75728 var hasOrphan = false;
75729 var listeners = window.__on;
75731 for (var i = 0; i < listeners.length; i++) {
75732 var listener = listeners[i];
75734 if (listener.name === 'zoom' && listener.type === 'mouseup') {
75741 var event = window.CustomEvent;
75744 event = new event('mouseup');
75746 event = window.document.createEvent('Event');
75747 event.initEvent('mouseup', false, false);
75748 } // Event needs to be dispatched with an event.view property.
75751 event.view = window;
75752 window.dispatchEvent(event);
75756 return d3_event.button !== 2; // ignore right clicks
75759 function pxCenter() {
75760 return [_dimensions[0] / 2, _dimensions[1] / 2];
75763 function drawEditable(difference, extent) {
75764 var mode = context.mode();
75765 var graph = context.graph();
75766 var features = context.features();
75767 var all = context.history().intersects(map.extent());
75768 var fullRedraw = false;
75772 var applyFeatureLayerFilters = true;
75774 if (map.isInWideSelection()) {
75776 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
75777 var entity = context.hasEntity(id);
75778 if (entity) data.push(entity);
75781 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
75783 applyFeatureLayerFilters = false;
75784 } else if (difference) {
75785 var complete = difference.complete(map.extent());
75786 data = Object.values(complete).filter(Boolean);
75787 set = new Set(Object.keys(complete));
75789 filter = function filter(d) {
75790 return set.has(d.id);
75793 features.clear(data);
75795 // force a full redraw if gatherStats detects that a feature
75796 // should be auto-hidden (e.g. points or buildings)..
75797 if (features.gatherStats(all, graph, _dimensions)) {
75798 extent = undefined;
75802 data = context.history().intersects(map.extent().intersection(extent));
75803 set = new Set(data.map(function (entity) {
75807 filter = function filter(d) {
75808 return set.has(d.id);
75813 filter = utilFunctor(true);
75817 if (applyFeatureLayerFilters) {
75818 data = features.filter(data, graph);
75820 context.features().resetStats();
75823 if (mode && mode.id === 'select') {
75824 // update selected vertices - the user might have just double-clicked a way,
75825 // creating a new vertex, triggering a partial redraw without a mode change
75826 surface.call(drawVertices.drawSelected, graph, map.extent());
75829 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);
75830 dispatch$1.call('drawn', this, {
75835 map.init = function () {
75836 drawLayers = svgLayers(projection, context);
75837 drawPoints = svgPoints(projection, context);
75838 drawVertices = svgVertices(projection, context);
75839 drawLines = svgLines(projection, context);
75840 drawAreas = svgAreas(projection, context);
75841 drawMidpoints = svgMidpoints(projection, context);
75842 drawLabels = svgLabels(projection, context);
75845 function editOff() {
75846 context.features().resetStats();
75847 surface.selectAll('.layer-osm *').remove();
75848 surface.selectAll('.layer-touch:not(.markers) *').remove();
75852 'select-note': true,
75853 'select-data': true,
75854 'select-error': true
75856 var mode = context.mode();
75858 if (mode && !allowed[mode.id]) {
75859 context.enter(modeBrowse(context));
75862 dispatch$1.call('drawn', this, {
75867 function gestureChange(d3_event) {
75868 // Remap Safari gesture events to wheel events - #5492
75869 // We want these disabled most places, but enabled for zoom/unzoom on map surface
75870 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
75872 e.preventDefault();
75875 // dummy values to ignore in zoomPan
75877 // dummy values to ignore in zoomPan
75878 clientX: e.clientX,
75879 clientY: e.clientY,
75880 screenX: e.screenX,
75881 screenY: e.screenY,
75885 var e2 = new WheelEvent('wheel', props);
75886 e2._scale = e.scale; // preserve the original scale
75888 e2._rotation = e.rotation; // preserve the original rotation
75890 _selection.node().dispatchEvent(e2);
75893 function zoomPan(event, key, transform) {
75894 var source = event && event.sourceEvent || event;
75895 var eventTransform = transform || event && event.transform;
75896 var x = eventTransform.x;
75897 var y = eventTransform.y;
75898 var k = eventTransform.k; // Special handling of 'wheel' events:
75899 // They might be triggered by the user scrolling the mouse wheel,
75900 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
75902 if (source && source.type === 'wheel') {
75903 // assume that the gesture is already handled by pointer events
75904 if (_pointerDown) return;
75905 var detected = utilDetect();
75906 var dX = source.deltaX;
75907 var dY = source.deltaY;
75911 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
75912 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
75913 // We are essentially redoing the calculations that occur here:
75914 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
75915 // See this for more info:
75916 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
75918 if (source.deltaMode === 1
75921 // Convert from lines to pixels, more if the user is scrolling fast.
75922 // (I made up the exp function to roughly match Firefox to what Chrome does)
75923 // These numbers should be floats, because integers are treated as pan gesture below.
75924 var lines = Math.abs(source.deltaY);
75925 var sign = source.deltaY > 0 ? 1 : -1;
75926 dY = sign * clamp(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
75927 350.000244140625 // max
75928 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
75929 // There doesn't seem to be any scroll acceleration.
75930 // This multiplier increases the speed a little bit - #5512
75932 if (detected.os !== 'mac') {
75934 } // recalculate x2,y2,k2
75937 t0 = _isTransformed ? _transformLast : _transformStart;
75938 p0 = _getMouseCoords(source);
75939 p1 = t0.invert(p0);
75940 k2 = t0.k * Math.pow(2, -dY / 500);
75941 k2 = clamp(k2, kMin, kMax);
75942 x2 = p0[0] - p1[0] * k2;
75943 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
75944 // These are fake `wheel` events we made from Safari `gesturechange` events..
75945 } else if (source._scale) {
75946 // recalculate x2,y2,k2
75947 t0 = _gestureTransformStart;
75948 p0 = _getMouseCoords(source);
75949 p1 = t0.invert(p0);
75950 k2 = t0.k * source._scale;
75951 k2 = clamp(k2, kMin, kMax);
75952 x2 = p0[0] - p1[0] * k2;
75953 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
75954 // Pinch zooming via the `wheel` event will always have:
75955 // - `ctrlKey = true`
75956 // - `deltaY` is not round integer pixels (ignore `deltaX`)
75957 } else if (source.ctrlKey && !isInteger(dY)) {
75958 dY *= 6; // slightly scale up whatever the browser gave us
75959 // recalculate x2,y2,k2
75961 t0 = _isTransformed ? _transformLast : _transformStart;
75962 p0 = _getMouseCoords(source);
75963 p1 = t0.invert(p0);
75964 k2 = t0.k * Math.pow(2, -dY / 500);
75965 k2 = clamp(k2, kMin, kMax);
75966 x2 = p0[0] - p1[0] * k2;
75967 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
75968 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
75969 // recalculate x2,y2,k2
75970 t0 = _isTransformed ? _transformLast : _transformStart;
75971 p0 = _getMouseCoords(source);
75972 p1 = t0.invert(p0);
75973 k2 = t0.k * Math.pow(2, -dY / 500);
75974 k2 = clamp(k2, kMin, kMax);
75975 x2 = p0[0] - p1[0] * k2;
75976 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers) - #5492, #5512
75977 // Panning via the `wheel` event will always have:
75978 // - `ctrlKey = false`
75979 // - `deltaX`,`deltaY` are round integer pixels
75980 } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
75981 p1 = projection.translate();
75984 k2 = projection.scale();
75985 k2 = clamp(k2, kMin, kMax);
75986 } // something changed - replace the event transform
75989 if (x2 !== x || y2 !== y || k2 !== k) {
75993 eventTransform = identity$2.translate(x2, y2).scale(k2);
75995 if (_zoomerPanner._transform) {
75996 // utilZoomPan interface
75997 _zoomerPanner._transform(eventTransform);
75999 // d3_zoom interface
76000 _selection.node().__zoom = eventTransform;
76005 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
76006 return; // no change
76009 var withinEditableZoom = map.withinEditableZoom();
76011 if (_lastWithinEditableZoom !== withinEditableZoom) {
76012 if (_lastWithinEditableZoom !== undefined) {
76013 // notify that the map zoomed in or out over the editable zoom threshold
76014 dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
76017 _lastWithinEditableZoom = withinEditableZoom;
76020 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
76021 surface.interrupt();
76022 dispatch$1.call('hitMinZoom', this, map);
76023 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
76025 dispatch$1.call('move', this, map);
76029 projection.transform(eventTransform);
76030 var scale = k / _transformStart.k;
76031 var tX = (x / scale - _transformStart.x) * scale;
76032 var tY = (y / scale - _transformStart.y) * scale;
76034 if (context.inIntro()) {
76035 curtainProjection.transform({
76043 _lastPointerEvent = event;
76046 _isTransformed = true;
76047 _transformLast = eventTransform;
76048 utilSetTransform(supersurface, tX, tY, scale);
76050 dispatch$1.call('move', this, map);
76052 function isInteger(val) {
76053 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
76057 function resetTransform() {
76058 if (!_isTransformed) return false;
76059 utilSetTransform(supersurface, 0, 0);
76060 _isTransformed = false;
76062 if (context.inIntro()) {
76063 curtainProjection.transform(projection.transform());
76069 function redraw(difference, extent) {
76070 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
76071 // It would result in artifacts where differenced entities are redrawn with
76072 // one transform and unchanged entities with another.
76074 if (resetTransform()) {
76075 difference = extent = undefined;
76078 var zoom = map.zoom();
76079 var z = String(~~zoom);
76081 if (surface.attr('data-zoom') !== z) {
76082 surface.attr('data-zoom', z);
76083 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
76086 var lat = map.center()[1];
76087 var lowzoom = linear$2().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
76088 surface.classed('low-zoom', zoom <= lowzoom(lat));
76091 supersurface.call(context.background());
76092 wrapper.call(drawLayers);
76096 if (map.editableDataEnabled() || map.isInWideSelection()) {
76097 context.loadTiles(projection);
76098 drawEditable(difference, extent);
76103 _transformStart = projection.transform();
76107 var immediateRedraw = function immediateRedraw(difference, extent) {
76108 if (!difference && !extent) cancelPendingRedraw();
76109 redraw(difference, extent);
76112 map.lastPointerEvent = function () {
76113 return _lastPointerEvent;
76116 map.mouse = function (d3_event) {
76117 var event = _lastPointerEvent || d3_event;
76122 while (s = event.sourceEvent) {
76126 return _getMouseCoords(event);
76130 }; // returns Lng/Lat
76133 map.mouseCoordinates = function () {
76134 var coord = map.mouse() || pxCenter();
76135 return projection.invert(coord);
76138 map.dblclickZoomEnable = function (val) {
76139 if (!arguments.length) return _dblClickZoomEnabled;
76140 _dblClickZoomEnabled = val;
76144 map.redrawEnable = function (val) {
76145 if (!arguments.length) return _redrawEnabled;
76146 _redrawEnabled = val;
76150 map.isTransformed = function () {
76151 return _isTransformed;
76154 function setTransform(t2, duration, force) {
76155 var t = projection.transform();
76156 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
76159 _selection.transition().duration(duration).on('start', function () {
76161 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
76163 projection.transform(t2);
76164 _transformStart = t2;
76166 _selection.call(_zoomerPanner.transform, _transformStart);
76172 function setCenterZoom(loc2, z2, duration, force) {
76173 var c = map.center();
76174 var z = map.zoom();
76175 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
76176 var proj = geoRawMercator().transform(projection.transform()); // copy projection
76178 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
76180 var t = proj.translate();
76181 var point = proj(loc2);
76182 var center = pxCenter();
76183 t[0] += center[0] - point[0];
76184 t[1] += center[1] - point[1];
76185 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
76188 map.pan = function (delta, duration) {
76189 var t = projection.translate();
76190 var k = projection.scale();
76195 _selection.transition().duration(duration).on('start', function () {
76197 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
76199 projection.translate(t);
76200 _transformStart = projection.transform();
76202 _selection.call(_zoomerPanner.transform, _transformStart);
76204 dispatch$1.call('move', this, map);
76211 map.dimensions = function (val) {
76212 if (!arguments.length) return _dimensions;
76214 drawLayers.dimensions(_dimensions);
76215 context.background().dimensions(_dimensions);
76216 projection.clipExtent([[0, 0], _dimensions]);
76217 _getMouseCoords = utilFastMouse(supersurface.node());
76222 function zoomIn(delta) {
76223 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
76226 function zoomOut(delta) {
76227 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
76230 map.zoomIn = function () {
76234 map.zoomInFurther = function () {
76238 map.canZoomIn = function () {
76239 return map.zoom() < maxZoom;
76242 map.zoomOut = function () {
76246 map.zoomOutFurther = function () {
76250 map.canZoomOut = function () {
76251 return map.zoom() > minZoom;
76254 map.center = function (loc2) {
76255 if (!arguments.length) {
76256 return projection.invert(pxCenter());
76259 if (setCenterZoom(loc2, map.zoom())) {
76260 dispatch$1.call('move', this, map);
76267 map.unobscuredCenterZoomEase = function (loc, zoom) {
76268 var offset = map.unobscuredOffsetPx();
76269 var proj = geoRawMercator().transform(projection.transform()); // copy projection
76270 // use the target zoom to calculate the offset center
76272 proj.scale(geoZoomToScale(zoom, TILESIZE));
76273 var locPx = proj(loc);
76274 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
76275 var offsetLoc = proj.invert(offsetLocPx);
76276 map.centerZoomEase(offsetLoc, zoom);
76279 map.unobscuredOffsetPx = function () {
76280 var openPane = context.container().select('.map-panes .map-pane.shown');
76282 if (!openPane.empty()) {
76283 return [openPane.node().offsetWidth / 2, 0];
76289 map.zoom = function (z2) {
76290 if (!arguments.length) {
76291 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
76294 if (z2 < _minzoom) {
76295 surface.interrupt();
76296 dispatch$1.call('hitMinZoom', this, map);
76297 z2 = context.minEditableZoom();
76300 if (setCenterZoom(map.center(), z2)) {
76301 dispatch$1.call('move', this, map);
76308 map.centerZoom = function (loc2, z2) {
76309 if (setCenterZoom(loc2, z2)) {
76310 dispatch$1.call('move', this, map);
76317 map.zoomTo = function (entity) {
76318 var extent = entity.extent(context.graph());
76319 if (!isFinite(extent.area())) return map;
76320 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76321 return map.centerZoom(extent.center(), z2);
76324 map.centerEase = function (loc2, duration) {
76325 duration = duration || 250;
76326 setCenterZoom(loc2, map.zoom(), duration);
76330 map.zoomEase = function (z2, duration) {
76331 duration = duration || 250;
76332 setCenterZoom(map.center(), z2, duration, false);
76336 map.centerZoomEase = function (loc2, z2, duration) {
76337 duration = duration || 250;
76338 setCenterZoom(loc2, z2, duration, false);
76342 map.transformEase = function (t2, duration) {
76343 duration = duration || 250;
76344 setTransform(t2, duration, false
76350 map.zoomToEase = function (obj, duration) {
76353 if (Array.isArray(obj)) {
76354 obj.forEach(function (entity) {
76355 var entityExtent = entity.extent(context.graph());
76358 extent = entityExtent;
76360 extent = extent.extend(entityExtent);
76364 extent = obj.extent(context.graph());
76367 if (!isFinite(extent.area())) return map;
76368 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76369 return map.centerZoomEase(extent.center(), z2, duration);
76372 map.startEase = function () {
76373 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
76379 map.cancelEase = function () {
76380 _selection.interrupt();
76385 map.extent = function (val) {
76386 if (!arguments.length) {
76387 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
76389 var extent = geoExtent(val);
76390 map.centerZoom(extent.center(), map.extentZoom(extent));
76394 map.trimmedExtent = function (val) {
76395 if (!arguments.length) {
76399 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
76401 var extent = geoExtent(val);
76402 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
76406 function calcExtentZoom(extent, dim) {
76407 var tl = projection([extent[0][0], extent[1][1]]);
76408 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
76410 var hFactor = (br[0] - tl[0]) / dim[0];
76411 var vFactor = (br[1] - tl[1]) / dim[1];
76412 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
76413 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
76414 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
76418 map.extentZoom = function (val) {
76419 return calcExtentZoom(geoExtent(val), _dimensions);
76422 map.trimmedExtentZoom = function (val) {
76425 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
76426 return calcExtentZoom(geoExtent(val), trimmed);
76429 map.withinEditableZoom = function () {
76430 return map.zoom() >= context.minEditableZoom();
76433 map.isInWideSelection = function () {
76434 return !map.withinEditableZoom() && context.selectedIDs().length;
76437 map.editableDataEnabled = function (skipZoomCheck) {
76438 var layer = context.layers().layer('osm');
76439 if (!layer || !layer.enabled()) return false;
76440 return skipZoomCheck || map.withinEditableZoom();
76443 map.notesEditable = function () {
76444 var layer = context.layers().layer('notes');
76445 if (!layer || !layer.enabled()) return false;
76446 return map.withinEditableZoom();
76449 map.minzoom = function (val) {
76450 if (!arguments.length) return _minzoom;
76455 map.toggleHighlightEdited = function () {
76456 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
76457 map.pan([0, 0]); // trigger a redraw
76459 dispatch$1.call('changeHighlighting', this);
76462 map.areaFillOptions = ['wireframe', 'partial', 'full'];
76464 map.activeAreaFill = function (val) {
76465 if (!arguments.length) return corePreferences('area-fill') || 'partial';
76466 corePreferences('area-fill', val);
76468 if (val !== 'wireframe') {
76469 corePreferences('area-fill-toggle', val);
76473 map.pan([0, 0]); // trigger a redraw
76475 dispatch$1.call('changeAreaFill', this);
76479 map.toggleWireframe = function () {
76480 var activeFill = map.activeAreaFill();
76482 if (activeFill === 'wireframe') {
76483 activeFill = corePreferences('area-fill-toggle') || 'partial';
76485 activeFill = 'wireframe';
76488 map.activeAreaFill(activeFill);
76491 function updateAreaFill() {
76492 var activeFill = map.activeAreaFill();
76493 map.areaFillOptions.forEach(function (opt) {
76494 surface.classed('fill-' + opt, Boolean(opt === activeFill));
76498 map.layers = function () {
76502 map.doubleUpHandler = function () {
76503 return _doubleUpHandler;
76506 return utilRebind(map, dispatch$1, 'on');
76509 function rendererPhotos(context) {
76510 var dispatch$1 = dispatch('change');
76511 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
76512 var _allPhotoTypes = ['flat', 'panoramic'];
76514 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
76517 var _dateFilters = ['fromDate', 'toDate'];
76525 function photos() {}
76527 function updateStorage() {
76528 if (window.mocha) return;
76529 var hash = utilStringQs(window.location.hash);
76530 var enabled = context.layers().all().filter(function (d) {
76531 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
76532 }).map(function (d) {
76536 if (enabled.length) {
76537 hash.photo_overlay = enabled.join(',');
76539 delete hash.photo_overlay;
76542 window.location.replace('#' + utilQsString(hash, true));
76545 photos.overlayLayerIDs = function () {
76549 photos.allPhotoTypes = function () {
76550 return _allPhotoTypes;
76553 photos.dateFilters = function () {
76554 return _dateFilters;
76557 photos.dateFilterValue = function (val) {
76558 return val === _dateFilters[0] ? _fromDate : _toDate;
76561 photos.setDateFilter = function (type, val, updateUrl) {
76562 // validate the date
76563 var date = val && new Date(val);
76565 if (date && !isNaN(date)) {
76566 val = date.toISOString().substr(0, 10);
76571 if (type === _dateFilters[0]) {
76574 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76575 _toDate = _fromDate;
76579 if (type === _dateFilters[1]) {
76582 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76583 _fromDate = _toDate;
76587 dispatch$1.call('change', this);
76592 if (_fromDate || _toDate) {
76593 rangeString = (_fromDate || '') + '_' + (_toDate || '');
76596 setUrlFilterValue('photo_dates', rangeString);
76600 photos.setUsernameFilter = function (val, updateUrl) {
76601 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
76604 val = val.map(function (d) {
76606 }).filter(Boolean);
76614 dispatch$1.call('change', this);
76620 hashString = _usernames.join(',');
76623 setUrlFilterValue('photo_username', hashString);
76627 function setUrlFilterValue(property, val) {
76628 if (!window.mocha) {
76629 var hash = utilStringQs(window.location.hash);
76632 if (hash[property] === val) return;
76633 hash[property] = val;
76635 if (!(property in hash)) return;
76636 delete hash[property];
76639 window.location.replace('#' + utilQsString(hash, true));
76643 function showsLayer(id) {
76644 var layer = context.layers().layer(id);
76645 return layer && layer.supported() && layer.enabled();
76648 photos.shouldFilterByDate = function () {
76649 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76652 photos.shouldFilterByPhotoType = function () {
76653 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
76656 photos.shouldFilterByUsername = function () {
76657 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76660 photos.showsPhotoType = function (val) {
76661 if (!photos.shouldFilterByPhotoType()) return true;
76662 return _shownPhotoTypes.indexOf(val) !== -1;
76665 photos.showsFlat = function () {
76666 return photos.showsPhotoType('flat');
76669 photos.showsPanoramic = function () {
76670 return photos.showsPhotoType('panoramic');
76673 photos.fromDate = function () {
76677 photos.toDate = function () {
76681 photos.togglePhotoType = function (val) {
76682 var index = _shownPhotoTypes.indexOf(val);
76684 if (index !== -1) {
76685 _shownPhotoTypes.splice(index, 1);
76687 _shownPhotoTypes.push(val);
76690 dispatch$1.call('change', this);
76694 photos.usernames = function () {
76698 photos.init = function () {
76699 var hash = utilStringQs(window.location.hash);
76701 if (hash.photo_dates) {
76702 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
76703 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
76704 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
76705 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
76708 if (hash.photo_username) {
76709 this.setUsernameFilter(hash.photo_username, false);
76712 if (hash.photo_overlay) {
76713 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
76714 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
76715 hashOverlayIDs.forEach(function (id) {
76716 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
76717 if (layer && !layer.enabled()) layer.enabled(true);
76722 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
76723 var photoIds = hash.photo.replace(/;/g, ',').split(',');
76724 var photoId = photoIds.length && photoIds[0].trim();
76725 var results = /(.*)\/(.*)/g.exec(photoId);
76727 if (results && results.length >= 3) {
76728 var serviceId = results[1];
76729 var photoKey = results[2];
76730 var service = services[serviceId];
76732 if (service && service.ensureViewerLoaded) {
76733 // if we're showing a photo then make sure its layer is enabled too
76734 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
76735 if (layer && !layer.enabled()) layer.enabled(true);
76736 var baselineTime = Date.now();
76737 service.on('loadedImages.rendererPhotos', function () {
76738 // don't open the viewer if too much time has elapsed
76739 if (Date.now() - baselineTime > 45000) {
76740 service.on('loadedImages.rendererPhotos', null);
76744 if (!service.cachedImage(photoKey)) return;
76745 service.on('loadedImages.rendererPhotos', null);
76746 service.ensureViewerLoaded(context).then(function () {
76747 service.selectImage(context, photoKey).showViewer(context);
76754 context.layers().on('change.rendererPhotos', updateStorage);
76757 return utilRebind(photos, dispatch$1, 'on');
76760 function uiAccount(context) {
76761 var osm = context.connection();
76763 function update(selection) {
76766 if (!osm.authenticated()) {
76767 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
76771 osm.userDetails(function (err, details) {
76772 var userLink = selection.select('.userLink'),
76773 logoutLink = selection.select('.logoutLink');
76775 logoutLink.html('');
76776 if (err || !details) return;
76777 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
76779 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
76781 if (details.image_url) {
76782 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
76784 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
76788 userLinkA.append('span').attr('class', 'label').html(details.display_name);
76789 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
76790 d3_event.preventDefault();
76796 return function (selection) {
76797 selection.append('li').attr('class', 'userLink').classed('hide', true);
76798 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
76801 osm.on('change.account', function () {
76809 function uiAttribution(context) {
76810 var _selection = select(null);
76812 function render(selection, data, klass) {
76813 var div = selection.selectAll(".".concat(klass)).data([0]);
76814 div = div.enter().append('div').attr('class', klass).merge(div);
76815 var attributions = div.selectAll('.attribution').data(data, function (d) {
76818 attributions.exit().remove();
76819 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
76820 var attribution = select(nodes[i]);
76822 if (d.terms_html) {
76823 attribution.html(d.terms_html);
76828 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
76831 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
76832 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
76833 "default": d.terms_text || d.id || d.name()
76836 if (d.icon && !d.overlay) {
76837 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
76840 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
76841 }).merge(attributions);
76842 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
76843 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
76844 return notice ? [notice] : [];
76846 copyright.exit().remove();
76847 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
76848 copyright.html(String);
76851 function update() {
76852 var baselayer = context.background().baseLayerSource();
76854 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
76856 var z = context.map().zoom();
76857 var overlays = context.background().overlayLayerSources() || [];
76859 _selection.call(render, overlays.filter(function (s) {
76860 return s.validZoom(z);
76861 }), 'overlay-layer-attribution');
76864 return function (selection) {
76865 _selection = selection;
76866 context.background().on('change.attribution', update);
76867 context.map().on('move.attribution', throttle(update, 400, {
76874 function uiContributors(context) {
76875 var osm = context.connection(),
76876 debouncedUpdate = debounce(function () {
76881 wrap = select(null);
76883 function update() {
76886 entities = context.history().intersects(context.map().extent());
76887 entities.forEach(function (entity) {
76888 if (entity && entity.user) users[entity.user] = true;
76890 var u = Object.keys(users),
76891 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
76892 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
76893 var userList = select(document.createElement('span'));
76894 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
76895 return osm.userURL(d);
76896 }).attr('target', '_blank').html(String);
76898 if (u.length > limit) {
76899 var count = select(document.createElement('span'));
76900 var othersNum = u.length - limit + 1;
76901 count.append('a').attr('target', '_blank').attr('href', function () {
76902 return osm.changesetsURL(context.map().center(), context.map().zoom());
76903 }).html(othersNum);
76904 wrap.append('span').html(_t.html('contributors.truncated_list', {
76906 users: userList.html(),
76907 count: count.html()
76910 wrap.append('span').html(_t.html('contributors.list', {
76911 users: userList.html()
76917 wrap.transition().style('opacity', 0);
76918 } else if (hidden) {
76919 wrap.transition().style('opacity', 1);
76923 return function (selection) {
76927 osm.on('loaded.contributors', debouncedUpdate);
76928 context.map().on('move.contributors', debouncedUpdate);
76932 var _popoverID = 0;
76933 function uiPopover(klass) {
76934 var _id = _popoverID++;
76936 var _anchorSelection = select(null);
76938 var popover = function popover(selection) {
76939 _anchorSelection = selection;
76940 selection.each(setup);
76943 var _animation = utilFunctor(false);
76945 var _placement = utilFunctor('top'); // top, bottom, left, right
76948 var _alignment = utilFunctor('center'); // leading, center, trailing
76951 var _scrollContainer = utilFunctor(select(null));
76955 var _displayType = utilFunctor('');
76957 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
76960 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
76962 popover.displayType = function (val) {
76963 if (arguments.length) {
76964 _displayType = utilFunctor(val);
76967 return _displayType;
76971 popover.hasArrow = function (val) {
76972 if (arguments.length) {
76973 _hasArrow = utilFunctor(val);
76980 popover.placement = function (val) {
76981 if (arguments.length) {
76982 _placement = utilFunctor(val);
76989 popover.alignment = function (val) {
76990 if (arguments.length) {
76991 _alignment = utilFunctor(val);
76998 popover.scrollContainer = function (val) {
76999 if (arguments.length) {
77000 _scrollContainer = utilFunctor(val);
77003 return _scrollContainer;
77007 popover.content = function (val) {
77008 if (arguments.length) {
77016 popover.isShown = function () {
77017 var popoverSelection = _anchorSelection.select('.popover-' + _id);
77019 return !popoverSelection.empty() && popoverSelection.classed('in');
77022 popover.show = function () {
77023 _anchorSelection.each(show);
77026 popover.updateContent = function () {
77027 _anchorSelection.each(updateContent);
77030 popover.hide = function () {
77031 _anchorSelection.each(hide);
77034 popover.toggle = function () {
77035 _anchorSelection.each(toggle);
77038 popover.destroy = function (selection, selector) {
77039 // by default, just destroy the current popover
77040 selector = selector || '.popover-' + _id;
77041 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 () {
77042 return this.getAttribute('data-original-title') || this.getAttribute('title');
77043 }).attr('data-original-title', null).selectAll(selector).remove();
77046 popover.destroyAny = function (selection) {
77047 selection.call(popover.destroy, '.popover');
77051 var anchor = select(this);
77053 var animate = _animation.apply(this, arguments);
77055 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
77056 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
77057 enter.append('div').attr('class', 'popover-arrow');
77058 enter.append('div').attr('class', 'popover-inner');
77059 popoverSelection = enter.merge(popoverSelection);
77062 popoverSelection.classed('fade', true);
77065 var display = _displayType.apply(this, arguments);
77067 if (display === 'hover') {
77068 var _lastNonMouseEnterTime;
77070 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
77071 if (d3_event.pointerType) {
77072 if (d3_event.pointerType !== 'mouse') {
77073 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
77076 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
77077 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
77078 // event for non-mouse interactions right after sending
77079 // the correct type pointerenter event. Workaround by discarding
77080 // any mouse event that occurs immediately after a non-mouse event.
77083 } // don't show if buttons are pressed, e.g. during click and drag of map
77086 if (d3_event.buttons !== 0) return;
77087 show.apply(this, arguments);
77088 }).on(_pointerPrefix + 'leave.popover', function () {
77089 hide.apply(this, arguments);
77090 }) // show on focus too for better keyboard navigation support
77091 .on('focus.popover', function () {
77092 show.apply(this, arguments);
77093 }).on('blur.popover', function () {
77094 hide.apply(this, arguments);
77096 } else if (display === 'clickFocus') {
77097 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
77098 d3_event.preventDefault();
77099 d3_event.stopPropagation();
77100 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
77101 d3_event.preventDefault();
77102 d3_event.stopPropagation();
77103 }).on('click.popover', toggle);
77104 popoverSelection // This attribute lets the popover take focus
77105 .attr('tabindex', 0).on('blur.popover', function () {
77106 anchor.each(function () {
77107 hide.apply(this, arguments);
77114 var anchor = select(this);
77115 var popoverSelection = anchor.selectAll('.popover-' + _id);
77117 if (popoverSelection.empty()) {
77118 // popover was removed somehow, put it back
77119 anchor.call(popover.destroy);
77120 anchor.each(setup);
77121 popoverSelection = anchor.selectAll('.popover-' + _id);
77124 popoverSelection.classed('in', true);
77126 var displayType = _displayType.apply(this, arguments);
77128 if (displayType === 'clickFocus') {
77129 anchor.classed('active', true);
77130 popoverSelection.node().focus();
77133 anchor.each(updateContent);
77136 function updateContent() {
77137 var anchor = select(this);
77140 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
77143 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
77144 // set before the dynamic popover size is calculated by the browser
77146 updatePosition.apply(this, arguments);
77147 updatePosition.apply(this, arguments);
77150 function updatePosition() {
77151 var anchor = select(this);
77152 var popoverSelection = anchor.selectAll('.popover-' + _id);
77154 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
77156 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
77157 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
77158 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
77160 var placement = _placement.apply(this, arguments);
77162 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
77164 var alignment = _alignment.apply(this, arguments);
77166 var alignFactor = 0.5;
77168 if (alignment === 'leading') {
77170 } else if (alignment === 'trailing') {
77174 var anchorFrame = getFrame(anchor.node());
77175 var popoverFrame = getFrame(popoverSelection.node());
77178 switch (placement) {
77181 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77182 y: anchorFrame.y - popoverFrame.h
77188 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77189 y: anchorFrame.y + anchorFrame.h
77195 x: anchorFrame.x - popoverFrame.w,
77196 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77202 x: anchorFrame.x + anchorFrame.w,
77203 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77209 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
77210 var initialPosX = position.x;
77212 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
77213 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
77214 } else if (position.x < 10) {
77218 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
77220 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
77221 arrow.style('left', ~~arrowPosX + 'px');
77224 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
77226 popoverSelection.style('left', null).style('top', null);
77229 function getFrame(node) {
77230 var positionStyle = select(node).style('position');
77232 if (positionStyle === 'absolute' || positionStyle === 'static') {
77234 x: node.offsetLeft - scrollLeft,
77235 y: node.offsetTop - scrollTop,
77236 w: node.offsetWidth,
77237 h: node.offsetHeight
77243 w: node.offsetWidth,
77244 h: node.offsetHeight
77251 var anchor = select(this);
77253 if (_displayType.apply(this, arguments) === 'clickFocus') {
77254 anchor.classed('active', false);
77257 anchor.selectAll('.popover-' + _id).classed('in', false);
77260 function toggle() {
77261 if (select(this).select('.popover-' + _id).classed('in')) {
77262 hide.apply(this, arguments);
77264 show.apply(this, arguments);
77271 function uiTooltip(klass) {
77272 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
77274 var _title = function _title() {
77275 var title = this.getAttribute('data-original-title');
77280 title = this.getAttribute('title');
77281 this.removeAttribute('title');
77282 this.setAttribute('data-original-title', title);
77288 var _heading = utilFunctor(null);
77290 var _keys = utilFunctor(null);
77292 tooltip.title = function (val) {
77293 if (!arguments.length) return _title;
77294 _title = utilFunctor(val);
77298 tooltip.heading = function (val) {
77299 if (!arguments.length) return _heading;
77300 _heading = utilFunctor(val);
77304 tooltip.keys = function (val) {
77305 if (!arguments.length) return _keys;
77306 _keys = utilFunctor(val);
77310 tooltip.content(function () {
77311 var heading = _heading.apply(this, arguments);
77313 var text = _title.apply(this, arguments);
77315 var keys = _keys.apply(this, arguments);
77317 return function (selection) {
77318 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
77319 headingSelect.exit().remove();
77320 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
77321 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
77322 textSelect.exit().remove();
77323 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
77324 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
77325 keyhintWrap.exit().remove();
77326 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
77327 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
77328 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
77329 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
77337 function uiEditMenu(context) {
77338 var dispatch$1 = dispatch('toggled');
77340 var _menu = select(null);
77342 var _operations = []; // the position the menu should be displayed relative to
77344 var _anchorLoc = [0, 0];
77345 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
77347 var _triggerType = '';
77348 var _vpTopMargin = 85; // viewport top margin
77350 var _vpBottomMargin = 45; // viewport bottom margin
77352 var _vpSideMargin = 35; // viewport side margin
77354 var _menuTop = false;
77358 var _menuWidth; // hardcode these values to make menu positioning easier
77361 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
77363 var _tooltipWidth = 210; // offset the menu slightly from the target location
77365 var _menuSideMargin = 10;
77366 var _tooltips = [];
77368 var editMenu = function editMenu(selection) {
77369 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
77371 var ops = _operations.filter(function (op) {
77372 return !isTouchMenu || !op.mouseOnly;
77375 if (!ops.length) return;
77376 _tooltips = []; // Position the menu above the anchor for stylus and finger input
77377 // since the mapper's hand likely obscures the screen below the anchor
77379 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
77381 var showLabels = isTouchMenu;
77382 var buttonHeight = showLabels ? 32 : 34;
77385 // Get a general idea of the width based on the length of the label
77386 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
77387 return op.title.length;
77393 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
77394 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
77396 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
77399 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
77400 return 'edit-menu-item edit-menu-item-' + d.id;
77401 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
77402 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
77403 // don't let button presses also act as map input - #1869
77404 d3_event.stopPropagation();
77405 }).on('mouseenter.highlight', function (d3_event, d) {
77406 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
77407 utilHighlightEntities(d.relatedEntityIds(), true, context);
77408 }).on('mouseleave.highlight', function (d3_event, d) {
77409 if (!d.relatedEntityIds) return;
77410 utilHighlightEntities(d.relatedEntityIds(), false, context);
77412 buttonsEnter.each(function (d) {
77413 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
77415 _tooltips.push(tooltip);
77417 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
77421 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
77427 buttonsEnter.merge(buttons).classed('disabled', function (d) {
77428 return d.disabled();
77431 var initialScale = context.projection.scale();
77432 context.map().on('move.edit-menu', function () {
77433 if (initialScale !== context.projection.scale()) {
77436 }).on('drawn.edit-menu', function (info) {
77437 if (info.full) updatePosition();
77439 var lastPointerUpType; // `pointerup` is always called before `click`
77441 function pointerup(d3_event) {
77442 lastPointerUpType = d3_event.pointerType;
77445 function click(d3_event, operation) {
77446 d3_event.stopPropagation();
77448 if (operation.relatedEntityIds) {
77449 utilHighlightEntities(operation.relatedEntityIds(), false, context);
77452 if (operation.disabled()) {
77453 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77454 // there are no tooltips for touch interactions so flash feedback instead
77455 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
77458 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77459 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
77466 lastPointerUpType = null;
77469 dispatch$1.call('toggled', this, true);
77472 function updatePosition() {
77473 if (!_menu || _menu.empty()) return;
77474 var anchorLoc = context.projection(_anchorLocLonLat);
77475 var viewport = context.surfaceRect();
77477 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
77478 // close the menu if it's gone offscreen
77483 var menuLeft = displayOnLeft(viewport);
77484 var offset = [0, 0];
77485 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
77488 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
77489 // menu is near top viewport edge, shift downward
77490 offset[1] = -anchorLoc[1] + _vpTopMargin;
77492 offset[1] = -_menuHeight;
77495 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
77496 // menu is near bottom viewport edge, shift upwards
77497 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
77503 var origin = geoVecAdd(anchorLoc, offset);
77505 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
77507 var tooltipSide = tooltipPosition(viewport, menuLeft);
77509 _tooltips.forEach(function (tooltip) {
77510 tooltip.placement(tooltipSide);
77513 function displayOnLeft(viewport) {
77514 if (_mainLocalizer.textDirection() === 'ltr') {
77515 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
77516 // right menu would be too close to the right viewport edge, go left
77518 } // prefer right menu
77524 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
77525 // left menu would be too close to the left viewport edge, go right
77527 } // prefer left menu
77534 function tooltipPosition(viewport, menuLeft) {
77535 if (_mainLocalizer.textDirection() === 'ltr') {
77537 // if there's not room for a right-side menu then there definitely
77538 // isn't room for right-side tooltips
77542 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
77543 // right tooltips would be too close to the right viewport edge, go left
77545 } // prefer right tooltips
77555 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
77556 // left tooltips would be too close to the left viewport edge, go right
77558 } // prefer left tooltips
77566 editMenu.close = function () {
77567 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
77572 dispatch$1.call('toggled', this, false);
77575 editMenu.anchorLoc = function (val) {
77576 if (!arguments.length) return _anchorLoc;
77578 _anchorLocLonLat = context.projection.invert(_anchorLoc);
77582 editMenu.triggerType = function (val) {
77583 if (!arguments.length) return _triggerType;
77584 _triggerType = val;
77588 editMenu.operations = function (val) {
77589 if (!arguments.length) return _operations;
77594 return utilRebind(editMenu, dispatch$1, 'on');
77597 function uiFeatureInfo(context) {
77598 function update(selection) {
77599 var features = context.features();
77600 var stats = features.stats();
77602 var hiddenList = features.hidden().map(function (k) {
77605 return _t('inspector.title_count', {
77606 title: _t.html('feature.' + k + '.description'),
77612 }).filter(Boolean);
77613 selection.html('');
77615 if (hiddenList.length) {
77616 var tooltipBehavior = uiTooltip().placement('top').title(function () {
77617 return hiddenList.join('<br/>');
77619 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
77621 })).call(tooltipBehavior).on('click', function (d3_event) {
77622 tooltipBehavior.hide();
77623 d3_event.preventDefault(); // open the Map Data pane
77625 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
77629 selection.classed('hide', !hiddenList.length);
77632 return function (selection) {
77634 context.features().on('change.feature_info', function () {
77640 function uiFlash(context) {
77643 var _duration = 2000;
77644 var _iconName = '#iD-icon-no';
77645 var _iconClass = 'disabled';
77650 _flashTimer.stop();
77653 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
77654 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
77655 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
77657 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
77658 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
77659 iconEnter.append('circle').attr('r', 9);
77660 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
77661 contentEnter.append('div').attr('class', 'flash-text'); // Update
77663 content = content.merge(contentEnter);
77664 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
77665 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
77666 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
77667 _flashTimer = d3_timeout(function () {
77668 _flashTimer = null;
77669 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
77670 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
77675 flash.duration = function (_) {
77676 if (!arguments.length) return _duration;
77681 flash.label = function (_) {
77682 if (!arguments.length) return _label;
77687 flash.iconName = function (_) {
77688 if (!arguments.length) return _iconName;
77693 flash.iconClass = function (_) {
77694 if (!arguments.length) return _iconClass;
77702 function uiFullScreen(context) {
77703 var element = context.container().node(); // var button = d3_select(null);
77705 function getFullScreenFn() {
77706 if (element.requestFullscreen) {
77707 return element.requestFullscreen;
77708 } else if (element.msRequestFullscreen) {
77709 return element.msRequestFullscreen;
77710 } else if (element.mozRequestFullScreen) {
77711 return element.mozRequestFullScreen;
77712 } else if (element.webkitRequestFullscreen) {
77713 return element.webkitRequestFullscreen;
77717 function getExitFullScreenFn() {
77718 if (document.exitFullscreen) {
77719 return document.exitFullscreen;
77720 } else if (document.msExitFullscreen) {
77721 return document.msExitFullscreen;
77722 } else if (document.mozCancelFullScreen) {
77723 return document.mozCancelFullScreen;
77724 } else if (document.webkitExitFullscreen) {
77725 return document.webkitExitFullscreen;
77729 function isFullScreen() {
77730 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
77733 function isSupported() {
77734 return !!getFullScreenFn();
77737 function fullScreen(d3_event) {
77738 d3_event.preventDefault();
77740 if (!isFullScreen()) {
77741 // button.classed('active', true);
77742 getFullScreenFn().apply(element);
77744 // button.classed('active', false);
77745 getExitFullScreenFn().apply(document);
77749 return function () {
77751 if (!isSupported()) return; // button = selection.append('button')
77752 // .attr('title', t('full_screen'))
77753 // .on('click', fullScreen)
77755 // button.append('span')
77756 // .attr('class', 'icon full-screen');
77758 var detected = utilDetect();
77759 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
77760 context.keybinding().on(keys, fullScreen);
77764 function uiGeolocate(context) {
77765 var _geolocationOptions = {
77766 // prioritize speed and power usage over precision
77767 enableHighAccuracy: false,
77768 // don't hang indefinitely getting the location
77769 timeout: 6000 // 6sec
77773 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
77775 var _layer = context.layers().layer('geolocate');
77783 var _button = select(null);
77786 if (context.inIntro()) return;
77788 if (!_layer.enabled() && !_locating.isShown()) {
77789 // This timeout ensures that we still call finish() even if
77790 // the user declines to share their location in Firefox
77791 _timeoutID = setTimeout(error, 10000
77794 context.container().call(_locating); // get the latest position even if we already have one
77796 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
77800 _layer.enabled(null, false);
77802 updateButtonState();
77806 function zoomTo() {
77807 context.enter(modeBrowse(context));
77808 var map = context.map();
77810 _layer.enabled(_position, true);
77812 updateButtonState();
77813 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
77816 function success(geolocation) {
77817 _position = geolocation;
77818 var coords = _position.coords;
77819 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
77826 // use the position from a previous call if we have one
77829 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
77835 function finish() {
77836 _locating.close(); // unblock ui
77840 clearTimeout(_timeoutID);
77843 _timeoutID = undefined;
77846 function updateButtonState() {
77847 _button.classed('active', _layer.enabled());
77850 return function (selection) {
77851 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
77852 _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')]));
77853 context.keybinding().on(_t('geolocate.key'), click);
77857 function uiPanelBackground(context) {
77858 var background = context.background();
77859 var _currSourceName = null;
77860 var _metadata = {};
77861 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
77863 var debouncedRedraw = debounce(redraw, 250);
77865 function redraw(selection) {
77866 var source = background.baseLayerSource();
77867 if (!source) return;
77868 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
77869 var sourceLabel = source.label();
77871 if (_currSourceName !== sourceLabel) {
77872 _currSourceName = sourceLabel;
77876 selection.html('');
77877 var list = selection.append('ul').attr('class', 'background-info');
77878 list.append('li').html(_currSourceName);
77880 _metadataKeys.forEach(function (k) {
77881 // DigitalGlobe vintage is available in raster layers for now.
77882 if (isDG && k === 'vintage') return;
77883 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]);
77886 debouncedGetMetadata(selection);
77887 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
77888 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
77889 d3_event.preventDefault();
77890 context.setDebug('tile', !context.getDebug('tile'));
77891 selection.call(redraw);
77895 var key = source.id + '-vintage';
77896 var sourceVintage = context.background().findSource(key);
77897 var showsVintage = context.background().showsLayer(sourceVintage);
77898 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
77899 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
77900 d3_event.preventDefault();
77901 context.background().toggleOverlayLayer(sourceVintage);
77902 selection.call(redraw);
77904 } // disable if necessary
77907 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
77908 if (source.id !== layerId) {
77909 var key = layerId + '-vintage';
77910 var sourceVintage = context.background().findSource(key);
77912 if (context.background().showsLayer(sourceVintage)) {
77913 context.background().toggleOverlayLayer(sourceVintage);
77919 var debouncedGetMetadata = debounce(getMetadata, 250);
77921 function getMetadata(selection) {
77922 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
77924 if (tile.empty()) return;
77925 var sourceName = _currSourceName;
77926 var d = tile.datum();
77927 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
77928 var center = context.map().center(); // update zoom
77930 _metadata.zoom = String(zoom);
77931 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
77932 if (!d || !d.length >= 3) return;
77933 background.baseLayerSource().getMetadata(center, d, function (err, result) {
77934 if (err || _currSourceName !== sourceName) return; // update vintage
77936 var vintage = result.vintage;
77937 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
77938 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
77940 _metadataKeys.forEach(function (k) {
77941 if (k === 'zoom' || k === 'vintage') return; // done already
77943 var val = result[k];
77944 _metadata[k] = val;
77945 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
77950 var panel = function panel(selection) {
77951 selection.call(redraw);
77952 context.map().on('drawn.info-background', function () {
77953 selection.call(debouncedRedraw);
77954 }).on('move.info-background', function () {
77955 selection.call(debouncedGetMetadata);
77959 panel.off = function () {
77960 context.map().on('drawn.info-background', null).on('move.info-background', null);
77963 panel.id = 'background';
77964 panel.label = _t.html('info_panels.background.title');
77965 panel.key = _t('info_panels.background.key');
77969 function uiPanelHistory(context) {
77972 function displayTimestamp(timestamp) {
77973 if (!timestamp) return _t('info_panels.history.unknown');
77982 var d = new Date(timestamp);
77983 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
77984 return d.toLocaleString(_mainLocalizer.localeCode(), options);
77987 function displayUser(selection, userName) {
77989 selection.append('span').html(_t.html('info_panels.history.unknown'));
77993 selection.append('span').attr('class', 'user-name').html(userName);
77994 var links = selection.append('div').attr('class', 'links');
77997 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
78000 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
78003 function displayChangeset(selection, changeset) {
78005 selection.append('span').html(_t.html('info_panels.history.unknown'));
78009 selection.append('span').attr('class', 'changeset-id').html(changeset);
78010 var links = selection.append('div').attr('class', 'links');
78013 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
78016 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
78017 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
78020 function redraw(selection) {
78021 var selectedNoteID = context.selectedNoteID();
78022 osm = context.connection();
78023 var selected, note, entity;
78025 if (selectedNoteID && osm) {
78027 selected = [_t('note.note') + ' ' + selectedNoteID];
78028 note = osm.getNote(selectedNoteID);
78030 // selected 1..n entities
78031 selected = context.selectedIDs().filter(function (e) {
78032 return context.hasEntity(e);
78035 if (selected.length) {
78036 entity = context.entity(selected[0]);
78040 var singular = selected.length === 1 ? selected[0] : null;
78041 selection.html('');
78042 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
78045 if (!singular) return;
78048 selection.call(redrawEntity, entity);
78050 selection.call(redrawNote, note);
78054 function redrawNote(selection, note) {
78055 if (!note || note.isNew()) {
78056 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
78060 var list = selection.append('ul');
78061 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
78063 if (note.comments.length) {
78064 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
78065 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
78069 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'));
78073 function redrawEntity(selection, entity) {
78074 if (!entity || entity.isNew()) {
78075 selection.append('div').html(_t.html('info_panels.history.no_history'));
78079 var links = selection.append('div').attr('class', 'links');
78082 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');
78085 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');
78086 var list = selection.append('ul');
78087 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
78088 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
78089 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
78090 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
78093 var panel = function panel(selection) {
78094 selection.call(redraw);
78095 context.map().on('drawn.info-history', function () {
78096 selection.call(redraw);
78098 context.on('enter.info-history', function () {
78099 selection.call(redraw);
78103 panel.off = function () {
78104 context.map().on('drawn.info-history', null);
78105 context.on('enter.info-history', null);
78108 panel.id = 'history';
78109 panel.label = _t.html('info_panels.history.title');
78110 panel.key = _t('info_panels.history.key');
78114 var OSM_PRECISION = 7;
78116 * Returns a localized representation of the given length measurement.
78118 * @param {Number} m area in meters
78119 * @param {Boolean} isImperial true for U.S. customary units; false for metric
78122 function displayLength(m, isImperial) {
78123 var d = m * (isImperial ? 3.28084 : 1);
78136 unit = 'kilometers';
78142 return _t('units.' + unit, {
78143 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
78144 maximumSignificantDigits: 4
78149 * Returns a localized representation of the given area measurement.
78151 * @param {Number} m2 area in square meters
78152 * @param {Boolean} isImperial true for U.S. customary units; false for metric
78155 function displayArea(m2, isImperial) {
78156 var locale = _mainLocalizer.localeCode();
78157 var d = m2 * (isImperial ? 10.7639111056 : 1);
78163 if (d >= 6969600) {
78164 // > 0.25mi² show mi²
78166 unit1 = 'square_miles';
78169 unit1 = 'square_feet';
78172 if (d > 4356 && d < 43560000) {
78173 // 0.1 - 1000 acres
78179 // > 0.25km² show km²
78181 unit1 = 'square_kilometers';
78184 unit1 = 'square_meters';
78187 if (d > 1000 && d < 10000000) {
78188 // 0.1 - 1000 hectares
78190 unit2 = 'hectares';
78194 area = _t('units.' + unit1, {
78195 quantity: d1.toLocaleString(locale, {
78196 maximumSignificantDigits: 4
78201 return _t('units.area_pair', {
78203 area2: _t('units.' + unit2, {
78204 quantity: d2.toLocaleString(locale, {
78205 maximumSignificantDigits: 2
78214 function wrap$2(x, min, max) {
78216 return ((x - min) % d + d) % d + min;
78219 function clamp$1(x, min, max) {
78220 return Math.max(min, Math.min(x, max));
78223 function displayCoordinate(deg, pos, neg) {
78224 var locale = _mainLocalizer.localeCode();
78225 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
78226 var sec = (min - Math.floor(min)) * 60;
78227 var displayDegrees = _t('units.arcdegrees', {
78228 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
78230 var displayCoordinate;
78232 if (Math.floor(sec) > 0) {
78233 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78234 quantity: Math.floor(min).toLocaleString(locale)
78235 }) + _t('units.arcseconds', {
78236 quantity: Math.round(sec).toLocaleString(locale)
78238 } else if (Math.floor(min) > 0) {
78239 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78240 quantity: Math.round(min).toLocaleString(locale)
78243 displayCoordinate = _t('units.arcdegrees', {
78244 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
78249 return displayCoordinate;
78251 return _t('units.coordinate', {
78252 coordinate: displayCoordinate,
78253 direction: _t('units.' + (deg > 0 ? pos : neg))
78258 * Returns given coordinate pair in degree-minute-second format.
78260 * @param {Array<Number>} coord longitude and latitude
78264 function dmsCoordinatePair(coord) {
78265 return _t('units.coordinate_pair', {
78266 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
78267 longitude: displayCoordinate(wrap$2(coord[0], -180, 180), 'east', 'west')
78271 * Returns the given coordinate pair in decimal format.
78272 * note: unlocalized to avoid comma ambiguity - see #4765
78274 * @param {Array<Number>} coord longitude and latitude
78277 function decimalCoordinatePair(coord) {
78278 return _t('units.coordinate_pair', {
78279 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
78280 longitude: wrap$2(coord[0], -180, 180).toFixed(OSM_PRECISION)
78284 function uiPanelLocation(context) {
78285 var currLocation = '';
78287 function redraw(selection) {
78288 selection.html('');
78289 var list = selection.append('ul'); // Mouse coordinates
78291 var coord = context.map().mouseCoordinates();
78293 if (coord.some(isNaN)) {
78294 coord = context.map().center();
78297 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
78299 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
78300 debouncedGetLocation(selection, coord);
78303 var debouncedGetLocation = debounce(getLocation, 250);
78305 function getLocation(selection, coord) {
78306 if (!services.geocoder) {
78307 currLocation = _t('info_panels.location.unknown_location');
78308 selection.selectAll('.location-info').html(currLocation);
78310 services.geocoder.reverse(coord, function (err, result) {
78311 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
78312 selection.selectAll('.location-info').html(currLocation);
78317 var panel = function panel(selection) {
78318 selection.call(redraw);
78319 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
78320 selection.call(redraw);
78324 panel.off = function () {
78325 context.surface().on('.info-location', null);
78328 panel.id = 'location';
78329 panel.label = _t.html('info_panels.location.title');
78330 panel.key = _t('info_panels.location.key');
78334 function uiPanelMeasurement(context) {
78335 function radiansToMeters(r) {
78336 // using WGS84 authalic radius (6371007.1809 m)
78337 return r * 6371007.1809;
78340 function steradiansToSqmeters(r) {
78341 // http://gis.stackexchange.com/a/124857/40446
78342 return r / (4 * Math.PI) * 510065621724000;
78345 function toLineString(feature) {
78346 if (feature.type === 'LineString') return feature;
78348 type: 'LineString',
78352 if (feature.type === 'Polygon') {
78353 result.coordinates = feature.coordinates[0];
78354 } else if (feature.type === 'MultiPolygon') {
78355 result.coordinates = feature.coordinates[0][0];
78361 var _isImperial = !_mainLocalizer.usesMetric();
78363 function redraw(selection) {
78364 var graph = context.graph();
78365 var selectedNoteID = context.selectedNoteID();
78366 var osm = services.osm;
78367 var localeCode = _mainLocalizer.localeCode();
78369 var center, location, centroid;
78370 var closed, geometry;
78371 var totalNodeCount,
78376 if (selectedNoteID && osm) {
78378 var note = osm.getNote(selectedNoteID);
78379 heading = _t('note.note') + ' ' + selectedNoteID;
78380 location = note.loc;
78383 // selected 1..n entities
78384 var selectedIDs = context.selectedIDs().filter(function (id) {
78385 return context.hasEntity(id);
78387 var selected = selectedIDs.map(function (id) {
78388 return context.entity(id);
78390 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
78394 if (selected.length) {
78395 var extent = geoExtent();
78397 for (var i in selected) {
78398 var entity = selected[i];
78400 extent._extend(entity.extent(graph));
78402 geometry = entity.geometry(graph);
78404 if (geometry === 'line' || geometry === 'area') {
78405 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
78406 var feature = entity.asGeoJSON(graph);
78407 length += radiansToMeters(d3_geoLength(toLineString(feature))); // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
78409 centroid = d3_geoCentroid(geojsonRewind(Object.assign({}, feature), true));
78412 area += steradiansToSqmeters(entity.area(graph));
78417 if (selected.length > 1) {
78423 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
78424 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
78427 if (selected.length === 1 && selected[0].type === 'node') {
78428 location = selected[0].loc;
78430 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
78433 if (!location && !centroid) {
78434 center = extent.center();
78439 selection.html('');
78442 selection.append('h4').attr('class', 'measurement-heading').html(heading);
78445 var list = selection.append('ul');
78449 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
78452 if (totalNodeCount) {
78453 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
78457 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
78461 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
78464 if (typeof distance === 'number') {
78465 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
78469 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
78470 coordItem.append('span').html(dmsCoordinatePair(location));
78471 coordItem.append('span').html(decimalCoordinatePair(location));
78475 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
78476 coordItem.append('span').html(dmsCoordinatePair(centroid));
78477 coordItem.append('span').html(decimalCoordinatePair(centroid));
78481 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
78482 coordItem.append('span').html(dmsCoordinatePair(center));
78483 coordItem.append('span').html(decimalCoordinatePair(center));
78486 if (length || area || typeof distance === 'number') {
78487 var toggle = _isImperial ? 'imperial' : 'metric';
78488 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
78489 d3_event.preventDefault();
78490 _isImperial = !_isImperial;
78491 selection.call(redraw);
78496 var panel = function panel(selection) {
78497 selection.call(redraw);
78498 context.map().on('drawn.info-measurement', function () {
78499 selection.call(redraw);
78501 context.on('enter.info-measurement', function () {
78502 selection.call(redraw);
78506 panel.off = function () {
78507 context.map().on('drawn.info-measurement', null);
78508 context.on('enter.info-measurement', null);
78511 panel.id = 'measurement';
78512 panel.label = _t.html('info_panels.measurement.title');
78513 panel.key = _t('info_panels.measurement.key');
78517 var uiInfoPanels = {
78518 background: uiPanelBackground,
78519 history: uiPanelHistory,
78520 location: uiPanelLocation,
78521 measurement: uiPanelMeasurement
78524 function uiInfo(context) {
78525 var ids = Object.keys(uiInfoPanels);
78526 var wasActive = ['measurement'];
78528 var active = {}; // create panels
78530 ids.forEach(function (k) {
78532 panels[k] = uiInfoPanels[k](context);
78537 function info(selection) {
78538 function redraw() {
78539 var activeids = ids.filter(function (k) {
78542 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
78545 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
78546 select(this).call(panels[d].off).remove();
78548 var enter = containers.enter().append('div').attr('class', function (d) {
78549 return 'fillD2 panel-container panel-container-' + d;
78551 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
78552 var title = enter.append('div').attr('class', 'panel-title fillD2');
78553 title.append('h3').html(function (d) {
78554 return panels[d].label;
78556 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
78557 d3_event.stopImmediatePropagation();
78558 d3_event.preventDefault();
78560 }).call(svgIcon('#iD-icon-close'));
78561 enter.append('div').attr('class', function (d) {
78562 return 'panel-content panel-content-' + d;
78563 }); // redraw the panels
78565 infoPanels.selectAll('.panel-content').each(function (d) {
78566 select(this).call(panels[d]);
78570 info.toggle = function (which) {
78571 var activeids = ids.filter(function (k) {
78577 active[which] = !active[which];
78579 if (activeids.length === 1 && activeids[0] === which) {
78580 // none active anymore
78581 wasActive = [which];
78584 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
78587 if (activeids.length) {
78588 wasActive = activeids;
78589 activeids.forEach(function (k) {
78593 wasActive.forEach(function (k) {
78602 var infoPanels = selection.selectAll('.info-panels').data([0]);
78603 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
78605 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
78606 d3_event.stopImmediatePropagation();
78607 d3_event.preventDefault();
78610 ids.forEach(function (k) {
78611 var key = _t('info_panels.' + k + '.key', {
78615 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
78616 d3_event.stopImmediatePropagation();
78617 d3_event.preventDefault();
78626 function pointBox(loc, context) {
78627 var rect = context.surfaceRect();
78628 var point = context.curtainProjection(loc);
78630 left: point[0] + rect.left - 40,
78631 top: point[1] + rect.top - 60,
78636 function pad(locOrBox, padding, context) {
78639 if (locOrBox instanceof Array) {
78640 var rect = context.surfaceRect();
78641 var point = context.curtainProjection(locOrBox);
78643 left: point[0] + rect.left,
78644 top: point[1] + rect.top
78651 left: box.left - padding,
78652 top: box.top - padding,
78653 width: (box.width || 0) + 2 * padding,
78654 height: (box.width || 0) + 2 * padding
78657 function icon(name, svgklass, useklass) {
78658 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
78660 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
78661 // label replacements suitable for tutorials and documentation. Optionally supplemented
78662 // with custom `replacements`
78664 function helpHtml(id, replacements) {
78665 // only load these the first time
78666 if (!helpStringReplacements) helpStringReplacements = {
78667 // insert icons corresponding to various UI elements
78668 point_icon: icon('#iD-icon-point', 'inline'),
78669 line_icon: icon('#iD-icon-line', 'inline'),
78670 area_icon: icon('#iD-icon-area', 'inline'),
78671 note_icon: icon('#iD-icon-note', 'inline add-note'),
78672 plus: icon('#iD-icon-plus', 'inline'),
78673 minus: icon('#iD-icon-minus', 'inline'),
78674 layers_icon: icon('#iD-icon-layers', 'inline'),
78675 data_icon: icon('#iD-icon-data', 'inline'),
78676 inspect: icon('#iD-icon-inspect', 'inline'),
78677 help_icon: icon('#iD-icon-help', 'inline'),
78678 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
78679 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
78680 save_icon: icon('#iD-icon-save', 'inline'),
78682 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
78683 continue_icon: icon('#iD-operation-continue', 'inline operation'),
78684 copy_icon: icon('#iD-operation-copy', 'inline operation'),
78685 delete_icon: icon('#iD-operation-delete', 'inline operation'),
78686 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
78687 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
78688 extract_icon: icon('#iD-operation-extract', 'inline operation'),
78689 merge_icon: icon('#iD-operation-merge', 'inline operation'),
78690 move_icon: icon('#iD-operation-move', 'inline operation'),
78691 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
78692 paste_icon: icon('#iD-operation-paste', 'inline operation'),
78693 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
78694 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
78695 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
78696 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
78697 split_icon: icon('#iD-operation-split', 'inline operation'),
78698 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
78699 // interaction icons
78700 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
78701 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
78702 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
78703 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
78704 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
78705 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
78706 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
78707 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
78708 // insert keys; may be localized and platform-dependent
78709 shift: uiCmd.display('⇧'),
78710 alt: uiCmd.display('⌥'),
78711 "return": uiCmd.display('↵'),
78712 esc: _t.html('shortcuts.key.esc'),
78713 space: _t.html('shortcuts.key.space'),
78714 add_note_key: _t.html('modes.add_note.key'),
78715 help_key: _t.html('help.key'),
78716 shortcuts_key: _t.html('shortcuts.toggle.key'),
78717 // reference localized UI labels directly so that they'll always match
78718 save: _t.html('save.title'),
78719 undo: _t.html('undo.title'),
78720 redo: _t.html('redo.title'),
78721 upload: _t.html('commit.save'),
78722 point: _t.html('modes.add_point.title'),
78723 line: _t.html('modes.add_line.title'),
78724 area: _t.html('modes.add_area.title'),
78725 note: _t.html('modes.add_note.label'),
78726 circularize: _t.html('operations.circularize.title'),
78727 "continue": _t.html('operations.continue.title'),
78728 copy: _t.html('operations.copy.title'),
78729 "delete": _t.html('operations.delete.title'),
78730 disconnect: _t.html('operations.disconnect.title'),
78731 downgrade: _t.html('operations.downgrade.title'),
78732 extract: _t.html('operations.extract.title'),
78733 merge: _t.html('operations.merge.title'),
78734 move: _t.html('operations.move.title'),
78735 orthogonalize: _t.html('operations.orthogonalize.title'),
78736 paste: _t.html('operations.paste.title'),
78737 reflect_long: _t.html('operations.reflect.title.long'),
78738 reflect_short: _t.html('operations.reflect.title.short'),
78739 reverse: _t.html('operations.reverse.title'),
78740 rotate: _t.html('operations.rotate.title'),
78741 split: _t.html('operations.split.title'),
78742 straighten: _t.html('operations.straighten.title'),
78743 map_data: _t.html('map_data.title'),
78744 osm_notes: _t.html('map_data.layers.notes.title'),
78745 fields: _t.html('inspector.fields'),
78746 tags: _t.html('inspector.tags'),
78747 relations: _t.html('inspector.relations'),
78748 new_relation: _t.html('inspector.new_relation'),
78749 turn_restrictions: _t.html('presets.fields.restrictions.label'),
78750 background_settings: _t.html('background.description'),
78751 imagery_offset: _t.html('background.fix_misalignment'),
78752 start_the_walkthrough: _t.html('splash.walkthrough'),
78753 help: _t.html('help.title'),
78754 ok: _t.html('intro.ok')
78758 if (replacements) {
78759 reps = Object.assign(replacements, helpStringReplacements);
78761 reps = helpStringReplacements;
78764 return _t.html(id, reps) // use keyboard key styling for shortcuts
78765 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
78768 function slugify(text) {
78769 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
78770 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
78771 .replace(/\-\-+/g, '-') // Replace multiple - with single -
78772 .replace(/^-+/, '') // Trim - from start of text
78773 .replace(/-+$/, ''); // Trim - from end of text
78774 } // console warning for missing walkthrough names
78777 var missingStrings = {};
78779 function checkKey(key, text) {
78781 "default": undefined
78782 }) === undefined) {
78783 if (missingStrings.hasOwnProperty(key)) return; // warn once
78785 missingStrings[key] = text;
78786 var missing = key + ': ' + text;
78787 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
78791 function localize(obj) {
78792 var key; // Assign name if entity has one..
78794 var name = obj.tags && obj.tags.name;
78797 key = 'intro.graph.name.' + slugify(name);
78798 obj.tags.name = _t(key, {
78801 checkKey(key, name);
78802 } // Assign street name if entity has one..
78805 var street = obj.tags && obj.tags['addr:street'];
78808 key = 'intro.graph.name.' + slugify(street);
78809 obj.tags['addr:street'] = _t(key, {
78812 checkKey(key, street); // Add address details common across walkthrough..
78814 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
78815 addrTags.forEach(function (k) {
78816 var key = 'intro.graph.' + k;
78817 var tag = 'addr:' + k;
78818 var val = obj.tags && obj.tags[tag];
78819 var str = _t(key, {
78824 if (str.match(/^<.*>$/) !== null) {
78825 delete obj.tags[tag];
78827 obj.tags[tag] = str;
78834 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
78836 function isMostlySquare(points) {
78837 // note: uses 15 here instead of the 12 from actionOrthogonalize because
78838 // actionOrthogonalize can actually straighten some larger angles as it iterates
78839 var threshold = 15; // degrees within right or straight
78841 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
78843 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
78845 for (var i = 0; i < points.length; i++) {
78846 var a = points[(i - 1 + points.length) % points.length];
78847 var origin = points[i];
78848 var b = points[(i + 1) % points.length];
78849 var dotp = geoVecNormalizedDot(a, b, origin);
78850 var mag = Math.abs(dotp);
78852 if (mag > lowerBound && mag < upperBound) {
78859 function selectMenuItem(context, operation) {
78860 return context.container().select('.edit-menu .edit-menu-item-' + operation);
78862 function transitionTime(point1, point2) {
78863 var distance = geoSphericalDistance(point1, point2);
78864 if (distance === 0) return 0;else if (distance < 80) return 500;else return 1000;
78867 function uiCurtain(containerNode) {
78868 var surface = select(null),
78869 tooltip = select(null),
78870 darkness = select(null);
78872 function curtain(selection) {
78873 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
78874 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
78875 select(window).on('resize.curtain', resize);
78876 tooltip = selection.append('div').attr('class', 'tooltip');
78877 tooltip.append('div').attr('class', 'popover-arrow');
78878 tooltip.append('div').attr('class', 'popover-inner');
78881 function resize() {
78882 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
78883 curtain.cut(darkness.datum());
78887 * Reveal cuts the curtain to highlight the given box,
78888 * and shows a tooltip with instructions next to the box.
78890 * @param {String|ClientRect} [box] box used to cut the curtain
78891 * @param {String} [text] text for a tooltip
78892 * @param {Object} [options]
78893 * @param {string} [options.tooltipClass] optional class to add to the tooltip
78894 * @param {integer} [options.duration] transition time in milliseconds
78895 * @param {string} [options.buttonText] if set, create a button with this text label
78896 * @param {function} [options.buttonCallback] if set, the callback for the button
78897 * @param {function} [options.padding] extra margin in px to put around bbox
78898 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
78902 curtain.reveal = function (box, html, options) {
78903 options = options || {};
78905 if (typeof box === 'string') {
78906 box = select(box).node();
78909 if (box && box.getBoundingClientRect) {
78910 box = copyBox(box.getBoundingClientRect());
78911 var containerRect = containerNode.getBoundingClientRect();
78912 box.top -= containerRect.top;
78913 box.left -= containerRect.left;
78916 if (box && options.padding) {
78917 box.top -= options.padding;
78918 box.left -= options.padding;
78919 box.bottom += options.padding;
78920 box.right += options.padding;
78921 box.height += options.padding * 2;
78922 box.width += options.padding * 2;
78927 if (options.tooltipBox) {
78928 tooltipBox = options.tooltipBox;
78930 if (typeof tooltipBox === 'string') {
78931 tooltipBox = select(tooltipBox).node();
78934 if (tooltipBox && tooltipBox.getBoundingClientRect) {
78935 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
78941 if (tooltipBox && html) {
78942 if (html.indexOf('**') !== -1) {
78943 if (html.indexOf('<span') === 0) {
78944 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
78946 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
78947 } // pseudo markdown bold text for the instruction section..
78950 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
78953 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
78955 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
78957 if (options.buttonText && options.buttonCallback) {
78958 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
78961 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
78962 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
78964 if (options.buttonText && options.buttonCallback) {
78965 var button = tooltip.selectAll('.button-section .button.action');
78966 button.on('click', function (d3_event) {
78967 d3_event.preventDefault();
78968 options.buttonCallback();
78972 var tip = copyBox(tooltip.node().getBoundingClientRect()),
78973 w = containerNode.clientWidth,
78974 h = containerNode.clientHeight,
78975 tooltipWidth = 200,
78978 pos; // hack: this will have bottom placement,
78979 // so need to reserve extra space for the tooltip illustration.
78981 if (options.tooltipClass === 'intro-mouse') {
78983 } // trim box dimensions to just the portion that fits in the container..
78986 if (tooltipBox.top + tooltipBox.height > h) {
78987 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
78990 if (tooltipBox.left + tooltipBox.width > w) {
78991 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
78992 } // determine tooltip placement..
78995 if (tooltipBox.top + tooltipBox.height < 100) {
78996 // tooltip below box..
78998 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
78999 } else if (tooltipBox.top > h - 140) {
79000 // tooltip above box..
79002 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
79004 // tooltip to the side of the tooltipBox..
79005 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
79007 if (_mainLocalizer.textDirection() === 'rtl') {
79008 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
79010 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
79013 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
79016 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
79018 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
79021 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
79026 if (options.duration !== 0 || !tooltip.classed(side)) {
79027 tooltip.call(uiToggle(true));
79030 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
79031 // (doesn't affect the placement of the popover-arrow)
79035 if (side === 'left' || side === 'right') {
79037 shiftY = 60 - pos[1];
79038 } else if (pos[1] + tip.height > h - 100) {
79039 shiftY = h - pos[1] - tip.height - 100;
79043 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
79045 tooltip.classed('in', false).call(uiToggle(false));
79048 curtain.cut(box, options.duration);
79052 curtain.cut = function (datum, duration) {
79053 darkness.datum(datum).interrupt();
79056 if (duration === 0) {
79057 selection = darkness;
79059 selection = darkness.transition().duration(duration || 600).ease(linear$1);
79062 selection.attr('d', function (d) {
79063 var containerWidth = containerNode.clientWidth;
79064 var containerHeight = containerNode.clientHeight;
79065 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
79066 if (!d) return string;
79067 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';
79071 curtain.remove = function () {
79074 select(window).on('resize.curtain', null);
79075 }; // ClientRects are immutable, so copy them to an object,
79076 // in case we need to trim the height/width.
79079 function copyBox(src) {
79083 bottom: src.bottom,
79093 function uiIntroWelcome(context, reveal) {
79094 var dispatch$1 = dispatch('done');
79096 title: 'intro.welcome.title'
79099 function welcome() {
79100 context.map().centerZoom([-85.63591, 41.94285], 19);
79101 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
79102 buttonText: _t.html('intro.ok'),
79103 buttonCallback: practice
79107 function practice() {
79108 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
79109 buttonText: _t.html('intro.ok'),
79110 buttonCallback: words
79115 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
79116 buttonText: _t.html('intro.ok'),
79117 buttonCallback: chapters
79121 function chapters() {
79122 dispatch$1.call('done');
79123 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
79124 next: _t('intro.navigation.title')
79128 chapter.enter = function () {
79132 chapter.exit = function () {
79133 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
79136 chapter.restart = function () {
79141 return utilRebind(chapter, dispatch$1, 'on');
79144 function uiIntroNavigation(context, reveal) {
79145 var dispatch$1 = dispatch('done');
79147 var hallId = 'n2061';
79148 var townHall = [-85.63591, 41.94285];
79149 var springStreetId = 'w397';
79150 var springStreetEndId = 'n1834';
79151 var springStreet = [-85.63582, 41.94255];
79152 var onewayField = _mainPresetIndex.field('oneway');
79153 var maxspeedField = _mainPresetIndex.field('maxspeed');
79155 title: 'intro.navigation.title'
79158 function timeout(f, t) {
79159 timeouts.push(window.setTimeout(f, t));
79162 function eventCancel(d3_event) {
79163 d3_event.stopPropagation();
79164 d3_event.preventDefault();
79167 function isTownHallSelected() {
79168 var ids = context.selectedIDs();
79169 return ids.length === 1 && ids[0] === hallId;
79172 function dragMap() {
79173 context.enter(modeBrowse(context));
79174 context.history().reset('initial');
79175 var msec = transitionTime(townHall, context.map().center());
79178 reveal(null, null, {
79183 context.map().centerZoomEase(townHall, 19, msec);
79184 timeout(function () {
79185 var centerStart = context.map().center();
79186 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
79187 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
79188 reveal('.surface', dragString);
79189 context.map().on('drawn.intro', function () {
79190 reveal('.surface', dragString, {
79194 context.map().on('move.intro', function () {
79195 var centerNow = context.map().center();
79197 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
79198 context.map().on('move.intro', null);
79199 timeout(function () {
79200 continueTo(zoomMap);
79206 function continueTo(nextStep) {
79207 context.map().on('move.intro drawn.intro', null);
79212 function zoomMap() {
79213 var zoomStart = context.map().zoom();
79214 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
79215 var zoomString = helpHtml('intro.navigation.' + textId);
79216 reveal('.surface', zoomString);
79217 context.map().on('drawn.intro', function () {
79218 reveal('.surface', zoomString, {
79222 context.map().on('move.intro', function () {
79223 if (context.map().zoom() !== zoomStart) {
79224 context.map().on('move.intro', null);
79225 timeout(function () {
79226 continueTo(features);
79231 function continueTo(nextStep) {
79232 context.map().on('move.intro drawn.intro', null);
79237 function features() {
79238 var onClick = function onClick() {
79239 continueTo(pointsLinesAreas);
79242 reveal('.surface', helpHtml('intro.navigation.features'), {
79243 buttonText: _t.html('intro.ok'),
79244 buttonCallback: onClick
79246 context.map().on('drawn.intro', function () {
79247 reveal('.surface', helpHtml('intro.navigation.features'), {
79249 buttonText: _t.html('intro.ok'),
79250 buttonCallback: onClick
79254 function continueTo(nextStep) {
79255 context.map().on('drawn.intro', null);
79260 function pointsLinesAreas() {
79261 var onClick = function onClick() {
79262 continueTo(nodesWays);
79265 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79266 buttonText: _t.html('intro.ok'),
79267 buttonCallback: onClick
79269 context.map().on('drawn.intro', function () {
79270 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79272 buttonText: _t.html('intro.ok'),
79273 buttonCallback: onClick
79277 function continueTo(nextStep) {
79278 context.map().on('drawn.intro', null);
79283 function nodesWays() {
79284 var onClick = function onClick() {
79285 continueTo(clickTownHall);
79288 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79289 buttonText: _t.html('intro.ok'),
79290 buttonCallback: onClick
79292 context.map().on('drawn.intro', function () {
79293 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79295 buttonText: _t.html('intro.ok'),
79296 buttonCallback: onClick
79300 function continueTo(nextStep) {
79301 context.map().on('drawn.intro', null);
79306 function clickTownHall() {
79307 context.enter(modeBrowse(context));
79308 context.history().reset('initial');
79309 var entity = context.hasEntity(hallId);
79310 if (!entity) return;
79311 reveal(null, null, {
79314 context.map().centerZoomEase(entity.loc, 19, 500);
79315 timeout(function () {
79316 var entity = context.hasEntity(hallId);
79317 if (!entity) return;
79318 var box = pointBox(entity.loc, context);
79319 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
79320 reveal(box, helpHtml('intro.navigation.' + textId));
79321 context.map().on('move.intro drawn.intro', function () {
79322 var entity = context.hasEntity(hallId);
79323 if (!entity) return;
79324 var box = pointBox(entity.loc, context);
79325 reveal(box, helpHtml('intro.navigation.' + textId), {
79329 context.on('enter.intro', function () {
79330 if (isTownHallSelected()) continueTo(selectedTownHall);
79332 }, 550); // after centerZoomEase
79334 context.history().on('change.intro', function () {
79335 if (!context.hasEntity(hallId)) {
79336 continueTo(clickTownHall);
79340 function continueTo(nextStep) {
79341 context.on('enter.intro', null);
79342 context.map().on('move.intro drawn.intro', null);
79343 context.history().on('change.intro', null);
79348 function selectedTownHall() {
79349 if (!isTownHallSelected()) return clickTownHall();
79350 var entity = context.hasEntity(hallId);
79351 if (!entity) return clickTownHall();
79352 var box = pointBox(entity.loc, context);
79354 var onClick = function onClick() {
79355 continueTo(editorTownHall);
79358 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79359 buttonText: _t.html('intro.ok'),
79360 buttonCallback: onClick
79362 context.map().on('move.intro drawn.intro', function () {
79363 var entity = context.hasEntity(hallId);
79364 if (!entity) return;
79365 var box = pointBox(entity.loc, context);
79366 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79368 buttonText: _t.html('intro.ok'),
79369 buttonCallback: onClick
79372 context.history().on('change.intro', function () {
79373 if (!context.hasEntity(hallId)) {
79374 continueTo(clickTownHall);
79378 function continueTo(nextStep) {
79379 context.map().on('move.intro drawn.intro', null);
79380 context.history().on('change.intro', null);
79385 function editorTownHall() {
79386 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
79388 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79390 var onClick = function onClick() {
79391 continueTo(presetTownHall);
79394 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
79395 buttonText: _t.html('intro.ok'),
79396 buttonCallback: onClick
79398 context.on('exit.intro', function () {
79399 continueTo(clickTownHall);
79401 context.history().on('change.intro', function () {
79402 if (!context.hasEntity(hallId)) {
79403 continueTo(clickTownHall);
79407 function continueTo(nextStep) {
79408 context.on('exit.intro', null);
79409 context.history().on('change.intro', null);
79410 context.container().select('.inspector-wrap').on('wheel.intro', null);
79415 function presetTownHall() {
79416 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79418 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79420 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
79422 var entity = context.entity(context.selectedIDs()[0]);
79423 var preset = _mainPresetIndex.match(entity, context.graph());
79425 var onClick = function onClick() {
79426 continueTo(fieldsTownHall);
79429 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
79430 preset: preset.name()
79432 buttonText: _t.html('intro.ok'),
79433 buttonCallback: onClick
79435 context.on('exit.intro', function () {
79436 continueTo(clickTownHall);
79438 context.history().on('change.intro', function () {
79439 if (!context.hasEntity(hallId)) {
79440 continueTo(clickTownHall);
79444 function continueTo(nextStep) {
79445 context.on('exit.intro', null);
79446 context.history().on('change.intro', null);
79447 context.container().select('.inspector-wrap').on('wheel.intro', null);
79452 function fieldsTownHall() {
79453 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79455 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79457 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79459 var onClick = function onClick() {
79460 continueTo(closeTownHall);
79463 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
79464 buttonText: _t.html('intro.ok'),
79465 buttonCallback: onClick
79467 context.on('exit.intro', function () {
79468 continueTo(clickTownHall);
79470 context.history().on('change.intro', function () {
79471 if (!context.hasEntity(hallId)) {
79472 continueTo(clickTownHall);
79476 function continueTo(nextStep) {
79477 context.on('exit.intro', null);
79478 context.history().on('change.intro', null);
79479 context.container().select('.inspector-wrap').on('wheel.intro', null);
79484 function closeTownHall() {
79485 if (!isTownHallSelected()) return clickTownHall();
79486 var selector = '.entity-editor-pane button.close svg use';
79487 var href = select(selector).attr('href') || '#iD-icon-close';
79488 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
79489 button: icon(href, 'inline')
79491 context.on('exit.intro', function () {
79492 continueTo(searchStreet);
79494 context.history().on('change.intro', function () {
79495 // update the close icon in the tooltip if the user edits something.
79496 var selector = '.entity-editor-pane button.close svg use';
79497 var href = select(selector).attr('href') || '#iD-icon-close';
79498 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
79499 button: icon(href, 'inline')
79505 function continueTo(nextStep) {
79506 context.on('exit.intro', null);
79507 context.history().on('change.intro', null);
79512 function searchStreet() {
79513 context.enter(modeBrowse(context));
79514 context.history().reset('initial'); // ensure spring street exists
79516 var msec = transitionTime(springStreet, context.map().center());
79519 reveal(null, null, {
79524 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
79526 timeout(function () {
79527 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
79528 name: _t('intro.graph.name.spring-street')
79530 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
79534 function checkSearchResult() {
79535 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
79537 var firstName = first.select('.entity-name');
79538 var name = _t('intro.graph.name.spring-street');
79540 if (!firstName.empty() && firstName.html() === name) {
79541 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
79546 context.on('exit.intro', function () {
79547 continueTo(selectedStreet);
79549 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79552 function continueTo(nextStep) {
79553 context.on('exit.intro', null);
79554 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
79559 function selectedStreet() {
79560 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79561 return searchStreet();
79564 var onClick = function onClick() {
79565 continueTo(editorStreet);
79568 var entity = context.entity(springStreetEndId);
79569 var box = pointBox(entity.loc, context);
79571 reveal(box, helpHtml('intro.navigation.selected_street', {
79572 name: _t('intro.graph.name.spring-street')
79575 buttonText: _t.html('intro.ok'),
79576 buttonCallback: onClick
79578 timeout(function () {
79579 context.map().on('move.intro drawn.intro', function () {
79580 var entity = context.hasEntity(springStreetEndId);
79581 if (!entity) return;
79582 var box = pointBox(entity.loc, context);
79584 reveal(box, helpHtml('intro.navigation.selected_street', {
79585 name: _t('intro.graph.name.spring-street')
79588 buttonText: _t.html('intro.ok'),
79589 buttonCallback: onClick
79592 }, 600); // after reveal.
79594 context.on('enter.intro', function (mode) {
79595 if (!context.hasEntity(springStreetId)) {
79596 return continueTo(searchStreet);
79599 var ids = context.selectedIDs();
79601 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
79602 // keep Spring Street selected..
79603 context.enter(modeSelect(context, [springStreetId]));
79606 context.history().on('change.intro', function () {
79607 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79608 timeout(function () {
79609 continueTo(searchStreet);
79610 }, 300); // after any transition (e.g. if user deleted intersection)
79614 function continueTo(nextStep) {
79615 context.map().on('move.intro drawn.intro', null);
79616 context.on('enter.intro', null);
79617 context.history().on('change.intro', null);
79622 function editorStreet() {
79623 var selector = '.entity-editor-pane button.close svg use';
79624 var href = select(selector).attr('href') || '#iD-icon-close';
79625 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79626 button: icon(href, 'inline'),
79627 field1: onewayField.label(),
79628 field2: maxspeedField.label()
79630 context.on('exit.intro', function () {
79633 context.history().on('change.intro', function () {
79634 // update the close icon in the tooltip if the user edits something.
79635 var selector = '.entity-editor-pane button.close svg use';
79636 var href = select(selector).attr('href') || '#iD-icon-close';
79637 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79638 button: icon(href, 'inline'),
79639 field1: onewayField.label(),
79640 field2: maxspeedField.label()
79646 function continueTo(nextStep) {
79647 context.on('exit.intro', null);
79648 context.history().on('change.intro', null);
79654 dispatch$1.call('done');
79655 reveal('.ideditor', helpHtml('intro.navigation.play', {
79656 next: _t('intro.points.title')
79658 tooltipBox: '.intro-nav-wrap .chapter-point',
79659 buttonText: _t.html('intro.ok'),
79660 buttonCallback: function buttonCallback() {
79661 reveal('.ideditor');
79666 chapter.enter = function () {
79670 chapter.exit = function () {
79671 timeouts.forEach(window.clearTimeout);
79672 context.on('enter.intro exit.intro', null);
79673 context.map().on('move.intro drawn.intro', null);
79674 context.history().on('change.intro', null);
79675 context.container().select('.inspector-wrap').on('wheel.intro', null);
79676 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
79679 chapter.restart = function () {
79684 return utilRebind(chapter, dispatch$1, 'on');
79687 function uiIntroPoint(context, reveal) {
79688 var dispatch$1 = dispatch('done');
79690 var intersection = [-85.63279, 41.94394];
79691 var building = [-85.632422, 41.944045];
79692 var cafePreset = _mainPresetIndex.item('amenity/cafe');
79693 var _pointID = null;
79695 title: 'intro.points.title'
79698 function timeout(f, t) {
79699 timeouts.push(window.setTimeout(f, t));
79702 function eventCancel(d3_event) {
79703 d3_event.stopPropagation();
79704 d3_event.preventDefault();
79707 function addPoint() {
79708 context.enter(modeBrowse(context));
79709 context.history().reset('initial');
79710 var msec = transitionTime(intersection, context.map().center());
79713 reveal(null, null, {
79718 context.map().centerZoomEase(intersection, 19, msec);
79719 timeout(function () {
79720 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
79722 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
79723 context.on('enter.intro', function (mode) {
79724 if (mode.id !== 'add-point') return;
79725 continueTo(placePoint);
79729 function continueTo(nextStep) {
79730 context.on('enter.intro', null);
79735 function placePoint() {
79736 if (context.mode().id !== 'add-point') {
79737 return chapter.restart();
79740 var pointBox = pad(building, 150, context);
79741 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
79742 reveal(pointBox, helpHtml('intro.points.' + textId));
79743 context.map().on('move.intro drawn.intro', function () {
79744 pointBox = pad(building, 150, context);
79745 reveal(pointBox, helpHtml('intro.points.' + textId), {
79749 context.on('enter.intro', function (mode) {
79750 if (mode.id !== 'select') return chapter.restart();
79751 _pointID = context.mode().selectedIDs()[0];
79752 continueTo(searchPreset);
79755 function continueTo(nextStep) {
79756 context.map().on('move.intro drawn.intro', null);
79757 context.on('enter.intro', null);
79762 function searchPreset() {
79763 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79765 } // disallow scrolling
79768 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79769 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79770 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79771 preset: cafePreset.name()
79773 context.on('enter.intro', function (mode) {
79774 if (!_pointID || !context.hasEntity(_pointID)) {
79775 return continueTo(addPoint);
79778 var ids = context.selectedIDs();
79780 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
79781 // keep the user's point selected..
79782 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
79784 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79785 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79786 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79787 preset: cafePreset.name()
79789 context.history().on('change.intro', null);
79793 function checkPresetSearch() {
79794 var first = context.container().select('.preset-list-item:first-child');
79796 if (first.classed('preset-amenity-cafe')) {
79797 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79798 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
79799 preset: cafePreset.name()
79803 context.history().on('change.intro', function () {
79804 continueTo(aboutFeatureEditor);
79809 function continueTo(nextStep) {
79810 context.on('enter.intro', null);
79811 context.history().on('change.intro', null);
79812 context.container().select('.inspector-wrap').on('wheel.intro', null);
79813 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79818 function aboutFeatureEditor() {
79819 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79823 timeout(function () {
79824 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
79825 tooltipClass: 'intro-points-describe',
79826 buttonText: _t.html('intro.ok'),
79827 buttonCallback: function buttonCallback() {
79828 continueTo(addName);
79832 context.on('exit.intro', function () {
79833 // if user leaves select mode here, just continue with the tutorial.
79834 continueTo(reselectPoint);
79837 function continueTo(nextStep) {
79838 context.on('exit.intro', null);
79843 function addName() {
79844 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79846 } // reset pane, in case user happened to change it..
79849 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79850 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
79851 timeout(function () {
79852 // It's possible for the user to add a name in a previous step..
79853 // If so, don't tell them to add the name in this step.
79854 // Give them an OK button instead.
79855 var entity = context.entity(_pointID);
79857 if (entity.tags.name) {
79858 var tooltip = reveal('.entity-editor-pane', addNameString, {
79859 tooltipClass: 'intro-points-describe',
79860 buttonText: _t.html('intro.ok'),
79861 buttonCallback: function buttonCallback() {
79862 continueTo(addCloseEditor);
79865 tooltip.select('.instruction').style('display', 'none');
79867 reveal('.entity-editor-pane', addNameString, {
79868 tooltipClass: 'intro-points-describe'
79872 context.history().on('change.intro', function () {
79873 continueTo(addCloseEditor);
79875 context.on('exit.intro', function () {
79876 // if user leaves select mode here, just continue with the tutorial.
79877 continueTo(reselectPoint);
79880 function continueTo(nextStep) {
79881 context.on('exit.intro', null);
79882 context.history().on('change.intro', null);
79887 function addCloseEditor() {
79888 // reset pane, in case user happened to change it..
79889 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79890 var selector = '.entity-editor-pane button.close svg use';
79891 var href = select(selector).attr('href') || '#iD-icon-close';
79892 context.on('exit.intro', function () {
79893 continueTo(reselectPoint);
79895 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
79896 button: icon(href, 'inline')
79899 function continueTo(nextStep) {
79900 context.on('exit.intro', null);
79905 function reselectPoint() {
79906 if (!_pointID) return chapter.restart();
79907 var entity = context.hasEntity(_pointID);
79908 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
79910 var oldPreset = _mainPresetIndex.match(entity, context.graph());
79911 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
79912 context.enter(modeBrowse(context));
79913 var msec = transitionTime(entity.loc, context.map().center());
79916 reveal(null, null, {
79921 context.map().centerEase(entity.loc, msec);
79922 timeout(function () {
79923 var box = pointBox(entity.loc, context);
79924 reveal(box, helpHtml('intro.points.reselect'), {
79927 timeout(function () {
79928 context.map().on('move.intro drawn.intro', function () {
79929 var entity = context.hasEntity(_pointID);
79930 if (!entity) return chapter.restart();
79931 var box = pointBox(entity.loc, context);
79932 reveal(box, helpHtml('intro.points.reselect'), {
79936 }, 600); // after reveal..
79938 context.on('enter.intro', function (mode) {
79939 if (mode.id !== 'select') return;
79940 continueTo(updatePoint);
79944 function continueTo(nextStep) {
79945 context.map().on('move.intro drawn.intro', null);
79946 context.on('enter.intro', null);
79951 function updatePoint() {
79952 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79953 return continueTo(reselectPoint);
79954 } // reset pane, in case user happened to untag the point..
79957 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79958 context.on('exit.intro', function () {
79959 continueTo(reselectPoint);
79961 context.history().on('change.intro', function () {
79962 continueTo(updateCloseEditor);
79964 timeout(function () {
79965 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
79966 tooltipClass: 'intro-points-describe'
79970 function continueTo(nextStep) {
79971 context.on('exit.intro', null);
79972 context.history().on('change.intro', null);
79977 function updateCloseEditor() {
79978 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79979 return continueTo(reselectPoint);
79980 } // reset pane, in case user happened to change it..
79983 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79984 context.on('exit.intro', function () {
79985 continueTo(rightClickPoint);
79987 timeout(function () {
79988 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
79989 button: icon('#iD-icon-close', 'inline')
79993 function continueTo(nextStep) {
79994 context.on('exit.intro', null);
79999 function rightClickPoint() {
80000 if (!_pointID) return chapter.restart();
80001 var entity = context.hasEntity(_pointID);
80002 if (!entity) return chapter.restart();
80003 context.enter(modeBrowse(context));
80004 var box = pointBox(entity.loc, context);
80005 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
80006 reveal(box, helpHtml('intro.points.' + textId), {
80009 timeout(function () {
80010 context.map().on('move.intro', function () {
80011 var entity = context.hasEntity(_pointID);
80012 if (!entity) return chapter.restart();
80013 var box = pointBox(entity.loc, context);
80014 reveal(box, helpHtml('intro.points.' + textId), {
80018 }, 600); // after reveal
80020 context.on('enter.intro', function (mode) {
80021 if (mode.id !== 'select') return;
80022 var ids = context.selectedIDs();
80023 if (ids.length !== 1 || ids[0] !== _pointID) return;
80024 timeout(function () {
80025 var node = selectMenuItem(context, 'delete').node();
80027 continueTo(enterDelete);
80028 }, 50); // after menu visible
80031 function continueTo(nextStep) {
80032 context.on('enter.intro', null);
80033 context.map().on('move.intro', null);
80038 function enterDelete() {
80039 if (!_pointID) return chapter.restart();
80040 var entity = context.hasEntity(_pointID);
80041 if (!entity) return chapter.restart();
80042 var node = selectMenuItem(context, 'delete').node();
80045 return continueTo(rightClickPoint);
80048 reveal('.edit-menu', helpHtml('intro.points.delete'), {
80051 timeout(function () {
80052 context.map().on('move.intro', function () {
80053 reveal('.edit-menu', helpHtml('intro.points.delete'), {
80058 }, 300); // after menu visible
80060 context.on('exit.intro', function () {
80061 if (!_pointID) return chapter.restart();
80062 var entity = context.hasEntity(_pointID);
80063 if (entity) return continueTo(rightClickPoint); // point still exists
80065 context.history().on('change.intro', function (changed) {
80066 if (changed.deleted().length) {
80071 function continueTo(nextStep) {
80072 context.map().on('move.intro', null);
80073 context.history().on('change.intro', null);
80074 context.on('exit.intro', null);
80080 context.history().on('change.intro', function () {
80083 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
80085 function continueTo(nextStep) {
80086 context.history().on('change.intro', null);
80092 dispatch$1.call('done');
80093 reveal('.ideditor', helpHtml('intro.points.play', {
80094 next: _t('intro.areas.title')
80096 tooltipBox: '.intro-nav-wrap .chapter-area',
80097 buttonText: _t.html('intro.ok'),
80098 buttonCallback: function buttonCallback() {
80099 reveal('.ideditor');
80104 chapter.enter = function () {
80108 chapter.exit = function () {
80109 timeouts.forEach(window.clearTimeout);
80110 context.on('enter.intro exit.intro', null);
80111 context.map().on('move.intro drawn.intro', null);
80112 context.history().on('change.intro', null);
80113 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80114 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80117 chapter.restart = function () {
80122 return utilRebind(chapter, dispatch$1, 'on');
80125 function uiIntroArea(context, reveal) {
80126 var dispatch$1 = dispatch('done');
80127 var playground = [-85.63552, 41.94159];
80128 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
80129 var nameField = _mainPresetIndex.field('name');
80130 var descriptionField = _mainPresetIndex.field('description');
80136 title: 'intro.areas.title'
80139 function timeout(f, t) {
80140 timeouts.push(window.setTimeout(f, t));
80143 function eventCancel(d3_event) {
80144 d3_event.stopPropagation();
80145 d3_event.preventDefault();
80148 function revealPlayground(center, text, options) {
80149 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
80150 var box = pad(center, padding, context);
80151 reveal(box, text, options);
80154 function addArea() {
80155 context.enter(modeBrowse(context));
80156 context.history().reset('initial');
80158 var msec = transitionTime(playground, context.map().center());
80161 reveal(null, null, {
80166 context.map().centerZoomEase(playground, 19, msec);
80167 timeout(function () {
80168 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
80169 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
80170 context.on('enter.intro', function (mode) {
80171 if (mode.id !== 'add-area') return;
80172 continueTo(startPlayground);
80176 function continueTo(nextStep) {
80177 context.on('enter.intro', null);
80182 function startPlayground() {
80183 if (context.mode().id !== 'add-area') {
80184 return chapter.restart();
80188 context.map().zoomEase(19.5, 500);
80189 timeout(function () {
80190 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
80191 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
80192 revealPlayground(playground, startDrawString, {
80195 timeout(function () {
80196 context.map().on('move.intro drawn.intro', function () {
80197 revealPlayground(playground, startDrawString, {
80201 context.on('enter.intro', function (mode) {
80202 if (mode.id !== 'draw-area') return chapter.restart();
80203 continueTo(continuePlayground);
80205 }, 250); // after reveal
80206 }, 550); // after easing
80208 function continueTo(nextStep) {
80209 context.map().on('move.intro drawn.intro', null);
80210 context.on('enter.intro', null);
80215 function continuePlayground() {
80216 if (context.mode().id !== 'draw-area') {
80217 return chapter.restart();
80221 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80224 timeout(function () {
80225 context.map().on('move.intro drawn.intro', function () {
80226 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80230 }, 250); // after reveal
80232 context.on('enter.intro', function (mode) {
80233 if (mode.id === 'draw-area') {
80234 var entity = context.hasEntity(context.selectedIDs()[0]);
80236 if (entity && entity.nodes.length >= 6) {
80237 return continueTo(finishPlayground);
80241 } else if (mode.id === 'select') {
80242 _areaID = context.selectedIDs()[0];
80243 return continueTo(searchPresets);
80245 return chapter.restart();
80249 function continueTo(nextStep) {
80250 context.map().on('move.intro drawn.intro', null);
80251 context.on('enter.intro', null);
80256 function finishPlayground() {
80257 if (context.mode().id !== 'draw-area') {
80258 return chapter.restart();
80262 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
80263 revealPlayground(playground, finishString, {
80266 timeout(function () {
80267 context.map().on('move.intro drawn.intro', function () {
80268 revealPlayground(playground, finishString, {
80272 }, 250); // after reveal
80274 context.on('enter.intro', function (mode) {
80275 if (mode.id === 'draw-area') {
80277 } else if (mode.id === 'select') {
80278 _areaID = context.selectedIDs()[0];
80279 return continueTo(searchPresets);
80281 return chapter.restart();
80285 function continueTo(nextStep) {
80286 context.map().on('move.intro drawn.intro', null);
80287 context.on('enter.intro', null);
80292 function searchPresets() {
80293 if (!_areaID || !context.hasEntity(_areaID)) {
80297 var ids = context.selectedIDs();
80299 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80300 context.enter(modeSelect(context, [_areaID]));
80301 } // disallow scrolling
80304 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80305 timeout(function () {
80306 // reset pane, in case user somehow happened to change it..
80307 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80308 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80309 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80310 preset: playgroundPreset.name()
80312 }, 400); // after preset list pane visible..
80314 context.on('enter.intro', function (mode) {
80315 if (!_areaID || !context.hasEntity(_areaID)) {
80316 return continueTo(addArea);
80319 var ids = context.selectedIDs();
80321 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
80322 // keep the user's area selected..
80323 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
80325 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
80327 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80328 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80329 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80330 preset: playgroundPreset.name()
80332 context.history().on('change.intro', null);
80336 function checkPresetSearch() {
80337 var first = context.container().select('.preset-list-item:first-child');
80339 if (first.classed('preset-leisure-playground')) {
80340 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
80341 preset: playgroundPreset.name()
80345 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
80346 context.history().on('change.intro', function () {
80347 continueTo(clickAddField);
80352 function continueTo(nextStep) {
80353 context.container().select('.inspector-wrap').on('wheel.intro', null);
80354 context.on('enter.intro', null);
80355 context.history().on('change.intro', null);
80356 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80361 function clickAddField() {
80362 if (!_areaID || !context.hasEntity(_areaID)) {
80366 var ids = context.selectedIDs();
80368 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80369 return searchPresets();
80372 if (!context.container().select('.form-field-description').empty()) {
80373 return continueTo(describePlayground);
80374 } // disallow scrolling
80377 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80378 timeout(function () {
80379 // reset pane, in case user somehow happened to change it..
80380 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
80381 // If they did this already, just continue to next step.
80383 var entity = context.entity(_areaID);
80385 if (entity.tags.description) {
80386 return continueTo(play);
80387 } // scroll "Add field" into view
80390 var box = context.container().select('.more-fields').node().getBoundingClientRect();
80392 if (box.top > 300) {
80393 var pane = context.container().select('.entity-editor-pane .inspector-body');
80394 var start = pane.node().scrollTop;
80395 var end = start + (box.top - 300);
80396 pane.transition().duration(250).tween('scroll.inspector', function () {
80398 var i = d3_interpolateNumber(start, end);
80399 return function (t) {
80400 node.scrollTop = i(t);
80405 timeout(function () {
80406 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
80407 name: nameField.label(),
80408 description: descriptionField.label()
80412 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
80413 // Watch for the combobox to appear...
80415 watcher = window.setInterval(function () {
80416 if (!context.container().select('div.combobox').empty()) {
80417 window.clearInterval(watcher);
80418 continueTo(chooseDescriptionField);
80422 }, 300); // after "Add Field" visible
80423 }, 400); // after editor pane visible
80425 context.on('exit.intro', function () {
80426 return continueTo(searchPresets);
80429 function continueTo(nextStep) {
80430 context.container().select('.inspector-wrap').on('wheel.intro', null);
80431 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80432 context.on('exit.intro', null);
80437 function chooseDescriptionField() {
80438 if (!_areaID || !context.hasEntity(_areaID)) {
80442 var ids = context.selectedIDs();
80444 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80445 return searchPresets();
80448 if (!context.container().select('.form-field-description').empty()) {
80449 return continueTo(describePlayground);
80450 } // Make sure combobox is ready..
80453 if (context.container().select('div.combobox').empty()) {
80454 return continueTo(clickAddField);
80455 } // Watch for the combobox to go away..
80459 watcher = window.setInterval(function () {
80460 if (context.container().select('div.combobox').empty()) {
80461 window.clearInterval(watcher);
80462 timeout(function () {
80463 if (context.container().select('.form-field-description').empty()) {
80464 continueTo(retryChooseDescription);
80466 continueTo(describePlayground);
80468 }, 300); // after description field added.
80471 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
80472 field: descriptionField.label()
80476 context.on('exit.intro', function () {
80477 return continueTo(searchPresets);
80480 function continueTo(nextStep) {
80481 if (watcher) window.clearInterval(watcher);
80482 context.on('exit.intro', null);
80487 function describePlayground() {
80488 if (!_areaID || !context.hasEntity(_areaID)) {
80492 var ids = context.selectedIDs();
80494 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80495 return searchPresets();
80496 } // reset pane, in case user happened to change it..
80499 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80501 if (context.container().select('.form-field-description').empty()) {
80502 return continueTo(retryChooseDescription);
80505 context.on('exit.intro', function () {
80508 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
80509 button: icon('#iD-icon-close', 'inline')
80514 function continueTo(nextStep) {
80515 context.on('exit.intro', null);
80520 function retryChooseDescription() {
80521 if (!_areaID || !context.hasEntity(_areaID)) {
80525 var ids = context.selectedIDs();
80527 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80528 return searchPresets();
80529 } // reset pane, in case user happened to change it..
80532 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80533 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
80534 field: descriptionField.label()
80536 buttonText: _t.html('intro.ok'),
80537 buttonCallback: function buttonCallback() {
80538 continueTo(clickAddField);
80541 context.on('exit.intro', function () {
80542 return continueTo(searchPresets);
80545 function continueTo(nextStep) {
80546 context.on('exit.intro', null);
80552 dispatch$1.call('done');
80553 reveal('.ideditor', helpHtml('intro.areas.play', {
80554 next: _t('intro.lines.title')
80556 tooltipBox: '.intro-nav-wrap .chapter-line',
80557 buttonText: _t.html('intro.ok'),
80558 buttonCallback: function buttonCallback() {
80559 reveal('.ideditor');
80564 chapter.enter = function () {
80568 chapter.exit = function () {
80569 timeouts.forEach(window.clearTimeout);
80570 context.on('enter.intro exit.intro', null);
80571 context.map().on('move.intro drawn.intro', null);
80572 context.history().on('change.intro', null);
80573 context.container().select('.inspector-wrap').on('wheel.intro', null);
80574 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80575 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80578 chapter.restart = function () {
80583 return utilRebind(chapter, dispatch$1, 'on');
80586 function uiIntroLine(context, reveal) {
80587 var dispatch$1 = dispatch('done');
80589 var _tulipRoadID = null;
80590 var flowerRoadID = 'w646';
80591 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
80592 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
80593 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
80594 var roadCategory = _mainPresetIndex.item('category-road_minor');
80595 var residentialPreset = _mainPresetIndex.item('highway/residential');
80596 var woodRoadID = 'w525';
80597 var woodRoadEndID = 'n2862';
80598 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
80599 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
80600 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
80601 var washingtonStreetID = 'w522';
80602 var twelfthAvenueID = 'w1';
80603 var eleventhAvenueEndID = 'n3550';
80604 var twelfthAvenueEndID = 'n5';
80605 var _washingtonSegmentID = null;
80606 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
80607 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
80608 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
80609 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
80611 title: 'intro.lines.title'
80614 function timeout(f, t) {
80615 timeouts.push(window.setTimeout(f, t));
80618 function eventCancel(d3_event) {
80619 d3_event.stopPropagation();
80620 d3_event.preventDefault();
80623 function addLine() {
80624 context.enter(modeBrowse(context));
80625 context.history().reset('initial');
80626 var msec = transitionTime(tulipRoadStart, context.map().center());
80629 reveal(null, null, {
80634 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
80635 timeout(function () {
80636 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
80637 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
80638 context.on('enter.intro', function (mode) {
80639 if (mode.id !== 'add-line') return;
80640 continueTo(startLine);
80644 function continueTo(nextStep) {
80645 context.on('enter.intro', null);
80650 function startLine() {
80651 if (context.mode().id !== 'add-line') return chapter.restart();
80652 _tulipRoadID = null;
80653 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
80654 var box = pad(tulipRoadStart, padding, context);
80655 box.height = box.height + 100;
80656 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
80657 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
80658 reveal(box, startLineString);
80659 context.map().on('move.intro drawn.intro', function () {
80660 padding = 70 * Math.pow(2, context.map().zoom() - 18);
80661 box = pad(tulipRoadStart, padding, context);
80662 box.height = box.height + 100;
80663 reveal(box, startLineString, {
80667 context.on('enter.intro', function (mode) {
80668 if (mode.id !== 'draw-line') return chapter.restart();
80669 continueTo(drawLine);
80672 function continueTo(nextStep) {
80673 context.map().on('move.intro drawn.intro', null);
80674 context.on('enter.intro', null);
80679 function drawLine() {
80680 if (context.mode().id !== 'draw-line') return chapter.restart();
80681 _tulipRoadID = context.mode().selectedIDs()[0];
80682 context.map().centerEase(tulipRoadMidpoint, 500);
80683 timeout(function () {
80684 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80685 var box = pad(tulipRoadMidpoint, padding, context);
80686 box.height = box.height * 2;
80687 reveal(box, helpHtml('intro.lines.intersect', {
80688 name: _t('intro.graph.name.flower-street')
80690 context.map().on('move.intro drawn.intro', function () {
80691 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80692 box = pad(tulipRoadMidpoint, padding, context);
80693 box.height = box.height * 2;
80694 reveal(box, helpHtml('intro.lines.intersect', {
80695 name: _t('intro.graph.name.flower-street')
80700 }, 550); // after easing..
80702 context.history().on('change.intro', function () {
80703 if (isLineConnected()) {
80704 continueTo(continueLine);
80707 context.on('enter.intro', function (mode) {
80708 if (mode.id === 'draw-line') {
80710 } else if (mode.id === 'select') {
80711 continueTo(retryIntersect);
80714 return chapter.restart();
80718 function continueTo(nextStep) {
80719 context.map().on('move.intro drawn.intro', null);
80720 context.history().on('change.intro', null);
80721 context.on('enter.intro', null);
80726 function isLineConnected() {
80727 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80729 if (!entity) return false;
80730 var drawNodes = context.graph().childNodes(entity);
80731 return drawNodes.some(function (node) {
80732 return context.graph().parentWays(node).some(function (parent) {
80733 return parent.id === flowerRoadID;
80738 function retryIntersect() {
80739 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
80740 var box = pad(tulipRoadIntersection, 80, context);
80741 reveal(box, helpHtml('intro.lines.retry_intersect', {
80742 name: _t('intro.graph.name.flower-street')
80744 timeout(chapter.restart, 3000);
80747 function continueLine() {
80748 if (context.mode().id !== 'draw-line') return chapter.restart();
80750 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80752 if (!entity) return chapter.restart();
80753 context.map().centerEase(tulipRoadIntersection, 500);
80754 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
80755 reveal('.surface', continueLineText);
80756 context.on('enter.intro', function (mode) {
80757 if (mode.id === 'draw-line') return;else if (mode.id === 'select') return continueTo(chooseCategoryRoad);else return chapter.restart();
80760 function continueTo(nextStep) {
80761 context.on('enter.intro', null);
80766 function chooseCategoryRoad() {
80767 if (context.mode().id !== 'select') return chapter.restart();
80768 context.on('exit.intro', function () {
80769 return chapter.restart();
80771 var button = context.container().select('.preset-category-road_minor .preset-list-button');
80772 if (button.empty()) return chapter.restart(); // disallow scrolling
80774 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80775 timeout(function () {
80776 // reset pane, in case user somehow happened to change it..
80777 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80778 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
80779 category: roadCategory.name()
80781 button.on('click.intro', function () {
80782 continueTo(choosePresetResidential);
80784 }, 400); // after editor pane visible
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 choosePresetResidential() {
80795 if (context.mode().id !== 'select') return chapter.restart();
80796 context.on('exit.intro', function () {
80797 return chapter.restart();
80799 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
80800 if (subgrid.empty()) return chapter.restart();
80801 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
80802 continueTo(retryPresetResidential);
80804 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
80805 continueTo(nameRoad);
80807 timeout(function () {
80808 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
80809 preset: residentialPreset.name()
80811 tooltipBox: '.preset-highway-residential .preset-list-button',
80816 function continueTo(nextStep) {
80817 context.container().select('.preset-list-button').on('click.intro', null);
80818 context.on('exit.intro', null);
80821 } // selected wrong road type
80824 function retryPresetResidential() {
80825 if (context.mode().id !== 'select') return chapter.restart();
80826 context.on('exit.intro', function () {
80827 return chapter.restart();
80828 }); // disallow scrolling
80830 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80831 timeout(function () {
80832 var button = context.container().select('.entity-editor-pane .preset-list-button');
80833 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
80834 preset: residentialPreset.name()
80836 button.on('click.intro', function () {
80837 continueTo(chooseCategoryRoad);
80841 function continueTo(nextStep) {
80842 context.container().select('.inspector-wrap').on('wheel.intro', null);
80843 context.container().select('.preset-list-button').on('click.intro', null);
80844 context.on('exit.intro', null);
80849 function nameRoad() {
80850 context.on('exit.intro', function () {
80851 continueTo(didNameRoad);
80853 timeout(function () {
80854 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
80855 button: icon('#iD-icon-close', 'inline')
80857 tooltipClass: 'intro-lines-name_road'
80861 function continueTo(nextStep) {
80862 context.on('exit.intro', null);
80867 function didNameRoad() {
80868 context.history().checkpoint('doneAddLine');
80869 timeout(function () {
80870 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
80871 buttonText: _t.html('intro.ok'),
80872 buttonCallback: function buttonCallback() {
80873 continueTo(updateLine);
80878 function continueTo(nextStep) {
80883 function updateLine() {
80884 context.history().reset('doneAddLine');
80886 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80887 return chapter.restart();
80890 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
80893 reveal(null, null, {
80898 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
80899 timeout(function () {
80900 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80901 var box = pad(woodRoadDragMidpoint, padding, context);
80903 var advance = function advance() {
80904 continueTo(addNode);
80907 reveal(box, helpHtml('intro.lines.update_line'), {
80908 buttonText: _t.html('intro.ok'),
80909 buttonCallback: advance
80911 context.map().on('move.intro drawn.intro', function () {
80912 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80913 var box = pad(woodRoadDragMidpoint, padding, context);
80914 reveal(box, helpHtml('intro.lines.update_line'), {
80916 buttonText: _t.html('intro.ok'),
80917 buttonCallback: advance
80922 function continueTo(nextStep) {
80923 context.map().on('move.intro drawn.intro', null);
80928 function addNode() {
80929 context.history().reset('doneAddLine');
80931 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80932 return chapter.restart();
80935 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80936 var box = pad(woodRoadAddNode, padding, context);
80937 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80938 reveal(box, addNodeString);
80939 context.map().on('move.intro drawn.intro', function () {
80940 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80941 var box = pad(woodRoadAddNode, padding, context);
80942 reveal(box, addNodeString, {
80946 context.history().on('change.intro', function (changed) {
80947 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80948 return continueTo(updateLine);
80951 if (changed.created().length === 1) {
80952 timeout(function () {
80953 continueTo(startDragEndpoint);
80957 context.on('enter.intro', function (mode) {
80958 if (mode.id !== 'select') {
80959 continueTo(updateLine);
80963 function continueTo(nextStep) {
80964 context.map().on('move.intro drawn.intro', null);
80965 context.history().on('change.intro', null);
80966 context.on('enter.intro', null);
80971 function startDragEndpoint() {
80972 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80973 return continueTo(updateLine);
80976 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80977 var box = pad(woodRoadDragEndpoint, padding, context);
80978 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
80979 reveal(box, startDragString);
80980 context.map().on('move.intro drawn.intro', function () {
80981 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80982 return continueTo(updateLine);
80985 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80986 var box = pad(woodRoadDragEndpoint, padding, context);
80987 reveal(box, startDragString, {
80990 var entity = context.entity(woodRoadEndID);
80992 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
80993 continueTo(finishDragEndpoint);
80997 function continueTo(nextStep) {
80998 context.map().on('move.intro drawn.intro', null);
81003 function finishDragEndpoint() {
81004 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81005 return continueTo(updateLine);
81008 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81009 var box = pad(woodRoadDragEndpoint, padding, context);
81010 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
81011 reveal(box, finishDragString);
81012 context.map().on('move.intro drawn.intro', function () {
81013 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81014 return continueTo(updateLine);
81017 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81018 var box = pad(woodRoadDragEndpoint, padding, context);
81019 reveal(box, finishDragString, {
81022 var entity = context.entity(woodRoadEndID);
81024 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
81025 continueTo(startDragEndpoint);
81028 context.on('enter.intro', function () {
81029 continueTo(startDragMidpoint);
81032 function continueTo(nextStep) {
81033 context.map().on('move.intro drawn.intro', null);
81034 context.on('enter.intro', null);
81039 function startDragMidpoint() {
81040 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81041 return continueTo(updateLine);
81044 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
81045 context.enter(modeSelect(context, [woodRoadID]));
81048 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
81049 var box = pad(woodRoadDragMidpoint, padding, context);
81050 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
81051 context.map().on('move.intro drawn.intro', function () {
81052 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81053 return continueTo(updateLine);
81056 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
81057 var box = pad(woodRoadDragMidpoint, padding, context);
81058 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
81062 context.history().on('change.intro', function (changed) {
81063 if (changed.created().length === 1) {
81064 continueTo(continueDragMidpoint);
81067 context.on('enter.intro', function (mode) {
81068 if (mode.id !== 'select') {
81069 // keep Wood Road selected so midpoint triangles are drawn..
81070 context.enter(modeSelect(context, [woodRoadID]));
81074 function continueTo(nextStep) {
81075 context.map().on('move.intro drawn.intro', null);
81076 context.history().on('change.intro', null);
81077 context.on('enter.intro', null);
81082 function continueDragMidpoint() {
81083 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81084 return continueTo(updateLine);
81087 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81088 var box = pad(woodRoadDragEndpoint, padding, context);
81091 var advance = function advance() {
81092 context.history().checkpoint('doneUpdateLine');
81093 continueTo(deleteLines);
81096 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
81097 buttonText: _t.html('intro.ok'),
81098 buttonCallback: advance
81100 context.map().on('move.intro drawn.intro', function () {
81101 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81102 return continueTo(updateLine);
81105 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81106 var box = pad(woodRoadDragEndpoint, padding, context);
81108 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
81110 buttonText: _t.html('intro.ok'),
81111 buttonCallback: advance
81115 function continueTo(nextStep) {
81116 context.map().on('move.intro drawn.intro', null);
81121 function deleteLines() {
81122 context.history().reset('doneUpdateLine');
81123 context.enter(modeBrowse(context));
81125 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81126 return chapter.restart();
81129 var msec = transitionTime(deleteLinesLoc, context.map().center());
81132 reveal(null, null, {
81137 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
81138 timeout(function () {
81139 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81140 var box = pad(deleteLinesLoc, padding, context);
81144 var advance = function advance() {
81145 continueTo(rightClickIntersection);
81148 reveal(box, helpHtml('intro.lines.delete_lines', {
81149 street: _t('intro.graph.name.12th-avenue')
81151 buttonText: _t.html('intro.ok'),
81152 buttonCallback: advance
81154 context.map().on('move.intro drawn.intro', function () {
81155 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81156 var box = pad(deleteLinesLoc, padding, context);
81159 reveal(box, helpHtml('intro.lines.delete_lines', {
81160 street: _t('intro.graph.name.12th-avenue')
81163 buttonText: _t.html('intro.ok'),
81164 buttonCallback: advance
81167 context.history().on('change.intro', function () {
81168 timeout(function () {
81169 continueTo(deleteLines);
81170 }, 500); // after any transition (e.g. if user deleted intersection)
81174 function continueTo(nextStep) {
81175 context.map().on('move.intro drawn.intro', null);
81176 context.history().on('change.intro', null);
81181 function rightClickIntersection() {
81182 context.history().reset('doneUpdateLine');
81183 context.enter(modeBrowse(context));
81184 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81185 var rightClickString = helpHtml('intro.lines.split_street', {
81186 street1: _t('intro.graph.name.11th-avenue'),
81187 street2: _t('intro.graph.name.washington-street')
81188 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
81189 timeout(function () {
81190 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81191 var box = pad(eleventhAvenueEnd, padding, context);
81192 reveal(box, rightClickString);
81193 context.map().on('move.intro drawn.intro', function () {
81194 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81195 var box = pad(eleventhAvenueEnd, padding, context);
81196 reveal(box, rightClickString, {
81200 context.on('enter.intro', function (mode) {
81201 if (mode.id !== 'select') return;
81202 var ids = context.selectedIDs();
81203 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
81204 timeout(function () {
81205 var node = selectMenuItem(context, 'split').node();
81207 continueTo(splitIntersection);
81208 }, 50); // after menu visible
81210 context.history().on('change.intro', function () {
81211 timeout(function () {
81212 continueTo(deleteLines);
81213 }, 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.on('enter.intro', null);
81220 context.history().on('change.intro', null);
81225 function splitIntersection() {
81226 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81227 return continueTo(deleteLines);
81230 var node = selectMenuItem(context, 'split').node();
81233 return continueTo(rightClickIntersection);
81236 var wasChanged = false;
81237 _washingtonSegmentID = null;
81238 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81239 street: _t('intro.graph.name.washington-street')
81243 context.map().on('move.intro drawn.intro', function () {
81244 var node = selectMenuItem(context, 'split').node();
81246 if (!wasChanged && !node) {
81247 return continueTo(rightClickIntersection);
81250 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81251 street: _t('intro.graph.name.washington-street')
81257 context.history().on('change.intro', function (changed) {
81259 timeout(function () {
81260 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
81263 _washingtonSegmentID = changed.created()[0].id;
81264 continueTo(didSplit);
81266 _washingtonSegmentID = null;
81267 continueTo(retrySplit);
81269 }, 300); // after any transition (e.g. if user deleted intersection)
81272 function continueTo(nextStep) {
81273 context.map().on('move.intro drawn.intro', null);
81274 context.history().on('change.intro', null);
81279 function retrySplit() {
81280 context.enter(modeBrowse(context));
81281 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81283 var advance = function advance() {
81284 continueTo(rightClickIntersection);
81287 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81288 var box = pad(eleventhAvenueEnd, padding, context);
81289 reveal(box, helpHtml('intro.lines.retry_split'), {
81290 buttonText: _t.html('intro.ok'),
81291 buttonCallback: advance
81293 context.map().on('move.intro drawn.intro', function () {
81294 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81295 var box = pad(eleventhAvenueEnd, padding, context);
81296 reveal(box, helpHtml('intro.lines.retry_split'), {
81298 buttonText: _t.html('intro.ok'),
81299 buttonCallback: advance
81303 function continueTo(nextStep) {
81304 context.map().on('move.intro drawn.intro', null);
81309 function didSplit() {
81310 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81311 return continueTo(rightClickIntersection);
81314 var ids = context.selectedIDs();
81315 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
81316 var street = _t('intro.graph.name.washington-street');
81317 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81318 var box = pad(twelfthAvenue, padding, context);
81319 box.width = box.width / 2;
81320 reveal(box, helpHtml(string, {
81326 timeout(function () {
81327 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81328 context.map().on('move.intro drawn.intro', function () {
81329 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81330 var box = pad(twelfthAvenue, padding, context);
81331 box.width = box.width / 2;
81332 reveal(box, helpHtml(string, {
81339 }, 600); // after initial reveal and curtain cut
81341 context.on('enter.intro', function () {
81342 var ids = context.selectedIDs();
81344 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
81345 continueTo(multiSelect);
81348 context.history().on('change.intro', function () {
81349 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81350 return continueTo(rightClickIntersection);
81354 function continueTo(nextStep) {
81355 context.map().on('move.intro drawn.intro', null);
81356 context.on('enter.intro', null);
81357 context.history().on('change.intro', null);
81362 function multiSelect() {
81363 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81364 return continueTo(rightClickIntersection);
81367 var ids = context.selectedIDs();
81368 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
81369 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
81371 if (hasWashington && hasTwelfth) {
81372 return continueTo(multiRightClick);
81373 } else if (!hasWashington && !hasTwelfth) {
81374 return continueTo(didSplit);
81377 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81378 timeout(function () {
81379 var selected, other, padding, box;
81381 if (hasWashington) {
81382 selected = _t('intro.graph.name.washington-street');
81383 other = _t('intro.graph.name.12th-avenue');
81384 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81385 box = pad(twelfthAvenueEnd, padding, context);
81388 selected = _t('intro.graph.name.12th-avenue');
81389 other = _t('intro.graph.name.washington-street');
81390 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81391 box = pad(twelfthAvenue, padding, context);
81395 reveal(box, helpHtml('intro.lines.multi_select', {
81396 selected: selected,
81398 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81399 selected: selected,
81402 context.map().on('move.intro drawn.intro', function () {
81403 if (hasWashington) {
81404 selected = _t('intro.graph.name.washington-street');
81405 other = _t('intro.graph.name.12th-avenue');
81406 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81407 box = pad(twelfthAvenueEnd, padding, context);
81410 selected = _t('intro.graph.name.12th-avenue');
81411 other = _t('intro.graph.name.washington-street');
81412 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81413 box = pad(twelfthAvenue, padding, context);
81417 reveal(box, helpHtml('intro.lines.multi_select', {
81418 selected: selected,
81420 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81421 selected: selected,
81427 context.on('enter.intro', function () {
81428 continueTo(multiSelect);
81430 context.history().on('change.intro', function () {
81431 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81432 return continueTo(rightClickIntersection);
81437 function continueTo(nextStep) {
81438 context.map().on('move.intro drawn.intro', null);
81439 context.on('enter.intro', null);
81440 context.history().on('change.intro', null);
81445 function multiRightClick() {
81446 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81447 return continueTo(rightClickIntersection);
81450 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81451 var box = pad(twelfthAvenue, padding, context);
81452 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
81453 reveal(box, rightClickString);
81454 context.map().on('move.intro drawn.intro', function () {
81455 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81456 var box = pad(twelfthAvenue, padding, context);
81457 reveal(box, rightClickString, {
81461 context.ui().editMenu().on('toggled.intro', function (open) {
81463 timeout(function () {
81464 var ids = context.selectedIDs();
81466 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81467 var node = selectMenuItem(context, 'delete').node();
81469 continueTo(multiDelete);
81470 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81471 return continueTo(multiSelect);
81473 return continueTo(didSplit);
81475 }, 300); // after edit menu visible
81477 context.history().on('change.intro', function () {
81478 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81479 return continueTo(rightClickIntersection);
81483 function continueTo(nextStep) {
81484 context.map().on('move.intro drawn.intro', null);
81485 context.ui().editMenu().on('toggled.intro', null);
81486 context.history().on('change.intro', null);
81491 function multiDelete() {
81492 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81493 return continueTo(rightClickIntersection);
81496 var node = selectMenuItem(context, 'delete').node();
81497 if (!node) return continueTo(multiRightClick);
81498 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81501 context.map().on('move.intro drawn.intro', function () {
81502 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81507 context.on('exit.intro', function () {
81508 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81509 return continueTo(multiSelect); // left select mode but roads still exist
81512 context.history().on('change.intro', function () {
81513 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81514 continueTo(retryDelete); // changed something but roads still exist
81520 function continueTo(nextStep) {
81521 context.map().on('move.intro drawn.intro', null);
81522 context.on('exit.intro', null);
81523 context.history().on('change.intro', null);
81528 function retryDelete() {
81529 context.enter(modeBrowse(context));
81530 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81531 var box = pad(twelfthAvenue, padding, context);
81532 reveal(box, helpHtml('intro.lines.retry_delete'), {
81533 buttonText: _t.html('intro.ok'),
81534 buttonCallback: function buttonCallback() {
81535 continueTo(multiSelect);
81539 function continueTo(nextStep) {
81545 dispatch$1.call('done');
81546 reveal('.ideditor', helpHtml('intro.lines.play', {
81547 next: _t('intro.buildings.title')
81549 tooltipBox: '.intro-nav-wrap .chapter-building',
81550 buttonText: _t.html('intro.ok'),
81551 buttonCallback: function buttonCallback() {
81552 reveal('.ideditor');
81557 chapter.enter = function () {
81561 chapter.exit = function () {
81562 timeouts.forEach(window.clearTimeout);
81563 select(window).on('pointerdown.intro mousedown.intro', null, true);
81564 context.on('enter.intro exit.intro', null);
81565 context.map().on('move.intro drawn.intro', null);
81566 context.history().on('change.intro', null);
81567 context.container().select('.inspector-wrap').on('wheel.intro', null);
81568 context.container().select('.preset-list-button').on('click.intro', null);
81571 chapter.restart = function () {
81576 return utilRebind(chapter, dispatch$1, 'on');
81579 function uiIntroBuilding(context, reveal) {
81580 var dispatch$1 = dispatch('done');
81581 var house = [-85.62815, 41.95638];
81582 var tank = [-85.62732, 41.95347];
81583 var buildingCatetory = _mainPresetIndex.item('category-building');
81584 var housePreset = _mainPresetIndex.item('building/house');
81585 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
81587 var _houseID = null;
81588 var _tankID = null;
81590 title: 'intro.buildings.title'
81593 function timeout(f, t) {
81594 timeouts.push(window.setTimeout(f, t));
81597 function eventCancel(d3_event) {
81598 d3_event.stopPropagation();
81599 d3_event.preventDefault();
81602 function revealHouse(center, text, options) {
81603 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
81604 var box = pad(center, padding, context);
81605 reveal(box, text, options);
81608 function revealTank(center, text, options) {
81609 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
81610 var box = pad(center, padding, context);
81611 reveal(box, text, options);
81614 function addHouse() {
81615 context.enter(modeBrowse(context));
81616 context.history().reset('initial');
81618 var msec = transitionTime(house, context.map().center());
81621 reveal(null, null, {
81626 context.map().centerZoomEase(house, 19, msec);
81627 timeout(function () {
81628 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
81629 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
81630 context.on('enter.intro', function (mode) {
81631 if (mode.id !== 'add-area') return;
81632 continueTo(startHouse);
81636 function continueTo(nextStep) {
81637 context.on('enter.intro', null);
81642 function startHouse() {
81643 if (context.mode().id !== 'add-area') {
81644 return continueTo(addHouse);
81648 context.map().zoomEase(20, 500);
81649 timeout(function () {
81650 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81651 revealHouse(house, startString);
81652 context.map().on('move.intro drawn.intro', function () {
81653 revealHouse(house, startString, {
81657 context.on('enter.intro', function (mode) {
81658 if (mode.id !== 'draw-area') return chapter.restart();
81659 continueTo(continueHouse);
81661 }, 550); // after easing
81663 function continueTo(nextStep) {
81664 context.map().on('move.intro drawn.intro', null);
81665 context.on('enter.intro', null);
81670 function continueHouse() {
81671 if (context.mode().id !== 'draw-area') {
81672 return continueTo(addHouse);
81676 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
81677 revealHouse(house, continueString);
81678 context.map().on('move.intro drawn.intro', function () {
81679 revealHouse(house, continueString, {
81683 context.on('enter.intro', function (mode) {
81684 if (mode.id === 'draw-area') {
81686 } else if (mode.id === 'select') {
81687 var graph = context.graph();
81688 var way = context.entity(context.selectedIDs()[0]);
81689 var nodes = graph.childNodes(way);
81690 var points = utilArrayUniq(nodes).map(function (n) {
81691 return context.projection(n.loc);
81694 if (isMostlySquare(points)) {
81696 return continueTo(chooseCategoryBuilding);
81698 return continueTo(retryHouse);
81701 return chapter.restart();
81705 function continueTo(nextStep) {
81706 context.map().on('move.intro drawn.intro', null);
81707 context.on('enter.intro', null);
81712 function retryHouse() {
81713 var onClick = function onClick() {
81714 continueTo(addHouse);
81717 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81718 buttonText: _t.html('intro.ok'),
81719 buttonCallback: onClick
81721 context.map().on('move.intro drawn.intro', function () {
81722 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81724 buttonText: _t.html('intro.ok'),
81725 buttonCallback: onClick
81729 function continueTo(nextStep) {
81730 context.map().on('move.intro drawn.intro', null);
81735 function chooseCategoryBuilding() {
81736 if (!_houseID || !context.hasEntity(_houseID)) {
81740 var ids = context.selectedIDs();
81742 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81743 context.enter(modeSelect(context, [_houseID]));
81744 } // disallow scrolling
81747 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81748 timeout(function () {
81749 // reset pane, in case user somehow happened to change it..
81750 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81751 var button = context.container().select('.preset-category-building .preset-list-button');
81752 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
81753 category: buildingCatetory.name()
81755 button.on('click.intro', function () {
81756 button.on('click.intro', null);
81757 continueTo(choosePresetHouse);
81759 }, 400); // after preset list pane visible..
81761 context.on('enter.intro', function (mode) {
81762 if (!_houseID || !context.hasEntity(_houseID)) {
81763 return continueTo(addHouse);
81766 var ids = context.selectedIDs();
81768 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81769 return continueTo(chooseCategoryBuilding);
81773 function continueTo(nextStep) {
81774 context.container().select('.inspector-wrap').on('wheel.intro', null);
81775 context.container().select('.preset-list-button').on('click.intro', null);
81776 context.on('enter.intro', null);
81781 function choosePresetHouse() {
81782 if (!_houseID || !context.hasEntity(_houseID)) {
81786 var ids = context.selectedIDs();
81788 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81789 context.enter(modeSelect(context, [_houseID]));
81790 } // disallow scrolling
81793 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81794 timeout(function () {
81795 // reset pane, in case user somehow happened to change it..
81796 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81797 var button = context.container().select('.preset-building-house .preset-list-button');
81798 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
81799 preset: housePreset.name()
81803 button.on('click.intro', function () {
81804 button.on('click.intro', null);
81805 continueTo(closeEditorHouse);
81807 }, 400); // after preset list pane visible..
81809 context.on('enter.intro', function (mode) {
81810 if (!_houseID || !context.hasEntity(_houseID)) {
81811 return continueTo(addHouse);
81814 var ids = context.selectedIDs();
81816 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81817 return continueTo(chooseCategoryBuilding);
81821 function continueTo(nextStep) {
81822 context.container().select('.inspector-wrap').on('wheel.intro', null);
81823 context.container().select('.preset-list-button').on('click.intro', null);
81824 context.on('enter.intro', null);
81829 function closeEditorHouse() {
81830 if (!_houseID || !context.hasEntity(_houseID)) {
81834 var ids = context.selectedIDs();
81836 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81837 context.enter(modeSelect(context, [_houseID]));
81840 context.history().checkpoint('hasHouse');
81841 context.on('exit.intro', function () {
81842 continueTo(rightClickHouse);
81844 timeout(function () {
81845 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
81846 button: icon('#iD-icon-close', 'inline')
81850 function continueTo(nextStep) {
81851 context.on('exit.intro', null);
81856 function rightClickHouse() {
81857 if (!_houseID) return chapter.restart();
81858 context.enter(modeBrowse(context));
81859 context.history().reset('hasHouse');
81860 var zoom = context.map().zoom();
81866 context.map().centerZoomEase(house, zoom, 500);
81867 context.on('enter.intro', function (mode) {
81868 if (mode.id !== 'select') return;
81869 var ids = context.selectedIDs();
81870 if (ids.length !== 1 || ids[0] !== _houseID) return;
81871 timeout(function () {
81872 var node = selectMenuItem(context, 'orthogonalize').node();
81874 continueTo(clickSquare);
81875 }, 50); // after menu visible
81877 context.map().on('move.intro drawn.intro', function () {
81878 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
81879 revealHouse(house, rightclickString, {
81883 context.history().on('change.intro', function () {
81884 continueTo(rightClickHouse);
81887 function continueTo(nextStep) {
81888 context.on('enter.intro', null);
81889 context.map().on('move.intro drawn.intro', null);
81890 context.history().on('change.intro', null);
81895 function clickSquare() {
81896 if (!_houseID) return chapter.restart();
81897 var entity = context.hasEntity(_houseID);
81898 if (!entity) return continueTo(rightClickHouse);
81899 var node = selectMenuItem(context, 'orthogonalize').node();
81902 return continueTo(rightClickHouse);
81905 var wasChanged = false;
81906 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81909 context.on('enter.intro', function (mode) {
81910 if (mode.id === 'browse') {
81911 continueTo(rightClickHouse);
81912 } else if (mode.id === 'move' || mode.id === 'rotate') {
81913 continueTo(retryClickSquare);
81916 context.map().on('move.intro', function () {
81917 var node = selectMenuItem(context, 'orthogonalize').node();
81919 if (!wasChanged && !node) {
81920 return continueTo(rightClickHouse);
81923 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81928 context.history().on('change.intro', function () {
81930 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
81932 timeout(function () {
81933 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
81936 continueTo(doneSquare);
81938 continueTo(retryClickSquare);
81940 }, 500); // after transitioned actions
81943 function continueTo(nextStep) {
81944 context.on('enter.intro', null);
81945 context.map().on('move.intro', null);
81946 context.history().on('change.intro', null);
81951 function retryClickSquare() {
81952 context.enter(modeBrowse(context));
81953 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
81954 buttonText: _t.html('intro.ok'),
81955 buttonCallback: function buttonCallback() {
81956 continueTo(rightClickHouse);
81960 function continueTo(nextStep) {
81965 function doneSquare() {
81966 context.history().checkpoint('doneSquare');
81967 revealHouse(house, helpHtml('intro.buildings.done_square'), {
81968 buttonText: _t.html('intro.ok'),
81969 buttonCallback: function buttonCallback() {
81970 continueTo(addTank);
81974 function continueTo(nextStep) {
81979 function addTank() {
81980 context.enter(modeBrowse(context));
81981 context.history().reset('doneSquare');
81983 var msec = transitionTime(tank, context.map().center());
81986 reveal(null, null, {
81991 context.map().centerZoomEase(tank, 19.5, msec);
81992 timeout(function () {
81993 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
81994 context.on('enter.intro', function (mode) {
81995 if (mode.id !== 'add-area') return;
81996 continueTo(startTank);
82000 function continueTo(nextStep) {
82001 context.on('enter.intro', null);
82006 function startTank() {
82007 if (context.mode().id !== 'add-area') {
82008 return continueTo(addTank);
82012 timeout(function () {
82013 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
82014 revealTank(tank, startString);
82015 context.map().on('move.intro drawn.intro', function () {
82016 revealTank(tank, startString, {
82020 context.on('enter.intro', function (mode) {
82021 if (mode.id !== 'draw-area') return chapter.restart();
82022 continueTo(continueTank);
82024 }, 550); // after easing
82026 function continueTo(nextStep) {
82027 context.map().on('move.intro drawn.intro', null);
82028 context.on('enter.intro', null);
82033 function continueTank() {
82034 if (context.mode().id !== 'draw-area') {
82035 return continueTo(addTank);
82039 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
82040 revealTank(tank, continueString);
82041 context.map().on('move.intro drawn.intro', function () {
82042 revealTank(tank, continueString, {
82046 context.on('enter.intro', function (mode) {
82047 if (mode.id === 'draw-area') {
82049 } else if (mode.id === 'select') {
82050 _tankID = context.selectedIDs()[0];
82051 return continueTo(searchPresetTank);
82053 return continueTo(addTank);
82057 function continueTo(nextStep) {
82058 context.map().on('move.intro drawn.intro', null);
82059 context.on('enter.intro', null);
82064 function searchPresetTank() {
82065 if (!_tankID || !context.hasEntity(_tankID)) {
82069 var ids = context.selectedIDs();
82071 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
82072 context.enter(modeSelect(context, [_tankID]));
82073 } // disallow scrolling
82076 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
82077 timeout(function () {
82078 // reset pane, in case user somehow happened to change it..
82079 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
82080 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
82081 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
82082 preset: tankPreset.name()
82084 }, 400); // after preset list pane visible..
82086 context.on('enter.intro', function (mode) {
82087 if (!_tankID || !context.hasEntity(_tankID)) {
82088 return continueTo(addTank);
82091 var ids = context.selectedIDs();
82093 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
82094 // keep the user's area selected..
82095 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
82097 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
82099 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
82100 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
82101 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
82102 preset: tankPreset.name()
82104 context.history().on('change.intro', null);
82108 function checkPresetSearch() {
82109 var first = context.container().select('.preset-list-item:first-child');
82111 if (first.classed('preset-man_made-storage_tank')) {
82112 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
82113 preset: tankPreset.name()
82117 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
82118 context.history().on('change.intro', function () {
82119 continueTo(closeEditorTank);
82124 function continueTo(nextStep) {
82125 context.container().select('.inspector-wrap').on('wheel.intro', null);
82126 context.on('enter.intro', null);
82127 context.history().on('change.intro', null);
82128 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
82133 function closeEditorTank() {
82134 if (!_tankID || !context.hasEntity(_tankID)) {
82138 var ids = context.selectedIDs();
82140 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
82141 context.enter(modeSelect(context, [_tankID]));
82144 context.history().checkpoint('hasTank');
82145 context.on('exit.intro', function () {
82146 continueTo(rightClickTank);
82148 timeout(function () {
82149 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
82150 button: icon('#iD-icon-close', 'inline')
82154 function continueTo(nextStep) {
82155 context.on('exit.intro', null);
82160 function rightClickTank() {
82161 if (!_tankID) return continueTo(addTank);
82162 context.enter(modeBrowse(context));
82163 context.history().reset('hasTank');
82164 context.map().centerEase(tank, 500);
82165 timeout(function () {
82166 context.on('enter.intro', function (mode) {
82167 if (mode.id !== 'select') return;
82168 var ids = context.selectedIDs();
82169 if (ids.length !== 1 || ids[0] !== _tankID) return;
82170 timeout(function () {
82171 var node = selectMenuItem(context, 'circularize').node();
82173 continueTo(clickCircle);
82174 }, 50); // after menu visible
82176 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
82177 revealTank(tank, rightclickString);
82178 context.map().on('move.intro drawn.intro', function () {
82179 revealTank(tank, rightclickString, {
82183 context.history().on('change.intro', function () {
82184 continueTo(rightClickTank);
82188 function continueTo(nextStep) {
82189 context.on('enter.intro', null);
82190 context.map().on('move.intro drawn.intro', null);
82191 context.history().on('change.intro', null);
82196 function clickCircle() {
82197 if (!_tankID) return chapter.restart();
82198 var entity = context.hasEntity(_tankID);
82199 if (!entity) return continueTo(rightClickTank);
82200 var node = selectMenuItem(context, 'circularize').node();
82203 return continueTo(rightClickTank);
82206 var wasChanged = false;
82207 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82210 context.on('enter.intro', function (mode) {
82211 if (mode.id === 'browse') {
82212 continueTo(rightClickTank);
82213 } else if (mode.id === 'move' || mode.id === 'rotate') {
82214 continueTo(retryClickCircle);
82217 context.map().on('move.intro', function () {
82218 var node = selectMenuItem(context, 'circularize').node();
82220 if (!wasChanged && !node) {
82221 return continueTo(rightClickTank);
82224 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82229 context.history().on('change.intro', function () {
82231 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
82233 timeout(function () {
82234 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
82239 continueTo(retryClickCircle);
82241 }, 500); // after transitioned actions
82244 function continueTo(nextStep) {
82245 context.on('enter.intro', null);
82246 context.map().on('move.intro', null);
82247 context.history().on('change.intro', null);
82252 function retryClickCircle() {
82253 context.enter(modeBrowse(context));
82254 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
82255 buttonText: _t.html('intro.ok'),
82256 buttonCallback: function buttonCallback() {
82257 continueTo(rightClickTank);
82261 function continueTo(nextStep) {
82267 dispatch$1.call('done');
82268 reveal('.ideditor', helpHtml('intro.buildings.play', {
82269 next: _t('intro.startediting.title')
82271 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
82272 buttonText: _t.html('intro.ok'),
82273 buttonCallback: function buttonCallback() {
82274 reveal('.ideditor');
82279 chapter.enter = function () {
82283 chapter.exit = function () {
82284 timeouts.forEach(window.clearTimeout);
82285 context.on('enter.intro exit.intro', null);
82286 context.map().on('move.intro drawn.intro', null);
82287 context.history().on('change.intro', null);
82288 context.container().select('.inspector-wrap').on('wheel.intro', null);
82289 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
82290 context.container().select('.more-fields .combobox-input').on('click.intro', null);
82293 chapter.restart = function () {
82298 return utilRebind(chapter, dispatch$1, 'on');
82301 function uiIntroStartEditing(context, reveal) {
82302 var dispatch$1 = dispatch('done', 'startEditing');
82303 var modalSelection = select(null);
82305 title: 'intro.startediting.title'
82308 function showHelp() {
82309 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
82310 buttonText: _t.html('intro.ok'),
82311 buttonCallback: function buttonCallback() {
82317 function shortcuts() {
82318 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
82319 buttonText: _t.html('intro.ok'),
82320 buttonCallback: function buttonCallback() {
82326 function showSave() {
82327 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82329 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
82330 buttonText: _t.html('intro.ok'),
82331 buttonCallback: function buttonCallback() {
82337 function showStart() {
82338 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82340 modalSelection = uiModal(context.container());
82341 modalSelection.select('.modal').attr('class', 'modal-splash modal');
82342 modalSelection.selectAll('.close').remove();
82343 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
82344 modalSelection.remove();
82346 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82347 startbutton.append('h2').html(_t.html('intro.startediting.start'));
82348 dispatch$1.call('startEditing');
82351 chapter.enter = function () {
82355 chapter.exit = function () {
82356 modalSelection.remove();
82357 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82360 return utilRebind(chapter, dispatch$1, 'on');
82364 welcome: uiIntroWelcome,
82365 navigation: uiIntroNavigation,
82366 point: uiIntroPoint,
82369 building: uiIntroBuilding,
82370 startEditing: uiIntroStartEditing
82372 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
82373 function uiIntro(context) {
82374 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
82375 var _introGraph = {};
82379 function intro(selection) {
82380 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
82381 // create entities for intro graph and localize names
82382 for (var id in dataIntroGraph) {
82383 if (!_introGraph[id]) {
82384 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
82388 selection.call(startIntro);
82389 })["catch"](function () {
82394 function startIntro(selection) {
82395 context.enter(modeBrowse(context)); // Save current map state
82397 var osm = context.connection();
82398 var history = context.history().toJSON();
82399 var hash = window.location.hash;
82400 var center = context.map().center();
82401 var zoom = context.map().zoom();
82402 var background = context.background().baseLayerSource();
82403 var overlays = context.background().overlayLayerSources();
82404 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
82405 var caches = osm && osm.caches();
82406 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
82407 // (this needs to be before `context.inIntro(true)`)
82409 context.ui().sidebar.expand();
82410 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
82412 context.inIntro(true); // Load semi-real data used in intro
82415 osm.toggle(false).reset();
82418 context.history().reset();
82419 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
82420 context.history().checkpoint('initial'); // Setup imagery
82422 var imagery = context.background().findSource(INTRO_IMAGERY);
82425 context.background().baseLayerSource(imagery);
82427 context.background().bing();
82430 overlays.forEach(function (d) {
82431 return context.background().toggleOverlayLayer(d);
82432 }); // Setup data layers (only OSM)
82434 var layers = context.layers();
82435 layers.all().forEach(function (item) {
82436 // if the layer has the function `enabled`
82437 if (typeof item.layer.enabled === 'function') {
82438 item.layer.enabled(item.id === 'osm');
82441 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
82442 var curtain = uiCurtain(context.container().node());
82443 selection.call(curtain); // Store that the user started the walkthrough..
82445 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
82447 var storedProgress = corePreferences('walkthrough_progress') || '';
82448 var progress = storedProgress.split(';').filter(Boolean);
82449 var chapters = chapterFlow.map(function (chapter, i) {
82450 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
82451 buttons.filter(function (d) {
82452 return d.title === s.title;
82453 }).classed('finished', true);
82455 if (i < chapterFlow.length - 1) {
82456 var next = chapterFlow[i + 1];
82457 context.container().select("button.chapter-".concat(next)).classed('next', true);
82458 } // Store walkthrough progress..
82461 progress.push(chapter);
82462 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
82466 chapters[chapters.length - 1].on('startEditing', function () {
82467 // Store walkthrough progress..
82468 progress.push('startEditing');
82469 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
82471 var incomplete = utilArrayDifference(chapterFlow, progress);
82473 if (!incomplete.length) {
82474 corePreferences('walkthrough_completed', 'yes');
82479 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
82480 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
82483 osm.toggle(true).reset().caches(caches);
82486 context.history().reset().merge(Object.values(baseEntities));
82487 context.background().baseLayerSource(background);
82488 overlays.forEach(function (d) {
82489 return context.background().toggleOverlayLayer(d);
82493 context.history().fromJSON(history, false);
82496 context.map().centerZoom(center, zoom);
82497 window.location.replace(hash);
82498 context.inIntro(false);
82500 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
82501 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82502 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
82503 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
82504 return "chapter chapter-".concat(chapterFlow[i]);
82505 }).on('click', enterChapter);
82506 buttons.append('span').html(function (d) {
82507 return _t.html(d.title);
82509 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
82510 enterChapter(null, chapters[0]);
82512 function enterChapter(d3_event, newChapter) {
82513 if (_currChapter) {
82514 _currChapter.exit();
82517 context.enter(modeBrowse(context));
82518 _currChapter = newChapter;
82520 _currChapter.enter();
82522 buttons.classed('next', false).classed('active', function (d) {
82523 return d.title === _currChapter.title;
82531 function uiIssuesInfo(context) {
82532 var warningsItem = {
82535 iconID: 'iD-icon-alert',
82536 descriptionID: 'issues.warnings_and_errors'
82538 var resolvedItem = {
82541 iconID: 'iD-icon-apply',
82542 descriptionID: 'issues.user_resolved_issues'
82545 function update(selection) {
82546 var shownItems = [];
82547 var liveIssues = context.validator().getIssues({
82548 what: corePreferences('validate-what') || 'edited',
82549 where: corePreferences('validate-where') || 'all'
82552 if (liveIssues.length) {
82553 warningsItem.count = liveIssues.length;
82554 shownItems.push(warningsItem);
82557 if (corePreferences('validate-what') === 'all') {
82558 var resolvedIssues = context.validator().getResolvedIssues();
82560 if (resolvedIssues.length) {
82561 resolvedItem.count = resolvedIssues.length;
82562 shownItems.push(resolvedItem);
82566 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
82569 chips.exit().remove();
82570 var enter = chips.enter().append('a').attr('class', function (d) {
82571 return 'chip ' + d.id + '-count';
82572 }).attr('href', '#').each(function (d) {
82573 var chipSelection = select(this);
82574 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
82575 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
82576 d3_event.preventDefault();
82577 tooltipBehavior.hide(select(this)); // open the Issues pane
82579 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
82581 chipSelection.call(svgIcon('#' + d.iconID));
82583 enter.append('span').attr('class', 'count');
82584 enter.merge(chips).selectAll('span.count').html(function (d) {
82585 return d.count.toString();
82589 return function (selection) {
82591 context.validator().on('validated.infobox', function () {
82597 function uiMapInMap(context) {
82598 function mapInMap(selection) {
82599 var backgroundLayer = rendererTileLayer(context);
82600 var overlayLayers = {};
82601 var projection = geoRawMercator();
82602 var dataLayer = svgData(projection, context).showLabels(false);
82603 var debugLayer = svgDebug(projection, context);
82604 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
82605 var wrap = select(null);
82606 var tiles = select(null);
82607 var viewport = select(null);
82608 var _isTransformed = false;
82609 var _isHidden = true;
82610 var _skipEvents = false;
82611 var _gesture = null;
82612 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
82614 var _dMini; // dimensions of minimap
82617 var _cMini; // center pixel of minimap
82620 var _tStart; // transform at start of gesture
82623 var _tCurr; // transform at most recent event
82628 function zoomStarted() {
82629 if (_skipEvents) return;
82630 _tStart = _tCurr = projection.transform();
82634 function zoomed(d3_event) {
82635 if (_skipEvents) return;
82636 var x = d3_event.transform.x;
82637 var y = d3_event.transform.y;
82638 var k = d3_event.transform.k;
82639 var isZooming = k !== _tStart.k;
82640 var isPanning = x !== _tStart.x || y !== _tStart.y;
82642 if (!isZooming && !isPanning) {
82643 return; // no change
82644 } // lock in either zooming or panning, don't allow both in minimap.
82648 _gesture = isZooming ? 'zoom' : 'pan';
82651 var tMini = projection.transform();
82654 if (_gesture === 'zoom') {
82655 scale = k / tMini.k;
82656 tX = (_cMini[0] / scale - _cMini[0]) * scale;
82657 tY = (_cMini[1] / scale - _cMini[1]) * scale;
82665 utilSetTransform(tiles, tX, tY, scale);
82666 utilSetTransform(viewport, 0, 0, scale);
82667 _isTransformed = true;
82668 _tCurr = identity$2.translate(x, y).scale(k);
82669 var zMain = geoScaleToZoom(context.projection.scale());
82670 var zMini = geoScaleToZoom(k);
82671 _zDiff = zMain - zMini;
82675 function zoomEnded() {
82676 if (_skipEvents) return;
82677 if (_gesture !== 'pan') return;
82678 updateProjection();
82680 context.map().center(projection.invert(_cMini)); // recenter main map..
82683 function updateProjection() {
82684 var loc = context.map().center();
82685 var tMain = context.projection.transform();
82686 var zMain = geoScaleToZoom(tMain.k);
82687 var zMini = Math.max(zMain - _zDiff, 0.5);
82688 var kMini = geoZoomToScale(zMini);
82689 projection.translate([tMain.x, tMain.y]).scale(kMini);
82690 var point = projection(loc);
82691 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
82692 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
82693 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
82694 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
82695 _tCurr = projection.transform();
82697 if (_isTransformed) {
82698 utilSetTransform(tiles, 0, 0);
82699 utilSetTransform(viewport, 0, 0);
82700 _isTransformed = false;
82703 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
82704 _skipEvents = true;
82705 wrap.call(zoom.transform, _tCurr);
82706 _skipEvents = false;
82709 function redraw() {
82710 clearTimeout(_timeoutID);
82711 if (_isHidden) return;
82712 updateProjection();
82713 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
82715 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
82716 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
82718 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
82719 var background = tiles.selectAll('.map-in-map-background').data([0]);
82720 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
82722 var overlaySources = context.background().overlayLayerSources();
82723 var activeOverlayLayers = [];
82725 for (var i = 0; i < overlaySources.length; i++) {
82726 if (overlaySources[i].validZoom(zMini)) {
82727 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
82728 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
82732 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
82733 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
82734 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
82735 return d.source().name();
82737 overlays.exit().remove();
82738 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
82739 select(this).call(layer);
82741 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
82742 dataLayers.exit().remove();
82743 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
82745 if (_gesture !== 'pan') {
82746 var getPath = d3_geoPath(projection);
82749 coordinates: [context.map().extent().polygon()]
82751 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
82752 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
82753 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
82754 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
82755 return getPath.area(d) < 30;
82760 function queueRedraw() {
82761 clearTimeout(_timeoutID);
82762 _timeoutID = setTimeout(function () {
82767 function toggle(d3_event) {
82768 if (d3_event) d3_event.preventDefault();
82769 _isHidden = !_isHidden;
82770 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
82773 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
82774 selection.selectAll('.map-in-map').style('display', 'none');
82777 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
82783 uiMapInMap.toggle = toggle;
82784 wrap = selection.selectAll('.map-in-map').data([0]);
82785 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..
82787 _dMini = [200, 150]; //utilGetDimensions(wrap);
82789 _cMini = geoVecScale(_dMini, 0.5);
82790 context.map().on('drawn.map-in-map', function (drawn) {
82791 if (drawn.full === true) {
82796 context.keybinding().on(_t('background.minimap.key'), toggle);
82802 function uiNotice(context) {
82803 return function (selection) {
82804 var div = selection.append('div').attr('class', 'notice');
82805 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
82806 context.map().zoomEase(context.minEditableZoom());
82807 }).on('wheel', function (d3_event) {
82808 // let wheel events pass through #4482
82809 var e2 = new WheelEvent(d3_event.type, d3_event);
82810 context.surface().node().dispatchEvent(e2);
82812 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
82814 function disableTooHigh() {
82815 var canEdit = context.map().zoom() >= context.minEditableZoom();
82816 div.style('display', canEdit ? 'none' : 'block');
82819 context.map().on('move.notice', debounce(disableTooHigh, 500));
82824 function uiPhotoviewer(context) {
82825 var dispatch$1 = dispatch('resize');
82827 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
82829 function photoviewer(selection) {
82830 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
82831 if (services.streetside) {
82832 services.streetside.hideViewer(context);
82835 if (services.mapillary) {
82836 services.mapillary.hideViewer(context);
82839 if (services.openstreetcam) {
82840 services.openstreetcam.hideViewer(context);
82842 }).append('div').call(svgIcon('#iD-icon-close'));
82844 function preventDefault(d3_event) {
82845 d3_event.preventDefault();
82848 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82852 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82855 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82859 function buildResizeListener(target, eventName, dispatch, options) {
82860 var resizeOnX = !!options.resizeOnX;
82861 var resizeOnY = !!options.resizeOnY;
82862 var minHeight = options.minHeight || 240;
82863 var minWidth = options.minWidth || 320;
82870 function startResize(d3_event) {
82871 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82872 d3_event.preventDefault();
82873 d3_event.stopPropagation();
82874 var mapSize = context.map().dimensions();
82877 var maxWidth = mapSize[0];
82878 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
82879 target.style('width', newWidth + 'px');
82883 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
82885 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
82886 target.style('height', newHeight + 'px');
82889 dispatch.call(eventName, target, utilGetDimensions(target, true));
82892 function clamp(num, min, max) {
82893 return Math.max(min, Math.min(num, max));
82896 function stopResize(d3_event) {
82897 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82898 d3_event.preventDefault();
82899 d3_event.stopPropagation(); // remove all the listeners we added
82901 select(window).on('.' + eventName, null);
82904 return function initResize(d3_event) {
82905 d3_event.preventDefault();
82906 d3_event.stopPropagation();
82907 pointerId = d3_event.pointerId || 'mouse';
82908 startX = d3_event.clientX;
82909 startY = d3_event.clientY;
82910 var targetRect = target.node().getBoundingClientRect();
82911 startWidth = targetRect.width;
82912 startHeight = targetRect.height;
82913 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
82915 if (_pointerPrefix === 'pointer') {
82916 select(window).on('pointercancel.' + eventName, stopResize, false);
82922 photoviewer.onMapResize = function () {
82923 var photoviewer = context.container().select('.photoviewer');
82924 var content = context.container().select('.main-content');
82925 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
82926 // (-90 preserves space at top and bottom of map used by menus)
82928 var photoDimensions = utilGetDimensions(photoviewer, true);
82930 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
82931 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
82932 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
82933 dispatch$1.call('resize', photoviewer, setPhotoDimensions);
82937 return utilRebind(photoviewer, dispatch$1, 'on');
82940 function uiRestore(context) {
82941 return function (selection) {
82942 if (!context.history().hasRestorableChanges()) return;
82943 var modalSelection = uiModal(selection, true);
82944 modalSelection.select('.modal').attr('class', 'modal fillL');
82945 var introModal = modalSelection.select('.content');
82946 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
82947 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
82948 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
82949 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
82950 context.history().restore();
82951 modalSelection.remove();
82953 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
82954 restore.append('div').html(_t.html('restore.restore'));
82955 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
82956 context.history().clearSaved();
82957 modalSelection.remove();
82959 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
82960 reset.append('div').html(_t.html('restore.reset'));
82961 restore.node().focus();
82965 function uiScale(context) {
82966 var projection = context.projection,
82967 isImperial = !_mainLocalizer.usesMetric(),
82971 function scaleDefs(loc1, loc2) {
82972 var lat = (loc2[1] + loc1[1]) / 2,
82973 conversion = isImperial ? 3.28084 : 1,
82974 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
82986 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
82988 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
82989 } // determine a user-friendly endpoint for the scale
82992 for (i = 0; i < buckets.length; i++) {
82996 scale.dist = Math.floor(dist / val) * val;
82999 scale.dist = +dist.toFixed(2);
83003 dLon = geoMetersToLon(scale.dist / conversion, lat);
83004 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
83005 scale.text = displayLength(scale.dist / conversion, isImperial);
83009 function update(selection) {
83010 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
83011 var dims = context.map().dimensions(),
83012 loc1 = projection.invert([0, dims[1]]),
83013 loc2 = projection.invert([maxLength, dims[1]]),
83014 scale = scaleDefs(loc1, loc2);
83015 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
83016 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
83019 return function (selection) {
83020 function switchUnits() {
83021 isImperial = !isImperial;
83022 selection.call(update);
83025 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
83026 scalegroup.append('path').attr('class', 'scale-path');
83027 selection.append('div').attr('class', 'scale-text');
83028 selection.call(update);
83029 context.map().on('move.scale', function () {
83035 function uiShortcuts(context) {
83036 var detected = utilDetect();
83037 var _activeTab = 0;
83039 var _modalSelection;
83041 var _selection = select(null);
83043 var _dataShortcuts;
83045 function shortcutsModal(_modalSelection) {
83046 _modalSelection.select('.modal').classed('modal-shortcuts', true);
83048 var content = _modalSelection.select('.content');
83050 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
83051 _mainFileFetcher.get('shortcuts').then(function (data) {
83052 _dataShortcuts = data;
83053 content.call(render);
83054 })["catch"](function () {
83059 function render(selection) {
83060 if (!_dataShortcuts) return;
83061 var wrapper = selection.selectAll('.wrapper').data([0]);
83062 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
83063 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
83064 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
83065 wrapper = wrapper.merge(wrapperEnter);
83066 var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
83067 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
83068 d3_event.preventDefault();
83070 var i = _dataShortcuts.indexOf(d);
83075 tabsEnter.append('span').html(function (d) {
83076 return _t.html(d.text);
83079 wrapper.selectAll('.tab').classed('active', function (d, i) {
83080 return i === _activeTab;
83082 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
83083 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
83084 return 'shortcut-tab shortcut-tab-' + d.tab;
83086 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
83088 }).enter().append('table').attr('class', 'shortcut-column');
83089 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
83091 }).enter().append('tr').attr('class', 'shortcut-row');
83092 var sectionRows = rowsEnter.filter(function (d) {
83093 return !d.shortcuts;
83095 sectionRows.append('td');
83096 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
83097 return _t.html(d.text);
83099 var shortcutRows = rowsEnter.filter(function (d) {
83100 return d.shortcuts;
83102 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
83103 var modifierKeys = shortcutKeys.filter(function (d) {
83104 return d.modifiers;
83106 modifierKeys.selectAll('kbd.modifier').data(function (d) {
83107 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
83109 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
83112 return d.modifiers;
83114 }).enter().each(function () {
83115 var selection = select(this);
83116 selection.append('kbd').attr('class', 'modifier').html(function (d) {
83117 return uiCmd.display(d);
83119 selection.append('span').html('+');
83121 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
83122 var arr = d.shortcuts;
83124 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
83126 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
83128 } // replace translations
83131 arr = arr.map(function (s) {
83132 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
83134 return utilArrayUniq(arr).map(function (s) {
83137 separator: d.separator,
83141 }).enter().each(function (d, i, nodes) {
83142 var selection = select(this);
83143 var click = d.shortcut.toLowerCase().match(/(.*).click/);
83145 if (click && click[1]) {
83146 // replace "left_click", "right_click" with mouse icon
83147 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
83148 } else if (d.shortcut.toLowerCase() === 'long-press') {
83149 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
83150 } else if (d.shortcut.toLowerCase() === 'tap') {
83151 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
83153 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
83158 if (i < nodes.length - 1) {
83159 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
83160 } else if (i === nodes.length - 1 && d.suffix) {
83161 selection.append('span').html(d.suffix);
83164 shortcutKeys.filter(function (d) {
83166 }).each(function () {
83167 var selection = select(this);
83168 selection.append('span').html('+');
83169 selection.append('span').attr('class', 'gesture').html(function (d) {
83170 return _t.html(d.gesture);
83173 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
83174 return d.text ? _t.html(d.text) : "\xA0";
83177 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
83178 return i === _activeTab ? 'flex' : 'none';
83182 return function (selection, show) {
83183 _selection = selection;
83186 _modalSelection = uiModal(selection);
83188 _modalSelection.call(shortcutsModal);
83190 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
83191 if (context.container().selectAll('.modal-shortcuts').size()) {
83193 if (_modalSelection) {
83194 _modalSelection.close();
83196 _modalSelection = null;
83199 _modalSelection = uiModal(_selection);
83201 _modalSelection.call(shortcutsModal);
83210 function search(input, dims) {
83211 if (!dims) dims = 'NSEW';
83212 if (typeof input !== 'string') return null;
83213 input = input.toUpperCase();
83214 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
83215 var m = input.match(regex);
83216 if (!m) return null; // no match
83218 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
83222 if (m[1] && m[5]) {
83223 // if matched both..
83224 dim = m[1]; // keep leading
83226 matched = matched.slice(0, -1); // remove trailing dimension from match
83228 dim = m[1] || m[5];
83229 } // if unrecognized dimension
83232 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
83234 var deg = m[2] ? parseFloat(m[2]) : 0;
83235 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
83236 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
83237 var sign = deg < 0 ? -1 : 1;
83238 if (dim === 'S' || dim === 'W') sign *= -1;
83240 val: (Math.abs(deg) + min + sec) * sign,
83243 remain: input.slice(matched.length)
83247 function pair(input, dims) {
83248 input = input.trim();
83249 var one = search(input, dims);
83250 if (!one) return null;
83251 input = one.remain.trim();
83252 var two = search(input, dims);
83253 if (!two || two.remain) return null;
83256 return swapdim(one.val, two.val, one.dim);
83258 return [one.val, two.val];
83262 function swapdim(a, b, dim) {
83263 if (dim === 'N' || dim === 'S') return [a, b];
83264 if (dim === 'W' || dim === 'E') return [b, a];
83267 function uiFeatureList(context) {
83268 var _geocodeResults;
83270 function featureList(selection) {
83271 var header = selection.append('div').attr('class', 'header fillL');
83272 header.append('h3').html(_t.html('inspector.feature_list'));
83273 var searchWrap = selection.append('div').attr('class', 'search-header');
83274 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
83275 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
83276 var listWrap = selection.append('div').attr('class', 'inspector-body');
83277 var list = listWrap.append('div').attr('class', 'feature-list');
83278 context.on('exit.feature-list', clearSearch);
83279 context.map().on('drawn.feature-list', mapDrawn);
83280 context.keybinding().on(uiCmd('⌘F'), focusSearch);
83282 function focusSearch(d3_event) {
83283 var mode = context.mode() && context.mode().id;
83284 if (mode !== 'browse') return;
83285 d3_event.preventDefault();
83286 search.node().focus();
83289 function keydown(d3_event) {
83290 if (d3_event.keyCode === 27) {
83292 search.node().blur();
83296 function keypress(d3_event) {
83297 var q = search.property('value'),
83298 items = list.selectAll('.feature-list-item');
83300 if (d3_event.keyCode === 13 && // ↩ Return
83301 q.length && items.size()) {
83302 click(items.datum());
83306 function inputevent() {
83307 _geocodeResults = undefined;
83311 function clearSearch() {
83312 search.property('value', '');
83316 function mapDrawn(e) {
83322 function features() {
83324 var graph = context.graph();
83325 var visibleCenter = context.map().extent().center();
83326 var q = search.property('value').toLowerCase();
83327 if (!q) return result;
83328 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
83330 if (locationMatch) {
83331 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
83335 type: _t('inspector.location'),
83336 name: dmsCoordinatePair([loc[1], loc[0]]),
83339 } // A location search takes priority over an ID search
83342 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
83345 var elemType = idMatch[1].charAt(0);
83346 var elemId = idMatch[2];
83348 id: elemType + elemId,
83349 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
83350 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
83355 var allEntities = graph.entities;
83356 var localResults = [];
83358 for (var id in allEntities) {
83359 var entity = allEntities[id];
83360 if (!entity) continue;
83361 var name = utilDisplayName(entity) || '';
83362 if (name.toLowerCase().indexOf(q) < 0) continue;
83363 var matched = _mainPresetIndex.match(entity, graph);
83364 var type = matched && matched.name() || utilDisplayType(entity.id);
83365 var extent = entity.extent(graph);
83366 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
83367 localResults.push({
83370 geometry: entity.geometry(graph),
83375 if (localResults.length > 100) break;
83378 localResults = localResults.sort(function byDistance(a, b) {
83379 return a.distance - b.distance;
83381 result = result.concat(localResults);
83383 (_geocodeResults || []).forEach(function (d) {
83384 if (d.osm_type && d.osm_id) {
83385 // some results may be missing these - #1890
83386 // Make a temporary osmEntity so we can preset match
83387 // and better localize the search result - #4725
83388 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
83390 tags[d["class"]] = d.type;
83397 if (d.osm_type === 'way') {
83398 // for ways, add some fake closed nodes
83399 attrs.nodes = ['a', 'a']; // so that geometry area is possible
83402 var tempEntity = osmEntity(attrs);
83403 var tempGraph = coreGraph([tempEntity]);
83404 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
83405 var type = matched && matched.name() || utilDisplayType(id);
83408 geometry: tempEntity.geometry(tempGraph),
83410 name: d.display_name,
83411 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
83416 if (q.match(/^[0-9]+$/)) {
83417 // if query is just a number, possibly an OSM ID without a prefix
83421 type: _t('inspector.node'),
83427 type: _t('inspector.way'),
83432 geometry: 'relation',
83433 type: _t('inspector.relation'),
83441 function drawList() {
83442 var value = search.property('value');
83443 var results = features();
83444 list.classed('filtered', value.length);
83445 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'));
83446 resultsIndicator.append('span').attr('class', 'entity-name');
83447 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
83449 if (services.geocoder) {
83450 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'));
83453 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
83454 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
83455 list.selectAll('.feature-list-item').data([-1]).remove();
83456 var items = list.selectAll('.feature-list-item').data(results, function (d) {
83459 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
83460 var label = enter.append('div').attr('class', 'label');
83461 label.each(function (d) {
83462 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
83464 label.append('span').attr('class', 'entity-type').html(function (d) {
83467 label.append('span').attr('class', 'entity-name').html(function (d) {
83470 enter.style('opacity', 0).transition().style('opacity', 1);
83472 items.exit().remove();
83475 function mouseover(d3_event, d) {
83476 if (d.id === -1) return;
83477 utilHighlightEntities([d.id], true, context);
83480 function mouseout(d3_event, d) {
83481 if (d.id === -1) return;
83482 utilHighlightEntities([d.id], false, context);
83485 function click(d3_event, d) {
83486 d3_event.preventDefault();
83489 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
83490 } else if (d.entity) {
83491 utilHighlightEntities([d.id], false, context);
83492 context.enter(modeSelect(context, [d.entity.id]));
83493 context.map().zoomToEase(d.entity);
83495 // download, zoom to, and select the entity with the given ID
83496 context.zoomToEntity(d.id);
83500 function geocoderSearch() {
83501 services.geocoder.search(search.property('value'), function (err, resp) {
83502 _geocodeResults = resp || [];
83508 return featureList;
83511 function uiSectionEntityIssues(context) {
83512 var _entityIDs = [];
83515 var _activeIssueID;
83517 var section = uiSection('entity-issues', context).shouldDisplay(function () {
83518 return _issues.length > 0;
83519 }).label(function () {
83520 return _t('inspector.title_count', {
83521 title: _t.html('issues.list_title'),
83522 count: _issues.length
83524 }).disclosureContent(renderDisclosureContent);
83525 context.validator().on('validated.entity_issues', function () {
83526 // Refresh on validated events
83528 section.reRender();
83529 }).on('focusedIssue.entity_issues', function (issue) {
83530 makeActiveIssue(issue.id);
83533 function reloadIssues() {
83534 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
83535 includeDisabledRules: true
83539 function makeActiveIssue(issueID) {
83540 _activeIssueID = issueID;
83541 section.selection().selectAll('.issue-container').classed('active', function (d) {
83542 return d.id === _activeIssueID;
83546 function renderDisclosureContent(selection) {
83547 selection.classed('grouped-items-area', true);
83548 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
83549 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
83553 containers.exit().remove(); // Enter
83555 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
83556 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
83557 return 'issue severity-' + d.severity;
83558 }).on('mouseover.highlight', function (d3_event, d) {
83559 // don't hover-highlight the selected entity
83560 var ids = d.entityIds.filter(function (e) {
83561 return _entityIDs.indexOf(e) === -1;
83563 utilHighlightEntities(ids, true, context);
83564 }).on('mouseout.highlight', function (d3_event, d) {
83565 var ids = d.entityIds.filter(function (e) {
83566 return _entityIDs.indexOf(e) === -1;
83568 utilHighlightEntities(ids, false, context);
83570 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
83571 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
83572 makeActiveIssue(d.id); // expand only the clicked item
83574 var extent = d.extent(context.graph());
83577 var setZoom = Math.max(context.map().zoom(), 19);
83578 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
83581 textEnter.each(function (d) {
83582 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
83583 select(this).call(svgIcon(iconName, 'issue-icon'));
83585 textEnter.append('span').attr('class', 'issue-message');
83586 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
83587 infoButton.on('click', function (d3_event) {
83588 d3_event.stopPropagation();
83589 d3_event.preventDefault();
83590 this.blur(); // avoid keeping focus on the button - #4641
83592 var container = select(this.parentNode.parentNode.parentNode);
83593 var info = container.selectAll('.issue-info');
83594 var isExpanded = info.classed('expanded');
83597 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
83598 info.classed('expanded', false);
83601 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
83602 info.style('max-height', null);
83606 itemsEnter.append('ul').attr('class', 'issue-fix-list');
83607 containersEnter.append('div').attr('class', 'issue-info').style('max-height', '0').style('opacity', '0').each(function (d) {
83608 if (typeof d.reference === 'function') {
83609 select(this).call(d.reference);
83611 select(this).html(_t.html('inspector.no_documentation_key'));
83615 containers = containers.merge(containersEnter).classed('active', function (d) {
83616 return d.id === _activeIssueID;
83618 containers.selectAll('.issue-message').html(function (d) {
83619 return d.message(context);
83622 var fixLists = containers.selectAll('.issue-fix-list');
83623 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
83624 return d.fixes ? d.fixes(context) : [];
83625 }, function (fix) {
83628 fixes.exit().remove();
83629 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
83630 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
83631 // not all fixes are actionable
83632 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
83633 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
83635 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
83636 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
83638 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
83639 new Promise(function (resolve, reject) {
83640 d.onClick(context, resolve, reject);
83642 if (d.onClick.length <= 1) {
83643 // if the fix doesn't take any completion parameters then consider it resolved
83646 }).then(function () {
83647 // revalidate whenever the fix has finished running successfully
83648 context.validator().validate();
83650 }).on('mouseover.highlight', function (d3_event, d) {
83651 utilHighlightEntities(d.entityIds, true, context);
83652 }).on('mouseout.highlight', function (d3_event, d) {
83653 utilHighlightEntities(d.entityIds, false, context);
83655 buttons.each(function (d) {
83656 var iconName = d.icon || 'iD-icon-wrench';
83658 if (iconName.startsWith('maki')) {
83662 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
83664 buttons.append('span').attr('class', 'fix-message').html(function (d) {
83667 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
83669 }).attr('disabled', function (d) {
83670 return d.onClick ? null : 'true';
83671 }).attr('title', function (d) {
83672 if (d.disabledReason) {
83673 return d.disabledReason;
83680 section.entityIDs = function (val) {
83681 if (!arguments.length) return _entityIDs;
83683 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
83685 _activeIssueID = null;
83695 function uiPresetIcon() {
83700 var _sizeClass = 'medium';
83702 function isSmall() {
83703 return _sizeClass === 'small';
83706 function presetIcon(selection) {
83707 selection.each(render);
83710 function getIcon(p, geom) {
83711 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';
83714 function renderPointBorder(container, drawPoint) {
83715 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
83716 pointBorder.exit().remove();
83717 var pointBorderEnter = pointBorder.enter();
83720 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');
83721 pointBorder = pointBorderEnter.merge(pointBorder);
83724 function renderCircleFill(container, drawVertex) {
83725 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
83726 vertexFill.exit().remove();
83727 var vertexFillEnter = vertexFill.enter();
83731 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);
83732 vertexFill = vertexFillEnter.merge(vertexFill);
83735 function renderSquareFill(container, drawArea, tagClasses) {
83736 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
83737 fill.exit().remove();
83738 var fillEnter = fill.enter();
83739 var d = isSmall() ? 40 : 60;
83743 var c1 = (w - l) / 2;
83745 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));
83746 ['fill', 'stroke'].forEach(function (klass) {
83747 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));
83750 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
83751 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
83755 var rMidpoint = 1.25;
83756 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
83757 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
83761 fill = fillEnter.merge(fill);
83762 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
83763 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
83766 function renderLine(container, drawLine, tagClasses) {
83767 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
83768 line.exit().remove();
83769 var lineEnter = line.enter();
83770 var d = isSmall() ? 40 : 60; // draw the line parametrically
83774 var y = Math.round(d * 0.72);
83775 var l = Math.round(d * 0.6);
83777 var x1 = (w - l) / 2;
83779 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83780 ['casing', 'stroke'].forEach(function (klass) {
83781 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
83783 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
83784 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83786 line = lineEnter.merge(line);
83787 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
83788 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
83791 function renderRoute(container, drawRoute, p) {
83792 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
83793 route.exit().remove();
83794 var routeEnter = route.enter();
83795 var d = isSmall() ? 40 : 60; // draw the route parametrically
83799 var y1 = Math.round(d * 0.80);
83800 var y2 = Math.round(d * 0.68);
83801 var l = Math.round(d * 0.6);
83803 var x1 = (w - l) / 2;
83804 var x2 = x1 + l / 3;
83805 var x3 = x2 + l / 3;
83806 var x4 = x3 + l / 3;
83807 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83808 ['casing', 'stroke'].forEach(function (klass) {
83809 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
83810 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
83811 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
83813 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
83814 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83816 route = routeEnter.merge(route);
83819 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83820 var segmentPresetIDs = routeSegments[routeType];
83822 for (var i in segmentPresetIDs) {
83823 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83824 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83825 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
83826 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
83829 } // Route icons are drawn with a zigzag annotation underneath:
83833 // This dataset defines the styles that are used to draw the zigzag segments.
83836 var routeSegments = {
83837 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
83838 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83839 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83840 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
83841 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
83842 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
83843 hiking: ['highway/path', 'highway/path', 'highway/path'],
83844 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
83845 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
83846 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
83847 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
83848 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
83849 power: ['power/line', 'power/line', 'power/line'],
83850 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
83851 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
83852 train: ['railway/rail', 'railway/rail', 'railway/rail'],
83853 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
83854 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
83857 function render() {
83858 var p = _preset.apply(this, arguments);
83860 var geom = _geometry ? _geometry.apply(this, arguments) : null;
83862 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
83866 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
83867 var isFallback = isSmall() && p.isFallback && p.isFallback();
83868 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
83869 var picon = getIcon(p, geom);
83870 var isMaki = picon && /^maki-/.test(picon);
83871 var isTemaki = picon && /^temaki-/.test(picon);
83872 var isFa = picon && /^fa[srb]-/.test(picon);
83873 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
83874 var isCategory = !p.setTags;
83875 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
83876 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
83877 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
83878 var drawArea = picon && geom === 'area' && !isFallback;
83879 var drawRoute = picon && geom === 'route';
83880 var isFramed = drawVertex || drawArea || drawLine || drawRoute;
83881 var tags = !isCategory ? p.setTags({}, geom) : {};
83883 for (var k in tags) {
83884 if (tags[k] === '*') {
83889 var tagClasses = svgTagClasses().getClassesString(tags, '');
83890 var selection = select(this);
83891 var container = selection.selectAll('.preset-icon-container').data([0]);
83892 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
83893 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
83894 renderPointBorder(container, drawPoint);
83895 renderCircleFill(container, drawVertex);
83896 renderSquareFill(container, drawArea, tagClasses);
83897 renderLine(container, drawLine, tagClasses);
83898 renderRoute(container, drawRoute, p);
83899 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
83900 icon.exit().remove();
83901 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
83902 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
83903 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
83904 icon.selectAll('use').attr('href', '#' + picon + (isMaki ? isSmall() && geom === 'point' ? '-11' : '-15' : ''));
83905 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
83906 imageIcon.exit().remove();
83907 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
83908 return container.classed('showing-img', true);
83909 }).on('error', function () {
83910 return container.classed('showing-img', false);
83911 }).merge(imageIcon);
83912 imageIcon.attr('src', imageURL);
83915 presetIcon.preset = function (val) {
83916 if (!arguments.length) return _preset;
83917 _preset = utilFunctor(val);
83921 presetIcon.geometry = function (val) {
83922 if (!arguments.length) return _geometry;
83923 _geometry = utilFunctor(val);
83927 presetIcon.sizeClass = function (val) {
83928 if (!arguments.length) return _sizeClass;
83936 function uiSectionFeatureType(context) {
83937 var dispatch$1 = dispatch('choose');
83938 var _entityIDs = [];
83943 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
83945 function renderDisclosureContent(selection) {
83946 selection.classed('preset-list-item', true);
83947 selection.classed('mixed-types', _presets.length > 1);
83948 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
83949 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
83950 presetButton.append('div').attr('class', 'preset-icon-container');
83951 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83952 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
83953 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
83954 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
83956 if (_tagReference) {
83957 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
83958 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
83961 selection.selectAll('.preset-reset').on('click', function () {
83962 dispatch$1.call('choose', this, _presets);
83963 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
83964 d3_event.preventDefault();
83965 d3_event.stopPropagation();
83967 var geometries = entityGeometries();
83968 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')));
83969 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
83970 var label = selection.select('.label-inner');
83971 var nameparts = label.selectAll('.namepart').data(names, function (d) {
83974 nameparts.exit().remove();
83975 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
83980 section.entityIDs = function (val) {
83981 if (!arguments.length) return _entityIDs;
83986 section.presets = function (val) {
83987 if (!arguments.length) return _presets; // don't reload the same preset
83989 if (!utilArrayIdentical(val, _presets)) {
83992 if (_presets.length === 1) {
83993 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
84000 function entityGeometries() {
84003 for (var i in _entityIDs) {
84004 var geometry = context.graph().geometry(_entityIDs[i]);
84005 if (!counts[geometry]) counts[geometry] = 0;
84006 counts[geometry] += 1;
84009 return Object.keys(counts).sort(function (geom1, geom2) {
84010 return counts[geom2] - counts[geom1];
84014 return utilRebind(section, dispatch$1, 'on');
84017 // It borrows some code from uiHelp
84019 function uiFieldHelp(context, fieldName) {
84020 var fieldHelp = {};
84022 var _inspector = select(null);
84024 var _wrap = select(null);
84026 var _body = select(null);
84028 var fieldHelpKeys = {
84029 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']]]
84031 var fieldHelpHeadings = {};
84032 var replacements = {
84033 distField: _t.html('restriction.controls.distance'),
84034 viaField: _t.html('restriction.controls.via'),
84035 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
84036 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
84037 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
84038 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
84039 allowTurn: icon('#iD-turn-yes', 'inline turn'),
84040 restrictTurn: icon('#iD-turn-no', 'inline turn'),
84041 onlyTurn: icon('#iD-turn-only', 'inline turn')
84042 }; // For each section, squash all the texts into a single markdown document
84044 var docs = fieldHelpKeys[fieldName].map(function (key) {
84045 var helpkey = 'help.field.' + fieldName + '.' + key[0];
84046 var text = key[1].reduce(function (all, part) {
84047 var subkey = helpkey + '.' + part;
84048 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
84050 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
84052 return all + hhh + _t.html(subkey, replacements) + '\n\n';
84056 title: _t.html(helpkey + '.title'),
84057 html: marked_1(text.trim())
84064 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
84068 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
84069 _body.classed('hide', true);
84073 function clickHelp(index) {
84074 var d = docs[index];
84075 var tkeys = fieldHelpKeys[fieldName][index][1];
84077 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
84078 return i === index;
84081 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
84084 content.selectAll('p').attr('class', function (d, i) {
84086 }); // insert special content for certain help sections
84088 if (d.key === 'help.field.restrictions.inspecting') {
84089 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
84090 } else if (d.key === 'help.field.restrictions.modifying') {
84091 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
84095 fieldHelp.button = function (selection) {
84096 if (_body.empty()) return;
84097 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
84099 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
84100 d3_event.stopPropagation();
84101 d3_event.preventDefault();
84103 if (_body.classed('hide')) {
84111 function updatePosition() {
84112 var wrap = _wrap.node();
84114 var inspector = _inspector.node();
84116 var wRect = wrap.getBoundingClientRect();
84117 var iRect = inspector.getBoundingClientRect();
84119 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
84122 fieldHelp.body = function (selection) {
84123 // This control expects the field to have a form-field-input-wrap div
84124 _wrap = selection.selectAll('.form-field-input-wrap');
84125 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
84127 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
84128 if (_inspector.empty()) return;
84129 _body = _inspector.selectAll('.field-help-body').data([0]);
84131 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
84134 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
84135 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
84136 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
84137 d3_event.stopPropagation();
84138 d3_event.preventDefault();
84140 }).call(svgIcon('#iD-icon-close'));
84141 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
84142 var titles = docs.map(function (d) {
84145 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
84147 }).on('click', function (d3_event, d) {
84148 d3_event.stopPropagation();
84149 d3_event.preventDefault();
84150 clickHelp(titles.indexOf(d));
84152 enter.append('div').attr('class', 'field-help-content');
84153 _body = _body.merge(enter);
84160 function uiFieldCheck(field, context) {
84161 var dispatch$1 = dispatch('change');
84162 var options = field.strings && field.strings.options;
84168 var input = select(null);
84169 var text = select(null);
84170 var label = select(null);
84171 var reverser = select(null);
84175 var _entityIDs = [];
84180 for (var k in options) {
84181 values.push(k === 'undefined' ? undefined : k);
84182 texts.push(field.t.html('options.' + k, {
84183 'default': options[k]
84187 values = [undefined, 'yes'];
84188 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
84190 if (field.type !== 'defaultCheck') {
84192 texts.push(_t.html('inspector.check.no'));
84194 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
84197 function checkImpliedYes() {
84198 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
84199 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
84201 if (field.id === 'oneway') {
84202 var entity = context.entity(_entityIDs[0]);
84204 for (var key in entity.tags) {
84205 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
84206 _impliedYes = true;
84207 texts[0] = _t.html('presets.fields.oneway_yes.options.undefined');
84214 function reverserHidden() {
84215 if (!context.container().select('div.inspector-hover').empty()) return true;
84216 return !(_value === 'yes' || _impliedYes && !_value);
84219 function reverserSetText(selection) {
84220 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84221 if (reverserHidden() || !entity) return selection;
84222 var first = entity.first();
84223 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
84224 var pseudoDirection = first < last;
84225 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
84226 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
84230 var check = function check(selection) {
84232 label = selection.selectAll('.form-field-input-wrap').data([0]);
84233 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
84234 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
84235 enter.append('span').html(texts[0]).attr('class', 'value');
84237 if (field.type === 'onewayCheck') {
84238 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
84241 label = label.merge(enter);
84242 input = label.selectAll('input');
84243 text = label.selectAll('span.value');
84244 input.on('click', function (d3_event) {
84245 d3_event.stopPropagation();
84248 if (Array.isArray(_tags[field.key])) {
84249 if (values.indexOf('yes') !== -1) {
84250 t[field.key] = 'yes';
84252 t[field.key] = values[0];
84255 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
84256 } // Don't cycle through `alternating` or `reversible` states - #4970
84257 // (They are supported as translated strings, but should not toggle with clicks)
84260 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
84261 t[field.key] = values[0];
84264 dispatch$1.call('change', this, t);
84267 if (field.type === 'onewayCheck') {
84268 reverser = label.selectAll('.reverser');
84269 reverser.call(reverserSetText).on('click', function (d3_event) {
84270 d3_event.preventDefault();
84271 d3_event.stopPropagation();
84272 context.perform(function (graph) {
84273 for (var i in _entityIDs) {
84274 graph = actionReverse(_entityIDs[i])(graph);
84278 }, _t('operations.reverse.annotation.line', {
84280 })); // must manually revalidate since no 'change' event was called
84282 context.validator().validate();
84283 select(this).call(reverserSetText);
84288 check.entityIDs = function (val) {
84289 if (!arguments.length) return _entityIDs;
84294 check.tags = function (tags) {
84297 function isChecked(val) {
84298 return val !== 'no' && val !== '' && val !== undefined && val !== null;
84301 function textFor(val) {
84302 if (val === '') val = undefined;
84303 var index = values.indexOf(val);
84304 return index !== -1 ? texts[index] : '"' + val + '"';
84308 var isMixed = Array.isArray(tags[field.key]);
84309 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
84311 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
84315 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
84316 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
84317 label.classed('set', !!_value);
84319 if (field.type === 'onewayCheck') {
84320 reverser.classed('hide', reverserHidden()).call(reverserSetText);
84324 check.focus = function () {
84325 input.node().focus();
84328 return utilRebind(check, dispatch$1, 'on');
84331 function uiFieldCombo(field, context) {
84332 var dispatch$1 = dispatch('change');
84334 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
84336 var _isNetwork = field.type === 'networkCombo';
84338 var _isSemi = field.type === 'semiCombo';
84340 var _optstrings = field.strings && field.strings.options;
84342 var _optarray = field.options;
84344 var _snake_case = field.snake_case || field.snake_case === undefined;
84346 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
84348 var _container = select(null);
84350 var _inputWrap = select(null);
84352 var _input = select(null);
84354 var _comboData = [];
84355 var _multiData = [];
84356 var _entityIDs = [];
84362 var _staticPlaceholder; // initialize deprecated tags array
84365 var _dataDeprecated = [];
84366 _mainFileFetcher.get('deprecated').then(function (d) {
84367 _dataDeprecated = d;
84368 })["catch"](function () {
84370 }); // ensure multiCombo field.key ends with a ':'
84372 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
84376 function snake(s) {
84377 return s.replace(/\s+/g, '_');
84380 function unsnake(s) {
84381 return s.replace(/_+/g, ' ');
84384 function clean(s) {
84385 return s.split(';').map(function (s) {
84388 } // returns the tag value for a display value
84389 // (for multiCombo, dval should be the key suffix, not the entire key)
84392 function tagValue(dval) {
84393 dval = clean(dval || '');
84396 var found = _comboData.find(function (o) {
84397 return o.key && clean(o.value) === dval;
84405 if (field.type === 'typeCombo' && !dval) {
84409 return (_snake_case ? snake(dval) : dval) || undefined;
84410 } // returns the display value for a tag value
84411 // (for multiCombo, tval should be the key suffix, not the entire key)
84414 function displayValue(tval) {
84418 var found = _comboData.find(function (o) {
84419 return o.key === tval && o.value;
84423 return found.value;
84427 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
84431 return _snake_case ? unsnake(tval) : tval;
84432 } // Compute the difference between arrays of objects by `value` property
84434 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
84435 // > [{value:1}, {value:3}]
84439 function objectDifference(a, b) {
84440 return a.filter(function (d1) {
84441 return !b.some(function (d2) {
84442 return !d2.isMixed && d1.value === d2.value;
84447 function initCombo(selection, attachTo) {
84449 selection.attr('readonly', 'readonly');
84450 selection.call(_combobox, attachTo);
84451 setStaticValues(setPlaceholder);
84452 } else if (_optarray) {
84453 selection.call(_combobox, attachTo);
84454 setStaticValues(setPlaceholder);
84455 } else if (services.taginfo) {
84456 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
84457 setTaginfoValues('', setPlaceholder);
84461 function setStaticValues(callback) {
84462 if (!(_optstrings || _optarray)) return;
84465 _comboData = Object.keys(_optstrings).map(function (k) {
84466 var v = field.t('options.' + k, {
84467 'default': _optstrings[k]
84473 display: field.t.html('options.' + k, {
84474 'default': _optstrings[k]
84478 } else if (_optarray) {
84479 _comboData = _optarray.map(function (k) {
84480 var v = _snake_case ? unsnake(k) : k;
84489 _combobox.data(objectDifference(_comboData, _multiData));
84491 if (callback) callback(_comboData);
84494 function setTaginfoValues(q, callback) {
84495 var fn = _isMulti ? 'multikeys' : 'values';
84496 var query = (_isMulti ? field.key : '') + q;
84497 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
84499 if (hasCountryPrefix) {
84500 query = _countryCode + ':';
84504 debounce: q !== '',
84509 if (_entityIDs.length) {
84510 params.geometry = context.graph().geometry(_entityIDs[0]);
84513 services.taginfo[fn](params, function (err, data) {
84515 data = data.filter(function (d) {
84516 if (field.type === 'typeCombo' && d.value === 'yes') {
84517 // don't show the fallback value
84519 } // don't show values with very low usage
84522 return !d.count || d.count > 10;
84524 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
84526 if (deprecatedValues) {
84527 // don't suggest deprecated tag values
84528 data = data.filter(function (d) {
84529 return deprecatedValues.indexOf(d.value) === -1;
84533 if (hasCountryPrefix) {
84534 data = data.filter(function (d) {
84535 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
84537 } // hide the caret if there are no suggestions
84540 _container.classed('empty-combobox', data.length === 0);
84542 _comboData = data.map(function (d) {
84544 if (_isMulti) k = k.replace(field.key, '');
84545 var v = _snake_case ? unsnake(k) : k;
84549 title: _isMulti ? v : d.title
84552 _comboData = objectDifference(_comboData, _multiData);
84553 if (callback) callback(_comboData);
84557 function setPlaceholder(values) {
84558 if (_isMulti || _isSemi) {
84559 _staticPlaceholder = field.placeholder() || _t('inspector.add');
84561 var vals = values.map(function (d) {
84563 }).filter(function (s) {
84564 return s.length < 20;
84566 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
84569 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
84572 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
84573 _staticPlaceholder += '…';
84578 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
84579 ph = _t('inspector.multiple_values');
84581 ph = _staticPlaceholder;
84584 _container.selectAll('input').attr('placeholder', ph);
84587 function change() {
84591 if (_isMulti || _isSemi) {
84592 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
84594 _container.classed('active', false);
84596 utilGetSetValue(_input, '');
84597 var vals = val.split(';').filter(Boolean);
84598 if (!vals.length) return;
84601 utilArrayUniq(vals).forEach(function (v) {
84602 var key = (field.key || '') + v;
84605 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84606 // e.g. `language:de=main`
84607 var old = _tags[key];
84608 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
84611 key = context.cleanTagKey(key);
84612 field.keys.push(key);
84615 } else if (_isSemi) {
84616 var arr = _multiData.map(function (d) {
84620 arr = arr.concat(vals);
84621 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84624 window.setTimeout(function () {
84625 _input.node().focus();
84628 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
84630 if (!rawValue && Array.isArray(_tags[field.key])) return;
84631 val = context.cleanTagValue(tagValue(rawValue));
84632 t[field.key] = val || undefined;
84635 dispatch$1.call('change', this, t);
84638 function removeMultikey(d3_event, d) {
84639 d3_event.preventDefault();
84640 d3_event.stopPropagation();
84644 t[d.key] = undefined;
84645 } else if (_isSemi) {
84646 var arr = _multiData.map(function (md) {
84647 return md.key === d.key ? null : md.key;
84648 }).filter(Boolean);
84650 arr = utilArrayUniq(arr);
84651 t[field.key] = arr.length ? arr.join(';') : undefined;
84654 dispatch$1.call('change', this, t);
84657 function combo(selection) {
84658 _container = selection.selectAll('.form-field-input-wrap').data([0]);
84659 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
84660 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
84662 if (_isMulti || _isSemi) {
84663 _container = _container.selectAll('.chiplist').data([0]);
84664 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations field
84665 // to mimic highway exit signs
84667 if (field.key === 'destination') {
84668 listClass += ' full-line-chips';
84671 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
84672 window.setTimeout(function () {
84673 _input.node().focus();
84675 }).merge(_container);
84676 _inputWrap = _container.selectAll('.input-wrap').data([0]);
84677 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
84678 _input = _inputWrap.selectAll('input').data([0]);
84680 _input = _container.selectAll('input').data([0]);
84683 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
84686 var extent = combinedEntityExtent();
84687 var countryCode = extent && iso1A2Code(extent.center());
84688 _countryCode = countryCode && countryCode.toLowerCase();
84691 _input.on('change', change).on('blur', change);
84693 _input.on('keydown.field', function (d3_event) {
84694 switch (d3_event.keyCode) {
84697 _input.node().blur(); // blurring also enters the value
84700 d3_event.stopPropagation();
84705 if (_isMulti || _isSemi) {
84706 _combobox.on('accept', function () {
84707 _input.node().blur();
84709 _input.node().focus();
84712 _input.on('focus', function () {
84713 _container.classed('active', true);
84718 combo.tags = function (tags) {
84721 if (_isMulti || _isSemi) {
84726 // Build _multiData array containing keys already set..
84727 for (var k in tags) {
84728 if (field.key && k.indexOf(field.key) !== 0) continue;
84729 if (!field.key && field.keys.indexOf(k) === -1) continue;
84731 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
84732 var suffix = field.key ? k.substr(field.key.length) : k;
84736 value: displayValue(suffix),
84737 isMixed: Array.isArray(v)
84742 // Set keys for form-field modified (needed for undo and reset buttons)..
84743 field.keys = _multiData.map(function (d) {
84745 }); // limit the input length so it fits after prepending the key prefix
84747 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84749 maxLength = context.maxCharsForTagKey();
84751 } else if (_isSemi) {
84752 var allValues = [];
84755 if (Array.isArray(tags[field.key])) {
84756 tags[field.key].forEach(function (tagVal) {
84757 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84758 allValues = allValues.concat(thisVals);
84760 if (!commonValues) {
84761 commonValues = thisVals;
84763 commonValues = commonValues.filter(function (value) {
84764 return thisVals.includes(value);
84768 allValues = utilArrayUniq(allValues).filter(Boolean);
84770 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84771 commonValues = allValues;
84774 _multiData = allValues.map(function (v) {
84777 value: displayValue(v),
84778 isMixed: !commonValues.includes(v)
84781 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
84783 maxLength = context.maxCharsForTagValue() - currLength;
84785 if (currLength > 0) {
84786 // account for the separator if a new value will be appended to existing
84789 } // a negative maxlength doesn't make sense
84792 maxLength = Math.max(0, maxLength);
84793 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
84794 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
84796 var available = objectDifference(_comboData, _multiData);
84798 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
84799 // translateable _optstrings and they're all currently used,
84800 // or if the field is already at its character limit
84803 var hideAdd = _optstrings && !available.length || maxLength <= 0;
84805 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
84808 var chips = _container.selectAll('.chip').data(_multiData);
84810 chips.exit().remove();
84811 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
84812 enter.append('span');
84814 chips = chips.merge(enter).order().classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
84816 }).attr('title', function (d) {
84817 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84820 if (allowDragAndDrop) {
84821 registerDragAndDrop(chips);
84824 chips.select('span').html(function (d) {
84827 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
84829 var isMixed = Array.isArray(tags[field.key]);
84830 var mixedValues = isMixed && tags[field.key].map(function (val) {
84831 return displayValue(val);
84832 }).filter(Boolean);
84833 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);
84837 function registerDragAndDrop(selection) {
84838 // allow drag and drop re-ordering of chips
84839 var dragOrigin, targetIndex;
84840 selection.call(d3_drag().on('start', function (d3_event) {
84845 targetIndex = null;
84846 }).on('drag', function (d3_event) {
84847 var x = d3_event.x - dragOrigin.x,
84848 y = d3_event.y - dragOrigin.y;
84849 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
84850 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84851 var index = selection.nodes().indexOf(this);
84852 select(this).classed('dragging', true);
84853 targetIndex = null;
84854 var targetIndexOffsetTop = null;
84855 var draggedTagWidth = select(this).node().offsetWidth;
84857 if (field.key === 'destination') {
84858 // meaning tags are full width
84859 _container.selectAll('.chip').style('transform', function (d2, index2) {
84860 var node = select(this).node();
84862 if (index === index2) {
84863 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
84864 } else if (index2 > index && d3_event.y > node.offsetTop) {
84865 if (targetIndex === null || index2 > targetIndex) {
84866 targetIndex = index2;
84869 return 'translateY(-100%)'; // move the dragged tag down the order
84870 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
84871 if (targetIndex === null || index2 < targetIndex) {
84872 targetIndex = index2;
84875 return 'translateY(100%)';
84881 _container.selectAll('.chip').each(function (d2, index2) {
84882 var node = select(this).node(); // check the cursor is in the bounding box
84884 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) {
84885 targetIndex = index2;
84886 targetIndexOffsetTop = node.offsetTop;
84888 }).style('transform', function (d2, index2) {
84889 var node = select(this).node();
84891 if (index === index2) {
84892 return 'translate(' + x + 'px, ' + y + 'px)';
84893 } // only translate tags in the same row
84896 if (node.offsetTop === targetIndexOffsetTop) {
84897 if (index2 < index && index2 >= targetIndex) {
84898 return 'translateX(' + draggedTagWidth + 'px)';
84899 } else if (index2 > index && index2 <= targetIndex) {
84900 return 'translateX(-' + draggedTagWidth + 'px)';
84907 }).on('end', function () {
84908 if (!select(this).classed('dragging')) {
84912 var index = selection.nodes().indexOf(this);
84913 select(this).classed('dragging', false);
84915 _container.selectAll('.chip').style('transform', null);
84917 if (typeof targetIndex === 'number') {
84918 var element = _multiData[index];
84920 _multiData.splice(index, 1);
84922 _multiData.splice(targetIndex, 0, element);
84926 if (_multiData.length) {
84927 t[field.key] = _multiData.map(function (element) {
84928 return element.key;
84931 t[field.key] = undefined;
84934 dispatch$1.call('change', this, t);
84937 dragOrigin = undefined;
84938 targetIndex = undefined;
84942 combo.focus = function () {
84943 _input.node().focus();
84946 combo.entityIDs = function (val) {
84947 if (!arguments.length) return _entityIDs;
84952 function combinedEntityExtent() {
84953 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84956 return utilRebind(combo, dispatch$1, 'on');
84959 function uiFieldText(field, context) {
84960 var dispatch$1 = dispatch('change');
84961 var input = select(null);
84962 var outlinkButton = select(null);
84963 var _entityIDs = [];
84967 var _phoneFormats = {};
84969 if (field.type === 'tel') {
84970 _mainFileFetcher.get('phone_formats').then(function (d) {
84972 updatePhonePlaceholder();
84973 })["catch"](function () {
84978 function i(selection) {
84979 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84980 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84981 var isLocked = preset && preset.suggestion && field.id === 'brand';
84982 field.locked(isLocked);
84983 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84984 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84985 input = wrap.selectAll('input').data([0]);
84986 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);
84987 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
84989 if (field.type === 'tel') {
84990 updatePhonePlaceholder();
84991 } else if (field.type === 'number') {
84992 var rtl = _mainLocalizer.textDirection() === 'rtl';
84993 input.attr('type', 'text');
84994 var inc = field.increment;
84995 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
84996 buttons.enter().append('button').attr('class', function (d) {
84997 var which = d > 0 ? 'increment' : 'decrement';
84998 return 'form-field-button ' + which;
84999 }).merge(buttons).on('click', function (d3_event, d) {
85000 d3_event.preventDefault();
85001 var raw_vals = input.node().value || '0';
85002 var vals = raw_vals.split(';');
85003 vals = vals.map(function (v) {
85004 var num = parseFloat(v.trim(), 10);
85005 return isFinite(num) ? clamped(num + d) : v.trim();
85007 input.node().value = vals.join(';');
85010 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
85011 input.attr('type', 'text');
85012 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
85013 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
85014 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
85016 if (domainResults.length >= 2 && domainResults[1]) {
85017 var domain = domainResults[1];
85018 return _t('icons.view_on', {
85024 }).on('click', function (d3_event) {
85025 d3_event.preventDefault();
85026 var value = validIdentifierValueForLink();
85029 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
85030 window.open(url, '_blank');
85032 }).merge(outlinkButton);
85036 function updatePhonePlaceholder() {
85037 if (input.empty() || !Object.keys(_phoneFormats).length) return;
85038 var extent = combinedEntityExtent();
85039 var countryCode = extent && iso1A2Code(extent.center());
85041 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
85043 if (format) input.attr('placeholder', format);
85046 function validIdentifierValueForLink() {
85047 if (field.type === 'identifier' && field.pattern) {
85048 var value = utilGetSetValue(input).trim().split(';')[0];
85049 return value && value.match(new RegExp(field.pattern));
85053 } // clamp number to min/max
85056 function clamped(num) {
85057 if (field.minValue !== undefined) {
85058 num = Math.max(num, field.minValue);
85061 if (field.maxValue !== undefined) {
85062 num = Math.min(num, field.maxValue);
85068 function change(onInput) {
85069 return function () {
85071 var val = utilGetSetValue(input);
85072 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
85074 if (!val && Array.isArray(_tags[field.key])) return;
85077 if (field.type === 'number' && val) {
85078 var vals = val.split(';');
85079 vals = vals.map(function (v) {
85080 var num = parseFloat(v.trim(), 10);
85081 return isFinite(num) ? clamped(num) : v.trim();
85083 val = vals.join(';');
85086 utilGetSetValue(input, val);
85089 t[field.key] = val || undefined;
85090 dispatch$1.call('change', this, t, onInput);
85094 i.entityIDs = function (val) {
85095 if (!arguments.length) return _entityIDs;
85100 i.tags = function (tags) {
85102 var isMixed = Array.isArray(tags[field.key]);
85103 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);
85105 if (outlinkButton && !outlinkButton.empty()) {
85106 var disabled = !validIdentifierValueForLink();
85107 outlinkButton.classed('disabled', disabled);
85111 i.focus = function () {
85112 var node = input.node();
85113 if (node) node.focus();
85116 function combinedEntityExtent() {
85117 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85120 return utilRebind(i, dispatch$1, 'on');
85123 function uiFieldAccess(field, context) {
85124 var dispatch$1 = dispatch('change');
85125 var items = select(null);
85129 function access(selection) {
85130 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85131 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85132 var list = wrap.selectAll('ul').data([0]);
85133 list = list.enter().append('ul').attr('class', 'rows').merge(list);
85134 items = list.selectAll('li').data(field.keys); // Enter
85136 var enter = items.enter().append('li').attr('class', function (d) {
85137 return 'labeled-input preset-access-' + d;
85139 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
85140 return 'preset-input-access-' + d;
85141 }).html(function (d) {
85142 return field.t.html('types.' + d);
85144 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
85145 return 'preset-input-access preset-input-access-' + d;
85146 }).call(utilNoAuto).each(function (d) {
85147 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
85150 items = items.merge(enter);
85151 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
85154 function change(d3_event, d) {
85156 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
85158 if (!value && typeof _tags[d] !== 'string') return;
85159 tag[d] = value || undefined;
85160 dispatch$1.call('change', this, tag);
85163 access.options = function (type) {
85164 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
85166 if (type !== 'access') {
85167 options.unshift('yes');
85168 options.push('designated');
85170 if (type === 'bicycle') {
85171 options.push('dismount');
85175 return options.map(function (option) {
85177 title: field.t('options.' + option + '.description'),
85183 var placeholdersByHighway = {
85185 foot: 'designated',
85186 motor_vehicle: 'no'
85190 motor_vehicle: 'no',
85196 motor_vehicle: 'no'
85199 motor_vehicle: 'no',
85200 bicycle: 'designated'
85203 motor_vehicle: 'no',
85204 horse: 'designated'
85208 motor_vehicle: 'no',
85214 motor_vehicle: 'yes',
85219 motor_vehicle: 'yes'
85223 motor_vehicle: 'yes',
85229 motor_vehicle: 'yes',
85235 motor_vehicle: 'yes',
85241 motor_vehicle: 'yes',
85247 motor_vehicle: 'yes',
85253 motor_vehicle: 'yes',
85259 motor_vehicle: 'yes',
85264 motor_vehicle: 'yes'
85268 motor_vehicle: 'yes',
85274 motor_vehicle: 'yes',
85280 motor_vehicle: 'yes',
85286 access.tags = function (tags) {
85288 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
85289 return typeof tags[d] === 'string' ? tags[d] : '';
85290 }).classed('mixed', function (d) {
85291 return tags[d] && Array.isArray(tags[d]);
85292 }).attr('title', function (d) {
85293 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
85294 }).attr('placeholder', function (d) {
85295 if (tags[d] && Array.isArray(tags[d])) {
85296 return _t('inspector.multiple_values');
85299 if (d === 'access') {
85303 if (tags.access && typeof tags.access === 'string') {
85304 return tags.access;
85307 if (tags.highway) {
85308 if (typeof tags.highway === 'string') {
85309 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
85310 return placeholdersByHighway[tags.highway][d];
85313 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
85314 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
85315 }).filter(Boolean);
85317 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
85318 // if all the highway values have the same implied access for this type then use that
85319 return impliedAccesses[0];
85324 return field.placeholder();
85328 access.focus = function () {
85329 items.selectAll('.preset-input-access').node().focus();
85332 return utilRebind(access, dispatch$1, 'on');
85335 function uiFieldAddress(field, context) {
85336 var dispatch$1 = dispatch('change');
85338 var _selection = select(null);
85340 var _wrap = select(null);
85342 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
85344 var _entityIDs = [];
85350 var _addressFormats = [{
85351 format: [['housenumber', 'street'], ['city', 'postcode']]
85353 _mainFileFetcher.get('address_formats').then(function (d) {
85354 _addressFormats = d;
85356 if (!_selection.empty()) {
85357 _selection.call(address);
85359 })["catch"](function () {
85363 function getNearStreets() {
85364 var extent = combinedEntityExtent();
85365 var l = extent.center();
85366 var box = geoExtent(l).padByMeters(200);
85367 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
85368 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
85369 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
85371 title: d.tags.name,
85372 value: d.tags.name,
85373 dist: choice.distance
85375 }).sort(function (a, b) {
85376 return a.dist - b.dist;
85378 return utilArrayUniqBy(streets, 'value');
85380 function isAddressable(d) {
85381 return d.tags.highway && d.tags.name && d.type === 'way';
85385 function getNearCities() {
85386 var extent = combinedEntityExtent();
85387 var l = extent.center();
85388 var box = geoExtent(l).padByMeters(200);
85389 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
85391 title: d.tags['addr:city'] || d.tags.name,
85392 value: d.tags['addr:city'] || d.tags.name,
85393 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85395 }).sort(function (a, b) {
85396 return a.dist - b.dist;
85398 return utilArrayUniqBy(cities, 'value');
85400 function isAddressable(d) {
85402 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
85403 if (d.tags.border_type === 'city') return true;
85404 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
85407 if (d.tags['addr:city']) return true;
85412 function getNearValues(key) {
85413 var extent = combinedEntityExtent();
85414 var l = extent.center();
85415 var box = geoExtent(l).padByMeters(200);
85416 var results = context.history().intersects(box).filter(function hasTag(d) {
85417 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
85418 }).map(function (d) {
85420 title: d.tags[key],
85421 value: d.tags[key],
85422 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85424 }).sort(function (a, b) {
85425 return a.dist - b.dist;
85427 return utilArrayUniqBy(results, 'value');
85430 function updateForCountryCode() {
85431 if (!_countryCode) return;
85434 for (var i = 0; i < _addressFormats.length; i++) {
85435 var format = _addressFormats[i];
85437 if (!format.countryCodes) {
85438 addressFormat = format; // choose the default format, keep going
85439 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85440 addressFormat = format; // choose the country format, stop here
85446 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
85447 var widths = addressFormat.widths || {
85448 housenumber: 1 / 3,
85456 // Normalize widths.
85457 var total = r.reduce(function (sum, key) {
85458 return sum + (widths[key] || 0.5);
85460 return r.map(function (key) {
85463 width: (widths[key] || 0.5) / total
85468 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
85469 return d.toString();
85472 rows.exit().remove();
85473 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
85474 return 'addr-' + d.id;
85475 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
85476 return d.width * 100 + '%';
85479 function addDropdown(d) {
85480 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
85482 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
85483 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
85484 callback(nearValues('addr:' + d.id));
85488 _wrap.selectAll('input').on('blur', change()).on('change', change());
85490 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
85492 if (_tags) updateTags(_tags);
85495 function address(selection) {
85496 _selection = selection;
85497 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85498 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
85499 var extent = combinedEntityExtent();
85504 if (context.inIntro()) {
85505 // localize the address format for the walkthrough
85506 countryCode = _t('intro.graph.countrycode');
85508 var center = extent.center();
85509 countryCode = iso1A2Code(center);
85513 _countryCode = countryCode.toLowerCase();
85514 updateForCountryCode();
85519 function change(onInput) {
85520 return function () {
85523 _wrap.selectAll('input').each(function (subfield) {
85524 var key = field.key + ':' + subfield.id;
85525 var value = this.value;
85526 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
85528 if (Array.isArray(_tags[key]) && !value) return;
85529 tags[key] = value || undefined;
85532 dispatch$1.call('change', this, tags, onInput);
85536 function updatePlaceholder(inputSelection) {
85537 return inputSelection.attr('placeholder', function (subfield) {
85538 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85539 return _t('inspector.multiple_values');
85542 if (_countryCode) {
85543 var localkey = subfield.id + '!' + _countryCode;
85544 var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85545 return addrField.t('placeholders.' + tkey);
85550 function updateTags(tags) {
85551 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85552 var val = tags[field.key + ':' + subfield.id];
85553 return typeof val === 'string' ? val : '';
85554 }).attr('title', function (subfield) {
85555 var val = tags[field.key + ':' + subfield.id];
85556 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85557 }).classed('mixed', function (subfield) {
85558 return Array.isArray(tags[field.key + ':' + subfield.id]);
85559 }).call(updatePlaceholder);
85562 function combinedEntityExtent() {
85563 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85566 address.entityIDs = function (val) {
85567 if (!arguments.length) return _entityIDs;
85572 address.tags = function (tags) {
85577 address.focus = function () {
85578 var node = _wrap.selectAll('input').node();
85580 if (node) node.focus();
85583 return utilRebind(address, dispatch$1, 'on');
85586 function uiFieldCycleway(field, context) {
85587 var dispatch$1 = dispatch('change');
85588 var items = select(null);
85589 var wrap = select(null);
85593 function cycleway(selection) {
85594 function stripcolon(s) {
85595 return s.replace(':', '');
85598 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85599 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85600 var div = wrap.selectAll('ul').data([0]);
85601 div = div.enter().append('ul').attr('class', 'rows').merge(div);
85602 var keys = ['cycleway:left', 'cycleway:right'];
85603 items = div.selectAll('li').data(keys);
85604 var enter = items.enter().append('li').attr('class', function (d) {
85605 return 'labeled-input preset-cycleway-' + stripcolon(d);
85607 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
85608 return 'preset-input-cycleway-' + stripcolon(d);
85609 }).html(function (d) {
85610 return field.t.html('types.' + d);
85612 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
85613 return 'preset-input-cycleway preset-input-' + stripcolon(d);
85614 }).call(utilNoAuto).each(function (d) {
85615 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
85617 items = items.merge(enter); // Update
85619 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
85622 function change(d3_event, key) {
85623 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
85625 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
85627 if (newValue === 'none' || newValue === '') {
85628 newValue = undefined;
85631 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85632 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85634 if (otherValue && Array.isArray(otherValue)) {
85635 // we must always have an explicit value for comparison
85636 otherValue = otherValue[0];
85639 if (otherValue === 'none' || otherValue === '') {
85640 otherValue = undefined;
85643 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
85644 // sides the same way
85646 if (newValue === otherValue) {
85648 cycleway: newValue,
85649 'cycleway:left': undefined,
85650 'cycleway:right': undefined
85653 // Always set both left and right as changing one can affect the other
85655 cycleway: undefined
85657 tag[key] = newValue;
85658 tag[otherKey] = otherValue;
85661 dispatch$1.call('change', this, tag);
85664 cycleway.options = function () {
85665 return Object.keys(field.strings.options).map(function (option) {
85667 title: field.t('options.' + option + '.description'),
85673 cycleway.tags = function (tags) {
85674 _tags = tags; // If cycleway is set, use that instead of individual values
85676 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85677 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
85678 if (commonValue) return commonValue;
85679 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85680 }).attr('title', function (d) {
85681 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85684 if (Array.isArray(tags.cycleway)) {
85685 vals = vals.concat(tags.cycleway);
85688 if (Array.isArray(tags[d])) {
85689 vals = vals.concat(tags[d]);
85692 return vals.filter(Boolean).join('\n');
85696 }).attr('placeholder', function (d) {
85697 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85698 return _t('inspector.multiple_values');
85701 return field.placeholder();
85702 }).classed('mixed', function (d) {
85703 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85707 cycleway.focus = function () {
85708 var node = wrap.selectAll('input').node();
85709 if (node) node.focus();
85712 return utilRebind(cycleway, dispatch$1, 'on');
85715 function uiFieldLanes(field, context) {
85716 var dispatch$1 = dispatch('change');
85717 var LANE_WIDTH = 40;
85718 var LANE_HEIGHT = 200;
85719 var _entityIDs = [];
85721 function lanes(selection) {
85722 var lanesData = context.entity(_entityIDs[0]).lanes();
85724 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85725 selection.call(lanes.off);
85729 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85730 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85731 var surface = wrap.selectAll('.surface').data([0]);
85732 var d = utilGetDimensions(wrap);
85733 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85734 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
85735 var lanesSelection = surface.selectAll('.lanes').data([0]);
85736 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
85737 lanesSelection.attr('transform', function () {
85738 return 'translate(' + freeSpace / 2 + ', 0)';
85740 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
85741 lane.exit().remove();
85742 var enter = lane.enter().append('g').attr('class', 'lane');
85743 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
85744 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
85745 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
85746 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
85747 lane = lane.merge(enter);
85748 lane.attr('transform', function (d) {
85749 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
85751 lane.select('.forward').style('visibility', function (d) {
85752 return d.direction === 'forward' ? 'visible' : 'hidden';
85754 lane.select('.bothways').style('visibility', function (d) {
85755 return d.direction === 'bothways' ? 'visible' : 'hidden';
85757 lane.select('.backward').style('visibility', function (d) {
85758 return d.direction === 'backward' ? 'visible' : 'hidden';
85762 lanes.entityIDs = function (val) {
85766 lanes.tags = function () {};
85768 lanes.focus = function () {};
85770 lanes.off = function () {};
85772 return utilRebind(lanes, dispatch$1, 'on');
85774 uiFieldLanes.supportsMultiselection = false;
85776 var _languagesArray = [];
85777 function uiFieldLocalized(field, context) {
85778 var dispatch$1 = dispatch('change', 'input');
85779 var wikipedia = services.wikipedia;
85780 var input = select(null);
85781 var localizedInputs = select(null);
85785 var _tags; // A concern here in switching to async data means that _languagesArray will not
85786 // be available the first time through, so things like the fetchers and
85787 // the language() function will not work immediately.
85790 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
85793 var _territoryLanguages = {};
85794 _mainFileFetcher.get('territory_languages').then(function (d) {
85795 _territoryLanguages = d;
85796 })["catch"](function () {
85799 var allSuggestions = _mainPresetIndex.collection.filter(function (p) {
85800 return p.suggestion === true;
85801 }); // reuse these combos
85803 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
85804 var brandCombo = uiCombobox(context, 'localized-brand').canAutocomplete(false).minItems(1);
85806 var _selection = select(null);
85808 var _multilingual = [];
85810 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
85814 var _entityIDs = [];
85816 function loadLanguagesArray(dataLanguages) {
85817 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
85819 var replacements = {
85821 // in OSM, `sr` implies Cyrillic
85822 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
85826 for (var code in dataLanguages) {
85827 if (replacements[code] === false) continue;
85828 var metaCode = code;
85829 if (replacements[code]) metaCode = replacements[code];
85831 _languagesArray.push({
85832 localName: _mainLocalizer.languageName(metaCode, {
85835 nativeName: dataLanguages[metaCode].nativeName,
85837 label: _mainLocalizer.languageName(metaCode)
85842 function calcLocked() {
85843 // only lock the Name field
85844 var isLocked = field.id === 'name' && _entityIDs.length && // lock the field if any feature needs it
85845 _entityIDs.some(function (entityID) {
85846 var entity = context.graph().hasEntity(entityID);
85847 if (!entity) return false;
85849 var original = context.graph().base().entities[_entityIDs[0]];
85851 var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name; // if the name was already edited manually then allow further editing
85853 if (!hasOriginalName) return false; // features linked to Wikidata are likely important and should be protected
85855 if (entity.tags.wikidata) return true; // assume the name has already been confirmed if its source has been researched
85857 if (entity.tags['name:etymology:wikidata']) return true;
85858 var preset = _mainPresetIndex.match(entity, context.graph());
85859 var isSuggestion = preset && preset.suggestion;
85860 var showsBrand = preset && preset.originalFields.filter(function (d) {
85861 return d.id === 'brand';
85862 }).length; // protect standardized brand names
85864 return isSuggestion && !showsBrand;
85867 field.locked(isLocked);
85868 } // update _multilingual, maintaining the existing order
85871 function calcMultilingual(tags) {
85872 var existingLangsOrdered = _multilingual.map(function (item) {
85876 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85878 for (var k in tags) {
85879 var m = k.match(/^(.*):(.+)$/);
85881 if (m && m[1] === field.key && m[2]) {
85887 if (existingLangs.has(item.lang)) {
85888 // update the value
85889 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85890 existingLangs["delete"](item.lang);
85892 _multilingual.push(item);
85897 _multilingual = _multilingual.filter(function (item) {
85898 return !item.lang || !existingLangs.has(item.lang);
85902 function localized(selection) {
85903 _selection = selection;
85905 var isLocked = field.locked();
85906 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85907 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85908 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
85910 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85911 input = wrap.selectAll('.localized-main').data([0]); // enter/update
85913 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
85915 if (preset && field.id === 'name') {
85916 var pTag = preset.id.split('/', 2);
85917 var pKey = pTag[0];
85918 var pValue = pTag[1];
85920 if (!preset.suggestion) {
85921 // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85922 // This code attempts to determine if the matched preset is the
85923 // kind of preset that even can benefit from name suggestions..
85924 // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85925 // - false = churches, parks, hospitals, etc. (things not in the index)
85926 var isFallback = preset.isFallback();
85927 var goodSuggestions = allSuggestions.filter(function (s) {
85928 if (isFallback) return true;
85929 var sTag = s.id.split('/', 2);
85930 var sKey = sTag[0];
85931 var sValue = sTag[1];
85932 return pKey === sKey && (!pValue || pValue === sValue);
85933 }); // Show the suggestions.. If the user picks one, change the tags..
85935 if (allSuggestions.length && goodSuggestions.length) {
85936 input.on('blur.localized', checkBrandOnBlur).call(brandCombo.fetcher(fetchBrandNames(preset, allSuggestions)).on('accept', acceptBrand).on('cancel', cancelBrand));
85941 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
85942 var translateButton = wrap.selectAll('.localized-add').data([0]);
85943 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
85944 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
85946 if (_tags && !_multilingual.length) {
85947 calcMultilingual(_tags);
85950 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
85951 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
85952 localizedInputs.call(renderMultilingual);
85953 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.
85954 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85955 // So compare the current field value against the suggestions one last time.
85957 function checkBrandOnBlur() {
85958 var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85959 if (!latest) return; // deleting the entity blurred the field?
85961 var preset = _mainPresetIndex.match(latest, context.graph());
85962 if (preset && preset.suggestion) return; // already accepted
85964 var name = utilGetSetValue(input).trim();
85965 var matched = allSuggestions.filter(function (s) {
85966 return name === s.name();
85969 if (matched.length === 1) {
85971 suggestion: matched[0]
85978 function acceptBrand(d) {
85979 var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85981 if (!d || !entity) {
85986 var tags = entity.tags;
85987 var geometry = entity.geometry(context.graph());
85988 var removed = preset.unsetTags(tags, geometry);
85990 for (var k in tags) {
85991 tags[k] = removed[k]; // set removed tags to `undefined`
85994 tags = d.suggestion.setTags(tags, geometry);
85995 utilGetSetValue(input, tags.name);
85996 dispatch$1.call('change', this, tags);
85997 } // user hit escape
86000 function cancelBrand() {
86001 var name = utilGetSetValue(input);
86002 dispatch$1.call('change', this, {
86007 function fetchBrandNames(preset, suggestions) {
86008 var pTag = preset.id.split('/', 2);
86009 var pKey = pTag[0];
86010 var pValue = pTag[1];
86011 return function (value, callback) {
86014 if (value && value.length > 2) {
86015 for (var i = 0; i < suggestions.length; i++) {
86016 var s = suggestions[i]; // don't suggest brands from incompatible countries
86018 if (_countryCode && s.countryCodes && s.countryCodes.indexOf(_countryCode) === -1) continue;
86019 var sTag = s.id.split('/', 2);
86020 var sKey = sTag[0];
86021 var sValue = sTag[1];
86022 var subtitle = s.subtitle();
86023 var name = s.name();
86024 if (subtitle) name += ' – ' + subtitle;
86025 var dist = utilEditDistance(value, name.substring(0, value.length));
86026 var matchesPreset = pKey === sKey && (!pValue || pValue === sValue);
86028 if (dist < 1 || matchesPreset && dist < 3) {
86032 display: s.nameLabel() + (subtitle ? ' – ' + s.subtitleLabel() : ''),
86034 dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
86041 results.sort(function (a, b) {
86042 return a.dist - b.dist;
86046 results = results.slice(0, 10);
86051 function addNew(d3_event) {
86052 d3_event.preventDefault();
86053 if (field.locked()) return;
86054 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
86056 var langExists = _multilingual.find(function (datum) {
86057 return datum.lang === defaultLang;
86060 var isLangEn = defaultLang.indexOf('en') > -1;
86062 if (isLangEn || langExists) {
86064 langExists = _multilingual.find(function (datum) {
86065 return datum.lang === defaultLang;
86070 // prepend the value so it appears at the top
86071 _multilingual.unshift({
86076 localizedInputs.call(renderMultilingual);
86080 function change(onInput) {
86081 return function (d3_event) {
86082 if (field.locked()) {
86083 d3_event.preventDefault();
86087 var val = utilGetSetValue(select(this));
86088 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
86090 if (!val && Array.isArray(_tags[field.key])) return;
86092 t[field.key] = val || undefined;
86093 dispatch$1.call('change', this, t, onInput);
86098 function key(lang) {
86099 return field.key + ':' + lang;
86102 function changeLang(d3_event, d) {
86103 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
86105 var lang = utilGetSetValue(select(this)).toLowerCase();
86107 var language = _languagesArray.find(function (d) {
86108 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
86111 if (language) lang = language.code;
86113 if (d.lang && d.lang !== lang) {
86114 tags[key(d.lang)] = undefined;
86117 var newKey = lang && context.cleanTagKey(key(lang));
86118 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
86120 if (newKey && value) {
86121 tags[newKey] = value;
86122 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
86123 tags[newKey] = _wikiTitles[d.lang];
86127 dispatch$1.call('change', this, tags);
86130 function changeValue(d3_event, d) {
86131 if (!d.lang) return;
86132 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
86134 if (!value && Array.isArray(d.value)) return;
86136 t[key(d.lang)] = value;
86138 dispatch$1.call('change', this, t);
86141 function fetchLanguages(value, cb) {
86142 var v = value.toLowerCase(); // show the user's language first
86144 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
86146 if (_countryCode && _territoryLanguages[_countryCode]) {
86147 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
86150 var langItems = [];
86151 langCodes.forEach(function (code) {
86152 var langItem = _languagesArray.find(function (item) {
86153 return item.code === code;
86156 if (langItem) langItems.push(langItem);
86158 langItems = utilArrayUniq(langItems.concat(_languagesArray));
86159 cb(langItems.filter(function (d) {
86160 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;
86161 }).map(function (d) {
86168 function renderMultilingual(selection) {
86169 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
86172 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
86173 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
86174 var wrap = select(this);
86175 var domId = utilUniqueDomId(index);
86176 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
86177 var text = label.append('span').attr('class', 'label-text');
86178 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
86179 text.append('span').attr('class', 'label-textannotation');
86180 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
86181 if (field.locked()) return;
86182 d3_event.preventDefault();
86184 if (!d.lang || !d.value) {
86185 _multilingual.splice(index, 1);
86187 renderMultilingual(selection);
86189 // remove from entity tags
86191 t[key(d.lang)] = undefined;
86192 dispatch$1.call('change', this, t);
86194 }).call(svgIcon('#iD-operation-delete'));
86195 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);
86196 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
86198 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 () {
86199 select(this).style('max-height', '').style('overflow', 'visible');
86201 entries = entries.merge(entriesEnter);
86203 entries.classed('present', function (d) {
86204 return d.lang && d.value;
86206 utilGetSetValue(entries.select('.localized-lang'), function (d) {
86207 var langItem = _languagesArray.find(function (item) {
86208 return item.code === d.lang;
86211 if (langItem) return langItem.label;
86214 utilGetSetValue(entries.select('.localized-value'), function (d) {
86215 return typeof d.value === 'string' ? d.value : '';
86216 }).attr('title', function (d) {
86217 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86218 }).attr('placeholder', function (d) {
86219 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86220 }).classed('mixed', function (d) {
86221 return Array.isArray(d.value);
86225 localized.tags = function (tags) {
86226 _tags = tags; // Fetch translations from wikipedia
86228 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86230 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86232 if (wm && wm[0] && wm[1]) {
86233 wikipedia.translations(wm[1], wm[2], function (err, d) {
86234 if (err || !d) return;
86240 var isMixed = Array.isArray(tags[field.key]);
86241 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);
86242 calcMultilingual(tags);
86244 _selection.call(localized);
86247 localized.focus = function () {
86248 input.node().focus();
86251 localized.entityIDs = function (val) {
86252 if (!arguments.length) return _entityIDs;
86254 _multilingual = [];
86259 function loadCountryCode() {
86260 var extent = combinedEntityExtent();
86261 var countryCode = extent && iso1A2Code(extent.center());
86262 _countryCode = countryCode && countryCode.toLowerCase();
86265 function combinedEntityExtent() {
86266 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86269 return utilRebind(localized, dispatch$1, 'on');
86272 function uiFieldMaxspeed(field, context) {
86273 var dispatch$1 = dispatch('change');
86274 var unitInput = select(null);
86275 var input = select(null);
86276 var _entityIDs = [];
86282 var speedCombo = uiCombobox(context, 'maxspeed');
86283 var unitCombo = uiCombobox(context, 'maxspeed-unit').data(['km/h', 'mph'].map(comboValues));
86284 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86285 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86287 function maxspeed(selection) {
86288 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86289 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86290 input = wrap.selectAll('input.maxspeed-number').data([0]);
86291 input = input.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
86292 input.on('change', change).on('blur', change);
86293 var loc = combinedEntityExtent().center();
86294 _isImperial = roadSpeedUnit(loc) === 'mph';
86295 unitInput = wrap.selectAll('input.maxspeed-unit').data([0]);
86296 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-unit').call(unitCombo).merge(unitInput);
86297 unitInput.on('blur', changeUnits).on('change', changeUnits);
86299 function changeUnits() {
86300 _isImperial = utilGetSetValue(unitInput) === 'mph';
86301 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86302 setUnitSuggestions();
86307 function setUnitSuggestions() {
86308 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86309 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86312 function comboValues(d) {
86314 value: d.toString(),
86315 title: d.toString()
86319 function change() {
86321 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
86323 if (!value && Array.isArray(_tags[field.key])) return;
86326 tag[field.key] = undefined;
86327 } else if (isNaN(value) || !_isImperial) {
86328 tag[field.key] = context.cleanTagValue(value);
86330 tag[field.key] = context.cleanTagValue(value + ' mph');
86333 dispatch$1.call('change', this, tag);
86336 maxspeed.tags = function (tags) {
86338 var value = tags[field.key];
86339 var isMixed = Array.isArray(value);
86342 if (value && value.indexOf('mph') >= 0) {
86343 value = parseInt(value, 10).toString();
86344 _isImperial = true;
86345 } else if (value) {
86346 _isImperial = false;
86350 setUnitSuggestions();
86351 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);
86354 maxspeed.focus = function () {
86355 input.node().focus();
86358 maxspeed.entityIDs = function (val) {
86362 function combinedEntityExtent() {
86363 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86366 return utilRebind(maxspeed, dispatch$1, 'on');
86369 function uiFieldRadio(field, context) {
86370 var dispatch$1 = dispatch('change');
86371 var placeholder = select(null);
86372 var wrap = select(null);
86373 var labels = select(null);
86374 var radios = select(null);
86375 var radioData = (field.options || field.strings && field.strings.options && Object.keys(field.strings.options) || field.keys).slice(); // shallow copy
86380 var _entityIDs = [];
86382 function selectedKey() {
86383 var node = wrap.selectAll('.form-field-input-radio label.active input');
86384 return !node.empty() && node.datum();
86387 function radio(selection) {
86388 selection.classed('preset-radio', true);
86389 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86390 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
86391 enter.append('span').attr('class', 'placeholder');
86392 wrap = wrap.merge(enter);
86393 placeholder = wrap.selectAll('.placeholder');
86394 labels = wrap.selectAll('label').data(radioData);
86395 enter = labels.enter().append('label');
86396 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
86397 return field.t('options.' + d, {
86400 }).attr('checked', false);
86401 enter.append('span').html(function (d) {
86402 return field.t.html('options.' + d, {
86406 labels = labels.merge(enter);
86407 radios = labels.selectAll('input').on('change', changeRadio);
86410 function structureExtras(selection, tags) {
86411 var selected = selectedKey() || tags.layer !== undefined;
86412 var type = _mainPresetIndex.field(selected);
86413 var layer = _mainPresetIndex.field('layer');
86414 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
86415 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
86416 extrasWrap.exit().remove();
86417 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
86418 var list = extrasWrap.selectAll('ul').data([0]);
86419 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
86422 if (!typeField || typeField.id !== selected) {
86423 typeField = uiField(context, type, _entityIDs, {
86425 }).on('change', changeType);
86428 typeField.tags(tags);
86433 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
86437 typeItem.exit().remove(); // Enter
86439 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
86440 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
86441 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
86443 typeItem = typeItem.merge(typeEnter);
86446 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
86450 if (layer && showLayer) {
86452 layerField = uiField(context, layer, _entityIDs, {
86454 }).on('change', changeLayer);
86457 layerField.tags(tags);
86458 field.keys = utilArrayUnion(field.keys, ['layer']);
86461 field.keys = field.keys.filter(function (k) {
86462 return k !== 'layer';
86466 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
86468 layerItem.exit().remove(); // Enter
86470 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
86471 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
86472 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
86474 layerItem = layerItem.merge(layerEnter);
86477 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
86481 function changeType(t, onInput) {
86482 var key = selectedKey();
86486 if (val !== 'no') {
86487 _oldType[key] = val;
86490 if (field.type === 'structureRadio') {
86491 // remove layer if it should not be set
86492 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
86493 t.layer = undefined;
86494 } // add layer if it should be set
86497 if (t.layer === undefined) {
86498 if (key === 'bridge' && val !== 'no') {
86502 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86508 dispatch$1.call('change', this, t, onInput);
86511 function changeLayer(t, onInput) {
86512 if (t.layer === '0') {
86513 t.layer = undefined;
86516 dispatch$1.call('change', this, t, onInput);
86519 function changeRadio() {
86524 t[field.key] = undefined;
86527 radios.each(function (d) {
86528 var active = select(this).property('checked');
86529 if (active) activeKey = d;
86532 if (active) t[field.key] = d;
86534 var val = _oldType[activeKey] || 'yes';
86535 t[d] = active ? val : undefined;
86539 if (field.type === 'structureRadio') {
86540 if (activeKey === 'bridge') {
86542 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86545 t.layer = undefined;
86549 dispatch$1.call('change', this, t);
86552 radio.tags = function (tags) {
86553 radios.property('checked', function (d) {
86555 return tags[field.key] === d;
86558 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86561 function isMixed(d) {
86563 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86566 return Array.isArray(tags[d]);
86569 labels.classed('active', function (d) {
86571 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
86574 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86575 }).classed('mixed', isMixed).attr('title', function (d) {
86576 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86578 var selection = radios.filter(function () {
86579 return this.checked;
86582 if (selection.empty()) {
86583 placeholder.html(_t.html('inspector.none'));
86585 placeholder.html(selection.attr('value'));
86586 _oldType[selection.datum()] = tags[selection.datum()];
86589 if (field.type === 'structureRadio') {
86590 // For waterways without a tunnel tag, set 'culvert' as
86591 // the _oldType to default to if the user picks 'tunnel'
86592 if (!!tags.waterway && !_oldType.tunnel) {
86593 _oldType.tunnel = 'culvert';
86596 wrap.call(structureExtras, tags);
86600 radio.focus = function () {
86601 radios.node().focus();
86604 radio.entityIDs = function (val) {
86605 if (!arguments.length) return _entityIDs;
86611 radio.isAllowed = function () {
86612 return _entityIDs.length === 1;
86615 return utilRebind(radio, dispatch$1, 'on');
86618 function uiFieldRestrictions(field, context) {
86619 var dispatch$1 = dispatch('change');
86620 var breathe = behaviorBreathe();
86621 corePreferences('turn-restriction-via-way', null); // remove old key
86623 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
86625 var storedDistance = corePreferences('turn-restriction-distance');
86627 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
86629 var _maxDistance = storedDistance ? +storedDistance : 30;
86631 var _initialized = false;
86633 var _parent = select(null); // the entire field
86636 var _container = select(null); // just the map
86651 function restrictions(selection) {
86652 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
86654 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86655 _graph = context.graph();
86656 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86657 } // It's possible for there to be no actual intersection here.
86658 // for example, a vertex of two `highway=path`
86659 // In this case, hide the field.
86662 var isOK = _intersection && _intersection.vertices.length && // has vertices
86663 _intersection.vertices // has the vertex that the user selected
86664 .filter(function (vertex) {
86665 return vertex.id === _vertexID;
86666 }).length && _intersection.ways.length > 2 && // has more than 2 ways
86667 _intersection.ways // has more than 1 TO way
86668 .filter(function (way) {
86670 }).length > 1; // Also hide in the case where
86672 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
86674 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
86675 selection.call(restrictions.off);
86679 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86680 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86681 var container = wrap.selectAll('.restriction-container').data([0]); // enter
86683 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
86684 containerEnter.append('div').attr('class', 'restriction-help'); // update
86686 _container = containerEnter.merge(container).call(renderViewer);
86687 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
86689 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
86692 function renderControls(selection) {
86693 var distControl = selection.selectAll('.restriction-distance').data([0]);
86694 distControl.exit().remove();
86695 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
86696 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
86697 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
86698 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
86700 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
86701 var val = select(this).property('value');
86702 _maxDistance = +val;
86703 _intersection = null;
86705 _container.selectAll('.layer-osm .layer-turns *').remove();
86707 corePreferences('turn-restriction-distance', _maxDistance);
86709 _parent.call(restrictions);
86711 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
86712 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
86713 viaControl.exit().remove();
86714 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
86715 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
86716 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
86717 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
86719 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
86720 var val = select(this).property('value');
86723 _container.selectAll('.layer-osm .layer-turns *').remove();
86725 corePreferences('turn-restriction-via-way0', _maxViaWay);
86727 _parent.call(restrictions);
86729 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
86732 function renderViewer(selection) {
86733 if (!_intersection) return;
86734 var vgraph = _intersection.graph;
86735 var filter = utilFunctor(true);
86736 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86737 // Instead of asking the restriction-container for its dimensions,
86738 // we can ask the .sidebar, which can have its dimensions cached.
86739 // width: calc as sidebar - padding
86740 // height: hardcoded (from `80_app.css`)
86741 // var d = utilGetDimensions(selection);
86743 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86744 var d = [sdims[0] - 50, 370];
86745 var c = geoVecScale(d, 0.5);
86747 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
86749 var extent = geoExtent();
86751 for (var i = 0; i < _intersection.vertices.length; i++) {
86752 extent._extend(_intersection.vertices[i].extent());
86753 } // If this is a large intersection, adjust zoom to fit extent
86756 if (_intersection.vertices.length > 1) {
86757 var padding = 180; // in z22 pixels
86759 var tl = projection([extent[0][0], extent[1][1]]);
86760 var br = projection([extent[1][0], extent[0][1]]);
86761 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86762 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86763 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86764 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86765 z = z - Math.max(hZoomDiff, vZoomDiff);
86766 projection.scale(geoZoomToScale(z));
86769 var padTop = 35; // reserve top space for hint text
86771 var extentCenter = projection(extent.center());
86772 extentCenter[1] = extentCenter[1] - padTop;
86773 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
86774 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
86775 var drawVertices = svgVertices(projection, context);
86776 var drawLines = svgLines(projection, context);
86777 var drawTurns = svgTurns(projection, context);
86778 var firstTime = selection.selectAll('.surface').empty();
86779 selection.call(drawLayers);
86780 var surface = selection.selectAll('.surface').classed('tr', true);
86783 _initialized = true;
86784 surface.call(breathe);
86785 } // This can happen if we've lowered the detail while a FROM way
86786 // is selected, and that way is no longer part of the intersection.
86789 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86794 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));
86795 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
86796 surface.selectAll('.selected').classed('selected', false);
86797 surface.selectAll('.related').classed('related', false);
86801 way = vgraph.entity(_fromWayID);
86802 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86805 document.addEventListener('resizeWindow', function () {
86806 utilSetDimensions(_container, null);
86811 function click(d3_event) {
86812 surface.call(breathe.off).call(breathe);
86813 var datum = d3_event.target.__data__;
86814 var entity = datum && datum.properties && datum.properties.entity;
86820 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86821 _fromWayID = datum.id;
86824 } else if (datum instanceof osmTurn) {
86825 var actions, extraActions, turns, i;
86826 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86828 if (datum.restrictionID && !datum.direct) {
86830 } else if (datum.restrictionID && !datum.only) {
86833 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
86835 datumOnly.only = true; // but change this property
86837 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86838 // We will remember them in _oldTurns, and restore them if the user clicks again.
86840 turns = _intersection.turns(_fromWayID, 2);
86844 for (i = 0; i < turns.length; i++) {
86845 var turn = turns[i];
86846 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
86848 if (turn.direct && turn.path[1] === datum.path[1]) {
86849 seen[turns[i].restrictionID] = true;
86850 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86852 _oldTurns.push(turn);
86854 extraActions.push(actionUnrestrictTurn(turn));
86858 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
86859 } else if (datum.restrictionID) {
86861 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86862 // This relies on the assumption that the intersection was already split up when we
86863 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86864 turns = _oldTurns || [];
86867 for (i = 0; i < turns.length; i++) {
86868 if (turns[i].key !== datum.key) {
86869 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86874 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
86877 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
86880 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
86881 // Refresh it and update the help..
86883 var s = surface.selectAll('.' + datum.key);
86884 datum = s.empty() ? null : s.datum();
86885 updateHints(datum);
86893 function mouseover(d3_event) {
86894 var datum = d3_event.target.__data__;
86895 updateHints(datum);
86898 _lastXPos = _lastXPos || sdims[0];
86900 function redraw(minChange) {
86904 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86907 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
86908 if (context.hasEntity(_vertexID)) {
86911 _container.call(renderViewer);
86916 function highlightPathsFrom(wayID) {
86917 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
86918 surface.selectAll('.' + wayID).classed('related', true);
86921 var turns = _intersection.turns(wayID, _maxViaWay);
86923 for (var i = 0; i < turns.length; i++) {
86924 var turn = turns[i];
86925 var ids = [turn.to.way];
86926 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
86928 if (turn.only || turns.length === 1) {
86929 if (turn.via.ways) {
86930 ids = ids.concat(turn.via.ways);
86932 } else if (turn.to.way === wayID) {
86936 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
86941 function updateHints(datum) {
86942 var help = _container.selectAll('.restriction-help').html('');
86944 var placeholders = {};
86945 ['from', 'via', 'to'].forEach(function (k) {
86946 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86948 var entity = datum && datum.properties && datum.properties.entity;
86955 way = vgraph.entity(_fromWayID);
86956 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86957 } // Hovering a way
86960 if (datum instanceof osmWay && datum.__from) {
86962 highlightPathsFrom(_fromWayID ? null : way.id);
86963 surface.selectAll('.' + way.id).classed('related', true);
86964 var clickSelect = !_fromWayID || _fromWayID !== way.id;
86965 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
86966 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86967 from: placeholders.from,
86968 fromName: displayName(way.id, vgraph)
86969 })); // Hovering a turn arrow
86970 } else if (datum instanceof osmTurn) {
86971 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86972 var turnType = restrictionType.replace(/^(only|no)\_/, '');
86973 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
86974 var klass, turnText, nextText;
86977 klass = 'restrict';
86978 turnText = _t.html('restriction.help.turn.no_' + turnType, {
86981 nextText = _t.html('restriction.help.turn.only_' + turnType, {
86984 } else if (datum.only) {
86986 turnText = _t.html('restriction.help.turn.only_' + turnType, {
86989 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
86994 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
86997 nextText = _t.html('restriction.help.turn.no_' + turnType, {
87002 help.append('div') // "NO Right Turn (indirect)"
87003 .attr('class', 'qualifier ' + klass).html(turnText);
87004 help.append('div') // "FROM {fromName} TO {toName}"
87005 .html(_t.html('restriction.help.from_name_to_name', {
87006 from: placeholders.from,
87007 fromName: displayName(datum.from.way, vgraph),
87008 to: placeholders.to,
87009 toName: displayName(datum.to.way, vgraph)
87012 if (datum.via.ways && datum.via.ways.length) {
87015 for (var i = 0; i < datum.via.ways.length; i++) {
87016 var prev = names[names.length - 1];
87017 var curr = displayName(datum.via.ways[i], vgraph);
87018 if (!prev || curr !== prev) // collapse identical names
87022 help.append('div') // "VIA {viaNames}"
87023 .html(_t.html('restriction.help.via_names', {
87024 via: placeholders.via,
87025 viaNames: names.join(', ')
87030 help.append('div') // Click for "No Right Turn"
87031 .html(_t.html('restriction.help.toggle', {
87032 turn: nextText.trim()
87036 highlightPathsFrom(null);
87037 var alongIDs = datum.path.slice();
87038 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
87040 highlightPathsFrom(null);
87043 help.append('div') // "FROM {fromName}"
87044 .html(_t.html('restriction.help.from_name', {
87045 from: placeholders.from,
87046 fromName: displayName(_fromWayID, vgraph)
87049 help.append('div') // "Click to select a FROM segment."
87050 .html(_t.html('restriction.help.select_from', {
87051 from: placeholders.from
87058 function displayMaxDistance(maxDist) {
87059 var isImperial = !_mainLocalizer.usesMetric();
87064 // imprecise conversion for prettier display
87074 distance: _t('units.feet', {
87075 quantity: distToFeet
87080 distance: _t('units.meters', {
87086 return _t.html('restriction.controls.distance_up_to', opts);
87089 function displayMaxVia(maxVia) {
87090 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');
87093 function displayName(entityID, graph) {
87094 var entity = graph.entity(entityID);
87095 var name = utilDisplayName(entity) || '';
87096 var matched = _mainPresetIndex.match(entity, graph);
87097 var type = matched && matched.name() || utilDisplayType(entity.id);
87098 return name || type;
87101 restrictions.entityIDs = function (val) {
87102 _intersection = null;
87105 _vertexID = val[0];
87108 restrictions.tags = function () {};
87110 restrictions.focus = function () {};
87112 restrictions.off = function (selection) {
87113 if (!_initialized) return;
87114 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
87115 select(window).on('resize.restrictions', null);
87118 return utilRebind(restrictions, dispatch$1, 'on');
87120 uiFieldRestrictions.supportsMultiselection = false;
87122 function uiFieldTextarea(field, context) {
87123 var dispatch$1 = dispatch('change');
87124 var input = select(null);
87128 function textarea(selection) {
87129 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87130 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
87131 input = wrap.selectAll('textarea').data([0]);
87132 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
87135 function change(onInput) {
87136 return function () {
87137 var val = utilGetSetValue(input);
87138 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
87140 if (!val && Array.isArray(_tags[field.key])) return;
87142 t[field.key] = val || undefined;
87143 dispatch$1.call('change', this, t, onInput);
87147 textarea.tags = function (tags) {
87149 var isMixed = Array.isArray(tags[field.key]);
87150 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);
87153 textarea.focus = function () {
87154 input.node().focus();
87157 return utilRebind(textarea, dispatch$1, 'on');
87160 function uiFieldWikidata(field, context) {
87161 var wikidata = services.wikidata;
87162 var dispatch$1 = dispatch('change');
87164 var _selection = select(null);
87166 var _searchInput = select(null);
87169 var _wikidataEntity = null;
87171 var _entityIDs = [];
87173 var _wikipediaKey = field.keys && field.keys.find(function (key) {
87174 return key.includes('wikipedia');
87176 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87178 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
87180 function wiki(selection) {
87181 _selection = selection;
87182 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87183 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
87184 var list = wrap.selectAll('ul').data([0]);
87185 list = list.enter().append('ul').attr('class', 'rows').merge(list);
87186 var searchRow = list.selectAll('li.wikidata-search').data([0]);
87187 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
87188 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
87189 var node = select(this).node();
87190 node.setSelectionRange(0, node.value.length);
87191 }).on('blur', function () {
87192 setLabelForEntity();
87193 }).call(combobox.fetcher(fetchWikidataItems));
87194 combobox.on('accept', function (d) {
87199 }).on('cancel', function () {
87200 setLabelForEntity();
87202 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87203 domain: 'wikidata.org'
87204 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
87205 d3_event.preventDefault();
87206 if (_wikiURL) window.open(_wikiURL, '_blank');
87208 searchRow = searchRow.merge(searchRowEnter);
87209 _searchInput = searchRow.select('input');
87210 var wikidataProperties = ['description', 'identifier'];
87211 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
87213 var enter = items.enter().append('li').attr('class', function (d) {
87214 return 'labeled-input preset-wikidata-' + d;
87216 enter.append('span').attr('class', 'label').html(function (d) {
87217 return _t.html('wikidata.' + d);
87219 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
87220 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
87221 d3_event.preventDefault();
87222 select(this.parentNode).select('input').node().select();
87223 document.execCommand('copy');
87227 function fetchWikidataItems(q, callback) {
87228 if (!q && _hintKey) {
87229 // other tags may be good search terms
87230 for (var i in _entityIDs) {
87231 var entity = context.hasEntity(_entityIDs[i]);
87233 if (entity.tags[_hintKey]) {
87234 q = entity.tags[_hintKey];
87240 wikidata.itemsForSearchQuery(q, function (err, data) {
87243 for (var i in data) {
87244 data[i].value = data[i].label + ' (' + data[i].id + ')';
87245 data[i].title = data[i].description;
87248 if (callback) callback(data);
87252 function change() {
87254 syncTags[field.key] = _qid;
87255 dispatch$1.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
87257 var initGraph = context.graph();
87258 var initEntityIDs = _entityIDs;
87259 wikidata.entityByQID(_qid, function (err, entity) {
87260 if (err) return; // If graph has changed, we can't apply this update.
87262 if (context.graph() !== initGraph) return;
87263 if (!entity.sitelinks) return;
87264 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
87266 ['labels', 'descriptions'].forEach(function (key) {
87267 if (!entity[key]) return;
87268 var valueLangs = Object.keys(entity[key]);
87269 if (valueLangs.length === 0) return;
87270 var valueLang = valueLangs[0];
87272 if (langs.indexOf(valueLang) === -1) {
87273 langs.push(valueLang);
87276 var newWikipediaValue;
87278 if (_wikipediaKey) {
87279 var foundPreferred;
87281 for (var i in langs) {
87282 var lang = langs[i];
87283 var siteID = lang.replace('-', '_') + 'wiki';
87285 if (entity.sitelinks[siteID]) {
87286 foundPreferred = true;
87287 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
87293 if (!foundPreferred) {
87294 // No wikipedia sites available in the user's language or the fallback languages,
87295 // default to any wikipedia sitelink
87296 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
87297 return site.endsWith('wiki');
87300 if (wikiSiteKeys.length === 0) {
87301 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87302 newWikipediaValue = null;
87304 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87305 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87306 newWikipediaValue = wikiLang + ':' + wikiTitle;
87311 if (newWikipediaValue) {
87312 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87315 if (typeof newWikipediaValue === 'undefined') return;
87316 var actions = initEntityIDs.map(function (entityID) {
87317 var entity = context.hasEntity(entityID);
87318 if (!entity) return null;
87319 var currTags = Object.assign({}, entity.tags); // shallow copy
87321 if (newWikipediaValue === null) {
87322 if (!currTags[_wikipediaKey]) return null;
87323 delete currTags[_wikipediaKey];
87325 currTags[_wikipediaKey] = newWikipediaValue;
87328 return actionChangeTags(entityID, currTags);
87329 }).filter(Boolean);
87330 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87332 context.overwrite(function actionUpdateWikipediaTags(graph) {
87333 actions.forEach(function (action) {
87334 graph = action(graph);
87337 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87338 // changeTags() is not intended to be called asynchronously
87342 function setLabelForEntity() {
87345 if (_wikidataEntity) {
87346 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87348 if (label.length === 0) {
87349 label = _wikidataEntity.id.toString();
87353 utilGetSetValue(_searchInput, label);
87356 wiki.tags = function (tags) {
87357 var isMixed = Array.isArray(tags[field.key]);
87359 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
87361 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87363 if (!/^Q[0-9]*$/.test(_qid)) {
87364 // not a proper QID
87367 } // QID value in correct format
87370 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87371 wikidata.entityByQID(_qid, function (err, entity) {
87377 _wikidataEntity = entity;
87378 setLabelForEntity();
87379 var description = entityPropertyForDisplay(entity, 'descriptions');
87381 _selection.select('button.wiki-link').classed('disabled', false);
87383 _selection.select('.preset-wikidata-description').style('display', function () {
87384 return description.length > 0 ? 'flex' : 'none';
87385 }).select('input').attr('value', description);
87387 _selection.select('.preset-wikidata-identifier').style('display', function () {
87388 return entity.id ? 'flex' : 'none';
87389 }).select('input').attr('value', entity.id);
87390 }); // not a proper QID
87392 function unrecognized() {
87393 _wikidataEntity = null;
87394 setLabelForEntity();
87396 _selection.select('.preset-wikidata-description').style('display', 'none');
87398 _selection.select('.preset-wikidata-identifier').style('display', 'none');
87400 _selection.select('button.wiki-link').classed('disabled', true);
87402 if (_qid && _qid !== '') {
87403 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87410 function entityPropertyForDisplay(wikidataEntity, propKey) {
87411 if (!wikidataEntity[propKey]) return '';
87412 var propObj = wikidataEntity[propKey];
87413 var langKeys = Object.keys(propObj);
87414 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
87416 var langs = wikidata.languagesToQuery();
87418 for (var i in langs) {
87419 var lang = langs[i];
87420 var valueObj = propObj[lang];
87421 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
87422 } // default to any available value
87425 return propObj[langKeys[0]].value;
87428 wiki.entityIDs = function (val) {
87429 if (!arguments.length) return _entityIDs;
87434 wiki.focus = function () {
87435 _searchInput.node().focus();
87438 return utilRebind(wiki, dispatch$1, 'on');
87441 function uiFieldWikipedia(field, context) {
87442 var _arguments = arguments;
87443 var dispatch$1 = dispatch('change');
87444 var wikipedia = services.wikipedia;
87445 var wikidata = services.wikidata;
87447 var _langInput = select(null);
87449 var _titleInput = select(null);
87457 var _dataWikipedia = [];
87458 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
87459 _dataWikipedia = d;
87460 if (_tags) updateForTags(_tags);
87461 })["catch"](function () {
87464 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
87465 var v = value.toLowerCase();
87466 callback(_dataWikipedia.filter(function (d) {
87467 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
87468 }).map(function (d) {
87474 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
87478 for (var i in _entityIDs) {
87479 var entity = context.hasEntity(_entityIDs[i]);
87481 if (entity.tags.name) {
87482 value = entity.tags.name;
87488 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87489 searchfn(language()[2], value, function (query, data) {
87490 callback(data.map(function (d) {
87498 function wiki(selection) {
87499 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87500 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
87501 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
87502 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
87503 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
87504 _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);
87506 _langInput.on('blur', changeLang).on('change', changeLang);
87508 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
87509 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
87510 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
87511 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
87513 _titleInput.on('blur', function () {
87515 }).on('change', function () {
87519 var link = titleContainer.selectAll('.wiki-link').data([0]);
87520 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87521 domain: 'wikipedia.org'
87522 })).call(svgIcon('#iD-icon-out-link')).merge(link);
87523 link.on('click', function (d3_event) {
87524 d3_event.preventDefault();
87525 if (_wikiURL) window.open(_wikiURL, '_blank');
87529 function defaultLanguageInfo(skipEnglishFallback) {
87530 var langCode = _mainLocalizer.languageCode().toLowerCase();
87532 for (var i in _dataWikipedia) {
87533 var d = _dataWikipedia[i]; // default to the language of iD's current locale
87535 if (d[2] === langCode) return d;
87536 } // fallback to English
87539 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
87542 function language(skipEnglishFallback) {
87543 var value = utilGetSetValue(_langInput).toLowerCase();
87545 for (var i in _dataWikipedia) {
87546 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
87548 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
87549 } // fallback to English
87552 return defaultLanguageInfo(skipEnglishFallback);
87555 function changeLang() {
87556 utilGetSetValue(_langInput, language()[1]);
87560 function change(skipWikidata) {
87561 var value = utilGetSetValue(_titleInput);
87562 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87564 var langInfo = m && _dataWikipedia.find(function (d) {
87565 return m[1] === d[2];
87571 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87573 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87576 var anchor; // try {
87577 // leave this out for now - #6232
87578 // Best-effort `anchordecode:` implementation
87579 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87582 anchor = decodeURIComponent(m[3]); // }
87584 value += '#' + anchor.replace(/_/g, ' ');
87587 value = value.slice(0, 1).toUpperCase() + value.slice(1);
87588 utilGetSetValue(_langInput, nativeLangName);
87589 utilGetSetValue(_titleInput, value);
87593 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87595 syncTags.wikipedia = undefined;
87598 dispatch$1.call('change', this, syncTags);
87599 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
87601 var initGraph = context.graph();
87602 var initEntityIDs = _entityIDs;
87603 wikidata.itemsByTitle(language()[2], value, function (err, data) {
87604 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
87606 if (context.graph() !== initGraph) return;
87607 var qids = Object.keys(data);
87608 var value = qids && qids.find(function (id) {
87609 return id.match(/^Q\d+$/);
87611 var actions = initEntityIDs.map(function (entityID) {
87612 var entity = context.entity(entityID).tags;
87613 var currTags = Object.assign({}, entity); // shallow copy
87615 if (currTags.wikidata !== value) {
87616 currTags.wikidata = value;
87617 return actionChangeTags(entityID, currTags);
87621 }).filter(Boolean);
87622 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87624 context.overwrite(function actionUpdateWikidataTags(graph) {
87625 actions.forEach(function (action) {
87626 graph = action(graph);
87629 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87630 // changeTags() is not intended to be called asynchronously
87634 wiki.tags = function (tags) {
87636 updateForTags(tags);
87639 function updateForTags(tags) {
87640 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
87641 // optional suffix of `#anchor`
87643 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87644 var tagLang = m && m[1];
87645 var tagArticleTitle = m && m[2];
87646 var anchor = m && m[3];
87648 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
87649 return tagLang === d[2];
87650 }); // value in correct format
87654 var nativeLangName = tagLangInfo[1];
87655 utilGetSetValue(_langInput, nativeLangName);
87656 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
87660 // Best-effort `anchorencode:` implementation
87661 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87663 anchor = anchor.replace(/ /g, '_');
87667 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
87669 utilGetSetValue(_titleInput, value);
87671 if (value && value !== '') {
87672 utilGetSetValue(_langInput, '');
87673 var defaultLangInfo = defaultLanguageInfo();
87674 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
87676 var shownOrDefaultLangInfo = language(true
87677 /* skipEnglishFallback */
87679 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
87685 wiki.entityIDs = function (val) {
87686 if (!_arguments.length) return _entityIDs;
87691 wiki.focus = function () {
87692 _titleInput.node().focus();
87695 return utilRebind(wiki, dispatch$1, 'on');
87697 uiFieldWikipedia.supportsMultiselection = false;
87700 access: uiFieldAccess,
87701 address: uiFieldAddress,
87702 check: uiFieldCheck,
87703 combo: uiFieldCombo,
87704 cycleway: uiFieldCycleway,
87705 defaultCheck: uiFieldCheck,
87706 email: uiFieldText,
87707 identifier: uiFieldText,
87708 lanes: uiFieldLanes,
87709 localized: uiFieldLocalized,
87710 maxspeed: uiFieldMaxspeed,
87711 manyCombo: uiFieldCombo,
87712 multiCombo: uiFieldCombo,
87713 networkCombo: uiFieldCombo,
87714 number: uiFieldText,
87715 onewayCheck: uiFieldCheck,
87716 radio: uiFieldRadio,
87717 restrictions: uiFieldRestrictions,
87718 semiCombo: uiFieldCombo,
87719 structureRadio: uiFieldRadio,
87722 textarea: uiFieldTextarea,
87723 typeCombo: uiFieldCombo,
87725 wikidata: uiFieldWikidata,
87726 wikipedia: uiFieldWikipedia
87729 function uiField(context, presetField, entityIDs, options) {
87730 options = Object.assign({
87737 var dispatch$1 = dispatch('change', 'revert');
87738 var field = Object.assign({}, presetField); // shallow copy
87740 field.domId = utilUniqueDomId('form-field-' + field.safeid);
87741 var _show = options.show;
87744 var _locked = false;
87746 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
87748 })).placement('bottom');
87750 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
87752 if (_show && !field.impl) {
87754 } // Creates the field.. This is done lazily,
87755 // once we know that the field will be shown.
87758 function createField() {
87759 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
87760 dispatch$1.call('change', field, t, onInput);
87764 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
87766 if (field.impl.entityIDs) {
87767 field.impl.entityIDs(entityIDs);
87772 function isModified() {
87773 if (!entityIDs || !entityIDs.length) return false;
87774 return entityIDs.some(function (entityID) {
87775 var original = context.graph().base().entities[entityID];
87776 var latest = context.graph().entity(entityID);
87777 return field.keys.some(function (key) {
87778 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
87783 function tagsContainFieldKey() {
87784 return field.keys.some(function (key) {
87785 if (field.type === 'multiCombo') {
87786 for (var tagKey in _tags) {
87787 if (tagKey.indexOf(key) === 0) {
87795 return _tags[key] !== undefined;
87799 function revert(d3_event, d) {
87800 d3_event.stopPropagation();
87801 d3_event.preventDefault();
87802 if (!entityIDs || _locked) return;
87803 dispatch$1.call('revert', d, d.keys);
87806 function remove(d3_event, d) {
87807 d3_event.stopPropagation();
87808 d3_event.preventDefault();
87809 if (_locked) return;
87811 d.keys.forEach(function (key) {
87812 t[key] = undefined;
87814 dispatch$1.call('change', d, t);
87817 field.render = function (selection) {
87818 var container = selection.selectAll('.form-field').data([field]); // Enter
87820 var enter = container.enter().append('div').attr('class', function (d) {
87821 return 'form-field form-field-' + d.safeid;
87822 }).classed('nowrap', !options.wrap);
87824 if (options.wrap) {
87825 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
87828 var textEnter = labelEnter.append('span').attr('class', 'label-text');
87829 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
87832 textEnter.append('span').attr('class', 'label-textannotation');
87834 if (options.remove) {
87835 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
87838 if (options.revert) {
87839 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
87844 container = container.merge(enter);
87845 container.select('.field-label > .remove-icon') // propagate bound data
87846 .on('click', remove);
87847 container.select('.field-label > .modified-icon') // propagate bound data
87848 .on('click', revert);
87849 container.each(function (d) {
87850 var selection = select(this);
87856 var reference, help; // instantiate field help
87858 if (options.wrap && field.type === 'restrictions') {
87859 help = uiFieldHelp(context, 'restrictions');
87860 } // instantiate tag reference
87863 if (options.wrap && options.info) {
87864 var referenceKey = d.key || '';
87866 if (d.type === 'multiCombo') {
87867 // lookup key without the trailing ':'
87868 referenceKey = referenceKey.replace(/:$/, '');
87871 reference = uiTagReference(d.reference || {
87875 if (_state === 'hover') {
87876 reference.showing(false);
87880 selection.call(d.impl); // add field help components
87883 selection.call(help.body).select('.field-label').call(help.button);
87884 } // add tag reference components
87888 selection.call(reference.body).select('.field-label').call(reference.button);
87891 d.impl.tags(_tags);
87893 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
87895 var annotation = container.selectAll('.field-label .label-textannotation');
87896 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
87897 icon.exit().remove();
87898 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
87899 container.call(_locked ? _lockedTip : _lockedTip.destroy);
87902 field.state = function (val) {
87903 if (!arguments.length) return _state;
87908 field.tags = function (val) {
87909 if (!arguments.length) return _tags;
87912 if (tagsContainFieldKey() && !_show) {
87913 // always show a field if it has a value to display
87924 field.locked = function (val) {
87925 if (!arguments.length) return _locked;
87930 field.show = function () {
87937 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
87939 t[field.key] = field["default"];
87940 dispatch$1.call('change', this, t);
87942 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
87945 field.isShown = function () {
87947 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
87948 // A non-allowed field is hidden from the user altogether
87951 field.isAllowed = function () {
87952 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
87953 if (field.geometry && !entityIDs.every(function (entityID) {
87954 return field.matchGeometry(context.graph().geometry(entityID));
87957 if (field.countryCodes || field.notCountryCodes) {
87958 var extent = combinedEntityExtent();
87959 if (!extent) return true;
87960 var center = extent.center();
87961 var countryCode = iso1A2Code(center);
87962 if (!countryCode) return false;
87963 countryCode = countryCode.toLowerCase();
87965 if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
87969 if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
87974 var prerequisiteTag = field.prerequisiteTag;
87976 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
87978 if (!entityIDs.every(function (entityID) {
87979 var entity = context.graph().entity(entityID);
87981 if (prerequisiteTag.key) {
87982 var value = entity.tags[prerequisiteTag.key];
87983 if (!value) return false;
87985 if (prerequisiteTag.valueNot) {
87986 return prerequisiteTag.valueNot !== value;
87989 if (prerequisiteTag.value) {
87990 return prerequisiteTag.value === value;
87992 } else if (prerequisiteTag.keyNot) {
87993 if (entity.tags[prerequisiteTag.keyNot]) return false;
88003 field.focus = function () {
88005 field.impl.focus();
88009 function combinedEntityExtent() {
88010 return entityIDs && entityIDs.length && entityIDs.reduce(function (extent, entityID) {
88011 var entity = context.graph().entity(entityID);
88012 return extent.extend(entity.extent(context.graph()));
88016 return utilRebind(field, dispatch$1, 'on');
88019 function uiFormFields(context) {
88020 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88021 var _fieldsArr = [];
88022 var _lastPlaceholder = '';
88026 function formFields(selection) {
88027 var allowedFields = _fieldsArr.filter(function (field) {
88028 return field.isAllowed();
88031 var shown = allowedFields.filter(function (field) {
88032 return field.isShown();
88034 var notShown = allowedFields.filter(function (field) {
88035 return !field.isShown();
88037 var container = selection.selectAll('.form-fields-container').data([0]);
88038 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
88039 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
88040 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
88042 fields.exit().remove(); // Enter
88044 var enter = fields.enter().append('div').attr('class', function (d) {
88045 return 'wrap-form-field wrap-form-field-' + d.safeid;
88048 fields = fields.merge(enter);
88049 fields.order().each(function (d) {
88050 select(this).call(d.render);
88053 var moreFields = notShown.map(function (field) {
88054 var title = field.title();
88055 titles.push(title);
88056 var terms = field.terms();
88057 if (field.key) terms.push(field.key);
88058 if (field.keys) terms = terms.concat(field.keys);
88060 display: field.label(),
88067 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
88068 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
88069 more.exit().remove();
88070 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
88071 moreEnter.append('span').html(_t.html('inspector.add_fields'));
88072 more = moreEnter.merge(more);
88073 var input = more.selectAll('.value').data([0]);
88074 input.exit().remove();
88075 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
88076 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
88077 if (!d) return; // user entered something that was not matched
88079 var field = d.field;
88081 selection.call(formFields); // rerender
88084 })); // avoid updating placeholder excessively (triggers style recalc)
88086 if (_lastPlaceholder !== placeholder) {
88087 input.attr('placeholder', placeholder);
88088 _lastPlaceholder = placeholder;
88092 formFields.fieldsArr = function (val) {
88093 if (!arguments.length) return _fieldsArr;
88094 _fieldsArr = val || [];
88098 formFields.state = function (val) {
88099 if (!arguments.length) return _state;
88104 formFields.klass = function (val) {
88105 if (!arguments.length) return _klass;
88113 function uiSectionPresetFields(context) {
88114 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
88115 var dispatch$1 = dispatch('change', 'revert');
88116 var formFields = uiFormFields(context);
88128 function renderDisclosureContent(selection) {
88130 var graph = context.graph();
88131 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
88132 geoms[graph.entity(entityID).geometry(graph)] = true;
88135 var presetsManager = _mainPresetIndex;
88136 var allFields = [];
88137 var allMoreFields = [];
88138 var sharedTotalFields;
88140 _presets.forEach(function (preset) {
88141 var fields = preset.fields();
88142 var moreFields = preset.moreFields();
88143 allFields = utilArrayUnion(allFields, fields);
88144 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88146 if (!sharedTotalFields) {
88147 sharedTotalFields = utilArrayUnion(fields, moreFields);
88149 sharedTotalFields = sharedTotalFields.filter(function (field) {
88150 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88155 var sharedFields = allFields.filter(function (field) {
88156 return sharedTotalFields.indexOf(field) !== -1;
88158 var sharedMoreFields = allMoreFields.filter(function (field) {
88159 return sharedTotalFields.indexOf(field) !== -1;
88162 sharedFields.forEach(function (field) {
88163 if (field.matchAllGeometry(geometries)) {
88164 _fieldsArr.push(uiField(context, field, _entityIDs));
88167 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88169 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88170 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
88173 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88174 additionalFields.sort(function (field1, field2) {
88175 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88177 additionalFields.forEach(function (field) {
88178 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
88179 _fieldsArr.push(uiField(context, field, _entityIDs, {
88185 _fieldsArr.forEach(function (field) {
88186 field.on('change', function (t, onInput) {
88187 dispatch$1.call('change', field, _entityIDs, t, onInput);
88188 }).on('revert', function (keys) {
88189 dispatch$1.call('revert', field, keys);
88194 _fieldsArr.forEach(function (field) {
88195 field.state(_state).tags(_tags);
88198 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
88199 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
88200 // if user presses enter, and combobox is not active, accept edits..
88201 if (d3_event.keyCode === 13 && // ↩ Return
88202 context.container().select('.combobox').empty()) {
88203 context.enter(modeBrowse(context));
88208 section.presets = function (val) {
88209 if (!arguments.length) return _presets;
88211 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88219 section.state = function (val) {
88220 if (!arguments.length) return _state;
88225 section.tags = function (val) {
88226 if (!arguments.length) return _tags;
88227 _tags = val; // Don't reset _fieldsArr here.
88232 section.entityIDs = function (val) {
88233 if (!arguments.length) return _entityIDs;
88235 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88243 return utilRebind(section, dispatch$1, 'on');
88246 function uiSectionRawMemberEditor(context) {
88247 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
88248 if (!_entityIDs || _entityIDs.length !== 1) return false;
88249 var entity = context.hasEntity(_entityIDs[0]);
88250 return entity && entity.type === 'relation';
88251 }).label(function () {
88252 var entity = context.hasEntity(_entityIDs[0]);
88253 if (!entity) return '';
88254 var gt = entity.members.length > _maxMembers ? '>' : '';
88255 var count = gt + entity.members.slice(0, _maxMembers).length;
88256 return _t('inspector.title_count', {
88257 title: _t.html('inspector.members'),
88260 }).disclosureContent(renderDisclosureContent);
88261 var taginfo = services.taginfo;
88265 var _maxMembers = 1000;
88267 function downloadMember(d3_event, d) {
88268 d3_event.preventDefault(); // display the loading indicator
88270 select(this.parentNode).classed('tag-reference-loading', true);
88271 context.loadEntity(d.id, function () {
88272 section.reRender();
88276 function zoomToMember(d3_event, d) {
88277 d3_event.preventDefault();
88278 var entity = context.entity(d.id);
88279 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
88281 utilHighlightEntities([d.id], true, context);
88284 function selectMember(d3_event, d) {
88285 d3_event.preventDefault(); // remove the hover-highlight styling
88287 utilHighlightEntities([d.id], false, context);
88288 var entity = context.entity(d.id);
88289 var mapExtent = context.map().extent();
88291 if (!entity.intersects(mapExtent, context.graph())) {
88292 // zoom to the entity if its extent is not visible now
88293 context.map().zoomToEase(entity);
88296 context.enter(modeSelect(context, [d.id]));
88299 function changeRole(d3_event, d) {
88300 var oldRole = d.role;
88301 var newRole = context.cleanRelationRole(select(this).property('value'));
88303 if (oldRole !== newRole) {
88309 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
88312 context.validator().validate();
88316 function deleteMember(d3_event, d) {
88317 // remove the hover-highlight styling
88318 utilHighlightEntities([d.id], false, context);
88319 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
88323 if (!context.hasEntity(d.relation.id)) {
88324 // Removing the last member will also delete the relation.
88325 // If this happens we need to exit the selection mode
88326 context.enter(modeBrowse(context));
88328 // Changing the mode also runs `validate`, but otherwise we need to
88329 // rerun it manually
88330 context.validator().validate();
88334 function renderDisclosureContent(selection) {
88335 var entityID = _entityIDs[0];
88336 var memberships = [];
88337 var entity = context.entity(entityID);
88338 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
88345 member: context.hasEntity(member.id),
88346 domId: utilUniqueDomId(entityID + '-member-' + index)
88349 var list = selection.selectAll('.member-list').data([0]);
88350 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88351 var items = list.selectAll('li').data(memberships, function (d) {
88352 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
88354 items.exit().each(unbind).remove();
88355 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
88358 itemsEnter.each(function (d) {
88359 var item = select(this);
88360 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
88363 // highlight the member feature in the map while hovering on the list item
88364 item.on('mouseover', function () {
88365 utilHighlightEntities([d.id], true, context);
88366 }).on('mouseout', function () {
88367 utilHighlightEntities([d.id], false, context);
88369 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
88370 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88371 var matched = _mainPresetIndex.match(d.member, context.graph());
88372 return matched && matched.name() || utilDisplayType(d.member.id);
88374 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88375 return utilDisplayName(d.member);
88377 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
88378 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
88380 var labelText = label.append('span').attr('class', 'label-text');
88381 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
88384 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
88387 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
88390 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88391 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88393 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
88396 wrapEnter.each(bindTypeahead);
88400 items = items.merge(itemsEnter).order();
88401 items.select('input.member-role').property('value', function (d) {
88403 }).on('blur', changeRole).on('change', changeRole);
88404 items.select('button.member-delete').on('click', deleteMember);
88405 var dragOrigin, targetIndex;
88406 items.call(d3_drag().on('start', function (d3_event) {
88411 targetIndex = null;
88412 }).on('drag', function (d3_event) {
88413 var x = d3_event.x - dragOrigin.x,
88414 y = d3_event.y - dragOrigin.y;
88415 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
88416 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
88417 var index = items.nodes().indexOf(this);
88418 select(this).classed('dragging', true);
88419 targetIndex = null;
88420 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
88421 var node = select(this).node();
88423 if (index === index2) {
88424 return 'translate(' + x + 'px, ' + y + 'px)';
88425 } else if (index2 > index && d3_event.y > node.offsetTop) {
88426 if (targetIndex === null || index2 > targetIndex) {
88427 targetIndex = index2;
88430 return 'translateY(-100%)';
88431 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
88432 if (targetIndex === null || index2 < targetIndex) {
88433 targetIndex = index2;
88436 return 'translateY(100%)';
88441 }).on('end', function (d3_event, d) {
88442 if (!select(this).classed('dragging')) return;
88443 var index = items.nodes().indexOf(this);
88444 select(this).classed('dragging', false);
88445 selection.selectAll('li.member-row').style('transform', null);
88447 if (targetIndex !== null) {
88448 // dragged to a new position, reorder
88449 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
88450 context.validator().validate();
88454 function bindTypeahead(d) {
88455 var row = select(this);
88456 var role = row.selectAll('input.member-role');
88457 var origValue = role.property('value');
88459 function sort(value, data) {
88460 var sameletter = [];
88463 for (var i = 0; i < data.length; i++) {
88464 if (data[i].value.substring(0, value.length) === value) {
88465 sameletter.push(data[i]);
88467 other.push(data[i]);
88471 return sameletter.concat(other);
88474 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88475 // The `geometry` param is used in the `taginfo.js` interface for
88476 // filtering results, as a key into the `tag_members_fractions`
88477 // object. If we don't know the geometry because the member is
88478 // not yet downloaded, it's ok to guess based on type.
88482 geometry = context.graph().geometry(d.member.id);
88483 } else if (d.type === 'relation') {
88484 geometry = 'relation';
88485 } else if (d.type === 'way') {
88488 geometry = 'point';
88491 var rtype = entity.tags.type;
88494 rtype: rtype || '',
88495 geometry: geometry,
88497 }, function (err, data) {
88498 if (!err) callback(sort(role, data));
88500 }).on('cancel', function () {
88501 role.property('value', origValue);
88505 function unbind() {
88506 var row = select(this);
88507 row.selectAll('input.member-role').call(uiCombobox.off, context);
88511 section.entityIDs = function (val) {
88512 if (!arguments.length) return _entityIDs;
88520 function actionDeleteMembers(relationId, memberIndexes) {
88521 return function (graph) {
88522 // Remove the members in descending order so removals won't shift what members
88523 // are at the remaining indexes
88524 memberIndexes.sort(function (a, b) {
88528 for (var i in memberIndexes) {
88529 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
88536 function uiSectionRawMembershipEditor(context) {
88537 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
88538 return _entityIDs && _entityIDs.length;
88539 }).label(function () {
88540 var parents = getSharedParentRelations();
88541 var gt = parents.length > _maxMemberships ? '>' : '';
88542 var count = gt + parents.slice(0, _maxMemberships).length;
88543 return _t('inspector.title_count', {
88544 title: _t.html('inspector.relations'),
88547 }).disclosureContent(renderDisclosureContent);
88548 var taginfo = services.taginfo;
88549 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
88550 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
88551 }).itemsMouseLeave(function (d3_event, d) {
88552 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88554 var _inChange = false;
88555 var _entityIDs = [];
88559 var _maxMemberships = 1000;
88561 function getSharedParentRelations() {
88564 for (var i = 0; i < _entityIDs.length; i++) {
88565 var entity = context.graph().hasEntity(_entityIDs[i]);
88566 if (!entity) continue;
88569 parents = context.graph().parentRelations(entity);
88571 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
88574 if (!parents.length) break;
88580 function getMemberships() {
88581 var memberships = [];
88582 var relations = getSharedParentRelations().slice(0, _maxMemberships);
88583 var isMultiselect = _entityIDs.length > 1;
88584 var i, relation, membership, index, member, indexedMember;
88586 for (i = 0; i < relations.length; i++) {
88587 relation = relations[i];
88589 relation: relation,
88591 hash: osmEntity.key(relation)
88594 for (index = 0; index < relation.members.length; index++) {
88595 member = relation.members[index];
88597 if (_entityIDs.indexOf(member.id) !== -1) {
88598 indexedMember = Object.assign({}, member, {
88601 membership.members.push(indexedMember);
88602 membership.hash += ',' + index.toString();
88604 if (!isMultiselect) {
88605 // For single selections, list one entry per membership per relation.
88606 // For multiselections, list one entry per relation.
88607 memberships.push(membership);
88609 relation: relation,
88611 hash: osmEntity.key(relation)
88617 if (membership.members.length) memberships.push(membership);
88620 memberships.forEach(function (membership) {
88621 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
88623 membership.members.forEach(function (member) {
88624 if (roles.indexOf(member.role) === -1) roles.push(member.role);
88626 membership.role = roles.length === 1 ? roles[0] : roles;
88628 return memberships;
88631 function selectRelation(d3_event, d) {
88632 d3_event.preventDefault(); // remove the hover-highlight styling
88634 utilHighlightEntities([d.relation.id], false, context);
88635 context.enter(modeSelect(context, [d.relation.id]));
88638 function zoomToRelation(d3_event, d) {
88639 d3_event.preventDefault();
88640 var entity = context.entity(d.relation.id);
88641 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
88643 utilHighlightEntities([d.relation.id], true, context);
88646 function changeRole(d3_event, d) {
88647 if (d === 0) return; // called on newrow (shouldn't happen)
88649 if (_inChange) return; // avoid accidental recursive call #5731
88651 var newRole = context.cleanRelationRole(select(this).property('value'));
88652 if (!newRole.trim() && typeof d.role !== 'string') return;
88653 var membersToUpdate = d.members.filter(function (member) {
88654 return member.role !== newRole;
88657 if (membersToUpdate.length) {
88659 context.perform(function actionChangeMemberRoles(graph) {
88660 membersToUpdate.forEach(function (member) {
88661 var newMember = Object.assign({}, member, {
88664 delete newMember.index;
88665 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
88668 }, _t('operations.change_role.annotation', {
88669 n: membersToUpdate.length
88671 context.validator().validate();
88677 function addMembership(d, role) {
88678 this.blur(); // avoid keeping focus on the button
88680 _showBlank = false;
88682 function actionAddMembers(relationId, ids, role) {
88683 return function (graph) {
88684 for (var i in ids) {
88687 type: graph.entity(ids[i]).type,
88690 graph = actionAddMember(relationId, member)(graph);
88698 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
88699 n: _entityIDs.length
88701 context.validator().validate();
88703 var relation = osmRelation();
88704 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
88706 context.enter(modeSelect(context, [relation.id]).newFeature(true));
88710 function deleteMembership(d3_event, d) {
88711 this.blur(); // avoid keeping focus on the button
88713 if (d === 0) return; // called on newrow (shouldn't happen)
88714 // remove the hover-highlight styling
88716 utilHighlightEntities([d.relation.id], false, context);
88717 var indexes = d.members.map(function (member) {
88718 return member.index;
88720 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
88721 n: _entityIDs.length
88723 context.validator().validate();
88726 function fetchNearbyRelations(q, callback) {
88727 var newRelation = {
88729 value: _t('inspector.new_relation'),
88730 display: _t.html('inspector.new_relation')
88732 var entityID = _entityIDs[0];
88734 var graph = context.graph();
88736 function baseDisplayLabel(entity) {
88737 var matched = _mainPresetIndex.match(entity, graph);
88738 var presetName = matched && matched.name() || _t('inspector.relation');
88739 var entityName = utilDisplayName(entity) || '';
88740 return presetName + ' ' + entityName;
88743 var explicitRelation = q && context.hasEntity(q.toLowerCase());
88745 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
88746 // loaded relation is specified explicitly, only show that
88748 relation: explicitRelation,
88749 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
88752 context.history().intersects(context.map().extent()).forEach(function (entity) {
88753 if (entity.type !== 'relation' || entity.id === entityID) return;
88754 var value = baseDisplayLabel(entity);
88755 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
88761 result.sort(function (a, b) {
88762 return osmRelation.creationOrder(a.relation, b.relation);
88763 }); // Dedupe identical names by appending relation id - see #2891
88765 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
88766 return v.length > 1;
88768 dupeGroups.forEach(function (group) {
88769 group.forEach(function (obj) {
88770 obj.value += ' ' + obj.relation.id;
88775 result.forEach(function (obj) {
88776 obj.title = obj.value;
88778 result.unshift(newRelation);
88782 function renderDisclosureContent(selection) {
88783 var memberships = getMemberships();
88784 var list = selection.selectAll('.member-list').data([0]);
88785 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88786 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
88789 items.exit().each(unbind).remove(); // Enter
88791 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
88793 itemsEnter.on('mouseover', function (d3_event, d) {
88794 utilHighlightEntities([d.relation.id], true, context);
88795 }).on('mouseout', function (d3_event, d) {
88796 utilHighlightEntities([d.relation.id], false, context);
88798 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
88801 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
88802 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88803 var matched = _mainPresetIndex.match(d.relation, context.graph());
88804 return matched && matched.name() || _t('inspector.relation');
88806 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88807 return utilDisplayName(d.relation);
88809 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
88810 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
88811 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88812 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88814 }).property('type', 'text').property('value', function (d) {
88815 return typeof d.role === 'string' ? d.role : '';
88816 }).attr('title', function (d) {
88817 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
88818 }).attr('placeholder', function (d) {
88819 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
88820 }).classed('mixed', function (d) {
88821 return Array.isArray(d.role);
88822 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
88825 wrapEnter.each(bindTypeahead);
88828 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
88830 newMembership.exit().remove(); // Enter
88832 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
88833 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
88834 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
88835 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
88836 list.selectAll('.member-row-new').remove();
88838 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88839 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
88841 newMembership = newMembership.merge(newMembershipEnter);
88842 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
88843 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
88845 var addRow = selection.selectAll('.add-row').data([0]); // enter
88847 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
88848 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
88849 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
88850 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
88851 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
88853 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
88856 addRow = addRow.merge(addRowEnter);
88857 addRow.select('.add-relation').on('click', function () {
88859 section.reRender();
88860 list.selectAll('.member-entity-input').node().focus();
88863 function acceptEntity(d) {
88867 } // remove hover-higlighting
88870 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88871 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
88872 addMembership(d, role);
88875 function cancelEntity() {
88876 var input = newMembership.selectAll('.member-entity-input');
88877 input.property('value', ''); // remove hover-higlighting
88879 context.surface().selectAll('.highlighted').classed('highlighted', false);
88882 function bindTypeahead(d) {
88883 var row = select(this);
88884 var role = row.selectAll('input.member-role');
88885 var origValue = role.property('value');
88887 function sort(value, data) {
88888 var sameletter = [];
88891 for (var i = 0; i < data.length; i++) {
88892 if (data[i].value.substring(0, value.length) === value) {
88893 sameletter.push(data[i]);
88895 other.push(data[i]);
88899 return sameletter.concat(other);
88902 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88903 var rtype = d.relation.tags.type;
88906 rtype: rtype || '',
88907 geometry: context.graph().geometry(_entityIDs[0]),
88909 }, function (err, data) {
88910 if (!err) callback(sort(role, data));
88912 }).on('cancel', function () {
88913 role.property('value', origValue);
88917 function unbind() {
88918 var row = select(this);
88919 row.selectAll('input.member-role').call(uiCombobox.off, context);
88923 section.entityIDs = function (val) {
88924 if (!arguments.length) return _entityIDs;
88926 _showBlank = false;
88933 function uiSectionSelectionList(context) {
88934 var _selectedIDs = [];
88935 var section = uiSection('selected-features', context).shouldDisplay(function () {
88936 return _selectedIDs.length > 1;
88937 }).label(function () {
88938 return _t('inspector.title_count', {
88939 title: _t.html('inspector.features'),
88940 count: _selectedIDs.length
88942 }).disclosureContent(renderDisclosureContent);
88943 context.history().on('change.selectionList', function (difference) {
88945 section.reRender();
88949 section.entityIDs = function (val) {
88950 if (!arguments.length) return _selectedIDs;
88951 _selectedIDs = val;
88955 function selectEntity(d3_event, entity) {
88956 context.enter(modeSelect(context, [entity.id]));
88959 function deselectEntity(d3_event, entity) {
88960 var selectedIDs = _selectedIDs.slice();
88962 var index = selectedIDs.indexOf(entity.id);
88965 selectedIDs.splice(index, 1);
88966 context.enter(modeSelect(context, selectedIDs));
88970 function renderDisclosureContent(selection) {
88971 var list = selection.selectAll('.feature-list').data([0]);
88972 list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
88974 var entities = _selectedIDs.map(function (id) {
88975 return context.hasEntity(id);
88976 }).filter(Boolean);
88978 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
88979 items.exit().remove(); // Enter
88981 var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
88982 select(this).on('mouseover', function () {
88983 utilHighlightEntities([d.id], true, context);
88984 }).on('mouseout', function () {
88985 utilHighlightEntities([d.id], false, context);
88988 var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
88989 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
88990 label.append('span').attr('class', 'entity-type');
88991 label.append('span').attr('class', 'entity-name');
88992 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
88994 items = items.merge(enter);
88995 items.selectAll('.entity-geom-icon use').attr('href', function () {
88996 var entity = this.parentNode.parentNode.__data__;
88997 return '#iD-icon-' + entity.geometry(context.graph());
88999 items.selectAll('.entity-type').html(function (entity) {
89000 return _mainPresetIndex.match(entity, context.graph()).name();
89002 items.selectAll('.entity-name').html(function (d) {
89003 // fetch latest entity
89004 var entity = context.entity(d.id);
89005 return utilDisplayName(entity);
89012 function uiEntityEditor(context) {
89013 var dispatch$1 = dispatch('choose');
89014 var _state = 'select';
89015 var _coalesceChanges = false;
89016 var _modified = false;
89022 var _activePresets = [];
89028 function entityEditor(selection) {
89029 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
89031 var header = selection.selectAll('.header').data([0]); // Enter
89033 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
89034 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
89035 headerEnter.append('button').attr('class', 'close').on('click', function () {
89036 context.enter(modeBrowse(context));
89037 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89038 headerEnter.append('h3'); // Update
89040 header = header.merge(headerEnter);
89041 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
89042 header.selectAll('.preset-reset').on('click', function () {
89043 dispatch$1.call('choose', this, _activePresets);
89046 var body = selection.selectAll('.inspector-body').data([0]); // Enter
89048 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
89050 body = body.merge(bodyEnter);
89053 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
89054 dispatch$1.call('choose', this, presets);
89055 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
89058 _sections.forEach(function (section) {
89059 if (section.entityIDs) {
89060 section.entityIDs(_entityIDs);
89063 if (section.presets) {
89064 section.presets(_activePresets);
89067 if (section.tags) {
89068 section.tags(combinedTags);
89071 if (section.state) {
89072 section.state(_state);
89075 body.call(section.render);
89078 context.history().on('change.entity-editor', historyChanged);
89080 function historyChanged(difference) {
89081 if (selection.selectAll('.entity-editor').empty()) return;
89082 if (_state === 'hide') return;
89083 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
89084 if (!significant) return;
89085 _entityIDs = _entityIDs.filter(context.hasEntity);
89086 if (!_entityIDs.length) return;
89087 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89088 loadActivePresets();
89089 var graph = context.graph();
89090 entityEditor.modified(_base !== graph);
89091 entityEditor(selection);
89093 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89094 // flash the button to indicate the preset changed
89095 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
89098 } // Tag changes that fire on input can all get coalesced into a single
89099 // history operation when the user leaves the field. #2342
89100 // Use explicit entityIDs in case the selection changes before the event is fired.
89103 function changeTags(entityIDs, changed, onInput) {
89106 for (var i in entityIDs) {
89107 var entityID = entityIDs[i];
89108 var entity = context.entity(entityID);
89109 var tags = Object.assign({}, entity.tags); // shallow copy
89111 for (var k in changed) {
89113 var v = changed[k];
89115 if (v !== undefined || tags.hasOwnProperty(k)) {
89121 tags = utilCleanTags(tags);
89124 if (!fastDeepEqual(entity.tags, tags)) {
89125 actions.push(actionChangeTags(entityID, tags));
89129 if (actions.length) {
89130 var combinedAction = function combinedAction(graph) {
89131 actions.forEach(function (action) {
89132 graph = action(graph);
89137 var annotation = _t('operations.change_tags.annotation');
89139 if (_coalesceChanges) {
89140 context.overwrite(combinedAction, annotation);
89142 context.perform(combinedAction, annotation);
89143 _coalesceChanges = !!onInput;
89145 } // if leaving field (blur event), rerun validation
89149 context.validator().validate();
89153 function revertTags(keys) {
89156 for (var i in _entityIDs) {
89157 var entityID = _entityIDs[i];
89158 var original = context.graph().base().entities[entityID];
89161 for (var j in keys) {
89163 changed[key] = original ? original.tags[key] : undefined;
89166 var entity = context.entity(entityID);
89167 var tags = Object.assign({}, entity.tags); // shallow copy
89169 for (var k in changed) {
89171 var v = changed[k];
89173 if (v !== undefined || tags.hasOwnProperty(k)) {
89178 tags = utilCleanTags(tags);
89180 if (!fastDeepEqual(entity.tags, tags)) {
89181 actions.push(actionChangeTags(entityID, tags));
89185 if (actions.length) {
89186 var combinedAction = function combinedAction(graph) {
89187 actions.forEach(function (action) {
89188 graph = action(graph);
89193 var annotation = _t('operations.change_tags.annotation');
89195 if (_coalesceChanges) {
89196 context.overwrite(combinedAction, annotation);
89198 context.perform(combinedAction, annotation);
89199 _coalesceChanges = false;
89203 context.validator().validate();
89206 entityEditor.modified = function (val) {
89207 if (!arguments.length) return _modified;
89209 return entityEditor;
89212 entityEditor.state = function (val) {
89213 if (!arguments.length) return _state;
89215 return entityEditor;
89218 entityEditor.entityIDs = function (val) {
89219 if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
89220 // could be reselecting after something like dragging a node
89222 _base = context.graph();
89223 _coalesceChanges = false;
89224 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
89227 loadActivePresets(true);
89228 return entityEditor.modified(false);
89231 entityEditor.newFeature = function (val) {
89232 if (!arguments.length) return _newFeature;
89234 return entityEditor;
89237 function loadActivePresets(isForNewSelection) {
89238 var graph = context.graph();
89241 for (var i in _entityIDs) {
89242 var entity = graph.hasEntity(_entityIDs[i]);
89243 if (!entity) return;
89244 var match = _mainPresetIndex.match(entity, graph);
89245 if (!counts[match.id]) counts[match.id] = 0;
89246 counts[match.id] += 1;
89249 var matches = Object.keys(counts).sort(function (p1, p2) {
89250 return counts[p2] - counts[p1];
89251 }).map(function (pID) {
89252 return _mainPresetIndex.item(pID);
89255 if (!isForNewSelection) {
89256 // A "weak" preset doesn't set any tags. (e.g. "Address")
89257 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")
89259 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
89262 entityEditor.presets(matches);
89265 entityEditor.presets = function (val) {
89266 if (!arguments.length) return _activePresets; // don't reload the same preset
89268 if (!utilArrayIdentical(val, _activePresets)) {
89269 _activePresets = val;
89272 return entityEditor;
89275 return utilRebind(entityEditor, dispatch$1, 'on');
89278 function uiPresetList(context) {
89279 var dispatch$1 = dispatch('cancel', 'choose');
89283 var _currentPresets;
89285 var _autofocus = false;
89287 function presetList(selection) {
89288 if (!_entityIDs) return;
89289 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89290 selection.html('');
89291 var messagewrap = selection.append('div').attr('class', 'header fillL');
89292 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
89293 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
89294 dispatch$1.call('cancel', this);
89295 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
89297 function initialKeydown(d3_event) {
89298 // hack to let delete shortcut work when search is autofocused
89299 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89300 d3_event.preventDefault();
89301 d3_event.stopPropagation();
89302 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
89303 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
89304 d3_event.preventDefault();
89305 d3_event.stopPropagation();
89307 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
89308 // don't check for delete/undo hack on future keydown events
89309 select(this).on('keydown', keydown);
89310 keydown.call(this, d3_event);
89314 function keydown(d3_event) {
89316 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
89317 search.node().selectionStart === search.property('value').length) {
89318 d3_event.preventDefault();
89319 d3_event.stopPropagation(); // move focus to the first item in the preset list
89321 var buttons = list.selectAll('.preset-list-button');
89322 if (!buttons.empty()) buttons.nodes()[0].focus();
89326 function keypress(d3_event) {
89328 var value = search.property('value');
89330 if (d3_event.keyCode === 13 && // ↩ Return
89332 list.selectAll('.preset-list-item:first-child').each(function (d) {
89333 d.choose.call(this);
89338 function inputevent() {
89339 var value = search.property('value');
89340 list.classed('filtered', value.length);
89341 var extent = combinedEntityExtent();
89342 var results, messageText;
89344 if (value.length && extent) {
89345 var center = extent.center();
89346 var countryCode = iso1A2Code(center);
89347 results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
89348 messageText = _t('inspector.results', {
89349 n: results.collection.length,
89353 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
89354 messageText = _t('inspector.choose');
89357 list.call(drawList, results);
89358 message.html(messageText);
89361 var searchWrap = selection.append('div').attr('class', 'search-header');
89362 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
89363 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);
89366 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
89367 // so try again on the next pass
89369 setTimeout(function () {
89370 search.node().focus();
89374 var listWrap = selection.append('div').attr('class', 'inspector-body');
89375 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
89376 context.features().on('change.preset-list', updateForFeatureHiddenState);
89379 function drawList(list, presets) {
89380 presets = presets.matchAllGeometry(entityGeometries());
89381 var collection = presets.collection.reduce(function (collection, preset) {
89382 if (!preset) return collection;
89384 if (preset.members) {
89385 if (preset.members.collection.filter(function (preset) {
89386 return preset.addable();
89388 collection.push(CategoryItem(preset));
89390 } else if (preset.addable()) {
89391 collection.push(PresetItem(preset));
89396 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
89397 return d.preset.id;
89400 items.exit().remove();
89401 items.enter().append('div').attr('class', function (item) {
89402 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
89403 }).classed('current', function (item) {
89404 return _currentPresets.indexOf(item.preset) !== -1;
89405 }).each(function (item) {
89406 select(this).call(item);
89407 }).style('opacity', 0).transition().style('opacity', 1);
89408 updateForFeatureHiddenState();
89411 function itemKeydown(d3_event) {
89412 // the actively focused item
89413 var item = select(this.closest('.preset-list-item'));
89414 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
89416 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
89417 d3_event.preventDefault();
89418 d3_event.stopPropagation(); // the next item in the list at the same level
89420 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
89422 if (nextItem.empty()) {
89423 // if there is a parent item
89424 if (!parentItem.empty()) {
89425 // the item is the last item of a sublist,
89426 // select the next item at the parent level
89427 nextItem = select(parentItem.node().nextElementSibling);
89428 } // if the focused item is expanded
89430 } else if (select(this).classed('expanded')) {
89431 // select the first subitem instead
89432 nextItem = item.select('.subgrid .preset-list-item:first-child');
89435 if (!nextItem.empty()) {
89436 // focus on the next item
89437 nextItem.select('.preset-list-button').node().focus();
89438 } // arrow up, move focus to the previous, higher item
89440 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
89441 d3_event.preventDefault();
89442 d3_event.stopPropagation(); // the previous item in the list at the same level
89444 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
89446 if (previousItem.empty()) {
89447 // if there is a parent item
89448 if (!parentItem.empty()) {
89449 // the item is the first subitem of a sublist select the parent item
89450 previousItem = parentItem;
89451 } // if the previous item is expanded
89453 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
89454 // select the last subitem of the sublist of the previous item
89455 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
89458 if (!previousItem.empty()) {
89459 // focus on the previous item
89460 previousItem.select('.preset-list-button').node().focus();
89462 // the focus is at the top of the list, move focus back to the search field
89463 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
89464 search.node().focus();
89465 } // arrow left, move focus to the parent item if there is one
89467 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89468 d3_event.preventDefault();
89469 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
89471 if (!parentItem.empty()) {
89472 parentItem.select('.preset-list-button').node().focus();
89473 } // arrow right, choose this item
89475 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89476 d3_event.preventDefault();
89477 d3_event.stopPropagation();
89478 item.datum().choose.call(select(this).node());
89482 function CategoryItem(preset) {
89487 function item(selection) {
89488 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
89491 var isExpanded = select(this).classed('expanded');
89492 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
89493 select(this).classed('expanded', !isExpanded);
89494 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
89498 var geometries = entityGeometries();
89499 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) {
89500 // right arrow, expand the focused item
89501 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89502 d3_event.preventDefault();
89503 d3_event.stopPropagation(); // if the item isn't expanded
89505 if (!select(this).classed('expanded')) {
89506 // toggle expansion (expand the item)
89507 click.call(this, d3_event);
89508 } // left arrow, collapse the focused item
89510 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89511 d3_event.preventDefault();
89512 d3_event.stopPropagation(); // if the item is expanded
89514 if (select(this).classed('expanded')) {
89515 // toggle expansion (collapse the item)
89516 click.call(this, d3_event);
89519 itemKeydown.call(this, d3_event);
89522 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89523 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
89524 return preset.nameLabel() + '…';
89526 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
89527 box.append('div').attr('class', 'arrow');
89528 sublist = box.append('div').attr('class', 'preset-list fillL3');
89531 item.choose = function () {
89532 if (!box || !sublist) return;
89536 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
89539 var members = preset.members.matchAllGeometry(entityGeometries());
89540 sublist.call(drawList, members);
89541 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
89545 item.preset = preset;
89549 function PresetItem(preset) {
89550 function item(selection) {
89551 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
89552 var geometries = entityGeometries();
89553 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);
89554 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89555 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
89556 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
89559 wrap.call(item.reference.button);
89560 selection.call(item.reference.body);
89563 item.choose = function () {
89564 if (select(this).classed('disabled')) return;
89566 if (!context.inIntro()) {
89567 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
89570 context.perform(function (graph) {
89571 for (var i in _entityIDs) {
89572 var entityID = _entityIDs[i];
89573 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
89574 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
89578 }, _t('operations.change_tags.annotation'));
89579 context.validator().validate(); // rerun validation
89581 dispatch$1.call('choose', this, preset);
89584 item.help = function (d3_event) {
89585 d3_event.stopPropagation();
89586 item.reference.toggle();
89589 item.preset = preset;
89590 item.reference = uiTagReference(preset.reference());
89594 function updateForFeatureHiddenState() {
89595 if (!_entityIDs.every(context.hasEntity)) return;
89596 var geometries = entityGeometries();
89597 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
89599 button.call(uiTooltip().destroyAny);
89600 button.each(function (item, index) {
89601 var hiddenPresetFeaturesId;
89603 for (var i in geometries) {
89604 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
89605 if (hiddenPresetFeaturesId) break;
89608 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
89609 select(this).classed('disabled', isHiddenPreset);
89611 if (isHiddenPreset) {
89612 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
89613 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
89614 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
89615 })).placement(index < 2 ? 'bottom' : 'top'));
89620 presetList.autofocus = function (val) {
89621 if (!arguments.length) return _autofocus;
89626 presetList.entityIDs = function (val) {
89627 if (!arguments.length) return _entityIDs;
89630 if (_entityIDs && _entityIDs.length) {
89631 var presets = _entityIDs.map(function (entityID) {
89632 return _mainPresetIndex.match(context.entity(entityID), context.graph());
89635 presetList.presets(presets);
89641 presetList.presets = function (val) {
89642 if (!arguments.length) return _currentPresets;
89643 _currentPresets = val;
89647 function entityGeometries() {
89650 for (var i in _entityIDs) {
89651 var entityID = _entityIDs[i];
89652 var entity = context.entity(entityID);
89653 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
89655 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
89656 geometry = 'point';
89659 if (!counts[geometry]) counts[geometry] = 0;
89660 counts[geometry] += 1;
89663 return Object.keys(counts).sort(function (geom1, geom2) {
89664 return counts[geom2] - counts[geom1];
89668 function combinedEntityExtent() {
89669 return _entityIDs.reduce(function (extent, entityID) {
89670 var entity = context.graph().entity(entityID);
89671 return extent.extend(entity.extent(context.graph()));
89675 return utilRebind(presetList, dispatch$1, 'on');
89678 function uiInspector(context) {
89679 var presetList = uiPresetList(context);
89680 var entityEditor = uiEntityEditor(context);
89681 var wrap = select(null),
89682 presetPane = select(null),
89683 editorPane = select(null);
89684 var _state = 'select';
89688 var _newFeature = false;
89690 function inspector(selection) {
89691 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
89692 inspector.setPreset();
89694 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
89695 wrap = selection.selectAll('.panewrap').data([0]);
89696 var enter = wrap.enter().append('div').attr('class', 'panewrap');
89697 enter.append('div').attr('class', 'preset-list-pane pane');
89698 enter.append('div').attr('class', 'entity-editor-pane pane');
89699 wrap = wrap.merge(enter);
89700 presetPane = wrap.selectAll('.preset-list-pane');
89701 editorPane = wrap.selectAll('.entity-editor-pane');
89703 function shouldDefaultToPresetList() {
89704 // always show the inspector on hover
89705 if (_state !== 'select') return false; // can only change preset on single selection
89707 if (_entityIDs.length !== 1) return false;
89708 var entityID = _entityIDs[0];
89709 var entity = context.hasEntity(entityID);
89710 if (!entity) return false; // default to inspector if there are already tags
89712 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
89714 if (_newFeature) return true; // all existing features except vertices should default to inspector
89716 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
89718 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
89720 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
89722 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
89727 if (shouldDefaultToPresetList()) {
89728 wrap.style('right', '-100%');
89729 editorPane.classed('hide', true);
89730 presetPane.classed('hide', false).call(presetList);
89732 wrap.style('right', '0%');
89733 presetPane.classed('hide', true);
89734 editorPane.classed('hide', false).call(entityEditor);
89737 var footer = selection.selectAll('.footer').data([0]);
89738 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
89739 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
89742 inspector.showList = function (presets) {
89743 presetPane.classed('hide', false);
89744 wrap.transition().styleTween('right', function () {
89745 return interpolate('0%', '-100%');
89746 }).on('end', function () {
89747 editorPane.classed('hide', true);
89751 presetList.presets(presets);
89754 presetPane.call(presetList.autofocus(true));
89757 inspector.setPreset = function (preset) {
89758 // upon setting multipolygon, go to the area preset list instead of the editor
89759 if (preset && preset.id === 'type/multipolygon') {
89760 presetPane.call(presetList.autofocus(true));
89762 editorPane.classed('hide', false);
89763 wrap.transition().styleTween('right', function () {
89764 return interpolate('-100%', '0%');
89765 }).on('end', function () {
89766 presetPane.classed('hide', true);
89770 entityEditor.presets([preset]);
89773 editorPane.call(entityEditor);
89777 inspector.state = function (val) {
89778 if (!arguments.length) return _state;
89780 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
89782 context.container().selectAll('.field-help-body').remove();
89786 inspector.entityIDs = function (val) {
89787 if (!arguments.length) return _entityIDs;
89792 inspector.newFeature = function (val) {
89793 if (!arguments.length) return _newFeature;
89801 function uiSidebar(context) {
89802 var inspector = uiInspector(context);
89803 var dataEditor = uiDataEditor(context);
89804 var noteEditor = uiNoteEditor(context);
89805 var improveOsmEditor = uiImproveOsmEditor(context);
89806 var keepRightEditor = uiKeepRightEditor(context);
89807 var osmoseEditor = uiOsmoseEditor(context);
89811 var _wasData = false;
89812 var _wasNote = false;
89813 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
89815 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
89817 function sidebar(selection) {
89818 var container = context.container();
89819 var minWidth = 240;
89821 var containerWidth;
89822 var dragOffset; // Set the initial width constraints
89824 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
89825 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
89826 var downPointerId, lastClientX, containerLocGetter;
89828 function pointerdown(d3_event) {
89829 if (downPointerId) return;
89830 if ('button' in d3_event && d3_event.button !== 0) return;
89831 downPointerId = d3_event.pointerId || 'mouse';
89832 lastClientX = d3_event.clientX;
89833 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
89835 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
89836 sidebarWidth = selection.node().getBoundingClientRect().width;
89837 containerWidth = container.node().getBoundingClientRect().width;
89838 var widthPct = sidebarWidth / containerWidth * 100;
89839 selection.style('width', widthPct + '%') // lock in current width
89840 .style('max-width', '85%'); // but allow larger widths
89842 resizer.classed('dragging', true);
89843 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
89844 // disable page scrolling while resizing on touch input
89845 d3_event.preventDefault();
89848 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
89851 function pointermove(d3_event) {
89852 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89853 d3_event.preventDefault();
89854 var dx = d3_event.clientX - lastClientX;
89855 lastClientX = d3_event.clientX;
89856 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89857 var scaleX = isRTL ? 0 : 1;
89858 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89859 var x = containerLocGetter(d3_event)[0] - dragOffset;
89860 sidebarWidth = isRTL ? containerWidth - x : x;
89861 var isCollapsed = selection.classed('collapsed');
89862 var shouldCollapse = sidebarWidth < minWidth;
89863 selection.classed('collapsed', shouldCollapse);
89865 if (shouldCollapse) {
89866 if (!isCollapsed) {
89867 selection.style(xMarginProperty, '-400px').style('width', '400px');
89868 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
89871 var widthPct = sidebarWidth / containerWidth * 100;
89872 selection.style(xMarginProperty, null).style('width', widthPct + '%');
89875 context.ui().onResize([-sidebarWidth * scaleX, 0]);
89877 context.ui().onResize([-dx * scaleX, 0]);
89882 function pointerup(d3_event) {
89883 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89884 downPointerId = null;
89885 resizer.classed('dragging', false);
89886 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
89889 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
89890 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
89892 var hoverModeSelect = function hoverModeSelect(targets) {
89893 context.container().selectAll('.feature-list-item button').classed('hover', false);
89895 if (context.selectedIDs().length > 1 && targets && targets.length) {
89896 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
89897 return targets.indexOf(node) !== -1;
89900 if (!elements.empty()) {
89901 elements.classed('hover', true);
89906 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
89908 function hover(targets) {
89909 var datum = targets && targets.length && targets[0];
89911 if (datum && datum.__featurehash__) {
89912 // hovering on data
89914 sidebar.show(dataEditor.datum(datum));
89915 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89916 } else if (datum instanceof osmNote) {
89917 if (context.mode().id === 'drag-note') return;
89919 var osm = services.osm;
89922 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
89925 sidebar.show(noteEditor.note(datum));
89926 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89927 } else if (datum instanceof QAItem) {
89929 var errService = services[datum.service];
89932 // marker may contain stale data - get latest
89933 datum = errService.getError(datum.id);
89934 } // Currently only three possible services
89939 if (datum.service === 'keepRight') {
89940 errEditor = keepRightEditor;
89941 } else if (datum.service === 'osmose') {
89942 errEditor = osmoseEditor;
89944 errEditor = improveOsmEditor;
89947 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
89948 return d.id === datum.id;
89950 sidebar.show(errEditor.error(datum));
89951 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89952 } else if (!_current && datum instanceof osmEntity) {
89953 featureListWrap.classed('inspector-hidden', true);
89954 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
89956 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
89957 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
89958 inspectorWrap.call(inspector);
89960 } else if (!_current) {
89961 featureListWrap.classed('inspector-hidden', false);
89962 inspectorWrap.classed('inspector-hidden', true);
89963 inspector.state('hide');
89964 } else if (_wasData || _wasNote || _wasQaItem) {
89967 _wasQaItem = false;
89968 context.container().selectAll('.note').classed('hover', false);
89969 context.container().selectAll('.qaItem').classed('hover', false);
89974 sidebar.hover = throttle(hover, 200);
89976 sidebar.intersects = function (extent) {
89977 var rect = selection.node().getBoundingClientRect();
89978 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
89981 sidebar.select = function (ids, newFeature) {
89984 if (ids && ids.length) {
89985 var entity = ids.length === 1 && context.entity(ids[0]);
89987 if (entity && newFeature && selection.classed('collapsed')) {
89988 // uncollapse the sidebar
89989 var extent = entity.extent(context.graph());
89990 sidebar.expand(sidebar.intersects(extent));
89993 featureListWrap.classed('inspector-hidden', true);
89994 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
89995 // themselves may have changed
89997 inspector.state('select').entityIDs(ids).newFeature(newFeature);
89998 inspectorWrap.call(inspector);
90000 inspector.state('hide');
90004 sidebar.showPresetList = function () {
90005 inspector.showList();
90008 sidebar.show = function (component, element) {
90009 featureListWrap.classed('inspector-hidden', true);
90010 inspectorWrap.classed('inspector-hidden', true);
90011 if (_current) _current.remove();
90012 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
90015 sidebar.hide = function () {
90016 featureListWrap.classed('inspector-hidden', false);
90017 inspectorWrap.classed('inspector-hidden', true);
90018 if (_current) _current.remove();
90022 sidebar.expand = function (moveMap) {
90023 if (selection.classed('collapsed')) {
90024 sidebar.toggle(moveMap);
90028 sidebar.collapse = function (moveMap) {
90029 if (!selection.classed('collapsed')) {
90030 sidebar.toggle(moveMap);
90034 sidebar.toggle = function (moveMap) {
90035 // Don't allow sidebar to toggle when the user is in the walkthrough.
90036 if (context.inIntro()) return;
90037 var isCollapsed = selection.classed('collapsed');
90038 var isCollapsing = !isCollapsed;
90039 var isRTL = _mainLocalizer.textDirection() === 'rtl';
90040 var scaleX = isRTL ? 0 : 1;
90041 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90042 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
90044 selection.style('width', sidebarWidth + 'px');
90045 var startMargin, endMargin, lastMargin;
90047 if (isCollapsing) {
90048 startMargin = lastMargin = 0;
90049 endMargin = -sidebarWidth;
90051 startMargin = lastMargin = -sidebarWidth;
90055 if (!isCollapsing) {
90056 // unhide the sidebar's content before it transitions onscreen
90057 selection.classed('collapsed', isCollapsing);
90060 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
90061 var i = d3_interpolateNumber(startMargin, endMargin);
90062 return function (t) {
90063 var dx = lastMargin - Math.round(i(t));
90064 lastMargin = lastMargin - dx;
90065 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90067 }).on('end', function () {
90068 if (isCollapsing) {
90069 // hide the sidebar's content after it transitions offscreen
90070 selection.classed('collapsed', isCollapsing);
90071 } // switch back from px to %
90074 if (!isCollapsing) {
90075 var containerWidth = container.node().getBoundingClientRect().width;
90076 var widthPct = sidebarWidth / containerWidth * 100;
90077 selection.style(xMarginProperty, null).style('width', widthPct + '%');
90080 }; // toggle the sidebar collapse when double-clicking the resizer
90083 resizer.on('dblclick', function (d3_event) {
90084 d3_event.preventDefault();
90086 if (d3_event.sourceEvent) {
90087 d3_event.sourceEvent.preventDefault();
90091 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
90093 context.map().on('crossEditableZoom.sidebar', function (within) {
90094 if (!within && !selection.select('.inspector-hover').empty()) {
90100 sidebar.showPresetList = function () {};
90102 sidebar.hover = function () {};
90104 sidebar.hover.cancel = function () {};
90106 sidebar.intersects = function () {};
90108 sidebar.select = function () {};
90110 sidebar.show = function () {};
90112 sidebar.hide = function () {};
90114 sidebar.expand = function () {};
90116 sidebar.collapse = function () {};
90118 sidebar.toggle = function () {};
90123 function uiSourceSwitch(context) {
90126 function click(d3_event) {
90127 d3_event.preventDefault();
90128 var osm = context.connection();
90130 if (context.inIntro()) return;
90131 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
90132 var isLive = select(this).classed('live');
90134 context.enter(modeBrowse(context));
90135 context.history().clearSaved(); // remove saved history
90137 context.flush(); // remove stored data
90139 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
90140 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
90143 var sourceSwitch = function sourceSwitch(selection) {
90144 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
90147 sourceSwitch.keys = function (_) {
90148 if (!arguments.length) return keys;
90150 return sourceSwitch;
90153 return sourceSwitch;
90156 function uiSpinner(context) {
90157 var osm = context.connection();
90158 return function (selection) {
90159 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
90162 osm.on('loading.spinner', function () {
90163 img.transition().style('opacity', 1);
90164 }).on('loaded.spinner', function () {
90165 img.transition().style('opacity', 0);
90171 function uiSplash(context) {
90172 return function (selection) {
90173 // Exception - if there are restorable changes, skip this splash screen.
90174 // This is because we currently only support one `uiModal` at a time
90175 // and we need to show them `uiRestore`` instead of this one.
90176 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
90178 var updateMessage = '';
90179 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
90180 var showSplash = !corePreferences('sawSplash');
90182 if (sawPrivacyVersion !== context.privacyVersion) {
90183 updateMessage = _t('splash.privacy_update');
90187 if (!showSplash) return;
90188 corePreferences('sawSplash', true);
90189 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
90191 _mainFileFetcher.get('intro_graph');
90192 var modalSelection = uiModal(selection);
90193 modalSelection.select('.modal').attr('class', 'modal-splash modal');
90194 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
90195 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
90196 var modalSection = introModal.append('div').attr('class', 'modal-section');
90197 modalSection.append('p').html(_t.html('splash.text', {
90198 version: context.version,
90199 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
90200 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
90202 modalSection.append('p').html(_t.html('splash.privacy', {
90203 updateMessage: updateMessage,
90204 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
90206 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
90207 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
90208 context.container().call(uiIntro(context));
90209 modalSelection.close();
90211 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
90212 walkthrough.append('div').html(_t.html('splash.walkthrough'));
90213 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
90214 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
90215 startEditing.append('div').html(_t.html('splash.start'));
90216 modalSelection.select('button.close').attr('class', 'hide');
90220 function uiStatus(context) {
90221 var osm = context.connection();
90222 return function (selection) {
90225 function update(err, apiStatus) {
90226 selection.html('');
90229 if (apiStatus === 'connectionSwitched') {
90230 // if the connection was just switched, we can't rely on
90231 // the status (we're getting the status of the previous api)
90233 } else if (apiStatus === 'rateLimited') {
90234 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) {
90235 d3_event.preventDefault();
90236 osm.authenticate();
90239 // don't allow retrying too rapidly
90240 var throttledRetry = throttle(function () {
90241 // try loading the visible tiles
90242 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
90244 osm.reloadApiStatus();
90245 }, 2000); // eslint-disable-next-line no-warning-comments
90246 // TODO: nice messages for different error types
90249 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
90250 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
90251 d3_event.preventDefault();
90255 } else if (apiStatus === 'readonly') {
90256 selection.html(_t.html('osm_api_status.message.readonly'));
90257 } else if (apiStatus === 'offline') {
90258 selection.html(_t.html('osm_api_status.message.offline'));
90261 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
90264 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
90266 window.setInterval(function () {
90267 osm.reloadApiStatus();
90268 }, 90000); // load the initial status in case no OSM data was loaded yet
90270 osm.reloadApiStatus();
90274 function modeDrawArea(context, wayID, startGraph, button) {
90279 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
90280 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
90282 mode.wayID = wayID;
90284 mode.enter = function () {
90285 context.install(behavior);
90288 mode.exit = function () {
90289 context.uninstall(behavior);
90292 mode.selectedIDs = function () {
90296 mode.activeID = function () {
90297 return behavior && behavior.activeID() || [];
90303 function modeAddArea(context, mode) {
90304 mode.id = 'add-area';
90305 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90306 var defaultTags = {
90309 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
90311 function actionClose(wayId) {
90312 return function (graph) {
90313 return graph.replace(graph.entity(wayId).close());
90317 function start(loc) {
90318 var startGraph = context.graph();
90319 var node = osmNode({
90325 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90326 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90329 function startFromWay(loc, edge) {
90330 var startGraph = context.graph();
90331 var node = osmNode({
90337 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
90341 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90344 function startFromNode(node) {
90345 var startGraph = context.graph();
90349 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90350 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90353 mode.enter = function () {
90354 context.install(behavior);
90357 mode.exit = function () {
90358 context.uninstall(behavior);
90364 function modeAddLine(context, mode) {
90365 mode.id = 'add-line';
90366 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90367 var defaultTags = {};
90368 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
90370 function start(loc) {
90371 var startGraph = context.graph();
90372 var node = osmNode({
90378 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
90379 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90382 function startFromWay(loc, edge) {
90383 var startGraph = context.graph();
90384 var node = osmNode({
90390 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
90394 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90397 function startFromNode(node) {
90398 var startGraph = context.graph();
90402 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
90403 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90406 mode.enter = function () {
90407 context.install(behavior);
90410 mode.exit = function () {
90411 context.uninstall(behavior);
90417 function modeAddPoint(context, mode) {
90418 mode.id = 'add-point';
90419 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
90420 var defaultTags = {};
90421 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
90423 function add(loc) {
90424 var node = osmNode({
90428 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
90429 enterSelectMode(node);
90432 function addWay(loc, edge) {
90433 var node = osmNode({
90436 context.perform(actionAddMidpoint({
90439 }, node), _t('operations.add.annotation.vertex'));
90440 enterSelectMode(node);
90443 function enterSelectMode(node) {
90444 context.enter(modeSelect(context, [node.id]).newFeature(true));
90447 function addNode(node) {
90448 if (Object.keys(defaultTags).length === 0) {
90449 enterSelectMode(node);
90453 var tags = Object.assign({}, node.tags); // shallow copy
90455 for (var key in defaultTags) {
90456 tags[key] = defaultTags[key];
90459 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
90460 enterSelectMode(node);
90463 function cancel() {
90464 context.enter(modeBrowse(context));
90467 mode.enter = function () {
90468 context.install(behavior);
90471 mode.exit = function () {
90472 context.uninstall(behavior);
90478 function modeAddNote(context) {
90482 description: _t.html('modes.add_note.description'),
90483 key: _t('modes.add_note.key')
90485 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
90487 function add(loc) {
90488 var osm = services.osm;
90490 var note = osmNote({
90495 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
90497 context.map().pan([0, 0]);
90498 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
90501 function cancel() {
90502 context.enter(modeBrowse(context));
90505 mode.enter = function () {
90506 context.install(behavior);
90509 mode.exit = function () {
90510 context.uninstall(behavior);
90516 function uiConflicts(context) {
90517 var dispatch$1 = dispatch('cancel', 'save');
90518 var keybinding = utilKeybinding('conflicts');
90524 var _shownConflictIndex;
90526 function keybindingOn() {
90527 select(document).call(keybinding.on('⎋', cancel, true));
90530 function keybindingOff() {
90531 select(document).call(keybinding.unbind);
90534 function tryAgain() {
90536 dispatch$1.call('save');
90539 function cancel() {
90541 dispatch$1.call('cancel');
90544 function conflicts(selection) {
90546 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
90547 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
90548 headerEnter.append('h3').html(_t.html('save.conflict.header'));
90549 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
90550 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
90552 var detected = utilDetect();
90553 var changeset = new osmChangeset();
90554 delete changeset.id; // Export without changeset_id
90556 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
90557 var blob = new Blob([data], {
90558 type: 'text/xml;charset=utf-8;'
90560 var fileName = 'changes.osc';
90561 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
90563 if (detected.download) {
90564 // All except IE11 and Edge
90565 linkEnter // download the data as a file
90566 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90569 linkEnter // open data uri in a new tab
90570 .attr('target', '_blank').on('click.download', function () {
90571 navigator.msSaveBlob(blob, fileName);
90575 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
90576 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
90577 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
90578 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
90579 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
90580 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
90583 function showConflict(selection, index) {
90584 index = utilWrap(index, _conflictList.length);
90585 _shownConflictIndex = index;
90586 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
90588 if (index === _conflictList.length - 1) {
90589 window.setTimeout(function () {
90590 parent.select('.conflicts-button').attr('disabled', null);
90591 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
90595 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
90596 conflict.exit().remove();
90597 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
90598 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
90600 total: _conflictList.length
90602 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
90604 }).on('click', function (d3_event, d) {
90605 d3_event.preventDefault();
90606 zoomToEntity(d.id);
90608 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
90609 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
90610 return d.details || [];
90611 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
90614 details.append('div').attr('class', 'conflict-choices').call(addChoices);
90615 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
90616 return _t.html('save.conflict.' + d);
90617 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
90618 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
90619 }).on('click', function (d3_event, d) {
90620 d3_event.preventDefault();
90621 var container = parent.selectAll('.conflict-container');
90622 var sign = d === 'previous' ? -1 : 1;
90623 container.selectAll('.conflict').remove();
90624 container.call(showConflict, index + sign);
90628 function addChoices(selection) {
90629 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
90630 return d.choices || [];
90633 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
90634 var labelEnter = choicesEnter.append('label');
90635 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
90637 }).on('change', function (d3_event, d) {
90638 var ul = this.parentNode.parentNode.parentNode;
90639 ul.__data__.chosen = d.id;
90640 choose(d3_event, ul, d);
90642 labelEnter.append('span').html(function (d) {
90646 choicesEnter.merge(choices).each(function (d) {
90647 var ul = this.parentNode;
90649 if (ul.__data__.chosen === d.id) {
90650 choose(null, ul, d);
90655 function choose(d3_event, ul, datum) {
90656 if (d3_event) d3_event.preventDefault();
90657 select(ul).selectAll('li').classed('active', function (d) {
90658 return d === datum;
90659 }).selectAll('input').property('checked', function (d) {
90660 return d === datum;
90662 var extent = geoExtent();
90664 entity = context.graph().hasEntity(datum.id);
90665 if (entity) extent._extend(entity.extent(context.graph()));
90667 entity = context.graph().hasEntity(datum.id);
90668 if (entity) extent._extend(entity.extent(context.graph()));
90669 zoomToEntity(datum.id, extent);
90672 function zoomToEntity(id, extent) {
90673 context.surface().selectAll('.hover').classed('hover', false);
90674 var entity = context.graph().hasEntity(id);
90678 context.map().trimmedExtent(extent);
90680 context.map().zoomToEase(entity);
90683 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90685 } // The conflict list should be an array of objects like:
90688 // name: entityName(local),
90689 // details: merge.conflicts(),
90692 // choice(id, keepMine, forceLocal),
90693 // choice(id, keepTheirs, forceRemote)
90698 conflicts.conflictList = function (_) {
90699 if (!arguments.length) return _conflictList;
90704 conflicts.origChanges = function (_) {
90705 if (!arguments.length) return _origChanges;
90710 conflicts.shownEntityIds = function () {
90711 if (_conflictList && typeof _shownConflictIndex === 'number') {
90712 return [_conflictList[_shownConflictIndex].id];
90718 return utilRebind(conflicts, dispatch$1, 'on');
90721 function uiConfirm(selection) {
90722 var modalSelection = uiModal(selection);
90723 modalSelection.select('.modal').classed('modal-alert', true);
90724 var section = modalSelection.select('.content');
90725 section.append('div').attr('class', 'modal-section header');
90726 section.append('div').attr('class', 'modal-section message-text');
90727 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
90729 modalSelection.okButton = function () {
90730 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
90731 modalSelection.remove();
90732 }).html(_t.html('confirm.okay')).node().focus();
90733 return modalSelection;
90736 return modalSelection;
90739 function uiChangesetEditor(context) {
90740 var dispatch$1 = dispatch('change');
90741 var formFields = uiFormFields(context);
90742 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
90750 function changesetEditor(selection) {
90754 function render(selection) {
90755 var initial = false;
90759 var presets = _mainPresetIndex;
90760 _fieldsArr = [uiField(context, presets.field('comment'), null, {
90763 }), uiField(context, presets.field('source'), null, {
90766 }), uiField(context, presets.field('hashtags'), null, {
90771 _fieldsArr.forEach(function (field) {
90772 field.on('change', function (t, onInput) {
90773 dispatch$1.call('change', field, undefined, t, onInput);
90778 _fieldsArr.forEach(function (field) {
90782 selection.call(formFields.fieldsArr(_fieldsArr));
90785 var commentField = selection.select('.form-field-comment textarea');
90786 var commentNode = commentField.node();
90789 commentNode.focus();
90790 commentNode.select();
90791 } // trigger a 'blur' event so that comment field can be cleaned
90792 // and checked for hashtags, even if retrieved from localstorage
90795 utilTriggerEvent(commentField, 'blur');
90796 var osm = context.connection();
90799 osm.userChangesets(function (err, changesets) {
90801 var comments = changesets.map(function (changeset) {
90802 var comment = changeset.tags.comment;
90807 }).filter(Boolean);
90808 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
90811 } // Add warning if comment mentions Google
90814 var hasGoogle = _tags.comment.match(/google/i);
90816 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
90817 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
90818 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
90819 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'));
90820 commentEnter.transition().duration(200).style('opacity', 1);
90823 changesetEditor.tags = function (_) {
90824 if (!arguments.length) return _tags;
90825 _tags = _; // Don't reset _fieldsArr here.
90827 return changesetEditor;
90830 changesetEditor.changesetID = function (_) {
90831 if (!arguments.length) return _changesetID;
90832 if (_changesetID === _) return changesetEditor;
90835 return changesetEditor;
90838 return utilRebind(changesetEditor, dispatch$1, 'on');
90841 function uiSectionChanges(context) {
90842 var detected = utilDetect();
90843 var _discardTags = {};
90844 _mainFileFetcher.get('discarded').then(function (d) {
90846 })["catch"](function () {
90849 var section = uiSection('changes-list', context).label(function () {
90850 var history = context.history();
90851 var summary = history.difference().summary();
90852 return _t('inspector.title_count', {
90853 title: _t.html('commit.changes'),
90854 count: summary.length
90856 }).disclosureContent(renderDisclosureContent);
90858 function renderDisclosureContent(selection) {
90859 var history = context.history();
90860 var summary = history.difference().summary();
90861 var container = selection.selectAll('.commit-section').data([0]);
90862 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
90863 containerEnter.append('ul').attr('class', 'changeset-list');
90864 container = containerEnter.merge(container);
90865 var items = container.select('ul').selectAll('li').data(summary);
90866 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
90867 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
90868 buttons.each(function (d) {
90869 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
90871 buttons.append('span').attr('class', 'change-type').html(function (d) {
90872 return _t.html('commit.' + d.changeType) + ' ';
90874 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
90875 var matched = _mainPresetIndex.match(d.entity, d.graph);
90876 return matched && matched.name() || utilDisplayType(d.entity.id);
90878 buttons.append('span').attr('class', 'entity-name').html(function (d) {
90879 var name = utilDisplayName(d.entity) || '',
90886 return string += ' ' + name;
90888 items = itemsEnter.merge(items); // Download changeset link
90890 var changeset = new osmChangeset().update({
90893 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
90894 delete changeset.id; // Export without chnageset_id
90896 var data = JXON.stringify(changeset.osmChangeJXON(changes));
90897 var blob = new Blob([data], {
90898 type: 'text/xml;charset=utf-8;'
90900 var fileName = 'changes.osc';
90901 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
90903 if (detected.download) {
90904 // All except IE11 and Edge
90905 linkEnter // download the data as a file
90906 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90909 linkEnter // open data uri in a new tab
90910 .attr('target', '_blank').on('click.download', function () {
90911 navigator.msSaveBlob(blob, fileName);
90915 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
90917 function mouseover(d) {
90919 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
90923 function mouseout() {
90924 context.surface().selectAll('.hover').classed('hover', false);
90927 function click(d3_event, change) {
90928 if (change.changeType !== 'deleted') {
90929 var entity = change.entity;
90930 context.map().zoomToEase(entity);
90931 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90939 function uiCommitWarnings(context) {
90940 function commitWarnings(selection) {
90941 var issuesBySeverity = context.validator().getIssuesBySeverity({
90944 includeDisabledRules: true
90947 for (var severity in issuesBySeverity) {
90948 var issues = issuesBySeverity[severity];
90949 var section = severity + '-section';
90950 var issueItem = severity + '-item';
90951 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
90952 container.exit().remove();
90953 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
90954 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
90955 containerEnter.append('ul').attr('class', 'changeset-list');
90956 container = containerEnter.merge(container);
90957 var items = container.select('ul').selectAll('li').data(issues, function (d) {
90960 items.exit().remove();
90961 var itemsEnter = items.enter().append('li').attr('class', issueItem);
90962 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
90964 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
90966 }).on('mouseout', function () {
90967 context.surface().selectAll('.hover').classed('hover', false);
90968 }).on('click', function (d3_event, d) {
90969 context.validator().focusIssue(d);
90971 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
90972 buttons.append('strong').attr('class', 'issue-message');
90973 buttons.filter(function (d) {
90975 }).call(uiTooltip().title(function (d) {
90977 }).placement('top'));
90978 items = itemsEnter.merge(items);
90979 items.selectAll('.issue-message').html(function (d) {
90980 return d.message(context);
90985 return commitWarnings;
90988 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
90989 // from https://stackoverflow.com/a/25575009
90991 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
90992 function uiCommit(context) {
90993 var dispatch$1 = dispatch('cancel');
90999 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
91000 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
91001 var commitChanges = uiSectionChanges(context);
91002 var commitWarnings = uiCommitWarnings(context);
91004 function commit(selection) {
91005 _selection = selection; // Initialize changeset if one does not exist yet.
91007 if (!context.changeset) initChangeset();
91008 loadDerivedChangesetTags();
91009 selection.call(render);
91012 function initChangeset() {
91013 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
91014 var commentDate = +corePreferences('commentDate') || 0;
91015 var currDate = Date.now();
91016 var cutoff = 2 * 86400 * 1000; // 2 days
91018 if (commentDate > currDate || currDate - commentDate > cutoff) {
91019 corePreferences('comment', null);
91020 corePreferences('hashtags', null);
91021 corePreferences('source', null);
91022 } // load in explicitly-set values, if any
91025 if (context.defaultChangesetComment()) {
91026 corePreferences('comment', context.defaultChangesetComment());
91027 corePreferences('commentDate', Date.now());
91030 if (context.defaultChangesetSource()) {
91031 corePreferences('source', context.defaultChangesetSource());
91032 corePreferences('commentDate', Date.now());
91035 if (context.defaultChangesetHashtags()) {
91036 corePreferences('hashtags', context.defaultChangesetHashtags());
91037 corePreferences('commentDate', Date.now());
91040 var detected = utilDetect();
91042 comment: corePreferences('comment') || '',
91043 created_by: context.cleanTagValue('iD ' + context.version),
91044 host: context.cleanTagValue(detected.host),
91045 locale: context.cleanTagValue(_mainLocalizer.localeCode())
91046 }; // call findHashtags initially - this will remove stored
91047 // hashtags if any hashtags are found in the comment - #4304
91049 findHashtags(tags, true);
91050 var hashtags = corePreferences('hashtags');
91053 tags.hashtags = hashtags;
91056 var source = corePreferences('source');
91059 tags.source = source;
91062 var photoOverlaysUsed = context.history().photoOverlaysUsed();
91064 if (photoOverlaysUsed.length) {
91065 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
91067 if (sources.indexOf('streetlevel imagery') === -1) {
91068 sources.push('streetlevel imagery');
91069 } // add the photo overlays used during editing as sources
91072 photoOverlaysUsed.forEach(function (photoOverlay) {
91073 if (sources.indexOf(photoOverlay) === -1) {
91074 sources.push(photoOverlay);
91077 tags.source = context.cleanTagValue(sources.join(';'));
91080 context.changeset = new osmChangeset({
91083 } // Calculates read-only metadata tags based on the user's editing session and applies
91084 // them to the changeset.
91087 function loadDerivedChangesetTags() {
91088 var osm = context.connection();
91090 var tags = Object.assign({}, context.changeset.tags); // shallow copy
91091 // assign tags for imagery used
91093 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
91094 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
91096 var osmClosed = osm.getClosedIDs();
91099 if (osmClosed.length) {
91100 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
91103 if (services.keepRight) {
91104 var krClosed = services.keepRight.getClosedIDs();
91106 if (krClosed.length) {
91107 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
91111 if (services.improveOSM) {
91112 var iOsmClosed = services.improveOSM.getClosedCounts();
91114 for (itemType in iOsmClosed) {
91115 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
91119 if (services.osmose) {
91120 var osmoseClosed = services.osmose.getClosedCounts();
91122 for (itemType in osmoseClosed) {
91123 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
91125 } // remove existing issue counts
91128 for (var key in tags) {
91129 if (key.match(/(^warnings:)|(^resolved:)/)) {
91134 function addIssueCounts(issues, prefix) {
91135 var issuesByType = utilArrayGroupBy(issues, 'type');
91137 for (var issueType in issuesByType) {
91138 var issuesOfType = issuesByType[issueType];
91140 if (issuesOfType[0].subtype) {
91141 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
91143 for (var issueSubtype in issuesBySubtype) {
91144 var issuesOfSubtype = issuesBySubtype[issueSubtype];
91145 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
91148 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
91151 } // add counts of warnings generated by the user's edits
91154 var warnings = context.validator().getIssuesBySeverity({
91157 includeIgnored: true,
91158 includeDisabledRules: true
91160 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
91162 var resolvedIssues = context.validator().getResolvedIssues();
91163 addIssueCounts(resolvedIssues, 'resolved');
91164 context.changeset = context.changeset.update({
91169 function render(selection) {
91170 var osm = context.connection();
91172 var header = selection.selectAll('.header').data([0]);
91173 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
91174 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
91175 headerTitle.append('button').attr('class', 'close').on('click', function () {
91176 dispatch$1.call('cancel', this);
91177 }).call(svgIcon('#iD-icon-close'));
91178 var body = selection.selectAll('.body').data([0]);
91179 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
91181 var changesetSection = body.selectAll('.changeset-editor').data([0]);
91182 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
91183 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
91185 body.call(commitWarnings); // Upload Explanation
91187 var saveSection = body.selectAll('.save-section').data([0]);
91188 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
91189 var prose = saveSection.selectAll('.commit-info').data([0]);
91191 if (prose.enter().size()) {
91192 // first time, make sure to update user details in prose
91193 _userDetails = null;
91196 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()
91197 // if needed, because it can trigger a style recalculation
91199 osm.userDetails(function (err, user) {
91201 if (_userDetails === user) return; // no change
91203 _userDetails = user;
91204 var userLink = select(document.createElement('div'));
91206 if (user.image_url) {
91207 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
91210 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
91211 prose.html(_t.html('commit.upload_explanation_with_user', {
91212 user: userLink.html()
91214 }); // Request Review
91216 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
91218 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
91219 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
91220 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
91221 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
91222 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
91224 requestReview = requestReview.merge(requestReviewEnter);
91225 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
91227 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
91229 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
91230 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
91231 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
91232 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
91233 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
91235 buttonSection = buttonSection.merge(buttonEnter);
91236 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
91237 dispatch$1.call('cancel', this);
91239 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
91240 if (!select(this).classed('disabled')) {
91241 this.blur(); // avoid keeping focus on the button - #4641
91243 for (var key in context.changeset.tags) {
91244 // remove any empty keys before upload
91245 if (!key) delete context.changeset.tags[key];
91248 context.uploader().save(context.changeset);
91250 }); // remove any existing tooltip
91252 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
91254 if (uploadBlockerTooltipText) {
91255 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
91256 } // Raw Tag Editor
91259 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
91260 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
91261 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91263 var changesSection = body.selectAll('.commit-changes-section').data([0]);
91264 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
91266 changesSection.call(commitChanges.render);
91268 function toggleRequestReview() {
91269 var rr = requestReviewInput.property('checked');
91271 review_requested: rr ? 'yes' : undefined
91273 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91278 function getUploadBlockerMessage() {
91279 var errors = context.validator().getIssuesBySeverity({
91284 if (errors.length) {
91285 return _t('commit.outstanding_errors_message', {
91286 count: errors.length
91289 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
91291 if (!hasChangesetComment) {
91292 return _t('commit.comment_needed_message');
91299 function changeTags(_, changed, onInput) {
91300 if (changed.hasOwnProperty('comment')) {
91301 if (changed.comment === undefined) {
91302 changed.comment = '';
91306 corePreferences('comment', changed.comment);
91307 corePreferences('commentDate', Date.now());
91311 if (changed.hasOwnProperty('source')) {
91312 if (changed.source === undefined) {
91313 corePreferences('source', null);
91314 } else if (!onInput) {
91315 corePreferences('source', changed.source);
91316 corePreferences('commentDate', Date.now());
91318 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
91321 updateChangeset(changed, onInput);
91324 _selection.call(render);
91328 function findHashtags(tags, commentOnly) {
91329 var detectedHashtags = commentHashtags();
91331 if (detectedHashtags.length) {
91332 // always remove stored hashtags if there are hashtags in the comment - #4304
91333 corePreferences('hashtags', null);
91336 if (!detectedHashtags.length || !commentOnly) {
91337 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
91340 var allLowerCase = new Set();
91341 return detectedHashtags.filter(function (hashtag) {
91342 // Compare tags as lowercase strings, but keep original case tags
91343 var lowerCase = hashtag.toLowerCase();
91345 if (!allLowerCase.has(lowerCase)) {
91346 allLowerCase.add(lowerCase);
91351 }); // Extract hashtags from `comment`
91353 function commentHashtags() {
91354 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
91355 .match(hashtagRegex);
91356 return matches || [];
91357 } // Extract and clean hashtags from `hashtags`
91360 function hashtagHashtags() {
91361 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
91362 if (s[0] !== '#') {
91367 var matched = s.match(hashtagRegex);
91368 return matched && matched[0];
91369 }).filter(Boolean); // exclude falsy
91371 return matches || [];
91375 function isReviewRequested(tags) {
91376 var rr = tags.review_requested;
91377 if (rr === undefined) return false;
91378 rr = rr.trim().toLowerCase();
91379 return !(rr === '' || rr === 'no');
91382 function updateChangeset(changed, onInput) {
91383 var tags = Object.assign({}, context.changeset.tags); // shallow copy
91385 Object.keys(changed).forEach(function (k) {
91386 var v = changed[k];
91387 k = context.cleanTagKey(k);
91388 if (readOnlyTags.indexOf(k) !== -1) return;
91390 if (v === undefined) {
91392 } else if (onInput) {
91395 tags[k] = context.cleanTagValue(v);
91400 // when changing the comment, override hashtags with any found in comment.
91401 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
91402 var arr = findHashtags(tags, commentOnly);
91405 tags.hashtags = context.cleanTagValue(arr.join(';'));
91406 corePreferences('hashtags', tags.hashtags);
91408 delete tags.hashtags;
91409 corePreferences('hashtags', null);
91411 } // always update userdetails, just in case user reauthenticates as someone else
91414 if (_userDetails && _userDetails.changesets_count !== undefined) {
91415 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
91417 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
91419 if (changesetsCount <= 100) {
91421 s = corePreferences('walkthrough_completed');
91424 tags['ideditor:walkthrough_completed'] = s;
91427 s = corePreferences('walkthrough_progress');
91430 tags['ideditor:walkthrough_progress'] = s;
91433 s = corePreferences('walkthrough_started');
91436 tags['ideditor:walkthrough_started'] = s;
91440 delete tags.changesets_count;
91443 if (!fastDeepEqual(context.changeset.tags, tags)) {
91444 context.changeset = context.changeset.update({
91450 commit.reset = function () {
91451 context.changeset = null;
91454 return utilRebind(commit, dispatch$1, 'on');
91457 var globalIsFinite = global_1.isFinite;
91459 // `Number.isFinite` method
91460 // https://tc39.github.io/ecma262/#sec-number.isfinite
91461 var numberIsFinite = Number.isFinite || function isFinite(it) {
91462 return typeof it == 'number' && globalIsFinite(it);
91465 // `Number.isFinite` method
91466 // https://tc39.github.io/ecma262/#sec-number.isfinite
91467 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
91469 var RADIUS = 6378137;
91470 var FLATTENING = 1 / 298.257223563;
91471 var POLAR_RADIUS$1 = 6356752.3142;
91474 FLATTENING: FLATTENING,
91475 POLAR_RADIUS: POLAR_RADIUS$1
91478 var geometry_1 = geometry;
91479 var ring = ringArea;
91481 function geometry(_) {
91487 return polygonArea(_.coordinates);
91489 case 'MultiPolygon':
91490 for (i = 0; i < _.coordinates.length; i++) {
91491 area += polygonArea(_.coordinates[i]);
91499 case 'MultiLineString':
91502 case 'GeometryCollection':
91503 for (i = 0; i < _.geometries.length; i++) {
91504 area += geometry(_.geometries[i]);
91511 function polygonArea(coords) {
91514 if (coords && coords.length > 0) {
91515 area += Math.abs(ringArea(coords[0]));
91517 for (var i = 1; i < coords.length; i++) {
91518 area -= Math.abs(ringArea(coords[i]));
91525 * Calculate the approximate area of the polygon were it projected onto
91526 * the earth. Note that this area will be positive if ring is oriented
91527 * clockwise, otherwise it will be negative.
91530 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
91531 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
91532 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
91535 * {float} The approximate signed geodesic area of the polygon in square
91540 function ringArea(coords) {
91549 coordsLength = coords.length;
91551 if (coordsLength > 2) {
91552 for (i = 0; i < coordsLength; i++) {
91553 if (i === coordsLength - 2) {
91555 lowerIndex = coordsLength - 2;
91556 middleIndex = coordsLength - 1;
91558 } else if (i === coordsLength - 1) {
91560 lowerIndex = coordsLength - 1;
91566 middleIndex = i + 1;
91567 upperIndex = i + 2;
91570 p1 = coords[lowerIndex];
91571 p2 = coords[middleIndex];
91572 p3 = coords[upperIndex];
91573 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
91576 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
91583 return _ * Math.PI / 180;
91586 var geojsonArea = {
91587 geometry: geometry_1,
91591 function toRadians(angleInDegrees) {
91592 return angleInDegrees * Math.PI / 180;
91595 function toDegrees(angleInRadians) {
91596 return angleInRadians * 180 / Math.PI;
91599 function offset(c1, distance, bearing) {
91600 var lat1 = toRadians(c1[1]);
91601 var lon1 = toRadians(c1[0]);
91602 var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
91604 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
91605 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
91606 return [toDegrees(lon), toDegrees(lat)];
91609 function validateCenter(center) {
91610 var validCenterLengths = [2, 3];
91612 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
91613 throw new Error("ERROR! Center has to be an array of length two or three");
91616 var _center = _slicedToArray(center, 2),
91620 if (typeof lng !== "number" || typeof lat !== "number") {
91621 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
91624 if (lng > 180 || lng < -180) {
91625 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
91628 if (lat > 90 || lat < -90) {
91629 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
91633 function validateRadius(radius) {
91634 if (typeof radius !== "number") {
91635 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
91639 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
91643 function validateNumberOfSegments(numberOfSegments) {
91644 if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
91645 throw new Error("ERROR! Number of segments has to be a number but was: ".concat(_typeof(numberOfSegments)));
91648 if (numberOfSegments < 3) {
91649 throw new Error("ERROR! Number of segments has to be at least 3 but was: ".concat(numberOfSegments));
91653 function validateInput(_ref) {
91654 var center = _ref.center,
91655 radius = _ref.radius,
91656 numberOfSegments = _ref.numberOfSegments;
91657 validateCenter(center);
91658 validateRadius(radius);
91659 validateNumberOfSegments(numberOfSegments);
91662 var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
91663 var n = numberOfSegments ? numberOfSegments : 32; // validateInput() throws error on invalid input and do nothing on valid input
91668 numberOfSegments: numberOfSegments
91670 var coordinates = [];
91672 for (var i = 0; i < n; ++i) {
91673 coordinates.push(offset(center, radius, 2 * Math.PI * -i / n));
91676 coordinates.push(coordinates[0]);
91679 coordinates: [coordinates]
91683 // `Number.EPSILON` constant
91684 // https://tc39.github.io/ecma262/#sec-number.epsilon
91685 _export({ target: 'Number', stat: true }, {
91686 EPSILON: Math.pow(2, -52)
91691 * Fast Splay tree for Node and browser
91693 * @author Alexander Milevski <info@w8r.name>
91697 var Node$1 = function Node(key, data) {
91698 _classCallCheck(this, Node);
91706 /* follows "An implementation of top-down splaying"
91707 * by D. Sleator <sleator@cs.cmu.edu> March 1992
91711 function DEFAULT_COMPARE$1(a, b) {
91712 return a > b ? 1 : a < b ? -1 : 0;
91715 * Simple top down splay, not requiring i to be in the tree t.
91719 function splay(i, t, comparator) {
91720 var N = new Node$1(null, null);
91725 var cmp = comparator(i, t.key); //if (i < t.key) {
91728 if (t.left === null) break; //if (i < t.left.key) {
91730 if (comparator(i, t.left.key) < 0) {
91737 if (t.left === null) break;
91744 t = t.left; //} else if (i > t.key) {
91745 } else if (cmp > 0) {
91746 if (t.right === null) break; //if (i > t.right.key) {
91748 if (comparator(i, t.right.key) > 0) {
91755 if (t.right === null) break;
91775 function _insert(i, data, t, comparator) {
91776 var node = new Node$1(i, data);
91779 node.left = node.right = null;
91783 t = splay(i, t, comparator);
91784 var cmp = comparator(i, t.key);
91787 node.left = t.left;
91790 } else if (cmp >= 0) {
91791 node.right = t.right;
91799 function _split(key, v, comparator) {
91804 v = splay(key, v, comparator);
91805 var cmp = comparator(v.key, key);
91810 } else if (cmp < 0) {
91827 function merge$4(left, right, comparator) {
91828 if (right === null) return left;
91829 if (left === null) return right;
91830 right = splay(left.key, right, comparator);
91835 * Prints level of the tree
91839 function printRow(root, prefix, isTail, out, printNode) {
91841 out("".concat(prefix).concat(isTail ? '└── ' : '├── ').concat(printNode(root), "\n"));
91842 var indent = prefix + (isTail ? ' ' : '│ ');
91843 if (root.left) printRow(root.left, indent, false, out, printNode);
91844 if (root.right) printRow(root.right, indent, true, out, printNode);
91848 var Tree = /*#__PURE__*/function () {
91850 var comparator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE$1;
91852 _classCallCheck(this, Tree);
91856 this._comparator = comparator;
91859 * Inserts a key, allows duplicates
91863 _createClass(Tree, [{
91865 value: function insert(key, data) {
91867 return this._root = _insert(key, data, this._root, this._comparator);
91870 * Adds a key, if it is not present in the tree
91875 value: function add(key, data) {
91876 var node = new Node$1(key, data);
91878 if (this._root === null) {
91879 node.left = node.right = null;
91884 var comparator = this._comparator;
91885 var t = splay(key, this._root, comparator);
91886 var cmp = comparator(key, t.key);
91887 if (cmp === 0) this._root = t;else {
91889 node.left = t.left;
91892 } else if (cmp > 0) {
91893 node.right = t.right;
91905 * @return {Node|null}
91910 value: function remove(key) {
91911 this._root = this._remove(key, this._root, this._comparator);
91914 * Deletes i from the tree if it's there
91919 value: function _remove(i, t, comparator) {
91921 if (t === null) return null;
91922 t = splay(i, t, comparator);
91923 var cmp = comparator(i, t.key);
91927 if (t.left === null) {
91930 x = splay(i, t.left, comparator);
91939 /* It wasn't there */
91942 * Removes and returns the node with smallest key
91947 value: function pop() {
91948 var node = this._root;
91951 while (node.left) {
91955 this._root = splay(node.key, this._root, this._comparator);
91956 this._root = this._remove(node.key, this._root, this._comparator);
91966 * Find without splaying
91971 value: function findStatic(key) {
91972 var current = this._root;
91973 var compare = this._comparator;
91976 var cmp = compare(key, current.key);
91977 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
91984 value: function find(key) {
91986 this._root = splay(key, this._root, this._comparator);
91987 if (this._comparator(key, this._root.key) !== 0) return null;
91994 value: function contains(key) {
91995 var current = this._root;
91996 var compare = this._comparator;
91999 var cmp = compare(key, current.key);
92000 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
92007 value: function forEach(visitor, ctx) {
92008 var current = this._root;
92010 /* Initialize stack s */
92015 if (current !== null) {
92017 current = current.left;
92019 if (Q.length !== 0) {
92021 visitor.call(ctx, current);
92022 current = current.right;
92023 } else done = true;
92030 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
92035 value: function range(low, high, fn, ctx) {
92037 var compare = this._comparator;
92038 var node = this._root;
92041 while (Q.length !== 0 || node) {
92047 cmp = compare(node.key, high);
92051 } else if (compare(node.key, low) >= 0) {
92052 if (fn.call(ctx, node)) return this; // stop if smth is returned
92062 * Returns array of keys
92067 value: function keys() {
92069 this.forEach(function (_ref) {
92070 var key = _ref.key;
92071 return keys.push(key);
92076 * Returns array of all the data in the nodes
92081 value: function values() {
92083 this.forEach(function (_ref2) {
92084 var data = _ref2.data;
92085 return values.push(data);
92091 value: function min() {
92092 if (this._root) return this.minNode(this._root).key;
92097 value: function max() {
92098 if (this._root) return this.maxNode(this._root).key;
92103 value: function minNode() {
92104 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
92105 if (t) while (t.left) {
92112 value: function maxNode() {
92113 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
92114 if (t) while (t.right) {
92120 * Returns node at given index
92125 value: function at(index) {
92126 var current = this._root;
92134 current = current.left;
92136 if (Q.length > 0) {
92138 if (i === index) return current;
92140 current = current.right;
92141 } else done = true;
92149 value: function next(d) {
92150 var root = this._root;
92151 var successor = null;
92154 successor = d.right;
92156 while (successor.left) {
92157 successor = successor.left;
92163 var comparator = this._comparator;
92166 var cmp = comparator(d.key, root.key);
92167 if (cmp === 0) break;else if (cmp < 0) {
92170 } else root = root.right;
92177 value: function prev(d) {
92178 var root = this._root;
92179 var predecessor = null;
92181 if (d.left !== null) {
92182 predecessor = d.left;
92184 while (predecessor.right) {
92185 predecessor = predecessor.right;
92188 return predecessor;
92191 var comparator = this._comparator;
92194 var cmp = comparator(d.key, root.key);
92195 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
92196 predecessor = root;
92201 return predecessor;
92205 value: function clear() {
92212 value: function toList() {
92213 return _toList(this._root);
92216 * Bulk-load items. Both array have to be same size
92221 value: function load(keys) {
92222 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
92223 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
92224 var size = keys.length;
92225 var comparator = this._comparator; // sort if needed
92227 if (presort) sort$1(keys, values, 0, size - 1, comparator);
92229 if (this._root === null) {
92231 this._root = loadRecursive$1(keys, values, 0, size);
92234 // that re-builds the whole tree from two in-order traversals
92235 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
92236 size = this._size + size;
92237 this._root = sortedListToBST({
92246 value: function isEmpty() {
92247 return this._root === null;
92251 value: function toString() {
92252 var printNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function (n) {
92253 return String(n.key);
92256 printRow(this._root, '', true, function (v) {
92257 return out.push(v);
92259 return out.join('');
92263 value: function update(key, newKey, newData) {
92264 var comparator = this._comparator;
92266 var _split2 = _split(key, this._root, comparator),
92267 left = _split2.left,
92268 right = _split2.right;
92270 if (comparator(key, newKey) < 0) {
92271 right = _insert(newKey, newData, right, comparator);
92273 left = _insert(newKey, newData, left, comparator);
92276 this._root = merge$4(left, right, comparator);
92280 value: function split(key) {
92281 return _split(key, this._root, this._comparator);
92285 get: function get() {
92290 get: function get() {
92298 function loadRecursive$1(keys, values, start, end) {
92299 var size = end - start;
92302 var middle = start + Math.floor(size / 2);
92303 var key = keys[middle];
92304 var data = values[middle];
92305 var node = new Node$1(key, data);
92306 node.left = loadRecursive$1(keys, values, start, middle);
92307 node.right = loadRecursive$1(keys, values, middle + 1, end);
92314 function createList(keys, values) {
92315 var head = new Node$1(null, null);
92318 for (var i = 0; i < keys.length; i++) {
92319 p = p.next = new Node$1(keys[i], values[i]);
92326 function _toList(root) {
92327 var current = root;
92330 var head = new Node$1(null, null);
92336 current = current.left;
92338 if (Q.length > 0) {
92339 current = p = p.next = Q.pop();
92340 current = current.right;
92341 } else done = true;
92345 p.next = null; // that'll work even if the tree was empty
92350 function sortedListToBST(list, start, end) {
92351 var size = end - start;
92354 var middle = start + Math.floor(size / 2);
92355 var left = sortedListToBST(list, start, middle);
92356 var root = list.head;
92358 list.head = list.head.next;
92359 root.right = sortedListToBST(list, middle + 1, end);
92366 function mergeLists(l1, l2, compare) {
92367 var head = new Node$1(null, null); // dummy
92373 while (p1 !== null && p2 !== null) {
92374 if (compare(p1.key, p2.key) < 0) {
92387 } else if (p2 !== null) {
92394 function sort$1(keys, values, left, right, compare) {
92395 if (left >= right) return;
92396 var pivot = keys[left + right >> 1];
92403 } while (compare(keys[i], pivot) < 0);
92407 } while (compare(keys[j], pivot) > 0);
92414 values[i] = values[j];
92418 sort$1(keys, values, left, j, compare);
92419 sort$1(keys, values, j + 1, right, compare);
92422 function _classCallCheck$1(instance, Constructor) {
92423 if (!(instance instanceof Constructor)) {
92424 throw new TypeError("Cannot call a class as a function");
92428 function _defineProperties$1(target, props) {
92429 for (var i = 0; i < props.length; i++) {
92430 var descriptor = props[i];
92431 descriptor.enumerable = descriptor.enumerable || false;
92432 descriptor.configurable = true;
92433 if ("value" in descriptor) descriptor.writable = true;
92434 Object.defineProperty(target, descriptor.key, descriptor);
92438 function _createClass$1(Constructor, protoProps, staticProps) {
92439 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
92440 if (staticProps) _defineProperties$1(Constructor, staticProps);
92441 return Constructor;
92444 * A bounding box has the format:
92446 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
92451 var isInBbox = function isInBbox(bbox, point) {
92452 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
92454 /* Returns either null, or a bbox (aka an ordered pair of points)
92455 * If there is only one point of overlap, a bbox with identical points
92456 * will be returned */
92459 var getBboxOverlap = function getBboxOverlap(b1, b2) {
92460 // check if the bboxes overlap at all
92461 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
92463 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
92464 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
92466 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
92467 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
92480 /* Javascript doesn't do integer math. Everything is
92481 * floating point with percision Number.EPSILON.
92483 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
92487 var epsilon$2 = Number.EPSILON; // IE Polyfill
92489 if (epsilon$2 === undefined) epsilon$2 = Math.pow(2, -52);
92490 var EPSILON_SQ = epsilon$2 * epsilon$2;
92491 /* FLP comparator */
92493 var cmp = function cmp(a, b) {
92494 // check if they're both 0
92495 if (-epsilon$2 < a && a < epsilon$2) {
92496 if (-epsilon$2 < b && b < epsilon$2) {
92499 } // check if they're flp equal
92504 if (ab * ab < EPSILON_SQ * a * b) {
92506 } // normal comparison
92509 return a < b ? -1 : 1;
92512 * This class rounds incoming values sufficiently so that
92513 * floating points problems are, for the most part, avoided.
92515 * Incoming points are have their x & y values tested against
92516 * all previously seen x & y values. If either is 'too close'
92517 * to a previously seen value, it's value is 'snapped' to the
92518 * previously seen value.
92520 * All points should be rounded by this class before being
92521 * stored in any data structures in the rest of this algorithm.
92525 var PtRounder = /*#__PURE__*/function () {
92526 function PtRounder() {
92527 _classCallCheck$1(this, PtRounder);
92532 _createClass$1(PtRounder, [{
92534 value: function reset() {
92535 this.xRounder = new CoordRounder();
92536 this.yRounder = new CoordRounder();
92540 value: function round(x, y) {
92542 x: this.xRounder.round(x),
92543 y: this.yRounder.round(y)
92551 var CoordRounder = /*#__PURE__*/function () {
92552 function CoordRounder() {
92553 _classCallCheck$1(this, CoordRounder);
92555 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
92558 } // Note: this can rounds input values backwards or forwards.
92559 // You might ask, why not restrict this to just rounding
92560 // forwards? Wouldn't that allow left endpoints to always
92561 // remain left endpoints during splitting (never change to
92562 // right). No - it wouldn't, because we snap intersections
92563 // to endpoints (to establish independence from the segment
92564 // angle for t-intersections).
92567 _createClass$1(CoordRounder, [{
92569 value: function round(coord) {
92570 var node = this.tree.add(coord);
92571 var prevNode = this.tree.prev(node);
92573 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
92574 this.tree.remove(coord);
92575 return prevNode.key;
92578 var nextNode = this.tree.next(node);
92580 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
92581 this.tree.remove(coord);
92582 return nextNode.key;
92589 return CoordRounder;
92590 }(); // singleton available by import
92593 var rounder = new PtRounder();
92594 /* Cross Product of two vectors with first point at origin */
92596 var crossProduct$1 = function crossProduct(a, b) {
92597 return a.x * b.y - a.y * b.x;
92599 /* Dot Product of two vectors with first point at origin */
92602 var dotProduct$1 = function dotProduct(a, b) {
92603 return a.x * b.x + a.y * b.y;
92605 /* Comparator for two vectors with same starting point */
92608 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
92610 x: endPt1.x - basePt.x,
92611 y: endPt1.y - basePt.y
92614 x: endPt2.x - basePt.x,
92615 y: endPt2.y - basePt.y
92617 var kross = crossProduct$1(v1, v2);
92618 return cmp(kross, 0);
92621 var length = function length(v) {
92622 return Math.sqrt(dotProduct$1(v, v));
92624 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
92627 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
92629 x: pBase.x - pShared.x,
92630 y: pBase.y - pShared.y
92633 x: pAngle.x - pShared.x,
92634 y: pAngle.y - pShared.y
92636 return crossProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92638 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
92641 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
92643 x: pBase.x - pShared.x,
92644 y: pBase.y - pShared.y
92647 x: pAngle.x - pShared.x,
92648 y: pAngle.y - pShared.y
92650 return dotProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92652 /* Get the x coordinate where the given line (defined by a point and vector)
92653 * crosses the horizontal line with the given y coordiante.
92654 * In the case of parrallel lines (including overlapping ones) returns null. */
92657 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
92658 if (v.y === 0) return null;
92660 x: pt.x + v.x / v.y * (y - pt.y),
92664 /* Get the y coordinate where the given line (defined by a point and vector)
92665 * crosses the vertical line with the given x coordiante.
92666 * In the case of parrallel lines (including overlapping ones) returns null. */
92669 var verticalIntersection = function verticalIntersection(pt, v, x) {
92670 if (v.x === 0) return null;
92673 y: pt.y + v.y / v.x * (x - pt.x)
92676 /* Get the intersection of two lines, each defined by a base point and a vector.
92677 * In the case of parrallel lines (including overlapping ones) returns null. */
92680 var intersection$1 = function intersection(pt1, v1, pt2, v2) {
92681 // take some shortcuts for vertical and horizontal lines
92682 // this also ensures we don't calculate an intersection and then discover
92683 // it's actually outside the bounding box of the line
92684 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
92685 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
92686 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
92687 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
92688 // This algorithm is based on Schneider and Eberly.
92689 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
92691 var kross = crossProduct$1(v1, v2);
92692 if (kross == 0) return null;
92697 var d1 = crossProduct$1(ve, v1) / kross;
92698 var d2 = crossProduct$1(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
92700 var x1 = pt1.x + d2 * v1.x,
92701 x2 = pt2.x + d1 * v2.x;
92702 var y1 = pt1.y + d2 * v1.y,
92703 y2 = pt2.y + d1 * v2.y;
92704 var x = (x1 + x2) / 2;
92705 var y = (y1 + y2) / 2;
92712 var SweepEvent$1 = /*#__PURE__*/function () {
92713 _createClass$1(SweepEvent, null, [{
92715 // for ordering sweep events in the sweep event queue
92716 value: function compare(a, b) {
92717 // favor event with a point that the sweep line hits first
92718 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
92719 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
92721 if (a.point !== b.point) a.link(b); // favor right events over left
92723 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
92724 // ordering of this case is the same as for their segments
92726 return Segment.compare(a.segment, b.segment);
92727 } // for ordering points in sweep line order
92730 key: "comparePoints",
92731 value: function comparePoints(aPt, bPt) {
92732 if (aPt.x < bPt.x) return -1;
92733 if (aPt.x > bPt.x) return 1;
92734 if (aPt.y < bPt.y) return -1;
92735 if (aPt.y > bPt.y) return 1;
92737 } // Warning: 'point' input will be modified and re-used (for performance)
92741 function SweepEvent(point, isLeft) {
92742 _classCallCheck$1(this, SweepEvent);
92744 if (point.events === undefined) point.events = [this];else point.events.push(this);
92745 this.point = point;
92746 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
92749 _createClass$1(SweepEvent, [{
92751 value: function link(other) {
92752 if (other.point === this.point) {
92753 throw new Error('Tried to link already linked events');
92756 var otherEvents = other.point.events;
92758 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
92759 var evt = otherEvents[i];
92760 this.point.events.push(evt);
92761 evt.point = this.point;
92764 this.checkForConsuming();
92766 /* Do a pass over our linked events and check to see if any pair
92767 * of segments match, and should be consumed. */
92770 key: "checkForConsuming",
92771 value: function checkForConsuming() {
92772 // FIXME: The loops in this method run O(n^2) => no good.
92773 // Maintain little ordered sweep event trees?
92774 // Can we maintaining an ordering that avoids the need
92775 // for the re-sorting with getLeftmostComparator in geom-out?
92776 // Compare each pair of events to see if other events also match
92777 var numEvents = this.point.events.length;
92779 for (var i = 0; i < numEvents; i++) {
92780 var evt1 = this.point.events[i];
92781 if (evt1.segment.consumedBy !== undefined) continue;
92783 for (var j = i + 1; j < numEvents; j++) {
92784 var evt2 = this.point.events[j];
92785 if (evt2.consumedBy !== undefined) continue;
92786 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
92787 evt1.segment.consume(evt2.segment);
92792 key: "getAvailableLinkedEvents",
92793 value: function getAvailableLinkedEvents() {
92794 // point.events is always of length 2 or greater
92797 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
92798 var evt = this.point.events[i];
92800 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
92808 * Returns a comparator function for sorting linked events that will
92809 * favor the event that will give us the smallest left-side angle.
92810 * All ring construction starts as low as possible heading to the right,
92811 * so by always turning left as sharp as possible we'll get polygons
92812 * without uncessary loops & holes.
92814 * The comparator function has a compute cache such that it avoids
92815 * re-computing already-computed values.
92819 key: "getLeftmostComparator",
92820 value: function getLeftmostComparator(baseEvent) {
92823 var cache = new Map();
92825 var fillCache = function fillCache(linkedEvent) {
92826 var nextEvent = linkedEvent.otherSE;
92827 cache.set(linkedEvent, {
92828 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
92829 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
92833 return function (a, b) {
92834 if (!cache.has(a)) fillCache(a);
92835 if (!cache.has(b)) fillCache(b);
92837 var _cache$get = cache.get(a),
92838 asine = _cache$get.sine,
92839 acosine = _cache$get.cosine;
92841 var _cache$get2 = cache.get(b),
92842 bsine = _cache$get2.sine,
92843 bcosine = _cache$get2.cosine; // both on or above x-axis
92846 if (asine >= 0 && bsine >= 0) {
92847 if (acosine < bcosine) return 1;
92848 if (acosine > bcosine) return -1;
92850 } // both below x-axis
92853 if (asine < 0 && bsine < 0) {
92854 if (acosine < bcosine) return -1;
92855 if (acosine > bcosine) return 1;
92857 } // one above x-axis, one below
92860 if (bsine < asine) return -1;
92861 if (bsine > asine) return 1;
92868 }(); // segments and sweep events when all else is identical
92873 var Segment = /*#__PURE__*/function () {
92874 _createClass$1(Segment, null, [{
92877 /* This compare() function is for ordering segments in the sweep
92878 * line tree, and does so according to the following criteria:
92880 * Consider the vertical line that lies an infinestimal step to the
92881 * right of the right-more of the two left endpoints of the input
92882 * segments. Imagine slowly moving a point up from negative infinity
92883 * in the increasing y direction. Which of the two segments will that
92884 * point intersect first? That segment comes 'before' the other one.
92886 * If neither segment would be intersected by such a line, (if one
92887 * or more of the segments are vertical) then the line to be considered
92888 * is directly on the right-more of the two left inputs.
92890 value: function compare(a, b) {
92891 var alx = a.leftSE.point.x;
92892 var blx = b.leftSE.point.x;
92893 var arx = a.rightSE.point.x;
92894 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
92896 if (brx < alx) return 1;
92897 if (arx < blx) return -1;
92898 var aly = a.leftSE.point.y;
92899 var bly = b.leftSE.point.y;
92900 var ary = a.rightSE.point.y;
92901 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
92904 // are the two segments in the same horizontal plane?
92905 if (bly < aly && bly < ary) return 1;
92906 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
92908 var aCmpBLeft = a.comparePoint(b.leftSE.point);
92909 if (aCmpBLeft < 0) return 1;
92910 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
92912 var bCmpARight = b.comparePoint(a.rightSE.point);
92913 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
92914 // left endpoint to be first (arbitrary?)
92917 } // is left endpoint of segment A the right-more?
92921 if (aly < bly && aly < bry) return -1;
92922 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
92924 var bCmpALeft = b.comparePoint(a.leftSE.point);
92925 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
92927 var aCmpBRight = a.comparePoint(b.rightSE.point);
92928 if (aCmpBRight < 0) return 1;
92929 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
92930 // left endpoint to be first (arbitrary?)
92933 } // if we get here, the two left endpoints are in the same
92934 // vertical plane, ie alx === blx
92935 // consider the lower left-endpoint to come first
92938 if (aly < bly) return -1;
92939 if (aly > bly) return 1; // left endpoints are identical
92940 // check for colinearity by using the left-more right endpoint
92941 // is the A right endpoint more left-more?
92944 var _bCmpARight = b.comparePoint(a.rightSE.point);
92946 if (_bCmpARight !== 0) return _bCmpARight;
92947 } // is the B right endpoint more left-more?
92951 var _aCmpBRight = a.comparePoint(b.rightSE.point);
92953 if (_aCmpBRight < 0) return 1;
92954 if (_aCmpBRight > 0) return -1;
92958 // are these two [almost] vertical segments with opposite orientation?
92959 // if so, the one with the lower right endpoint comes first
92960 var ay = ary - aly;
92961 var ax = arx - alx;
92962 var by = bry - bly;
92963 var bx = brx - blx;
92964 if (ay > ax && by < bx) return 1;
92965 if (ay < ax && by > bx) return -1;
92966 } // we have colinear segments with matching orientation
92967 // consider the one with more left-more right endpoint to be first
92970 if (arx > brx) return 1;
92971 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
92972 // vertical plane, ie arx === brx
92973 // consider the lower right-endpoint to come first
92975 if (ary < bry) return -1;
92976 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
92977 // fall back on creation order as consistent tie-breaker
92979 if (a.id < b.id) return -1;
92980 if (a.id > b.id) return 1; // identical segment, ie a === b
92984 /* Warning: a reference to ringWindings input will be stored,
92985 * and possibly will be later modified */
92989 function Segment(leftSE, rightSE, rings, windings) {
92990 _classCallCheck$1(this, Segment);
92992 this.id = ++segmentId;
92993 this.leftSE = leftSE;
92994 leftSE.segment = this;
92995 leftSE.otherSE = rightSE;
92996 this.rightSE = rightSE;
92997 rightSE.segment = this;
92998 rightSE.otherSE = leftSE;
92999 this.rings = rings;
93000 this.windings = windings; // left unset for performance, set later in algorithm
93001 // this.ringOut, this.consumedBy, this.prev
93004 _createClass$1(Segment, [{
93005 key: "replaceRightSE",
93007 /* When a segment is split, the rightSE is replaced with a new sweep event */
93008 value: function replaceRightSE(newRightSE) {
93009 this.rightSE = newRightSE;
93010 this.rightSE.segment = this;
93011 this.rightSE.otherSE = this.leftSE;
93012 this.leftSE.otherSE = this.rightSE;
93016 value: function bbox() {
93017 var y1 = this.leftSE.point.y;
93018 var y2 = this.rightSE.point.y;
93021 x: this.leftSE.point.x,
93022 y: y1 < y2 ? y1 : y2
93025 x: this.rightSE.point.x,
93026 y: y1 > y2 ? y1 : y2
93030 /* A vector from the left point to the right */
93034 value: function vector() {
93036 x: this.rightSE.point.x - this.leftSE.point.x,
93037 y: this.rightSE.point.y - this.leftSE.point.y
93041 key: "isAnEndpoint",
93042 value: function isAnEndpoint(pt) {
93043 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;
93045 /* Compare this segment with a point.
93047 * A point P is considered to be colinear to a segment if there
93048 * exists a distance D such that if we travel along the segment
93049 * from one * endpoint towards the other a distance D, we find
93050 * ourselves at point P.
93052 * Return value indicates:
93054 * 1: point lies above the segment (to the left of vertical)
93055 * 0: point is colinear to segment
93056 * -1: point lies below the segment (to the right of vertical)
93060 key: "comparePoint",
93061 value: function comparePoint(point) {
93062 if (this.isAnEndpoint(point)) return 0;
93063 var lPt = this.leftSE.point;
93064 var rPt = this.rightSE.point;
93065 var v = this.vector(); // Exactly vertical segments.
93067 if (lPt.x === rPt.x) {
93068 if (point.x === lPt.x) return 0;
93069 return point.x < lPt.x ? 1 : -1;
93070 } // Nearly vertical segments with an intersection.
93071 // Check to see where a point on the line with matching Y coordinate is.
93074 var yDist = (point.y - lPt.y) / v.y;
93075 var xFromYDist = lPt.x + yDist * v.x;
93076 if (point.x === xFromYDist) return 0; // General case.
93077 // Check to see where a point on the line with matching X coordinate is.
93079 var xDist = (point.x - lPt.x) / v.x;
93080 var yFromXDist = lPt.y + xDist * v.y;
93081 if (point.y === yFromXDist) return 0;
93082 return point.y < yFromXDist ? -1 : 1;
93085 * Given another segment, returns the first non-trivial intersection
93086 * between the two segments (in terms of sweep line ordering), if it exists.
93088 * A 'non-trivial' intersection is one that will cause one or both of the
93089 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
93091 * * endpoint of segA with endpoint of segB --> trivial
93092 * * endpoint of segA with point along segB --> non-trivial
93093 * * endpoint of segB with point along segA --> non-trivial
93094 * * point along segA with point along segB --> non-trivial
93096 * If no non-trivial intersection exists, return null
93097 * Else, return null.
93101 key: "getIntersection",
93102 value: function getIntersection(other) {
93103 // If bboxes don't overlap, there can't be any intersections
93104 var tBbox = this.bbox();
93105 var oBbox = other.bbox();
93106 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
93107 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
93108 // This will 'snap' intersections to endpoints if possible, and will
93109 // handle cases of colinearity.
93111 var tlp = this.leftSE.point;
93112 var trp = this.rightSE.point;
93113 var olp = other.leftSE.point;
93114 var orp = other.rightSE.point; // does each endpoint touch the other segment?
93115 // note that we restrict the 'touching' definition to only allow segments
93116 // to touch endpoints that lie forward from where we are in the sweep line pass
93118 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
93119 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
93120 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
93121 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
93123 if (touchesThisLSE && touchesOtherLSE) {
93124 // these two cases are for colinear segments with matching left
93125 // endpoints, and one segment being longer than the other
93126 if (touchesThisRSE && !touchesOtherRSE) return trp;
93127 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
93128 // or just on their left endpoint (one trivial intersection
93131 } // does this left endpoint matches (other doesn't)
93134 if (touchesThisLSE) {
93135 // check for segments that just intersect on opposing endpoints
93136 if (touchesOtherRSE) {
93137 if (tlp.x === orp.x && tlp.y === orp.y) return null;
93138 } // t-intersection on left endpoint
93142 } // does other left endpoint matches (this doesn't)
93145 if (touchesOtherLSE) {
93146 // check for segments that just intersect on opposing endpoints
93147 if (touchesThisRSE) {
93148 if (trp.x === olp.x && trp.y === olp.y) return null;
93149 } // t-intersection on left endpoint
93153 } // trivial intersection on right endpoints
93156 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
93158 if (touchesThisRSE) return trp;
93159 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
93160 // infinite lines laid over the segments
93162 var pt = intersection$1(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
93163 // they would have an endpoint intersection and that case was already handled above
93165 if (pt === null) return null; // is the intersection found between the lines not on the segments?
93167 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
93169 return rounder.round(pt.x, pt.y);
93172 * Split the given segment into multiple segments on the given points.
93173 * * Each existing segment will retain its leftSE and a new rightSE will be
93174 * generated for it.
93175 * * A new segment will be generated which will adopt the original segment's
93176 * rightSE, and a new leftSE will be generated for it.
93177 * * If there are more than two points given to split on, new segments
93178 * in the middle will be generated with new leftSE and rightSE's.
93179 * * An array of the newly generated SweepEvents will be returned.
93181 * Warning: input array of points is modified
93186 value: function split(point) {
93187 var newEvents = [];
93188 var alreadyLinked = point.events !== undefined;
93189 var newLeftSE = new SweepEvent$1(point, true);
93190 var newRightSE = new SweepEvent$1(point, false);
93191 var oldRightSE = this.rightSE;
93192 this.replaceRightSE(newRightSE);
93193 newEvents.push(newRightSE);
93194 newEvents.push(newLeftSE);
93195 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
93196 // sometimes one of the resulting new segments is vertical, in which
93197 // case its left and right events may need to be swapped
93199 if (SweepEvent$1.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
93200 newSeg.swapEvents();
93203 if (SweepEvent$1.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
93205 } // in the point we just used to create new sweep events with was already
93206 // linked to other events, we need to check if either of the affected
93207 // segments should be consumed
93210 if (alreadyLinked) {
93211 newLeftSE.checkForConsuming();
93212 newRightSE.checkForConsuming();
93217 /* Swap which event is left and right */
93221 value: function swapEvents() {
93222 var tmpEvt = this.rightSE;
93223 this.rightSE = this.leftSE;
93224 this.leftSE = tmpEvt;
93225 this.leftSE.isLeft = true;
93226 this.rightSE.isLeft = false;
93228 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
93229 this.windings[i] *= -1;
93232 /* Consume another segment. We take their rings under our wing
93233 * and mark them as consumed. Use for perfectly overlapping segments */
93237 value: function consume(other) {
93238 var consumer = this;
93239 var consumee = other;
93241 while (consumer.consumedBy) {
93242 consumer = consumer.consumedBy;
93245 while (consumee.consumedBy) {
93246 consumee = consumee.consumedBy;
93249 var cmp = Segment.compare(consumer, consumee);
93250 if (cmp === 0) return; // already consumed
93251 // the winner of the consumption is the earlier segment
93252 // according to sweep line ordering
93255 var tmp = consumer;
93256 consumer = consumee;
93258 } // make sure a segment doesn't consume it's prev
93261 if (consumer.prev === consumee) {
93262 var _tmp = consumer;
93263 consumer = consumee;
93267 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
93268 var ring = consumee.rings[i];
93269 var winding = consumee.windings[i];
93270 var index = consumer.rings.indexOf(ring);
93272 if (index === -1) {
93273 consumer.rings.push(ring);
93274 consumer.windings.push(winding);
93275 } else consumer.windings[index] += winding;
93278 consumee.rings = null;
93279 consumee.windings = null;
93280 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
93282 consumee.leftSE.consumedBy = consumer.leftSE;
93283 consumee.rightSE.consumedBy = consumer.rightSE;
93285 /* The first segment previous segment chain that is in the result */
93288 key: "prevInResult",
93289 value: function prevInResult() {
93290 if (this._prevInResult !== undefined) return this._prevInResult;
93291 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
93292 return this._prevInResult;
93295 key: "beforeState",
93296 value: function beforeState() {
93297 if (this._beforeState !== undefined) return this._beforeState;
93298 if (!this.prev) this._beforeState = {
93303 var seg = this.prev.consumedBy || this.prev;
93304 this._beforeState = seg.afterState();
93306 return this._beforeState;
93310 value: function afterState() {
93311 if (this._afterState !== undefined) return this._afterState;
93312 var beforeState = this.beforeState();
93313 this._afterState = {
93314 rings: beforeState.rings.slice(0),
93315 windings: beforeState.windings.slice(0),
93318 var ringsAfter = this._afterState.rings;
93319 var windingsAfter = this._afterState.windings;
93320 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
93322 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
93323 var ring = this.rings[i];
93324 var winding = this.windings[i];
93325 var index = ringsAfter.indexOf(ring);
93327 if (index === -1) {
93328 ringsAfter.push(ring);
93329 windingsAfter.push(winding);
93330 } else windingsAfter[index] += winding;
93331 } // calcualte polysAfter
93334 var polysAfter = [];
93335 var polysExclude = [];
93337 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
93338 if (windingsAfter[_i] === 0) continue; // non-zero rule
93340 var _ring = ringsAfter[_i];
93341 var poly = _ring.poly;
93342 if (polysExclude.indexOf(poly) !== -1) continue;
93343 if (_ring.isExterior) polysAfter.push(poly);else {
93344 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
93346 var _index = polysAfter.indexOf(_ring.poly);
93348 if (_index !== -1) polysAfter.splice(_index, 1);
93350 } // calculate multiPolysAfter
93353 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
93354 var mp = polysAfter[_i2].multiPoly;
93355 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
93358 return this._afterState;
93360 /* Is this segment part of the final result? */
93364 value: function isInResult() {
93365 // if we've been consumed, we're not in the result
93366 if (this.consumedBy) return false;
93367 if (this._isInResult !== undefined) return this._isInResult;
93368 var mpsBefore = this.beforeState().multiPolys;
93369 var mpsAfter = this.afterState().multiPolys;
93371 switch (operation.type) {
93374 // UNION - included iff:
93375 // * On one side of us there is 0 poly interiors AND
93376 // * On the other side there is 1 or more.
93377 var noBefores = mpsBefore.length === 0;
93378 var noAfters = mpsAfter.length === 0;
93379 this._isInResult = noBefores !== noAfters;
93383 case 'intersection':
93385 // INTERSECTION - included iff:
93386 // * on one side of us all multipolys are rep. with poly interiors AND
93387 // * on the other side of us, not all multipolys are repsented
93388 // with poly interiors
93392 if (mpsBefore.length < mpsAfter.length) {
93393 least = mpsBefore.length;
93394 most = mpsAfter.length;
93396 least = mpsAfter.length;
93397 most = mpsBefore.length;
93400 this._isInResult = most === operation.numMultiPolys && least < most;
93406 // XOR - included iff:
93407 // * the difference between the number of multipolys represented
93408 // with poly interiors on our two sides is an odd number
93409 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
93410 this._isInResult = diff % 2 === 1;
93416 // DIFFERENCE included iff:
93417 // * on exactly one side, we have just the subject
93418 var isJustSubject = function isJustSubject(mps) {
93419 return mps.length === 1 && mps[0].isSubject;
93422 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
93427 throw new Error("Unrecognized operation type found ".concat(operation.type));
93430 return this._isInResult;
93434 value: function fromRing(pt1, pt2, ring) {
93435 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
93437 var cmpPts = SweepEvent$1.comparePoints(pt1, pt2);
93443 } else if (cmpPts > 0) {
93447 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
93449 var leftSE = new SweepEvent$1(leftPt, true);
93450 var rightSE = new SweepEvent$1(rightPt, false);
93451 return new Segment(leftSE, rightSE, [ring], [winding]);
93458 var RingIn = /*#__PURE__*/function () {
93459 function RingIn(geomRing, poly, isExterior) {
93460 _classCallCheck$1(this, RingIn);
93462 if (!Array.isArray(geomRing) || geomRing.length === 0) {
93463 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93467 this.isExterior = isExterior;
93468 this.segments = [];
93470 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
93471 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93474 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
93485 var prevPoint = firstPoint;
93487 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
93488 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
93489 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93492 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
93494 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
93495 this.segments.push(Segment.fromRing(prevPoint, point, this));
93496 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
93497 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
93498 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
93499 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
93501 } // add segment from last to first if last is not the same as first
93504 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
93505 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
93509 _createClass$1(RingIn, [{
93510 key: "getSweepEvents",
93511 value: function getSweepEvents() {
93512 var sweepEvents = [];
93514 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
93515 var segment = this.segments[i];
93516 sweepEvents.push(segment.leftSE);
93517 sweepEvents.push(segment.rightSE);
93520 return sweepEvents;
93527 var PolyIn = /*#__PURE__*/function () {
93528 function PolyIn(geomPoly, multiPoly) {
93529 _classCallCheck$1(this, PolyIn);
93531 if (!Array.isArray(geomPoly)) {
93532 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93535 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
93539 x: this.exteriorRing.bbox.ll.x,
93540 y: this.exteriorRing.bbox.ll.y
93543 x: this.exteriorRing.bbox.ur.x,
93544 y: this.exteriorRing.bbox.ur.y
93547 this.interiorRings = [];
93549 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
93550 var ring = new RingIn(geomPoly[i], this, false);
93551 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
93552 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
93553 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
93554 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
93555 this.interiorRings.push(ring);
93558 this.multiPoly = multiPoly;
93561 _createClass$1(PolyIn, [{
93562 key: "getSweepEvents",
93563 value: function getSweepEvents() {
93564 var sweepEvents = this.exteriorRing.getSweepEvents();
93566 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93567 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
93569 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
93570 sweepEvents.push(ringSweepEvents[j]);
93574 return sweepEvents;
93581 var MultiPolyIn = /*#__PURE__*/function () {
93582 function MultiPolyIn(geom, isSubject) {
93583 _classCallCheck$1(this, MultiPolyIn);
93585 if (!Array.isArray(geom)) {
93586 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93590 // if the input looks like a polygon, convert it to a multipolygon
93591 if (typeof geom[0][0][0] === 'number') geom = [geom];
93592 } catch (ex) {// The input is either malformed or has empty arrays.
93593 // In either case, it will be handled later on.
93599 x: Number.POSITIVE_INFINITY,
93600 y: Number.POSITIVE_INFINITY
93603 x: Number.NEGATIVE_INFINITY,
93604 y: Number.NEGATIVE_INFINITY
93608 for (var i = 0, iMax = geom.length; i < iMax; i++) {
93609 var poly = new PolyIn(geom[i], this);
93610 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
93611 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
93612 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
93613 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
93614 this.polys.push(poly);
93617 this.isSubject = isSubject;
93620 _createClass$1(MultiPolyIn, [{
93621 key: "getSweepEvents",
93622 value: function getSweepEvents() {
93623 var sweepEvents = [];
93625 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93626 var polySweepEvents = this.polys[i].getSweepEvents();
93628 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
93629 sweepEvents.push(polySweepEvents[j]);
93633 return sweepEvents;
93637 return MultiPolyIn;
93640 var RingOut = /*#__PURE__*/function () {
93641 _createClass$1(RingOut, null, [{
93644 /* Given the segments from the sweep line pass, compute & return a series
93645 * of closed rings from all the segments marked to be part of the result */
93646 value: function factory(allSegments) {
93649 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
93650 var segment = allSegments[i];
93651 if (!segment.isInResult() || segment.ringOut) continue;
93652 var prevEvent = null;
93653 var event = segment.leftSE;
93654 var nextEvent = segment.rightSE;
93655 var events = [event];
93656 var startingPoint = event.point;
93657 var intersectionLEs = [];
93658 /* Walk the chain of linked events to form a closed ring */
93663 events.push(event);
93664 /* Is the ring complete? */
93666 if (event.point === startingPoint) break;
93669 var availableLEs = event.getAvailableLinkedEvents();
93670 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
93671 * part of the algorithm malfunctioned... please file a bug report. */
93673 if (availableLEs.length === 0) {
93674 var firstPt = events[0].point;
93675 var lastPt = events[events.length - 1].point;
93676 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, "]."));
93678 /* Only one way to go, so cotinue on the path */
93681 if (availableLEs.length === 1) {
93682 nextEvent = availableLEs[0].otherSE;
93685 /* We must have an intersection. Check for a completed loop */
93688 var indexLE = null;
93690 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
93691 if (intersectionLEs[j].point === event.point) {
93696 /* Found a completed loop. Cut that off and make a ring */
93699 if (indexLE !== null) {
93700 var intersectionLE = intersectionLEs.splice(indexLE)[0];
93701 var ringEvents = events.splice(intersectionLE.index);
93702 ringEvents.unshift(ringEvents[0].otherSE);
93703 ringsOut.push(new RingOut(ringEvents.reverse()));
93706 /* register the intersection */
93709 intersectionLEs.push({
93710 index: events.length,
93713 /* Choose the left-most option to continue the walk */
93715 var comparator = event.getLeftmostComparator(prevEvent);
93716 nextEvent = availableLEs.sort(comparator)[0].otherSE;
93721 ringsOut.push(new RingOut(events));
93728 function RingOut(events) {
93729 _classCallCheck$1(this, RingOut);
93731 this.events = events;
93733 for (var i = 0, iMax = events.length; i < iMax; i++) {
93734 events[i].segment.ringOut = this;
93740 _createClass$1(RingOut, [{
93742 value: function getGeom() {
93743 // Remove superfluous points (ie extra points along a straight line),
93744 var prevPt = this.events[0].point;
93745 var points = [prevPt];
93747 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
93748 var _pt = this.events[i].point;
93749 var _nextPt = this.events[i + 1].point;
93750 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
93753 } // ring was all (within rounding error of angle calc) colinear points
93756 if (points.length === 1) return null; // check if the starting point is necessary
93758 var pt = points[0];
93759 var nextPt = points[1];
93760 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
93761 points.push(points[0]);
93762 var step = this.isExteriorRing() ? 1 : -1;
93763 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
93764 var iEnd = this.isExteriorRing() ? points.length : -1;
93765 var orderedPoints = [];
93767 for (var _i = iStart; _i != iEnd; _i += step) {
93768 orderedPoints.push([points[_i].x, points[_i].y]);
93771 return orderedPoints;
93774 key: "isExteriorRing",
93775 value: function isExteriorRing() {
93776 if (this._isExteriorRing === undefined) {
93777 var enclosing = this.enclosingRing();
93778 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
93781 return this._isExteriorRing;
93784 key: "enclosingRing",
93785 value: function enclosingRing() {
93786 if (this._enclosingRing === undefined) {
93787 this._enclosingRing = this._calcEnclosingRing();
93790 return this._enclosingRing;
93792 /* Returns the ring that encloses this one, if any */
93795 key: "_calcEnclosingRing",
93796 value: function _calcEnclosingRing() {
93797 // start with the ealier sweep line event so that the prevSeg
93798 // chain doesn't lead us inside of a loop of ours
93799 var leftMostEvt = this.events[0];
93801 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
93802 var evt = this.events[i];
93803 if (SweepEvent$1.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
93806 var prevSeg = leftMostEvt.segment.prevInResult();
93807 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93810 // no segment found, thus no ring can enclose us
93811 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
93812 // segment must loop back around and enclose us
93814 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
93815 // segment must either loop around us or the ring of the prev prev
93816 // seg, which would make us and the ring of the prev peers
93818 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
93819 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
93820 return prevSeg.ringOut;
93821 } else return prevSeg.ringOut.enclosingRing();
93822 } // two segments are from the same ring, so this was a penisula
93823 // of that ring. iterate downward, keep searching
93826 prevSeg = prevPrevSeg.prevInResult();
93827 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93835 var PolyOut = /*#__PURE__*/function () {
93836 function PolyOut(exteriorRing) {
93837 _classCallCheck$1(this, PolyOut);
93839 this.exteriorRing = exteriorRing;
93840 exteriorRing.poly = this;
93841 this.interiorRings = [];
93844 _createClass$1(PolyOut, [{
93845 key: "addInterior",
93846 value: function addInterior(ring) {
93847 this.interiorRings.push(ring);
93852 value: function getGeom() {
93853 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
93855 if (geom[0] === null) return null;
93857 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93858 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
93860 if (ringGeom === null) continue;
93861 geom.push(ringGeom);
93871 var MultiPolyOut = /*#__PURE__*/function () {
93872 function MultiPolyOut(rings) {
93873 _classCallCheck$1(this, MultiPolyOut);
93875 this.rings = rings;
93876 this.polys = this._composePolys(rings);
93879 _createClass$1(MultiPolyOut, [{
93881 value: function getGeom() {
93884 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93885 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
93887 if (polyGeom === null) continue;
93888 geom.push(polyGeom);
93894 key: "_composePolys",
93895 value: function _composePolys(rings) {
93898 for (var i = 0, iMax = rings.length; i < iMax; i++) {
93899 var ring = rings[i];
93900 if (ring.poly) continue;
93901 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
93902 var enclosingRing = ring.enclosingRing();
93903 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
93904 enclosingRing.poly.addInterior(ring);
93912 return MultiPolyOut;
93915 * NOTE: We must be careful not to change any segments while
93916 * they are in the SplayTree. AFAIK, there's no way to tell
93917 * the tree to rebalance itself - thus before splitting
93918 * a segment that's in the tree, we remove it from the tree,
93919 * do the split, then re-insert it. (Even though splitting a
93920 * segment *shouldn't* change its correct position in the
93921 * sweep line tree, the reality is because of rounding errors,
93922 * it sometimes does.)
93926 var SweepLine = /*#__PURE__*/function () {
93927 function SweepLine(queue) {
93928 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
93930 _classCallCheck$1(this, SweepLine);
93932 this.queue = queue;
93933 this.tree = new Tree(comparator);
93934 this.segments = [];
93937 _createClass$1(SweepLine, [{
93939 value: function process(event) {
93940 var segment = event.segment;
93941 var newEvents = []; // if we've already been consumed by another segment,
93942 // clean up our body parts and get out
93944 if (event.consumedBy) {
93945 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
93949 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
93950 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.');
93951 var prevNode = node;
93952 var nextNode = node;
93953 var prevSeg = undefined;
93954 var nextSeg = undefined; // skip consumed segments still in tree
93956 while (prevSeg === undefined) {
93957 prevNode = this.tree.prev(prevNode);
93958 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
93959 } // skip consumed segments still in tree
93962 while (nextSeg === undefined) {
93963 nextNode = this.tree.next(nextNode);
93964 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
93967 if (event.isLeft) {
93968 // Check for intersections against the previous segment in the sweep line
93969 var prevMySplitter = null;
93972 var prevInter = prevSeg.getIntersection(segment);
93974 if (prevInter !== null) {
93975 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
93977 if (!prevSeg.isAnEndpoint(prevInter)) {
93978 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
93980 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
93981 newEvents.push(newEventsFromSplit[i]);
93985 } // Check for intersections against the next segment in the sweep line
93988 var nextMySplitter = null;
93991 var nextInter = nextSeg.getIntersection(segment);
93993 if (nextInter !== null) {
93994 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
93996 if (!nextSeg.isAnEndpoint(nextInter)) {
93997 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
93999 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
94000 newEvents.push(_newEventsFromSplit[_i]);
94004 } // For simplicity, even if we find more than one intersection we only
94005 // spilt on the 'earliest' (sweep-line style) of the intersections.
94006 // The other intersection will be handled in a future process().
94009 if (prevMySplitter !== null || nextMySplitter !== null) {
94010 var mySplitter = null;
94011 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
94012 var cmpSplitters = SweepEvent$1.comparePoints(prevMySplitter, nextMySplitter);
94013 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
94014 } // Rounding errors can cause changes in ordering,
94015 // so remove afected segments and right sweep events before splitting
94017 this.queue.remove(segment.rightSE);
94018 newEvents.push(segment.rightSE);
94020 var _newEventsFromSplit2 = segment.split(mySplitter);
94022 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
94023 newEvents.push(_newEventsFromSplit2[_i2]);
94027 if (newEvents.length > 0) {
94028 // We found some intersections, so re-do the current event to
94029 // make sure sweep line ordering is totally consistent for later
94030 // use with the segment 'prev' pointers
94031 this.tree.remove(segment);
94032 newEvents.push(event);
94034 // done with left event
94035 this.segments.push(segment);
94036 segment.prev = prevSeg;
94040 // since we're about to be removed from the sweep line, check for
94041 // intersections between our previous and next segments
94042 if (prevSeg && nextSeg) {
94043 var inter = prevSeg.getIntersection(nextSeg);
94045 if (inter !== null) {
94046 if (!prevSeg.isAnEndpoint(inter)) {
94047 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
94049 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
94050 newEvents.push(_newEventsFromSplit3[_i3]);
94054 if (!nextSeg.isAnEndpoint(inter)) {
94055 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
94057 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
94058 newEvents.push(_newEventsFromSplit4[_i4]);
94064 this.tree.remove(segment);
94069 /* Safely split a segment that is currently in the datastructures
94070 * IE - a segment other than the one that is currently being processed. */
94073 key: "_splitSafely",
94074 value: function _splitSafely(seg, pt) {
94075 // Rounding errors can cause changes in ordering,
94076 // so remove afected segments and right sweep events before splitting
94077 // removeNode() doesn't work, so have re-find the seg
94078 // https://github.com/w8r/splay-tree/pull/5
94079 this.tree.remove(seg);
94080 var rightSE = seg.rightSE;
94081 this.queue.remove(rightSE);
94082 var newEvents = seg.split(pt);
94083 newEvents.push(rightSE); // splitting can trigger consumption
94085 if (seg.consumedBy === undefined) this.tree.insert(seg);
94093 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
94094 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
94096 var Operation = /*#__PURE__*/function () {
94097 function Operation() {
94098 _classCallCheck$1(this, Operation);
94101 _createClass$1(Operation, [{
94103 value: function run(type, geom, moreGeoms) {
94104 operation.type = type;
94106 /* Convert inputs to MultiPoly objects */
94108 var multipolys = [new MultiPolyIn(geom, true)];
94110 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
94111 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
94114 operation.numMultiPolys = multipolys.length;
94115 /* BBox optimization for difference operation
94116 * If the bbox of a multipolygon that's part of the clipping doesn't
94117 * intersect the bbox of the subject at all, we can just drop that
94120 if (operation.type === 'difference') {
94121 // in place removal
94122 var subject = multipolys[0];
94125 while (_i < multipolys.length) {
94126 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
94129 /* BBox optimization for intersection operation
94130 * If we can find any pair of multipolygons whose bbox does not overlap,
94131 * then the result will be empty. */
94134 if (operation.type === 'intersection') {
94135 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
94136 // it could be optimized to O(n * ln(n))
94137 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
94138 var mpA = multipolys[_i2];
94140 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
94141 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
94145 /* Put segment endpoints in a priority queue */
94148 var queue = new Tree(SweepEvent$1.compare);
94150 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
94151 var sweepEvents = multipolys[_i3].getSweepEvents();
94153 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
94154 queue.insert(sweepEvents[_j]);
94156 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
94157 // prevents an infinite loop, an otherwise common manifestation of bugs
94158 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
94162 /* Pass the sweep line over those endpoints */
94165 var sweepLine = new SweepLine(queue);
94166 var prevQueueSize = queue.size;
94167 var node = queue.pop();
94170 var evt = node.key;
94172 if (queue.size === prevQueueSize) {
94173 // prevents an infinite loop, an otherwise common manifestation of bugs
94174 var seg = evt.segment;
94175 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.');
94178 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
94179 // prevents an infinite loop, an otherwise common manifestation of bugs
94180 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
94183 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
94184 // prevents an infinite loop, an otherwise common manifestation of bugs
94185 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
94188 var newEvents = sweepLine.process(evt);
94190 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
94191 var _evt = newEvents[_i4];
94192 if (_evt.consumedBy === undefined) queue.insert(_evt);
94195 prevQueueSize = queue.size;
94196 node = queue.pop();
94197 } // free some memory we don't need anymore
94201 /* Collect and compile segments we're keeping into a multipolygon */
94203 var ringsOut = RingOut.factory(sweepLine.segments);
94204 var result = new MultiPolyOut(ringsOut);
94205 return result.getGeom();
94210 }(); // singleton available by import
94213 var operation = new Operation();
94215 var union$1 = function union(geom) {
94216 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
94217 moreGeoms[_key - 1] = arguments[_key];
94220 return operation.run('union', geom, moreGeoms);
94223 var intersection$1$1 = function intersection(geom) {
94224 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
94225 moreGeoms[_key2 - 1] = arguments[_key2];
94228 return operation.run('intersection', geom, moreGeoms);
94231 var xor = function xor(geom) {
94232 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
94233 moreGeoms[_key3 - 1] = arguments[_key3];
94236 return operation.run('xor', geom, moreGeoms);
94239 var difference = function difference(subjectGeom) {
94240 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
94241 clippingGeoms[_key4 - 1] = arguments[_key4];
94244 return operation.run('difference', subjectGeom, clippingGeoms);
94249 intersection: intersection$1$1,
94251 difference: difference
94254 var geojsonPrecision = createCommonjsModule(function (module) {
94256 function parse(t, coordinatePrecision, extrasPrecision) {
94257 function point(p) {
94258 return p.map(function (e, index) {
94260 return 1 * e.toFixed(coordinatePrecision);
94262 return 1 * e.toFixed(extrasPrecision);
94267 function multi(l) {
94268 return l.map(point);
94272 return p.map(multi);
94275 function multiPoly(m) {
94276 return m.map(poly);
94279 function geometry(obj) {
94284 switch (obj.type) {
94286 obj.coordinates = point(obj.coordinates);
94291 obj.coordinates = multi(obj.coordinates);
94295 case "MultiLineString":
94296 obj.coordinates = poly(obj.coordinates);
94299 case "MultiPolygon":
94300 obj.coordinates = multiPoly(obj.coordinates);
94303 case "GeometryCollection":
94304 obj.geometries = obj.geometries.map(geometry);
94312 function feature(obj) {
94313 obj.geometry = geometry(obj.geometry);
94317 function featureCollection(f) {
94318 f.features = f.features.map(feature);
94322 function geometryCollection(g) {
94323 g.geometries = g.geometries.map(geometry);
94335 case "GeometryCollection":
94336 return geometryCollection(t);
94338 case "FeatureCollection":
94339 return featureCollection(t);
94345 case "MultiPolygon":
94346 case "MultiLineString":
94347 return geometry(t);
94354 module.exports = parse;
94355 module.exports.parse = parse;
94359 function isObject$4(obj) {
94360 return _typeof(obj) === 'object' && obj !== null;
94363 function forEach(obj, cb) {
94364 if (Array.isArray(obj)) {
94366 } else if (isObject$4(obj)) {
94367 Object.keys(obj).forEach(function (key) {
94368 var val = obj[key];
94374 function getTreeDepth(obj) {
94377 if (Array.isArray(obj) || isObject$4(obj)) {
94378 forEach(obj, function (val) {
94379 if (Array.isArray(val) || isObject$4(val)) {
94380 var tmpDepth = getTreeDepth(val);
94382 if (tmpDepth > depth) {
94393 function stringify(obj, options) {
94394 options = options || {};
94395 var indent = JSON.stringify([1], null, get$5(options, 'indent', 2)).slice(2, -3);
94396 var addMargin = get$5(options, 'margins', false);
94397 var addArrayMargin = get$5(options, 'arrayMargins', false);
94398 var addObjectMargin = get$5(options, 'objectMargins', false);
94399 var maxLength = indent === '' ? Infinity : get$5(options, 'maxLength', 80);
94400 var maxNesting = get$5(options, 'maxNesting', Infinity);
94401 return function _stringify(obj, currentIndent, reserved) {
94402 if (obj && typeof obj.toJSON === 'function') {
94403 obj = obj.toJSON();
94406 var string = JSON.stringify(obj);
94408 if (string === undefined) {
94412 var length = maxLength - currentIndent.length - reserved;
94413 var treeDepth = getTreeDepth(obj);
94415 if (treeDepth <= maxNesting && string.length <= length) {
94416 var prettified = prettify(string, {
94417 addMargin: addMargin,
94418 addArrayMargin: addArrayMargin,
94419 addObjectMargin: addObjectMargin
94422 if (prettified.length <= length) {
94427 if (isObject$4(obj)) {
94428 var nextIndent = currentIndent + indent;
94432 var comma = function comma(array, index) {
94433 return index === array.length - 1 ? 0 : 1;
94436 if (Array.isArray(obj)) {
94437 for (var index = 0; index < obj.length; index++) {
94438 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
94443 Object.keys(obj).forEach(function (key, index, array) {
94444 var keyPart = JSON.stringify(key) + ': ';
94446 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
94448 if (value !== undefined) {
94449 items.push(keyPart + value);
94455 if (items.length > 0) {
94456 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
94462 } // Note: This regex matches even invalid JSON strings, but since we’re
94463 // working on the output of `JSON.stringify` we know that only valid strings
94464 // are present (unless the user supplied a weird `options.indent` but in
94465 // that case we don’t care since the output would be invalid anyway).
94468 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
94470 function prettify(string, options) {
94471 options = options || {};
94481 if (options.addMargin || options.addObjectMargin) {
94482 tokens['{'] = '{ ';
94483 tokens['}'] = ' }';
94486 if (options.addMargin || options.addArrayMargin) {
94487 tokens['['] = '[ ';
94488 tokens[']'] = ' ]';
94491 return string.replace(stringOrChar, function (match, string) {
94492 return string ? match : tokens[match];
94496 function get$5(options, name, defaultValue) {
94497 return name in options ? options[name] : defaultValue;
94500 var jsonStringifyPrettyCompact = stringify;
94502 var _default$3 = /*#__PURE__*/function () {
94505 // `fc` Optional FeatureCollection of known features
94507 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
94508 // Each feature must have a filename-like `id`, for example: `something.geojson`
94511 // "type": "FeatureCollection"
94514 // "type": "Feature",
94515 // "id": "philly_metro.geojson",
94516 // "properties": { … },
94517 // "geometry": { … }
94521 function _default(fc) {
94524 _classCallCheck(this, _default);
94526 // The _cache retains resolved features, so if you ask for the same thing multiple times
94527 // we don't repeat the expensive resolving/clipping operations.
94529 // Each feature has a stable identifier that is used as the cache key.
94530 // The identifiers look like:
94531 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
94532 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
94533 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
94534 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
94535 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
94536 // When strict mode = false, return `null` for invalid locations or locationSets.
94538 this._strict = true; // process input FeatureCollection
94540 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
94541 fc.features.forEach(function (feature) {
94542 feature.properties = feature.properties || {};
94543 var props = feature.properties; // get `id` from either `id` or `properties`
94545 var id = feature.id || props.id;
94546 if (!id || !/^\S+\.geojson$/i.test(id)) return; // ensure `id` exists and is lowercase
94548 id = id.toLowerCase();
94550 props.id = id; // ensure `area` property exists
94553 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
94555 props.area = Number(area.toFixed(2));
94558 _this._cache[id] = feature;
94560 } // Replace CountryCoder world geometry to be a polygon covering the world.
94563 var world = _cloneDeep(feature('Q2'));
94567 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
94570 world.properties.id = 'Q2';
94571 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
94573 this._cache.Q2 = world;
94574 } // validateLocation
94575 // `location` The location to validate
94577 // Pass a `location` value to validate
94579 // Returns a result like:
94581 // type: 'point', 'geojson', or 'countrycoder'
94582 // location: the queried location
94583 // id: the stable identifier for the feature
94585 // or `null` if the location is invalid
94589 _createClass(_default, [{
94590 key: "validateLocation",
94591 value: function validateLocation(location) {
94592 if (Array.isArray(location)) {
94593 // a [lon,lat] coordinate pair?
94594 if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) && location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90) {
94595 var id = '[' + location.toString() + ']';
94598 location: location,
94602 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
94603 // a .geojson filename?
94604 var _id = location.toLowerCase();
94606 if (this._cache[_id]) {
94609 location: location,
94613 } else if (typeof location === 'string' || typeof location === 'number') {
94614 // a country-coder value?
94615 var feature$1 = feature(location);
94618 // Use wikidata QID as the identifier, since that seems to be the one
94619 // property that everything in CountryCoder is guaranteed to have.
94620 var _id2 = feature$1.properties.wikidata;
94622 type: 'countrycoder',
94623 location: location,
94629 if (this._strict) {
94630 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
94634 } // resolveLocation
94635 // `location` The location to resolve
94637 // Pass a `location` value to resolve
94639 // Returns a result like:
94641 // type: 'point', 'geojson', or 'countrycoder'
94642 // location: the queried location
94643 // id: a stable identifier for the feature
94644 // feature: the resolved GeoJSON feature
94646 // or `null` if the location is invalid
94650 key: "resolveLocation",
94651 value: function resolveLocation(location) {
94652 var valid = this.validateLocation(location);
94653 if (!valid) return null;
94654 var id = valid.id; // return a result from cache if we can
94656 if (this._cache[id]) {
94657 return Object.assign(valid, {
94658 feature: this._cache[id]
94660 } // a [lon,lat] coordinate pair?
94663 if (valid.type === 'point') {
94664 var RADIUS = 25000; // meters
94668 var area = Math.PI * RADIUS * RADIUS / 1e6; // m² to km²
94670 var feature$1 = this._cache[id] = geojsonPrecision({
94675 area: Number(area.toFixed(2))
94677 geometry: circleToPolygon(location, RADIUS, EDGES)
94679 return Object.assign(valid, {
94681 }); // a .geojson filename?
94682 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
94683 var _feature = _cloneDeep(feature(id));
94685 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
94686 // CountryCoder includes higher level features which are made up of members.
94687 // These features don't have their own geometry, but CountryCoder provides an
94688 // `aggregateFeature` method to combine these members into a MultiPolygon.
94689 // BUT, when we try to actually work with these aggregated MultiPolygons,
94690 // Turf/JSTS gets crashy because of topography bugs.
94691 // SO, we'll aggregate the features ourselves by unioning them together.
94692 // This approach also has the benefit of removing all the internal boaders and
94693 // simplifying the regional polygons a lot.
94695 if (Array.isArray(props.members)) {
94696 var seed = _feature.geometry ? _feature : null;
94697 var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
94698 _feature.geometry = aggregate.geometry;
94699 } // ensure `area` property exists
94703 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
94706 props.area = Number(_area.toFixed(2));
94707 } // ensure `id` property exists
94712 this._cache[id] = _feature;
94713 return Object.assign(valid, {
94718 if (this._strict) {
94719 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
94723 } // validateLocationSet
94724 // `locationSet` the locationSet to validate
94726 // Pass a locationSet Object to validate like:
94728 // include: [ Array of locations ],
94729 // exclude: [ Array of locations ]
94732 // Returns a result like:
94734 // type: 'locationset'
94735 // locationSet: the queried locationSet
94736 // id: the stable identifier for the feature
94738 // or `null` if the locationSet is invalid
94742 key: "validateLocationSet",
94743 value: function validateLocationSet(locationSet) {
94744 locationSet = locationSet || {};
94745 var validator = this.validateLocation.bind(this);
94746 var include = (locationSet.include || []).map(validator).filter(Boolean);
94747 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
94749 if (!include.length) {
94750 if (this._strict) {
94751 throw new Error("validateLocationSet: LocationSet includes nothing.");
94753 // non-strict mode, replace an empty locationSet with one that includes "the world"
94754 locationSet.include = ['Q2'];
94756 type: 'countrycoder',
94761 } // generate stable identifier
94764 include.sort(_sortLocations);
94765 var id = '+[' + include.map(function (d) {
94767 }).join(',') + ']';
94769 if (exclude.length) {
94770 exclude.sort(_sortLocations);
94771 id += '-[' + exclude.map(function (d) {
94773 }).join(',') + ']';
94777 type: 'locationset',
94778 locationSet: locationSet,
94781 } // resolveLocationSet
94782 // `locationSet` the locationSet to resolve
94784 // Pass a locationSet Object to validate like:
94786 // include: [ Array of locations ],
94787 // exclude: [ Array of locations ]
94790 // Returns a result like:
94792 // type: 'locationset'
94793 // locationSet: the queried locationSet
94794 // id: the stable identifier for the feature
94795 // feature: the resolved GeoJSON feature
94797 // or `null` if the locationSet is invalid
94801 key: "resolveLocationSet",
94802 value: function resolveLocationSet(locationSet) {
94803 locationSet = locationSet || {};
94804 var valid = this.validateLocationSet(locationSet);
94805 if (!valid) return null;
94806 var id = valid.id; // return a result from cache if we can
94808 if (this._cache[id]) {
94809 return Object.assign(valid, {
94810 feature: this._cache[id]
94814 var resolver = this.resolveLocation.bind(this);
94815 var include = (locationSet.include || []).map(resolver).filter(Boolean);
94816 var exclude = (locationSet.exclude || []).map(resolver).filter(Boolean); // return quickly if it's a single included location..
94818 if (include.length === 1 && exclude.length === 0) {
94819 return Object.assign(valid, {
94820 feature: include[0].feature
94822 } // calculate unions
94825 var includeGeoJSON = include.map(function (d) {
94827 }).reduce(_locationReducer.bind(this), null);
94828 var excludeGeoJSON = exclude.map(function (d) {
94830 }).reduce(_locationReducer.bind(this), null); // calculate difference, update `area` and return result
94832 var resultGeoJSON = excludeGeoJSON ? _clip(includeGeoJSON, excludeGeoJSON, 'DIFFERENCE') : includeGeoJSON;
94833 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
94835 resultGeoJSON.id = id;
94836 resultGeoJSON.properties = {
94838 area: Number(area.toFixed(2))
94840 this._cache[id] = resultGeoJSON;
94841 return Object.assign(valid, {
94842 feature: resultGeoJSON
94849 value: function strict(val) {
94850 if (val === undefined) {
94852 return this._strict;
94855 this._strict = val;
94859 // convenience method to access the internal cache
94863 value: function cache() {
94864 return this._cache;
94866 // convenience method to prettyStringify the given object
94870 value: function stringify(obj, options) {
94871 return jsonStringifyPrettyCompact(obj, options);
94876 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
94878 function _clip(a, b, which) {
94880 UNION: index$1.union,
94881 DIFFERENCE: index$1.difference
94883 var coords = fn(a.geometry.coordinates, b.geometry.coordinates);
94888 type: whichType(coords),
94889 coordinates: coords
94891 }; // is this a Polygon or a MultiPolygon?
94893 function whichType(coords) {
94894 var a = Array.isArray(coords);
94895 var b = a && Array.isArray(coords[0]);
94896 var c = b && Array.isArray(coords[0][0]);
94897 var d = c && Array.isArray(coords[0][0][0]);
94898 return d ? 'MultiPolygon' : 'Polygon';
94900 } // Reduce an array of locations into a single GeoJSON feature
94903 function _locationReducer(accumulator, location) {
94904 /* eslint-disable no-console, no-invalid-this */
94908 var resolved = this.resolveLocation(location);
94910 if (!resolved || !resolved.feature) {
94911 console.warn("Warning: Couldn't resolve location \"".concat(location, "\""));
94912 return accumulator;
94915 result = !accumulator ? resolved.feature : _clip(accumulator, resolved.feature, 'UNION');
94917 console.warn("Warning: Error resolving location \"".concat(location, "\""));
94919 result = accumulator;
94923 /* eslint-enable no-console, no-invalid-this */
94926 function _cloneDeep(obj) {
94927 return JSON.parse(JSON.stringify(obj));
94928 } // Sorting the location lists is ok because they end up unioned together.
94929 // This sorting makes it possible to generate a deterministic id.
94932 function _sortLocations(a, b) {
94938 var aRank = rank[a.type];
94939 var bRank = rank[b.type];
94940 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
94944 function uiSuccess(context) {
94946 var dispatch$1 = dispatch('cancel');
94952 ensureOSMCommunityIndex(); // start fetching the data
94954 function ensureOSMCommunityIndex() {
94955 var data = _mainFileFetcher;
94956 return Promise.all([data.get('oci_resources'), data.get('oci_features')]).then(function (vals) {
94957 if (_oci) return _oci;
94958 var ociResources = vals[0].resources;
94959 var loco = new _default$3(vals[1]);
94960 var ociFeatures = {};
94961 Object.values(ociResources).forEach(function (resource) {
94962 var feature = loco.resolveLocationSet(resource.locationSet).feature;
94963 var ociFeature = ociFeatures[feature.id];
94966 ociFeature = JSON.parse(JSON.stringify(feature)); // deep clone
94968 ociFeature.properties.resourceIDs = new Set();
94969 ociFeatures[feature.id] = ociFeature;
94972 ociFeature.properties.resourceIDs.add(resource.id);
94975 features: ociFeatures,
94976 resources: ociResources,
94977 query: whichPolygon_1({
94978 type: 'FeatureCollection',
94979 features: Object.values(ociFeatures)
94983 } // string-to-date parsing in JavaScript is weird
94986 function parseEventDate(when) {
94988 var raw = when.trim();
94991 if (!/Z$/.test(raw)) {
94992 // if no trailing 'Z', add one
94993 raw += 'Z'; // this forces date to be parsed as a UTC date
94996 var parsed = new Date(raw);
94997 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
95000 function success(selection) {
95001 var header = selection.append('div').attr('class', 'header fillL');
95002 header.append('h3').html(_t.html('success.just_edited'));
95003 header.append('button').attr('class', 'close').on('click', function () {
95004 return dispatch$1.call('cancel');
95005 }).call(svgIcon('#iD-icon-close'));
95006 var body = selection.append('div').attr('class', 'body save-success fillL');
95007 var summary = body.append('div').attr('class', 'save-summary');
95008 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
95011 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'));
95012 var osm = context.connection();
95014 var changesetURL = osm.changesetURL(_changeset.id);
95015 var table = summary.append('table').attr('class', 'summary-table');
95016 var row = table.append('tr').attr('class', 'summary-row');
95017 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');
95018 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
95019 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
95020 summaryDetail.append('div').html(_t.html('success.changeset_id', {
95021 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
95022 })); // Get OSM community index features intersecting the map..
95024 ensureOSMCommunityIndex().then(function (oci) {
95025 var communities = [];
95026 var properties = oci.query(context.map().center(), true) || []; // Gather the communities from the result
95028 properties.forEach(function (props) {
95029 var resourceIDs = Array.from(props.resourceIDs);
95030 resourceIDs.forEach(function (resourceID) {
95031 var resource = oci.resources[resourceID];
95033 area: props.area || Infinity,
95034 order: resource.order || 0,
95038 }); // sort communities by feature area ascending, community order descending
95040 communities.sort(function (a, b) {
95041 return a.area - b.area || b.order - a.order;
95043 body.call(showCommunityLinks, communities.map(function (c) {
95049 function showCommunityLinks(selection, resources) {
95050 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
95051 communityLinks.append('h3').html(_t.html('success.like_osm'));
95052 var table = communityLinks.append('table').attr('class', 'community-table');
95053 var row = table.selectAll('.community-row').data(resources);
95054 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
95055 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
95057 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
95058 return "#community-".concat(d.type);
95060 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
95061 communityDetail.each(showCommunityDetails);
95062 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'));
95065 function showCommunityDetails(d) {
95066 var selection = select(this);
95067 var communityID = d.id;
95068 var replacements = {
95069 url: linkify(d.url),
95070 signupUrl: linkify(d.signupUrl || d.url)
95072 selection.append('div').attr('class', 'community-name').append('a').attr('target', '_blank').attr('href', d.url).html(_t.html("community.".concat(d.id, ".name")));
95073 var descriptionHTML = _t.html("community.".concat(d.id, ".description"), replacements);
95075 if (d.type === 'reddit') {
95076 // linkify subreddits #4997
95077 descriptionHTML = descriptionHTML.replace(/(\/r\/\w*\/*)/i, function (match) {
95078 return linkify(d.url, match);
95082 selection.append('div').attr('class', 'community-description').html(descriptionHTML);
95084 if (d.extendedDescription || d.languageCodes && d.languageCodes.length) {
95085 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
95088 var nextEvents = (d.events || []).map(function (event) {
95089 event.date = parseEventDate(event.when);
95091 }).filter(function (event) {
95092 // date is valid and future (or today)
95093 var t = event.date.getTime();
95094 var now = new Date().setHours(0, 0, 0, 0);
95095 return !isNaN(t) && t >= now;
95096 }).sort(function (a, b) {
95097 // sort by date ascending
95098 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
95099 }).slice(0, MAXEVENTS); // limit number of events shown
95101 if (nextEvents.length) {
95102 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);
95105 function showMore(selection) {
95106 var more = selection.selectAll('.community-more').data([0]);
95107 var moreEnter = more.enter().append('div').attr('class', 'community-more');
95109 if (d.extendedDescription) {
95110 moreEnter.append('div').attr('class', 'community-extended-description').html(_t.html("community.".concat(d.id, ".extendedDescription"), replacements));
95113 if (d.languageCodes && d.languageCodes.length) {
95114 var languageList = d.languageCodes.map(function (code) {
95115 return _mainLocalizer.languageName(code);
95117 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
95118 languages: languageList
95123 function showNextEvents(selection) {
95124 var events = selection.append('div').attr('class', 'community-events');
95125 var item = events.selectAll('.community-event').data(nextEvents);
95126 var itemEnter = item.enter().append('div').attr('class', 'community-event');
95127 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
95129 }).html(function (d) {
95132 if (d.i18n && d.id) {
95133 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
95140 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
95148 if (d.date.getHours() || d.date.getMinutes()) {
95149 // include time if it has one
95150 options.hour = 'numeric';
95151 options.minute = 'numeric';
95154 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
95156 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
95157 var where = d.where;
95159 if (d.i18n && d.id) {
95160 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
95167 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
95168 var description = d.description;
95170 if (d.i18n && d.id) {
95171 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
95172 "default": description
95176 return description;
95180 function linkify(url, text) {
95181 text = text || url;
95182 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
95186 success.changeset = function (val) {
95187 if (!arguments.length) return _changeset;
95192 success.location = function (val) {
95193 if (!arguments.length) return _location;
95198 return utilRebind(success, dispatch$1, 'on');
95201 function modeSave(context) {
95205 var keybinding = utilKeybinding('modeSave');
95206 var commit = uiCommit(context).on('cancel', cancel);
95208 var _conflictsUi; // uiConflicts
95215 var uploader = context.uploader().on('saveStarted.modeSave', function () {
95217 }) // fire off some async work that we want to be ready later
95218 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
95220 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
95222 function cancel() {
95223 context.enter(modeBrowse(context));
95226 function showProgress(num, total) {
95227 var modal = context.container().select('.loading-modal .modal-section');
95228 var progress = modal.selectAll('.progress').data([0]); // enter/update
95230 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
95236 function showConflicts(changeset, conflicts, origChanges) {
95237 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
95238 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95239 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
95240 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95241 selection.remove();
95243 uploader.cancelConflictResolution();
95244 }).on('save', function () {
95245 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95246 selection.remove();
95247 uploader.processResolvedConflicts(changeset);
95249 selection.call(_conflictsUi);
95252 function showErrors(errors) {
95254 var selection = uiConfirm(context.container());
95255 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
95256 addErrors(selection, errors);
95257 selection.okButton();
95260 function addErrors(selection, data) {
95261 var message = selection.select('.modal-section.message-text');
95262 var items = message.selectAll('.error-container').data(data);
95263 var enter = items.enter().append('div').attr('class', 'error-container');
95264 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
95265 return d.msg || _t('save.unknown_error_details');
95266 }).on('click', function (d3_event) {
95267 d3_event.preventDefault();
95268 var error = select(this);
95269 var detail = select(this.nextElementSibling);
95270 var exp = error.classed('expanded');
95271 detail.style('display', exp ? 'none' : 'block');
95272 error.classed('expanded', !exp);
95274 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
95275 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
95276 return d.details || [];
95277 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
95280 items.exit().remove();
95283 function showSuccess(changeset) {
95286 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
95287 context.ui().sidebar.hide();
95290 context.enter(modeBrowse(context).sidebar(ui));
95293 function keybindingOn() {
95294 select(document).call(keybinding.on('⎋', cancel, true));
95297 function keybindingOff() {
95298 select(document).call(keybinding.unbind);
95299 } // Reverse geocode current map location so we can display a message on
95300 // the success screen like "Thank you for editing around place, region."
95303 function prepareForSuccess() {
95304 _success = uiSuccess(context);
95306 if (!services.geocoder) return;
95307 services.geocoder.reverse(context.map().center(), function (err, result) {
95308 if (err || !result || !result.address) return;
95309 var addr = result.address;
95310 var place = addr && (addr.town || addr.city || addr.county) || '';
95311 var region = addr && (addr.state || addr.country) || '';
95312 var separator = place && region ? _t('success.thank_you_where.separator') : '';
95313 _location = _t('success.thank_you_where.format', {
95315 separator: separator,
95321 mode.selectedIDs = function () {
95322 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
95325 mode.enter = function () {
95327 context.ui().sidebar.expand();
95330 context.ui().sidebar.show(commit);
95334 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95335 var osm = context.connection();
95342 if (osm.authenticated()) {
95345 osm.authenticate(function (err) {
95355 mode.exit = function () {
95357 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95358 context.ui().sidebar.hide();
95364 function uiToolOldDrawModes(context) {
95367 label: _t.html('toolbar.add_feature')
95369 var modes = [modeAddPoint(context, {
95370 title: _t.html('modes.add_point.title'),
95372 description: _t.html('modes.add_point.description'),
95373 preset: _mainPresetIndex.item('point'),
95375 }), modeAddLine(context, {
95376 title: _t.html('modes.add_line.title'),
95378 description: _t.html('modes.add_line.description'),
95379 preset: _mainPresetIndex.item('line'),
95381 }), modeAddArea(context, {
95382 title: _t.html('modes.add_area.title'),
95384 description: _t.html('modes.add_area.description'),
95385 preset: _mainPresetIndex.item('area'),
95389 function enabled() {
95390 return osmEditable();
95393 function osmEditable() {
95394 return context.editable();
95397 modes.forEach(function (mode) {
95398 context.keybinding().on(mode.key, function () {
95399 if (!enabled()) return;
95401 if (mode.id === context.mode().id) {
95402 context.enter(modeBrowse(context));
95404 context.enter(mode);
95409 tool.render = function (selection) {
95410 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
95412 var debouncedUpdate = debounce(update, 500, {
95417 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
95418 context.on('enter.modes', update);
95421 function update() {
95422 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
95426 buttons.exit().remove(); // enter
95428 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95429 return d.id + ' add-button bar-button';
95430 }).on('click.mode-buttons', function (d3_event, d) {
95431 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95433 var currMode = context.mode().id;
95434 if (/^draw/.test(currMode)) return;
95436 if (d.id === currMode) {
95437 context.enter(modeBrowse(context));
95441 }).call(uiTooltip().placement('bottom').title(function (d) {
95442 return d.description;
95443 }).keys(function (d) {
95445 }).scrollContainer(context.container().select('.top-toolbar')));
95446 buttonsEnter.each(function (d) {
95447 select(this).call(svgIcon('#iD-icon-' + d.button));
95449 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
95451 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95453 if (buttons.enter().size() || buttons.exit().size()) {
95454 context.ui().checkOverflow('.top-toolbar', true);
95458 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95460 }).classed('active', function (d) {
95461 return context.mode() && context.mode().button === d.button;
95469 function uiToolNotes(context) {
95472 label: _t.html('modes.add_note.label')
95474 var mode = modeAddNote(context);
95476 function enabled() {
95477 return notesEnabled() && notesEditable();
95480 function notesEnabled() {
95481 var noteLayer = context.layers().layer('notes');
95482 return noteLayer && noteLayer.enabled();
95485 function notesEditable() {
95486 var mode = context.mode();
95487 return context.map().notesEditable() && mode && mode.id !== 'save';
95490 context.keybinding().on(mode.key, function () {
95491 if (!enabled()) return;
95493 if (mode.id === context.mode().id) {
95494 context.enter(modeBrowse(context));
95496 context.enter(mode);
95500 tool.render = function (selection) {
95501 var debouncedUpdate = debounce(update, 500, {
95506 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
95507 context.on('enter.notes', update);
95510 function update() {
95511 var showNotes = notesEnabled();
95512 var data = showNotes ? [mode] : [];
95513 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
95517 buttons.exit().remove(); // enter
95519 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95520 return d.id + ' add-button bar-button';
95521 }).on('click.notes', function (d3_event, d) {
95522 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95524 var currMode = context.mode().id;
95525 if (/^draw/.test(currMode)) return;
95527 if (d.id === currMode) {
95528 context.enter(modeBrowse(context));
95532 }).call(uiTooltip().placement('bottom').title(function (d) {
95533 return d.description;
95534 }).keys(function (d) {
95536 }).scrollContainer(context.container().select('.top-toolbar')));
95537 buttonsEnter.each(function (d) {
95538 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
95539 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95541 if (buttons.enter().size() || buttons.exit().size()) {
95542 context.ui().checkOverflow('.top-toolbar', true);
95546 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95548 }).classed('active', function (d) {
95549 return context.mode() && context.mode().button === d.button;
95554 tool.uninstall = function () {
95555 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
95556 context.map().on('move.notes', null).on('drawn.notes', null);
95562 function uiToolSave(context) {
95565 label: _t.html('save.title')
95568 var tooltipBehavior = null;
95569 var history = context.history();
95570 var key = uiCmd('⌘S');
95571 var _numChanges = 0;
95573 function isSaving() {
95574 var mode = context.mode();
95575 return mode && mode.id === 'save';
95578 function isDisabled() {
95579 return _numChanges === 0 || isSaving();
95582 function save(d3_event) {
95583 d3_event.preventDefault();
95585 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
95586 context.enter(modeSave(context));
95590 function bgColor() {
95593 if (_numChanges === 0) {
95595 } else if (_numChanges <= 50) {
95596 step = _numChanges / 50;
95597 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
95599 step = Math.min((_numChanges - 50) / 50, 1.0);
95600 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
95604 function updateCount() {
95605 var val = history.difference().summary().length;
95606 if (val === _numChanges) return;
95609 if (tooltipBehavior) {
95610 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
95614 button.classed('disabled', isDisabled()).style('background', bgColor());
95615 button.select('span.count').html(_numChanges);
95619 tool.render = function (selection) {
95620 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
95621 var lastPointerUpType;
95622 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
95623 lastPointerUpType = d3_event.pointerType;
95624 }).on('click', function (d3_event) {
95627 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95628 // there are no tooltips for touch interactions so flash feedback instead
95629 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
95632 lastPointerUpType = null;
95633 }).call(tooltipBehavior);
95634 button.call(svgIcon('#iD-icon-save'));
95635 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
95637 context.keybinding().on(key, save, true);
95638 context.history().on('change.save', updateCount);
95639 context.on('enter.save', function () {
95641 button.classed('disabled', isDisabled());
95644 button.call(tooltipBehavior.hide);
95650 tool.uninstall = function () {
95651 context.keybinding().off(key, true);
95652 context.history().on('change.save', null);
95653 context.on('enter.save', null);
95655 tooltipBehavior = null;
95661 function uiToolSidebarToggle(context) {
95663 id: 'sidebar_toggle',
95664 label: _t.html('toolbar.inspect')
95667 tool.render = function (selection) {
95668 selection.append('button').attr('class', 'bar-button').on('click', function () {
95669 context.ui().sidebar.toggle();
95670 }).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')));
95676 function uiToolUndoRedo(context) {
95679 label: _t.html('toolbar.undo_redo')
95684 action: function action() {
95687 annotation: function annotation() {
95688 return context.history().undoAnnotation();
95690 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
95694 action: function action() {
95697 annotation: function annotation() {
95698 return context.history().redoAnnotation();
95700 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
95703 function editable() {
95704 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
95705 /* ignore min zoom */
95709 tool.render = function (selection) {
95710 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
95711 return d.annotation() ? _t.html(d.id + '.tooltip', {
95712 action: d.annotation()
95713 }) : _t.html(d.id + '.nothing');
95714 }).keys(function (d) {
95716 }).scrollContainer(context.container().select('.top-toolbar'));
95717 var lastPointerUpType;
95718 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
95719 return 'disabled ' + d.id + '-button bar-button';
95720 }).on('pointerup', function (d3_event) {
95721 // `pointerup` is always called before `click`
95722 lastPointerUpType = d3_event.pointerType;
95723 }).on('click', function (d3_event, d) {
95724 d3_event.preventDefault();
95725 var annotation = d.annotation();
95727 if (editable() && annotation) {
95731 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95732 // there are no tooltips for touch interactions so flash feedback instead
95733 var text = annotation ? _t(d.id + '.tooltip', {
95735 }) : _t(d.id + '.nothing');
95736 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
95739 lastPointerUpType = null;
95740 }).call(tooltipBehavior);
95741 buttons.each(function (d) {
95742 select(this).call(svgIcon('#' + d.icon));
95744 context.keybinding().on(commands[0].cmd, function (d3_event) {
95745 d3_event.preventDefault();
95746 if (editable()) commands[0].action();
95747 }).on(commands[1].cmd, function (d3_event) {
95748 d3_event.preventDefault();
95749 if (editable()) commands[1].action();
95752 var debouncedUpdate = debounce(update, 500, {
95757 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
95758 context.history().on('change.undo_redo', function (difference) {
95759 if (difference) update();
95761 context.on('enter.undo_redo', update);
95763 function update() {
95764 buttons.classed('disabled', function (d) {
95765 return !editable() || !d.annotation();
95766 }).each(function () {
95767 var selection = select(this);
95769 if (!selection.select('.tooltip.in').empty()) {
95770 selection.call(tooltipBehavior.updateContent);
95776 tool.uninstall = function () {
95777 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
95778 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
95779 context.history().on('change.undo_redo', null);
95780 context.on('enter.undo_redo', null);
95786 function uiTopToolbar(context) {
95787 var sidebarToggle = uiToolSidebarToggle(context),
95788 modes = uiToolOldDrawModes(context),
95789 notes = uiToolNotes(context),
95790 undoRedo = uiToolUndoRedo(context),
95791 save = uiToolSave(context);
95793 function notesEnabled() {
95794 var noteLayer = context.layers().layer('notes');
95795 return noteLayer && noteLayer.enabled();
95798 function topToolbar(bar) {
95799 bar.on('wheel.topToolbar', function (d3_event) {
95800 if (!d3_event.deltaX) {
95801 // translate vertical scrolling into horizontal scrolling in case
95802 // the user doesn't have an input device that can scroll horizontally
95803 bar.node().scrollLeft += d3_event.deltaY;
95807 var debouncedUpdate = debounce(update, 500, {
95812 context.layers().on('change.topToolbar', debouncedUpdate);
95815 function update() {
95816 var tools = [sidebarToggle, 'spacer', modes];
95817 tools.push('spacer');
95819 if (notesEnabled()) {
95820 tools = tools.concat([notes, 'spacer']);
95823 tools = tools.concat([undoRedo, save]);
95824 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
95827 toolbarItems.exit().each(function (d) {
95832 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
95833 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
95834 if (d.klass) classes += ' ' + d.klass;
95837 var actionableItems = itemsEnter.filter(function (d) {
95838 return d !== 'spacer';
95840 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
95841 select(this).call(d.render, bar);
95843 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
95852 var sawVersion = null;
95853 var isNewVersion = false;
95854 var isNewUser = false;
95855 function uiVersion(context) {
95856 var currVersion = context.version;
95857 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
95859 if (sawVersion === null && matchedVersion !== null) {
95860 if (corePreferences('sawVersion')) {
95862 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
95865 isNewVersion = true;
95868 corePreferences('sawVersion', currVersion);
95869 sawVersion = currVersion;
95872 return function (selection) {
95873 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
95875 if (isNewVersion && !isNewUser) {
95876 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', {
95877 version: currVersion
95878 })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
95883 function uiZoom(context) {
95886 icon: 'iD-icon-plus',
95887 title: _t.html('zoom.in'),
95889 disabled: function disabled() {
95890 return !context.map().canZoomIn();
95892 disabledTitle: _t.html('zoom.disabled.in'),
95896 icon: 'iD-icon-minus',
95897 title: _t.html('zoom.out'),
95899 disabled: function disabled() {
95900 return !context.map().canZoomOut();
95902 disabledTitle: _t.html('zoom.disabled.out'),
95906 function zoomIn(d3_event) {
95907 if (d3_event.shiftKey) return;
95908 d3_event.preventDefault();
95909 context.map().zoomIn();
95912 function zoomOut(d3_event) {
95913 if (d3_event.shiftKey) return;
95914 d3_event.preventDefault();
95915 context.map().zoomOut();
95918 function zoomInFurther(d3_event) {
95919 if (d3_event.shiftKey) return;
95920 d3_event.preventDefault();
95921 context.map().zoomInFurther();
95924 function zoomOutFurther(d3_event) {
95925 if (d3_event.shiftKey) return;
95926 d3_event.preventDefault();
95927 context.map().zoomOutFurther();
95930 return function (selection) {
95931 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
95932 if (d.disabled()) {
95933 return d.disabledTitle;
95937 }).keys(function (d) {
95940 var lastPointerUpType;
95941 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
95943 }).on('pointerup.editor', function (d3_event) {
95944 lastPointerUpType = d3_event.pointerType;
95945 }).on('click.editor', function (d3_event, d) {
95946 if (!d.disabled()) {
95947 d.action(d3_event);
95948 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
95949 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
95952 lastPointerUpType = null;
95953 }).call(tooltipBehavior);
95954 buttons.each(function (d) {
95955 select(this).call(svgIcon('#' + d.icon, 'light'));
95957 utilKeybinding.plusKeys.forEach(function (key) {
95958 context.keybinding().on([key], zoomIn);
95959 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
95961 utilKeybinding.minusKeys.forEach(function (key) {
95962 context.keybinding().on([key], zoomOut);
95963 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
95966 function updateButtonStates() {
95967 buttons.classed('disabled', function (d) {
95968 return d.disabled();
95969 }).each(function () {
95970 var selection = select(this);
95972 if (!selection.select('.tooltip.in').empty()) {
95973 selection.call(tooltipBehavior.updateContent);
95978 updateButtonStates();
95979 context.map().on('move.uiZoom', updateButtonStates);
95983 function uiZoomToSelection(context) {
95984 function isDisabled() {
95985 var mode = context.mode();
95986 return !mode || !mode.zoomToSelected;
95989 var _lastPointerUpType;
95991 function pointerup(d3_event) {
95992 _lastPointerUpType = d3_event.pointerType;
95995 function click(d3_event) {
95996 d3_event.preventDefault();
95998 if (isDisabled()) {
95999 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
96000 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
96003 var mode = context.mode();
96005 if (mode && mode.zoomToSelected) {
96006 mode.zoomToSelected();
96010 _lastPointerUpType = null;
96013 return function (selection) {
96014 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
96015 if (isDisabled()) {
96016 return _t.html('inspector.zoom_to.no_selection');
96019 return _t.html('inspector.zoom_to.title');
96020 }).keys([_t('inspector.zoom_to.key')]);
96021 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
96023 function setEnabledState() {
96024 button.classed('disabled', isDisabled());
96026 if (!button.select('.tooltip.in').empty()) {
96027 button.call(tooltipBehavior.updateContent);
96031 context.on('enter.uiZoomToSelection', setEnabledState);
96036 function uiPane(id, context) {
96040 var _description = '';
96041 var _iconName = '';
96043 var _sections; // array of uiSection objects
96046 var _paneSelection = select(null);
96054 pane.label = function (val) {
96055 if (!arguments.length) return _label;
96060 pane.key = function (val) {
96061 if (!arguments.length) return _key;
96066 pane.description = function (val) {
96067 if (!arguments.length) return _description;
96068 _description = val;
96072 pane.iconName = function (val) {
96073 if (!arguments.length) return _iconName;
96078 pane.sections = function (val) {
96079 if (!arguments.length) return _sections;
96084 pane.selection = function () {
96085 return _paneSelection;
96088 function hidePane() {
96089 context.ui().togglePanes();
96092 pane.togglePane = function (d3_event) {
96093 if (d3_event) d3_event.preventDefault();
96095 _paneTooltip.hide();
96097 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
96100 pane.renderToggleButton = function (selection) {
96101 if (!_paneTooltip) {
96102 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
96105 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
96108 pane.renderContent = function (selection) {
96109 // override to fully customize content
96111 _sections.forEach(function (section) {
96112 selection.call(section.render);
96117 pane.renderPane = function (selection) {
96118 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
96120 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
96122 heading.append('h2').html(_label);
96123 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
96125 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
96128 context.keybinding().on(_key, pane.togglePane);
96135 function uiSectionBackgroundDisplayOptions(context) {
96136 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
96138 var _detected = utilDetect();
96140 var _storedOpacity = corePreferences('background-opacity');
96144 var _maxVal = _detected.cssfilters ? 3 : 1;
96146 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
96149 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
96155 function clamp(x, min, max) {
96156 return Math.max(min, Math.min(x, max));
96159 function updateValue(d, val) {
96160 val = clamp(val, _minVal, _maxVal);
96162 context.background()[d](val);
96164 if (d === 'brightness') {
96165 corePreferences('background-opacity', val);
96168 section.reRender();
96171 function renderDisclosureContent(selection) {
96172 var container = selection.selectAll('.display-options-container').data([0]);
96173 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
96175 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
96176 return 'display-control display-control-' + d;
96178 slidersEnter.append('h5').html(function (d) {
96179 return _t.html('background.' + d);
96180 }).append('span').attr('class', function (d) {
96181 return 'display-option-value display-option-value-' + d;
96183 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
96184 sildersControlEnter.append('input').attr('class', function (d) {
96185 return 'display-option-input display-option-input-' + d;
96186 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
96187 var val = select(this).property('value');
96189 if (!val && d3_event && d3_event.target) {
96190 val = d3_event.target.value;
96193 updateValue(d, val);
96195 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
96196 return 'display-option-reset display-option-reset-' + d;
96197 }).on('click', function (d3_event, d) {
96198 if (d3_event.button !== 0) return;
96200 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
96202 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
96203 d3_event.preventDefault();
96205 for (var i = 0; i < _sliders.length; i++) {
96206 updateValue(_sliders[i], 1);
96210 container = containerEnter.merge(container);
96211 container.selectAll('.display-option-input').property('value', function (d) {
96212 return _options[d];
96214 container.selectAll('.display-option-value').html(function (d) {
96215 return Math.floor(_options[d] * 100) + '%';
96217 container.selectAll('.display-option-reset').classed('disabled', function (d) {
96218 return _options[d] === 1;
96219 }); // first time only, set brightness if needed
96221 if (containerEnter.size() && _options.brightness !== 1) {
96222 context.background().brightness(_options.brightness);
96229 function uiSettingsCustomBackground() {
96230 var dispatch$1 = dispatch('change');
96232 function render(selection) {
96233 // keep separate copies of original and current settings
96234 var _origSettings = {
96235 template: corePreferences('background-custom-template')
96237 var _currSettings = {
96238 template: corePreferences('background-custom-template')
96240 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
96241 var modal = uiConfirm(selection).okButton();
96242 modal.classed('settings-modal settings-custom-background', true);
96243 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
96244 var textSection = modal.select('.modal-section.message-text');
96245 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, "`");
96246 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
96247 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
96249 var buttonSection = modal.select('.modal-section.buttons');
96250 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
96251 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
96252 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
96254 function isSaveDisabled() {
96256 } // restore the original template
96259 function clickCancel() {
96260 textSection.select('.field-template').property('value', _origSettings.template);
96261 corePreferences('background-custom-template', _origSettings.template);
96264 } // accept the current template
96267 function clickSave() {
96268 _currSettings.template = textSection.select('.field-template').property('value');
96269 corePreferences('background-custom-template', _currSettings.template);
96272 dispatch$1.call('change', this, _currSettings);
96276 return utilRebind(render, dispatch$1, 'on');
96279 function uiSectionBackgroundList(context) {
96280 var _backgroundList = select(null);
96282 var _customSource = context.background().findSource('custom');
96284 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
96286 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
96288 function previousBackgroundID() {
96289 return corePreferences('background-last-used-toggle');
96292 function renderDisclosureContent(selection) {
96293 // the background list
96294 var container = selection.selectAll('.layer-background-list').data([0]);
96295 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
96297 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
96298 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'));
96299 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96300 d3_event.preventDefault();
96301 uiMapInMap.toggle();
96303 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
96304 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'));
96305 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96306 d3_event.preventDefault();
96307 context.ui().info.toggle('background');
96309 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
96310 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'));
96311 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96312 d3_event.preventDefault();
96313 context.ui().info.toggle('location');
96315 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
96317 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'));
96319 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
96320 chooseBackground(d);
96322 return !d.isHidden() && !d.overlay;
96326 function setTooltips(selection) {
96327 selection.each(function (d, i, nodes) {
96328 var item = select(this).select('label');
96329 var span = item.select('span');
96330 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96331 var description = d.description();
96332 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96333 item.call(uiTooltip().destroyAny);
96335 if (d.id === previousBackgroundID()) {
96336 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
96337 } else if (description || isOverflowing) {
96338 item.call(uiTooltip().placement(placement).title(description || d.label()));
96343 function drawListItems(layerList, type, change, filter) {
96344 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
96345 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96347 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
96348 // arrow key navigation of radio values likes to work in the order
96349 // they were added, not the display document order.
96350 .data(sources, function (d, i) {
96351 return d.id + '---' + i;
96353 layerLinks.exit().remove();
96354 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
96355 return d.id === 'custom';
96356 }).classed('best', function (d) {
96359 var label = enter.append('label');
96360 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
96362 }).on('change', change);
96363 label.append('span').html(function (d) {
96366 enter.filter(function (d) {
96367 return d.id === 'custom';
96368 }).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) {
96369 d3_event.preventDefault();
96371 }).call(svgIcon('#iD-icon-more'));
96372 enter.filter(function (d) {
96374 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
96375 layerList.call(updateLayerSelections);
96378 function updateLayerSelections(selection) {
96379 function active(d) {
96380 return context.background().showsLayer(d);
96383 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
96384 return d.id === previousBackgroundID();
96385 }).call(setTooltips).selectAll('input').property('checked', active);
96388 function chooseBackground(d) {
96389 if (d.id === 'custom' && !d.template()) {
96390 return editCustom();
96393 var previousBackground = context.background().baseLayerSource();
96394 corePreferences('background-last-used-toggle', previousBackground.id);
96395 corePreferences('background-last-used', d.id);
96396 context.background().baseLayerSource(d);
96399 function customChanged(d) {
96400 if (d && d.template) {
96401 _customSource.template(d.template);
96403 chooseBackground(_customSource);
96405 _customSource.template('');
96407 chooseBackground(context.background().findSource('none'));
96411 function editCustom() {
96412 context.container().call(_settingsCustomBackground);
96415 context.background().on('change.background_list', function () {
96416 _backgroundList.call(updateLayerSelections);
96418 context.map().on('move.background_list', debounce(function () {
96419 // layers in-view may have changed due to map move
96420 window.requestIdleCallback(section.reRender);
96425 function uiSectionBackgroundOffset(context) {
96426 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96428 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
96430 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
96432 function updateValue() {
96433 var meters = geoOffsetToMeters(context.background().offset());
96434 var x = +meters[0].toFixed(2);
96435 var y = +meters[1].toFixed(2);
96436 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
96437 context.container().selectAll('.nudge-reset').classed('disabled', function () {
96438 return x === 0 && y === 0;
96442 function resetOffset() {
96443 context.background().offset([0, 0]);
96447 function nudge(d) {
96448 context.background().nudge(d, context.map().zoom());
96452 function inputOffset() {
96453 var input = select(this);
96454 var d = input.node().value;
96455 if (d === '') return resetOffset();
96456 d = d.replace(/;/g, ',').split(',').map(function (n) {
96457 // if n is NaN, it will always get mapped to false.
96458 return !isNaN(n) && n;
96461 if (d.length !== 2 || !d[0] || !d[1]) {
96462 input.classed('error', true);
96466 context.background().offset(geoMetersToOffset(d));
96470 function dragOffset(d3_event) {
96471 if (d3_event.button !== 0) return;
96472 var origin = [d3_event.clientX, d3_event.clientY];
96473 var pointerId = d3_event.pointerId || 'mouse';
96474 context.container().append('div').attr('class', 'nudge-surface');
96475 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
96477 if (_pointerPrefix === 'pointer') {
96478 select(window).on('pointercancel.drag-bg-offset', pointerup);
96481 function pointermove(d3_event) {
96482 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96483 var latest = [d3_event.clientX, d3_event.clientY];
96484 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
96489 function pointerup(d3_event) {
96490 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96491 if (d3_event.button !== 0) return;
96492 context.container().selectAll('.nudge-surface').remove();
96493 select(window).on('.drag-bg-offset', null);
96497 function renderDisclosureContent(selection) {
96498 var container = selection.selectAll('.nudge-container').data([0]);
96499 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
96500 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
96501 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
96502 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
96503 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
96504 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
96505 return d[0] + ' nudge';
96506 }).on('click', function (d3_event, d) {
96509 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
96510 d3_event.preventDefault();
96512 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
96516 context.background().on('change.backgroundOffset-update', updateValue);
96520 function uiSectionOverlayList(context) {
96521 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
96523 var _overlayList = select(null);
96525 function setTooltips(selection) {
96526 selection.each(function (d, i, nodes) {
96527 var item = select(this).select('label');
96528 var span = item.select('span');
96529 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96530 var description = d.description();
96531 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96532 item.call(uiTooltip().destroyAny);
96534 if (description || isOverflowing) {
96535 item.call(uiTooltip().placement(placement).title(description || d.name()));
96540 function updateLayerSelections(selection) {
96541 function active(d) {
96542 return context.background().showsLayer(d);
96545 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
96548 function chooseOverlay(d3_event, d) {
96549 d3_event.preventDefault();
96550 context.background().toggleOverlayLayer(d);
96552 _overlayList.call(updateLayerSelections);
96554 document.activeElement.blur();
96557 function drawListItems(layerList, type, change, filter) {
96558 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
96559 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
96562 layerLinks.exit().remove();
96563 var enter = layerLinks.enter().append('li');
96564 var label = enter.append('label');
96565 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
96566 label.append('span').html(function (d) {
96569 layerList.selectAll('li').sort(sortSources);
96570 layerList.call(updateLayerSelections);
96572 function sortSources(a, b) {
96573 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96577 function renderDisclosureContent(selection) {
96578 var container = selection.selectAll('.layer-overlay-list').data([0]);
96579 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
96581 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
96582 return !d.isHidden() && d.overlay;
96586 context.map().on('move.overlay_list', debounce(function () {
96587 // layers in-view may have changed due to map move
96588 window.requestIdleCallback(section.reRender);
96593 function uiPaneBackground(context) {
96594 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)]);
96595 return backgroundPane;
96598 function uiPaneHelp(context) {
96599 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']]];
96601 'help.help.open_data_h': 3,
96602 'help.help.before_start_h': 3,
96603 'help.help.open_source_h': 3,
96604 'help.overview.navigation_h': 3,
96605 'help.overview.features_h': 3,
96606 'help.editing.select_h': 3,
96607 'help.editing.multiselect_h': 3,
96608 'help.editing.undo_redo_h': 3,
96609 'help.editing.save_h': 3,
96610 'help.editing.upload_h': 3,
96611 'help.editing.backups_h': 3,
96612 'help.editing.keyboard_h': 3,
96613 'help.feature_editor.type_h': 3,
96614 'help.feature_editor.fields_h': 3,
96615 'help.feature_editor.tags_h': 3,
96616 'help.points.add_point_h': 3,
96617 'help.points.move_point_h': 3,
96618 'help.points.delete_point_h': 3,
96619 'help.lines.add_line_h': 3,
96620 'help.lines.modify_line_h': 3,
96621 'help.lines.connect_line_h': 3,
96622 'help.lines.disconnect_line_h': 3,
96623 'help.lines.move_line_h': 3,
96624 'help.lines.delete_line_h': 3,
96625 'help.areas.point_or_area_h': 3,
96626 'help.areas.add_area_h': 3,
96627 'help.areas.square_area_h': 3,
96628 'help.areas.modify_area_h': 3,
96629 'help.areas.delete_area_h': 3,
96630 'help.relations.edit_relation_h': 3,
96631 'help.relations.maintain_relation_h': 3,
96632 'help.relations.relation_types_h': 2,
96633 'help.relations.multipolygon_h': 3,
96634 'help.relations.turn_restriction_h': 3,
96635 'help.relations.route_h': 3,
96636 'help.relations.boundary_h': 3,
96637 'help.notes.add_note_h': 3,
96638 'help.notes.update_note_h': 3,
96639 'help.notes.save_note_h': 3,
96640 'help.imagery.sources_h': 3,
96641 'help.imagery.offsets_h': 3,
96642 'help.streetlevel.using_h': 3,
96643 'help.gps.using_h': 3,
96644 'help.qa.tools_h': 3,
96645 'help.qa.issues_h': 3
96646 }; // For each section, squash all the texts into a single markdown document
96648 var docs = docKeys.map(function (key) {
96649 var helpkey = 'help.' + key[0];
96650 var helpPaneReplacements = {
96651 version: context.version
96653 var text = key[1].reduce(function (all, part) {
96654 var subkey = helpkey + '.' + part;
96655 var depth = headings[subkey]; // is this subkey a heading?
96657 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
96659 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
96662 title: _t.html(helpkey + '.title'),
96663 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
96664 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
96667 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
96669 helpPane.renderContent = function (content) {
96670 function clickHelp(d, i) {
96671 var rtl = _mainLocalizer.textDirection() === 'rtl';
96672 content.property('scrollTop', 0);
96673 helpPane.selection().select('.pane-heading h2').html(d.title);
96674 body.html(d.content);
96675 body.selectAll('a').attr('target', '_blank');
96676 menuItems.classed('selected', function (m) {
96677 return m.title === d.title;
96682 nav.call(drawNext).call(drawPrevious);
96684 nav.call(drawPrevious).call(drawNext);
96687 function drawNext(selection) {
96688 if (i < docs.length - 1) {
96689 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
96690 d3_event.preventDefault();
96691 clickHelp(docs[i + 1], i + 1);
96693 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
96697 function drawPrevious(selection) {
96699 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
96700 d3_event.preventDefault();
96701 clickHelp(docs[i - 1], i - 1);
96703 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
96708 function clickWalkthrough(d3_event) {
96709 d3_event.preventDefault();
96710 if (context.inIntro()) return;
96711 context.container().call(uiIntro(context));
96712 context.ui().togglePanes();
96715 function clickShortcuts(d3_event) {
96716 d3_event.preventDefault();
96717 context.container().call(context.ui().shortcuts, true);
96720 var toc = content.append('ul').attr('class', 'toc');
96721 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
96723 }).on('click', function (d3_event, d) {
96724 d3_event.preventDefault();
96725 clickHelp(d, docs.indexOf(d));
96727 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);
96728 shortcuts.append('div').html(_t.html('shortcuts.title'));
96729 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
96730 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
96731 walkthrough.append('div').html(_t.html('splash.walkthrough'));
96732 var helpContent = content.append('div').attr('class', 'left-content');
96733 var body = helpContent.append('div').attr('class', 'body');
96734 var nav = helpContent.append('div').attr('class', 'nav');
96735 clickHelp(docs[0], 0);
96741 function uiSectionValidationIssues(id, severity, context) {
96743 var section = uiSection(id, context).label(function () {
96744 if (!_issues) return '';
96745 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
96746 return _t('inspector.title_count', {
96747 title: _t.html('issues.' + severity + 's.list_title'),
96748 count: issueCountText
96750 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
96751 return _issues && _issues.length;
96754 function getOptions() {
96756 what: corePreferences('validate-what') || 'edited',
96757 where: corePreferences('validate-where') || 'all'
96759 } // get and cache the issues to display, unordered
96762 function reloadIssues() {
96763 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
96766 function renderDisclosureContent(selection) {
96767 var center = context.map().center();
96768 var graph = context.graph(); // sort issues by distance away from the center of the map
96770 var issues = _issues.map(function withDistance(issue) {
96771 var extent = issue.extent(graph);
96772 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
96773 return Object.assign(issue, {
96776 }).sort(function byDistance(a, b) {
96777 return a.dist - b.dist;
96778 }); // cut off at 1000
96781 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
96783 selection.call(drawIssuesList, issues);
96786 function drawIssuesList(selection, issues) {
96787 var list = selection.selectAll('.issues-list').data([0]);
96788 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
96789 var items = list.selectAll('li').data(issues, function (d) {
96793 items.exit().remove(); // Enter
96795 var itemsEnter = items.enter().append('li').attr('class', function (d) {
96796 return 'issue severity-' + d.severity;
96798 var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
96799 context.validator().focusIssue(d);
96800 }).on('mouseover', function (d3_event, d) {
96801 utilHighlightEntities(d.entityIds, true, context);
96802 }).on('mouseout', function (d3_event, d) {
96803 utilHighlightEntities(d.entityIds, false, context);
96805 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
96806 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
96807 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
96808 select(this).call(svgIcon(iconName));
96810 textEnter.append('span').attr('class', 'issue-message');
96814 .attr('class', 'issue-autofix')
96815 .each(function(d) {
96816 if (!d.autoFix) return;
96819 .attr('title', t('issues.fix_one.title'))
96820 .datum(d.autoFix) // set button datum to the autofix
96821 .attr('class', 'autofix action')
96822 .on('click', function(d3_event, d) {
96823 d3_event.preventDefault();
96824 d3_event.stopPropagation();
96825 var issuesEntityIDs = d.issue.entityIds;
96826 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
96827 context.perform.apply(context, d.autoArgs);
96828 context.validator().validate();
96830 .call(svgIcon('#iD-icon-wrench'));
96835 items = items.merge(itemsEnter).order();
96836 items.selectAll('.issue-message').html(function (d) {
96837 return d.message(context);
96841 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
96842 var autoFixAll = selection.selectAll('.autofix-all')
96843 .data(canAutoFix.length ? [0] : []);
96848 var autoFixAllEnter = autoFixAll.enter()
96849 .insert('div', '.issues-list')
96850 .attr('class', 'autofix-all');
96851 var linkEnter = autoFixAllEnter
96853 .attr('class', 'autofix-all-link')
96854 .attr('href', '#');
96857 .attr('class', 'autofix-all-link-text')
96858 .html(t.html('issues.fix_all.title'));
96861 .attr('class', 'autofix-all-link-icon')
96862 .call(svgIcon('#iD-icon-wrench'));
96863 if (severity === 'warning') {
96864 renderIgnoredIssuesReset(selection);
96867 autoFixAll = autoFixAll
96868 .merge(autoFixAllEnter);
96869 autoFixAll.selectAll('.autofix-all-link')
96870 .on('click', function() {
96871 context.pauseChangeDispatch();
96872 context.perform(actionNoop());
96873 canAutoFix.forEach(function(issue) {
96874 var args = issue.autoFix.autoArgs.slice(); // copy
96875 if (typeof args[args.length - 1] !== 'function') {
96878 args.push(t('issues.fix_all.annotation'));
96879 context.replace.apply(context, args);
96881 context.resumeChangeDispatch();
96882 context.validator().validate();
96887 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
96888 window.requestIdleCallback(function () {
96890 section.reRender();
96893 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
96894 window.requestIdleCallback(function () {
96895 if (getOptions().where === 'visible') {
96896 // must refetch issues if they are viewport-dependent
96898 } // always reload list to re-sort-by-distance
96901 section.reRender();
96907 function uiSectionValidationOptions(context) {
96908 var section = uiSection('issues-options', context).content(renderContent);
96910 function renderContent(selection) {
96911 var container = selection.selectAll('.issues-options-container').data([0]);
96912 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
96915 values: ['edited', 'all']
96918 values: ['visible', 'all']
96920 var options = container.selectAll('.issues-option').data(data, function (d) {
96923 var optionsEnter = options.enter().append('div').attr('class', function (d) {
96924 return 'issues-option issues-option-' + d.key;
96926 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
96927 return _t.html('issues.options.' + d.key + '.title');
96929 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
96930 return d.values.map(function (val) {
96936 }).enter().append('label');
96937 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
96938 return 'issues-option-' + d.key;
96939 }).attr('value', function (d) {
96941 }).property('checked', function (d) {
96942 return getOptions()[d.key] === d.value;
96943 }).on('change', function (d3_event, d) {
96944 updateOptionValue(d3_event, d.key, d.value);
96946 valuesEnter.append('span').html(function (d) {
96947 return _t.html('issues.options.' + d.key + '.' + d.value);
96951 function getOptions() {
96953 what: corePreferences('validate-what') || 'edited',
96955 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
96960 function updateOptionValue(d3_event, d, val) {
96961 if (!val && d3_event && d3_event.target) {
96962 val = d3_event.target.value;
96965 corePreferences('validate-' + d, val);
96966 context.validator().validate();
96972 function uiSectionValidationRules(context) {
96974 var MAXSQUARE = 20;
96975 var DEFAULTSQUARE = 5; // see also unsquare_way.js
96977 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
96979 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
96980 return key !== 'maprules';
96981 }).sort(function (key1, key2) {
96982 // alphabetize by localized title
96983 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
96986 function renderDisclosureContent(selection) {
96987 var container = selection.selectAll('.issues-rulelist-container').data([0]);
96988 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
96989 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
96990 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
96991 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
96992 d3_event.preventDefault();
96993 context.validator().disableRules(_ruleKeys);
96995 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
96996 d3_event.preventDefault();
96997 context.validator().disableRules([]);
97000 container = container.merge(containerEnter);
97001 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
97004 function drawListItems(selection, data, type, name, change, active) {
97005 var items = selection.selectAll('li').data(data); // Exit
97007 items.exit().remove(); // Enter
97009 var enter = items.enter().append('li');
97011 if (name === 'rule') {
97012 enter.call(uiTooltip().title(function (d) {
97013 return _t.html('issues.' + d + '.tip');
97014 }).placement('top'));
97017 var label = enter.append('label');
97018 label.append('input').attr('type', type).attr('name', name).on('change', change);
97019 label.append('span').html(function (d) {
97022 if (d === 'unsquare_way') {
97023 params.val = '<span class="square-degrees"></span>';
97026 return _t.html('issues.' + d + '.title', params);
97029 items = items.merge(enter);
97030 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
97032 var degStr = corePreferences('validate-square-degrees');
97034 if (degStr === null) {
97035 degStr = DEFAULTSQUARE.toString();
97038 var span = items.selectAll('.square-degrees');
97039 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
97041 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) {
97042 d3_event.preventDefault();
97043 d3_event.stopPropagation();
97045 }).on('keyup', function (d3_event) {
97046 if (d3_event.keyCode === 13) {
97051 }).on('blur', changeSquare).merge(input).property('value', degStr);
97054 function changeSquare() {
97055 var input = select(this);
97056 var degStr = utilGetSetValue(input).trim();
97057 var degNum = parseFloat(degStr, 10);
97059 if (!isFinite(degNum)) {
97060 degNum = DEFAULTSQUARE;
97061 } else if (degNum > MAXSQUARE) {
97062 degNum = MAXSQUARE;
97063 } else if (degNum < MINSQUARE) {
97064 degNum = MINSQUARE;
97067 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
97069 degStr = degNum.toString();
97070 input.property('value', degStr);
97071 corePreferences('validate-square-degrees', degStr);
97072 context.validator().reloadUnsquareIssues();
97075 function isRuleEnabled(d) {
97076 return context.validator().isRuleEnabled(d);
97079 function toggleRule(d3_event, d) {
97080 context.validator().toggleRule(d);
97083 context.validator().on('validated.uiSectionValidationRules', function () {
97084 window.requestIdleCallback(section.reRender);
97089 function uiSectionValidationStatus(context) {
97090 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
97091 var issues = context.validator().getIssues(getOptions());
97092 return issues.length === 0;
97095 function getOptions() {
97097 what: corePreferences('validate-what') || 'edited',
97098 where: corePreferences('validate-where') || 'all'
97102 function renderContent(selection) {
97103 var box = selection.selectAll('.box').data([0]);
97104 var boxEnter = box.enter().append('div').attr('class', 'box');
97105 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
97106 var noIssuesMessage = boxEnter.append('span');
97107 noIssuesMessage.append('strong').attr('class', 'message');
97108 noIssuesMessage.append('br');
97109 noIssuesMessage.append('span').attr('class', 'details');
97110 renderIgnoredIssuesReset(selection);
97111 setNoIssuesText(selection);
97114 function renderIgnoredIssuesReset(selection) {
97115 var ignoredIssues = context.validator().getIssues({
97118 includeDisabledRules: true,
97119 includeIgnored: 'only'
97121 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
97123 resetIgnored.exit().remove(); // enter
97125 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
97126 resetIgnoredEnter.append('a').attr('href', '#'); // update
97128 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
97129 resetIgnored.select('a').html(_t('inspector.title_count', {
97130 title: _t.html('issues.reset_ignored'),
97131 count: ignoredIssues.length
97133 resetIgnored.on('click', function (d3_event) {
97134 d3_event.preventDefault();
97135 context.validator().resetIgnoredIssues();
97139 function setNoIssuesText(selection) {
97140 var opts = getOptions();
97142 function checkForHiddenIssues(cases) {
97143 for (var type in cases) {
97144 var hiddenOpts = cases[type];
97145 var hiddenIssues = context.validator().getIssues(hiddenOpts);
97147 if (hiddenIssues.length) {
97148 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
97149 count: hiddenIssues.length.toString()
97155 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
97160 if (opts.what === 'edited' && opts.where === 'visible') {
97161 messageType = 'edits_in_view';
97162 checkForHiddenIssues({
97174 includeDisabledRules: 'only'
97176 everything_else_elsewhere: {
97180 disabled_rules_elsewhere: {
97183 includeDisabledRules: 'only'
97188 includeIgnored: 'only'
97190 ignored_issues_elsewhere: {
97193 includeIgnored: 'only'
97196 } else if (opts.what === 'edited' && opts.where === 'all') {
97197 messageType = 'edits';
97198 checkForHiddenIssues({
97206 includeDisabledRules: 'only'
97211 includeIgnored: 'only'
97214 } else if (opts.what === 'all' && opts.where === 'visible') {
97215 messageType = 'everything_in_view';
97216 checkForHiddenIssues({
97224 includeDisabledRules: 'only'
97226 disabled_rules_elsewhere: {
97229 includeDisabledRules: 'only'
97234 includeIgnored: 'only'
97236 ignored_issues_elsewhere: {
97239 includeIgnored: 'only'
97242 } else if (opts.what === 'all' && opts.where === 'all') {
97243 messageType = 'everything';
97244 checkForHiddenIssues({
97248 includeDisabledRules: 'only'
97253 includeIgnored: 'only'
97258 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
97259 messageType = 'no_edits';
97262 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
97265 context.validator().on('validated.uiSectionValidationStatus', function () {
97266 window.requestIdleCallback(section.reRender);
97268 context.map().on('move.uiSectionValidationStatus', debounce(function () {
97269 window.requestIdleCallback(section.reRender);
97274 function uiPaneIssues(context) {
97275 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)]);
97279 function uiSettingsCustomData(context) {
97280 var dispatch$1 = dispatch('change');
97282 function render(selection) {
97283 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
97285 var _origSettings = {
97286 fileList: dataLayer && dataLayer.fileList() || null,
97287 url: corePreferences('settings-custom-data-url')
97289 var _currSettings = {
97290 fileList: dataLayer && dataLayer.fileList() || null,
97291 url: corePreferences('settings-custom-data-url')
97292 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
97294 var modal = uiConfirm(selection).okButton();
97295 modal.classed('settings-modal settings-custom-data', true);
97296 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
97297 var textSection = modal.select('.modal-section.message-text');
97298 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
97299 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
97300 .on('change', function (d3_event) {
97301 var files = d3_event.target.files;
97303 if (files && files.length) {
97304 _currSettings.url = '';
97305 textSection.select('.field-url').property('value', '');
97306 _currSettings.fileList = files;
97308 _currSettings.fileList = null;
97311 textSection.append('h4').html(_t.html('settings.custom_data.or'));
97312 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
97313 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
97315 var buttonSection = modal.select('.modal-section.buttons');
97316 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
97317 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
97318 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
97320 function isSaveDisabled() {
97322 } // restore the original url
97325 function clickCancel() {
97326 textSection.select('.field-url').property('value', _origSettings.url);
97327 corePreferences('settings-custom-data-url', _origSettings.url);
97330 } // accept the current url
97333 function clickSave() {
97334 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
97336 if (_currSettings.url) {
97337 _currSettings.fileList = null;
97340 if (_currSettings.fileList) {
97341 _currSettings.url = '';
97344 corePreferences('settings-custom-data-url', _currSettings.url);
97347 dispatch$1.call('change', this, _currSettings);
97351 return utilRebind(render, dispatch$1, 'on');
97354 function uiSectionDataLayers(context) {
97355 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
97356 var layers = context.layers();
97357 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
97359 function renderDisclosureContent(selection) {
97360 var container = selection.selectAll('.data-layer-container').data([0]);
97361 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
97362 .call(drawPanelItems);
97365 function showsLayer(which) {
97366 var layer = layers.layer(which);
97369 return layer.enabled();
97375 function setLayer(which, enabled) {
97376 // Don't allow layer changes while drawing - #6584
97377 var mode = context.mode();
97378 if (mode && /^draw/.test(mode.id)) return;
97379 var layer = layers.layer(which);
97382 layer.enabled(enabled);
97384 if (!enabled && (which === 'osm' || which === 'notes')) {
97385 context.enter(modeBrowse(context));
97390 function toggleLayer(which) {
97391 setLayer(which, !showsLayer(which));
97394 function drawOsmItems(selection) {
97395 var osmKeys = ['osm', 'notes'];
97396 var osmLayers = layers.all().filter(function (obj) {
97397 return osmKeys.indexOf(obj.id) !== -1;
97399 var ul = selection.selectAll('.layer-list-osm').data([0]);
97400 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
97401 var li = ul.selectAll('.list-item').data(osmLayers);
97402 li.exit().remove();
97403 var liEnter = li.enter().append('li').attr('class', function (d) {
97404 return 'list-item list-item-' + d.id;
97406 var labelEnter = liEnter.append('label').each(function (d) {
97407 if (d.id === 'osm') {
97408 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
97410 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97413 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97416 labelEnter.append('span').html(function (d) {
97417 return _t.html('map_data.layers.' + d.id + '.title');
97420 li.merge(liEnter).classed('active', function (d) {
97421 return d.layer.enabled();
97422 }).selectAll('input').property('checked', function (d) {
97423 return d.layer.enabled();
97427 function drawQAItems(selection) {
97428 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
97429 var qaLayers = layers.all().filter(function (obj) {
97430 return qaKeys.indexOf(obj.id) !== -1;
97432 var ul = selection.selectAll('.layer-list-qa').data([0]);
97433 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
97434 var li = ul.selectAll('.list-item').data(qaLayers);
97435 li.exit().remove();
97436 var liEnter = li.enter().append('li').attr('class', function (d) {
97437 return 'list-item list-item-' + d.id;
97439 var labelEnter = liEnter.append('label').each(function (d) {
97440 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97442 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97445 labelEnter.append('span').html(function (d) {
97446 return _t.html('map_data.layers.' + d.id + '.title');
97449 li.merge(liEnter).classed('active', function (d) {
97450 return d.layer.enabled();
97451 }).selectAll('input').property('checked', function (d) {
97452 return d.layer.enabled();
97454 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
97455 // https://github.com/osmus/detroit-mapping-challenge
97458 function drawVectorItems(selection) {
97459 var dataLayer = layers.layer('data');
97461 name: 'Detroit Neighborhoods/Parks',
97462 src: 'neighborhoods-parks',
97463 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
97464 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'
97466 name: 'Detroit Composite POIs',
97467 src: 'composite-poi',
97468 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
97469 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'
97471 name: 'Detroit All-The-Places POIs',
97472 src: 'alltheplaces-poi',
97473 tooltip: 'Public domain business location data created by web scrapers.',
97474 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'
97475 }]; // Only show this if the map is around Detroit..
97477 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
97478 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
97479 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
97480 container.exit().remove();
97481 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
97482 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
97483 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
97484 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');
97485 container = container.merge(containerEnter);
97486 var ul = container.selectAll('.layer-list-vectortile');
97487 var li = ul.selectAll('.list-item').data(vtData);
97488 li.exit().remove();
97489 var liEnter = li.enter().append('li').attr('class', function (d) {
97490 return 'list-item list-item-' + d.src;
97492 var labelEnter = liEnter.append('label').each(function (d) {
97493 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
97495 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
97496 labelEnter.append('span').html(function (d) {
97500 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
97502 function isVTLayerSelected(d) {
97503 return dataLayer && dataLayer.template() === d.template;
97506 function selectVTLayer(d3_event, d) {
97507 corePreferences('settings-custom-data-url', d.template);
97510 dataLayer.template(d.template, d.src);
97511 dataLayer.enabled(true);
97516 function drawCustomDataItems(selection) {
97517 var dataLayer = layers.layer('data');
97518 var hasData = dataLayer && dataLayer.hasData();
97519 var showsData = hasData && dataLayer.enabled();
97520 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
97522 ul.exit().remove(); // Enter
97524 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
97525 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
97526 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
97527 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
97528 toggleLayer('data');
97530 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
97531 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) {
97532 d3_event.preventDefault();
97534 }).call(svgIcon('#iD-icon-more'));
97535 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) {
97536 if (select(this).classed('disabled')) return;
97537 d3_event.preventDefault();
97538 d3_event.stopPropagation();
97539 dataLayer.fitZoom();
97540 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
97542 ul = ul.merge(ulEnter);
97543 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
97544 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
97547 function editCustom() {
97548 context.container().call(settingsCustomData);
97551 function customChanged(d) {
97552 var dataLayer = layers.layer('data');
97555 dataLayer.url(d.url);
97556 } else if (d && d.fileList) {
97557 dataLayer.fileList(d.fileList);
97561 function drawPanelItems(selection) {
97562 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
97563 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'));
97564 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97565 d3_event.preventDefault();
97566 context.ui().info.toggle('history');
97568 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
97569 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'));
97570 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97571 d3_event.preventDefault();
97572 context.ui().info.toggle('measurement');
97574 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
97577 context.layers().on('change.uiSectionDataLayers', section.reRender);
97578 context.map().on('move.uiSectionDataLayers', debounce(function () {
97579 // Detroit layers may have moved in or out of view
97580 window.requestIdleCallback(section.reRender);
97585 function uiSectionMapFeatures(context) {
97586 var _features = context.features().keys();
97588 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97590 function renderDisclosureContent(selection) {
97591 var container = selection.selectAll('.layer-feature-list-container').data([0]);
97592 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
97593 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
97594 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
97595 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
97596 d3_event.preventDefault();
97597 context.features().disableAll();
97599 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
97600 d3_event.preventDefault();
97601 context.features().enableAll();
97604 container = container.merge(containerEnter);
97605 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
97608 function drawListItems(selection, data, type, name, change, active) {
97609 var items = selection.selectAll('li').data(data); // Exit
97611 items.exit().remove(); // Enter
97613 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97614 var tip = _t.html(name + '.' + d + '.tooltip');
97616 if (autoHiddenFeature(d)) {
97617 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
97618 tip += '<div>' + msg + '</div>';
97622 }).placement('top'));
97623 var label = enter.append('label');
97624 label.append('input').attr('type', type).attr('name', name).on('change', change);
97625 label.append('span').html(function (d) {
97626 return _t.html(name + '.' + d + '.description');
97629 items = items.merge(enter);
97630 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
97633 function autoHiddenFeature(d) {
97634 return context.features().autoHidden(d);
97637 function showsFeature(d) {
97638 return context.features().enabled(d);
97641 function clickFeature(d3_event, d) {
97642 context.features().toggle(d);
97645 function showsLayer(id) {
97646 var layer = context.layers().layer(id);
97647 return layer && layer.enabled();
97651 context.features().on('change.map_features', section.reRender);
97655 function uiSectionMapStyleOptions(context) {
97656 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97658 function renderDisclosureContent(selection) {
97659 var container = selection.selectAll('.layer-fill-list').data([0]);
97660 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
97661 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
97662 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
97663 return context.surface().classed('highlight-edited');
97667 function drawListItems(selection, data, type, name, change, active) {
97668 var items = selection.selectAll('li').data(data); // Exit
97670 items.exit().remove(); // Enter
97672 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97673 return _t.html(name + '.' + d + '.tooltip');
97674 }).keys(function (d) {
97675 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
97676 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
97677 return key ? [key] : null;
97678 }).placement('top'));
97679 var label = enter.append('label');
97680 label.append('input').attr('type', type).attr('name', name).on('change', change);
97681 label.append('span').html(function (d) {
97682 return _t.html(name + '.' + d + '.description');
97685 items = items.merge(enter);
97686 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
97689 function isActiveFill(d) {
97690 return context.map().activeAreaFill() === d;
97693 function toggleHighlightEdited(d3_event) {
97694 d3_event.preventDefault();
97695 context.map().toggleHighlightEdited();
97698 function setFill(d3_event, d) {
97699 context.map().activeAreaFill(d);
97702 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
97706 function uiSectionPhotoOverlays(context) {
97707 var layers = context.layers();
97708 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97710 function renderDisclosureContent(selection) {
97711 var container = selection.selectAll('.photo-overlay-container').data([0]);
97712 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
97715 function drawPhotoItems(selection) {
97716 var photoKeys = context.photos().overlayLayerIDs();
97717 var photoLayers = layers.all().filter(function (obj) {
97718 return photoKeys.indexOf(obj.id) !== -1;
97720 var data = photoLayers.filter(function (obj) {
97721 return obj.layer.supported();
97724 function layerSupported(d) {
97725 return d.layer && d.layer.supported();
97728 function layerEnabled(d) {
97729 return layerSupported(d) && d.layer.enabled();
97732 var ul = selection.selectAll('.layer-list-photos').data([0]);
97733 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
97734 var li = ul.selectAll('.list-item-photos').data(data);
97735 li.exit().remove();
97736 var liEnter = li.enter().append('li').attr('class', function (d) {
97737 var classes = 'list-item-photos list-item-' + d.id;
97739 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
97740 classes += ' indented';
97745 var labelEnter = liEnter.append('label').each(function (d) {
97747 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';
97748 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
97750 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97753 labelEnter.append('span').html(function (d) {
97755 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
97756 return _t.html(id.replace(/-/g, '_') + '.title');
97759 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
97762 function drawPhotoTypeItems(selection) {
97763 var data = context.photos().allPhotoTypes();
97765 function typeEnabled(d) {
97766 return context.photos().showsPhotoType(d);
97769 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
97770 ul.exit().remove();
97771 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
97772 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
97773 li.exit().remove();
97774 var liEnter = li.enter().append('li').attr('class', function (d) {
97775 return 'list-item-photo-types list-item-' + d;
97777 var labelEnter = liEnter.append('label').each(function (d) {
97778 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
97780 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97781 context.photos().togglePhotoType(d);
97783 labelEnter.append('span').html(function (d) {
97784 return _t.html('photo_overlays.photo_type.' + d + '.title');
97787 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
97790 function drawDateFilter(selection) {
97791 var data = context.photos().dateFilters();
97793 function filterEnabled(d) {
97794 return context.photos().dateFilterValue(d);
97797 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
97798 ul.exit().remove();
97799 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
97800 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
97801 li.exit().remove();
97802 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
97803 var labelEnter = liEnter.append('label').each(function (d) {
97804 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
97806 labelEnter.append('span').html(function (d) {
97807 return _t.html('photo_overlays.date_filter.' + d + '.title');
97809 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
97810 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97811 }).on('change', function (d3_event, d) {
97812 var value = utilGetSetValue(select(this)).trim();
97813 context.photos().setDateFilter(d, value, true); // reload the displayed dates
97815 li.selectAll('input').each(function (d) {
97816 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97819 li = li.merge(liEnter).classed('active', filterEnabled);
97822 function drawUsernameFilter(selection) {
97823 function filterEnabled() {
97824 return context.photos().usernames();
97827 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
97828 ul.exit().remove();
97829 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
97830 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
97831 li.exit().remove();
97832 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
97833 var labelEnter = liEnter.append('label').each(function () {
97834 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
97836 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
97837 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
97838 var value = select(this).property('value');
97839 context.photos().setUsernameFilter(value, true);
97840 select(this).property('value', usernameValue);
97842 li.merge(liEnter).classed('active', filterEnabled);
97844 function usernameValue() {
97845 var usernames = context.photos().usernames();
97846 if (usernames) return usernames.join('; ');
97851 function toggleLayer(which) {
97852 setLayer(which, !showsLayer(which));
97855 function showsLayer(which) {
97856 var layer = layers.layer(which);
97859 return layer.enabled();
97865 function setLayer(which, enabled) {
97866 var layer = layers.layer(which);
97869 layer.enabled(enabled);
97873 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
97874 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
97878 function uiPaneMapData(context) {
97879 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)]);
97880 return mapDataPane;
97883 function uiSectionPrivacy(context) {
97884 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
97886 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
97888 function renderDisclosureContent(selection) {
97890 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
97891 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'));
97892 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97893 d3_event.preventDefault();
97894 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
97895 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
97898 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
97900 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'));
97903 function update() {
97904 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
97911 function uiPanePreferences(context) {
97912 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)]);
97913 return preferencesPane;
97916 function uiInit(context) {
97917 var _initCounter = 0;
97918 var _needWidth = {};
97920 var _lastPointerType;
97922 function render(container) {
97923 container.on('click.ui', function (d3_event) {
97924 // we're only concerned with the primary mouse button
97925 if (d3_event.button !== 0) return;
97926 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
97928 var isOkayTarget = d3_event.composedPath().some(function (node) {
97929 // we only care about element nodes
97930 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
97931 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
97932 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
97933 node.nodeName === 'A');
97935 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
97937 d3_event.preventDefault();
97939 var detected = utilDetect(); // only WebKit supports gesture events
97941 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
97942 // but we only need to do this on desktop Safari anyway. – #7694
97943 !detected.isMobileWebKit) {
97944 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
97945 // CSS property, but on desktop Safari we need to manually cancel the
97946 // default gesture events.
97947 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
97948 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
97949 d3_event.preventDefault();
97953 if ('PointerEvent' in window) {
97954 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
97955 var pointerType = d3_event.pointerType || 'mouse';
97957 if (_lastPointerType !== pointerType) {
97958 _lastPointerType = pointerType;
97959 container.attr('pointer', pointerType);
97963 _lastPointerType = 'mouse';
97964 container.attr('pointer', 'mouse');
97967 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
97969 container.call(uiFullScreen(context));
97970 var map = context.map();
97971 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
97973 map.on('hitMinZoom.ui', function () {
97974 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
97976 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
97977 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
97978 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
97980 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
97981 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
97982 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
97983 // pressing, even if it's not targeted. This conflicts with long-pressing
97984 // to show the edit menu. We add a selectable offscreen element as the first
97985 // child to trick Safari into not showing the selection UI.
97987 overMap.append('div').attr('class', 'select-trap').text('t');
97988 overMap.call(uiMapInMap(context)).call(uiNotice(context));
97989 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
97991 var controls = overMap.append('div').attr('class', 'map-controls');
97992 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
97993 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
97994 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
97995 // This should happen after map is initialized, as some require surface()
97997 var panes = overMap.append('div').attr('class', 'map-panes');
97998 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
97999 uiPanes.forEach(function (pane) {
98000 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
98001 panes.call(pane.renderPane);
98003 ui.info = uiInfo(context);
98004 overMap.call(ui.info);
98005 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
98006 .classed('hide', true).call(ui.photoviewer);
98007 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
98009 var about = content.append('div').attr('class', 'map-footer');
98010 about.append('div').attr('class', 'api-status').call(uiStatus(context));
98011 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
98012 footer.append('div').attr('class', 'flash-wrap footer-hide');
98013 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
98014 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
98015 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
98016 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
98017 var apiConnections = context.apiConnections();
98019 if (apiConnections && apiConnections.length > 1) {
98020 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
98023 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
98024 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
98025 var issueLinks = aboutList.append('li');
98026 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'));
98027 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'));
98028 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
98030 if (!context.embed()) {
98031 aboutList.call(uiAccount(context));
98032 } // Setup map dimensions and move map to initial center/zoom.
98033 // This should happen after .main-content and toolbars exist.
98037 map.redrawEnable(true);
98038 ui.hash = behaviorHash(context);
98041 if (!ui.hash.hadHash) {
98042 map.centerZoom([0, 0], 2);
98046 window.onbeforeunload = function () {
98047 return context.save();
98050 window.onunload = function () {
98051 context.history().unlock();
98054 select(window).on('resize.editor', function () {
98057 var panPixels = 80;
98058 context.keybinding().on('⌫', function (d3_event) {
98059 d3_event.preventDefault();
98060 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
98061 .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) {
98063 d3_event.stopImmediatePropagation();
98064 d3_event.preventDefault();
98067 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
98069 if (previousBackground) {
98070 var currentBackground = context.background().baseLayerSource();
98071 corePreferences('background-last-used-toggle', currentBackground.id);
98072 corePreferences('background-last-used', previousBackground.id);
98073 context.background().baseLayerSource(previousBackground);
98075 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
98076 d3_event.preventDefault();
98077 d3_event.stopPropagation();
98078 context.map().toggleWireframe();
98079 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
98080 d3_event.preventDefault();
98081 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
98083 var mode = context.mode();
98084 if (mode && /^draw/.test(mode.id)) return;
98085 var layer = context.layers().layer('osm');
98088 layer.enabled(!layer.enabled());
98090 if (!layer.enabled()) {
98091 context.enter(modeBrowse(context));
98094 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
98095 d3_event.preventDefault();
98096 context.map().toggleHighlightEdited();
98098 context.on('enter.editor', function (entered) {
98099 container.classed('mode-' + entered.id, true);
98100 }).on('exit.editor', function (exited) {
98101 container.classed('mode-' + exited.id, false);
98103 context.enter(modeBrowse(context));
98105 if (!_initCounter++) {
98106 if (!ui.hash.startWalkthrough) {
98107 context.container().call(uiSplash(context)).call(uiRestore(context));
98110 context.container().call(ui.shortcuts);
98113 var osm = context.connection();
98114 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
98117 osm.on('authLoading.ui', function () {
98118 context.container().call(auth);
98119 }).on('authDone.ui', function () {
98126 if (ui.hash.startWalkthrough) {
98127 ui.hash.startWalkthrough = false;
98128 context.container().call(uiIntro(context));
98132 return function (d3_event) {
98133 if (d3_event.shiftKey) return;
98134 if (context.container().select('.combobox').size()) return;
98135 d3_event.preventDefault();
98136 context.map().pan(d, 100);
98143 var _loadPromise; // renders the iD interface into the container node
98146 ui.ensureLoaded = function () {
98147 if (_loadPromise) return _loadPromise;
98148 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
98149 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
98150 if (!context.container().empty()) render(context.container());
98151 })["catch"](function (err) {
98152 return console.error(err);
98153 }); // eslint-disable-line
98154 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
98155 // for example to switch the locale while iD is running.
98158 ui.restart = function () {
98159 context.keybinding().clear();
98160 _loadPromise = null;
98161 context.container().selectAll('*').remove();
98165 ui.lastPointerType = function () {
98166 return _lastPointerType;
98169 ui.svgDefs = svgDefs(context);
98170 ui.flash = uiFlash(context);
98171 ui.sidebar = uiSidebar(context);
98172 ui.photoviewer = uiPhotoviewer(context);
98173 ui.shortcuts = uiShortcuts(context);
98175 ui.onResize = function (withPan) {
98176 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
98177 // This will call `getBoundingClientRect` and trigger reflow,
98178 // but the values will be cached for later use.
98180 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
98181 utilGetDimensions(context.container().select('.sidebar'), true);
98183 if (withPan !== undefined) {
98184 map.redrawEnable(false);
98186 map.redrawEnable(true);
98189 map.dimensions(mapDimensions);
98190 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
98192 ui.checkOverflow('.top-toolbar');
98193 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
98195 var resizeWindowEvent = document.createEvent('Event');
98196 resizeWindowEvent.initEvent('resizeWindow', true, true);
98197 document.dispatchEvent(resizeWindowEvent);
98198 }; // Call checkOverflow when resizing or whenever the contents change.
98201 ui.checkOverflow = function (selector, reset) {
98203 delete _needWidth[selector];
98206 var selection = context.container().select(selector);
98207 if (selection.empty()) return;
98208 var scrollWidth = selection.property('scrollWidth');
98209 var clientWidth = selection.property('clientWidth');
98210 var needed = _needWidth[selector] || scrollWidth;
98212 if (scrollWidth > clientWidth) {
98213 // overflow happening
98214 selection.classed('narrow', true);
98216 if (!_needWidth[selector]) {
98217 _needWidth[selector] = scrollWidth;
98219 } else if (scrollWidth >= needed) {
98220 selection.classed('narrow', false);
98224 ui.togglePanes = function (showPane) {
98225 var hidePanes = context.container().selectAll('.map-pane.shown');
98226 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
98227 hidePanes.classed('shown', false).classed('hide', true);
98228 context.container().selectAll('.map-pane-control button').classed('active', false);
98231 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
98232 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
98233 showPane.classed('shown', true).classed('hide', false);
98235 if (hidePanes.empty()) {
98236 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
98238 showPane.style(side, '0px');
98241 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
98242 select(this).classed('shown', false).classed('hide', true);
98247 var _editMenu = uiEditMenu(context);
98249 ui.editMenu = function () {
98253 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
98254 // remove any displayed menu
98255 ui.closeEditMenu();
98256 if (!operations && context.mode().operations) operations = context.mode().operations();
98257 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
98259 if (!context.map().editableDataEnabled()) return;
98260 var surfaceNode = context.surface().node();
98262 if (surfaceNode.focus) {
98263 // FF doesn't support it
98264 // focus the surface or else clicking off the menu may not trigger modeBrowse
98265 surfaceNode.focus();
98268 operations.forEach(function (operation) {
98269 if (operation.point) operation.point(anchorPoint);
98272 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
98275 context.map().supersurface.call(_editMenu);
98278 ui.closeEditMenu = function () {
98279 // remove any existing menu no matter how it was added
98280 context.map().supersurface.select('.edit-menu').remove();
98283 var _saveLoading = select(null);
98285 context.uploader().on('saveStarted.ui', function () {
98286 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
98287 context.container().call(_saveLoading); // block input during upload
98288 }).on('saveEnded.ui', function () {
98289 _saveLoading.close();
98291 _saveLoading = select(null);
98296 function coreContext() {
98299 var dispatch$1 = dispatch('enter', 'exit', 'change');
98300 var context = utilRebind({}, dispatch$1, 'on');
98302 var _deferred = new Set();
98304 context.version = '2.19.5';
98305 context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
98307 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
98308 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
98310 // An osmChangeset object. Not loaded until needed.
98312 context.changeset = null;
98313 var _defaultChangesetComment = context.initialHashParams.comment;
98314 var _defaultChangesetSource = context.initialHashParams.source;
98315 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
98317 context.defaultChangesetComment = function (val) {
98318 if (!arguments.length) return _defaultChangesetComment;
98319 _defaultChangesetComment = val;
98323 context.defaultChangesetSource = function (val) {
98324 if (!arguments.length) return _defaultChangesetSource;
98325 _defaultChangesetSource = val;
98329 context.defaultChangesetHashtags = function (val) {
98330 if (!arguments.length) return _defaultChangesetHashtags;
98331 _defaultChangesetHashtags = val;
98334 /* Document title */
98336 /* (typically shown as the label for the browser window/tab) */
98337 // If true, iD will update the title based on what the user is doing
98340 var _setsDocumentTitle = true;
98342 context.setsDocumentTitle = function (val) {
98343 if (!arguments.length) return _setsDocumentTitle;
98344 _setsDocumentTitle = val;
98346 }; // The part of the title that is always the same
98349 var _documentTitleBase = document.title;
98351 context.documentTitleBase = function (val) {
98352 if (!arguments.length) return _documentTitleBase;
98353 _documentTitleBase = val;
98356 /* User interface and keybinding */
98361 context.ui = function () {
98365 context.lastPointerType = function () {
98366 return _ui.lastPointerType();
98369 var _keybinding = utilKeybinding('context');
98371 context.keybinding = function () {
98372 return _keybinding;
98375 select(document).call(_keybinding);
98376 /* Straight accessors. Avoid using these if you can. */
98377 // Instantiate the connection here because it doesn't require passing in
98378 // `context` and it's needed for pre-init calls like `preauth`
98380 var _connection = services.osm;
98388 context.connection = function () {
98389 return _connection;
98392 context.history = function () {
98396 context.validator = function () {
98400 context.uploader = function () {
98406 context.preauth = function (options) {
98408 _connection["switch"](options);
98413 /* connection options for source switcher (optional) */
98416 var _apiConnections;
98418 context.apiConnections = function (val) {
98419 if (!arguments.length) return _apiConnections;
98420 _apiConnections = val;
98422 }; // A string or array or locale codes to prefer over the browser's settings
98425 context.locale = function (locale) {
98426 if (!arguments.length) return _mainLocalizer.localeCode();
98427 _mainLocalizer.preferredLocaleCodes(locale);
98431 function afterLoad(cid, callback) {
98432 return function (err, result) {
98434 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
98435 if (err.status === 400 || err.status === 401 || err.status === 403) {
98437 _connection.logout();
98441 if (typeof callback === 'function') {
98446 } else if (_connection && _connection.getConnectionId() !== cid) {
98447 if (typeof callback === 'function') {
98449 message: 'Connection Switched',
98456 _history.merge(result.data, result.extent);
98458 if (typeof callback === 'function') {
98459 callback(err, result);
98467 context.loadTiles = function (projection, callback) {
98468 var handle = window.requestIdleCallback(function () {
98469 _deferred["delete"](handle);
98471 if (_connection && context.editableDataEnabled()) {
98472 var cid = _connection.getConnectionId();
98474 _connection.loadTiles(projection, afterLoad(cid, callback));
98478 _deferred.add(handle);
98481 context.loadTileAtLoc = function (loc, callback) {
98482 var handle = window.requestIdleCallback(function () {
98483 _deferred["delete"](handle);
98485 if (_connection && context.editableDataEnabled()) {
98486 var cid = _connection.getConnectionId();
98488 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
98492 _deferred.add(handle);
98495 context.loadEntity = function (entityID, callback) {
98497 var cid = _connection.getConnectionId();
98499 _connection.loadEntity(entityID, afterLoad(cid, callback));
98503 context.zoomToEntity = function (entityID, zoomTo) {
98504 // be sure to load the entity even if we're not going to zoom to it
98505 context.loadEntity(entityID, function (err, result) {
98508 if (zoomTo !== false) {
98509 var entity = result.data.find(function (e) {
98510 return e.id === entityID;
98514 _map.zoomTo(entity);
98519 _map.on('drawn.zoomToEntity', function () {
98520 if (!context.hasEntity(entityID)) return;
98522 _map.on('drawn.zoomToEntity', null);
98524 context.on('enter.zoomToEntity', null);
98525 context.enter(modeSelect(context, [entityID]));
98528 context.on('enter.zoomToEntity', function () {
98529 if (_mode.id !== 'browse') {
98530 _map.on('drawn.zoomToEntity', null);
98532 context.on('enter.zoomToEntity', null);
98537 var _minEditableZoom = 16;
98539 context.minEditableZoom = function (val) {
98540 if (!arguments.length) return _minEditableZoom;
98541 _minEditableZoom = val;
98544 _connection.tileZoom(val);
98548 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
98551 context.maxCharsForTagKey = function () {
98555 context.maxCharsForTagValue = function () {
98559 context.maxCharsForRelationRole = function () {
98563 function cleanOsmString(val, maxChars) {
98564 // be lenient with input
98565 if (val === undefined || val === null) {
98568 val = val.toString();
98569 } // remove whitespace
98572 val = val.trim(); // use the canonical form of the string
98574 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
98576 return utilUnicodeCharsTruncated(val, maxChars);
98579 context.cleanTagKey = function (val) {
98580 return cleanOsmString(val, context.maxCharsForTagKey());
98583 context.cleanTagValue = function (val) {
98584 return cleanOsmString(val, context.maxCharsForTagValue());
98587 context.cleanRelationRole = function (val) {
98588 return cleanOsmString(val, context.maxCharsForRelationRole());
98593 var _inIntro = false;
98595 context.inIntro = function (val) {
98596 if (!arguments.length) return _inIntro;
98599 }; // Immediately save the user's history to localstorage, if possible
98600 // This is called someteimes, but also on the `window.onbeforeunload` handler
98603 context.save = function () {
98604 // no history save, no message onbeforeunload
98605 if (_inIntro || context.container().select('.modal').size()) return;
98608 if (_mode && _mode.id === 'save') {
98609 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
98611 if (services.osm && services.osm.isChangesetInflight()) {
98612 _history.clearSaved();
98617 canSave = context.selectedIDs().every(function (id) {
98618 var entity = context.hasEntity(id);
98619 return entity && !entity.isDegenerate();
98627 if (_history.hasChanges()) {
98628 return _t('save.unsaved_changes');
98630 }; // Debounce save, since it's a synchronous localStorage write,
98631 // and history changes can happen frequently (e.g. when dragging).
98634 context.debouncedSave = debounce(context.save, 350);
98636 function withDebouncedSave(fn) {
98637 return function () {
98638 var result = fn.apply(_history, arguments);
98639 context.debouncedSave();
98646 context.hasEntity = function (id) {
98647 return _history.graph().hasEntity(id);
98650 context.entity = function (id) {
98651 return _history.graph().entity(id);
98658 context.mode = function () {
98662 context.enter = function (newMode) {
98666 dispatch$1.call('exit', _this, _mode);
98673 dispatch$1.call('enter', _this, _mode);
98676 context.selectedIDs = function () {
98677 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
98680 context.activeID = function () {
98681 return _mode && _mode.activeID && _mode.activeID();
98684 var _selectedNoteID;
98686 context.selectedNoteID = function (noteID) {
98687 if (!arguments.length) return _selectedNoteID;
98688 _selectedNoteID = noteID;
98690 }; // NOTE: Don't change the name of this until UI v3 is merged
98693 var _selectedErrorID;
98695 context.selectedErrorID = function (errorID) {
98696 if (!arguments.length) return _selectedErrorID;
98697 _selectedErrorID = errorID;
98703 context.install = function (behavior) {
98704 return context.surface().call(behavior);
98707 context.uninstall = function (behavior) {
98708 return context.surface().call(behavior.off);
98715 context.copyGraph = function () {
98721 context.copyIDs = function (val) {
98722 if (!arguments.length) return _copyIDs;
98724 _copyGraph = _history.graph();
98730 context.copyLonLat = function (val) {
98731 if (!arguments.length) return _copyLonLat;
98740 context.background = function () {
98741 return _background;
98748 context.features = function () {
98752 context.hasHiddenConnections = function (id) {
98753 var graph = _history.graph();
98755 var entity = graph.entity(id);
98756 return _features.hasHiddenConnections(entity, graph);
98763 context.photos = function () {
98771 context.map = function () {
98775 context.layers = function () {
98776 return _map.layers();
98779 context.surface = function () {
98780 return _map.surface;
98783 context.editableDataEnabled = function () {
98784 return _map.editableDataEnabled();
98787 context.surfaceRect = function () {
98788 return _map.surface.node().getBoundingClientRect();
98791 context.editable = function () {
98792 // don't allow editing during save
98793 var mode = context.mode();
98794 if (!mode || mode.id === 'save') return false;
98795 return _map.editableDataEnabled();
98800 var _debugFlags = {
98804 // label collision bounding boxes
98806 // imagery bounding polygons
98809 downloaded: false // downloaded data from osm
98813 context.debugFlags = function () {
98814 return _debugFlags;
98817 context.getDebug = function (flag) {
98818 return flag && _debugFlags[flag];
98821 context.setDebug = function (flag, val) {
98822 if (arguments.length === 1) val = true;
98823 _debugFlags[flag] = val;
98824 dispatch$1.call('change');
98830 var _container = select(null);
98832 context.container = function (val) {
98833 if (!arguments.length) return _container;
98836 _container.classed('ideditor', true);
98841 context.containerNode = function (val) {
98842 if (!arguments.length) return context.container().node();
98843 context.container(select(val));
98849 context.embed = function (val) {
98850 if (!arguments.length) return _embed;
98857 var _assetPath = '';
98859 context.assetPath = function (val) {
98860 if (!arguments.length) return _assetPath;
98862 _mainFileFetcher.assetPath(val);
98866 var _assetMap = {};
98868 context.assetMap = function (val) {
98869 if (!arguments.length) return _assetMap;
98871 _mainFileFetcher.assetMap(val);
98875 context.asset = function (val) {
98876 if (/^http(s)?:\/\//i.test(val)) return val;
98877 var filename = _assetPath + val;
98878 return _assetMap[filename] || filename;
98881 context.imagePath = function (val) {
98882 return context.asset("img/".concat(val));
98884 /* reset (aka flush) */
98887 context.reset = context.flush = function () {
98888 context.debouncedSave.cancel();
98889 Array.from(_deferred).forEach(function (handle) {
98890 window.cancelIdleCallback(handle);
98892 _deferred["delete"](handle);
98894 Object.values(services).forEach(function (service) {
98895 if (service && typeof service.reset === 'function') {
98896 service.reset(context);
98899 context.changeset = null;
98901 _validator.reset();
98907 _uploader.reset(); // don't leave stale state in the inspector
98910 context.container().select('.inspector-wrap *').remove();
98916 context.projection = geoRawMercator();
98917 context.curtainProjection = geoRawMercator();
98920 context.init = function () {
98921 instantiateInternal();
98922 initializeDependents();
98923 return context; // Load variables and properties. No property of `context` should be accessed
98924 // until this is complete since load statuses are indeterminate. The order
98925 // of instantiation shouldn't matter.
98927 function instantiateInternal() {
98928 _history = coreHistory(context);
98929 context.graph = _history.graph;
98930 context.pauseChangeDispatch = _history.pauseChangeDispatch;
98931 context.resumeChangeDispatch = _history.resumeChangeDispatch;
98932 context.perform = withDebouncedSave(_history.perform);
98933 context.replace = withDebouncedSave(_history.replace);
98934 context.pop = withDebouncedSave(_history.pop);
98935 context.overwrite = withDebouncedSave(_history.overwrite);
98936 context.undo = withDebouncedSave(_history.undo);
98937 context.redo = withDebouncedSave(_history.redo);
98938 _validator = coreValidator(context);
98939 _uploader = coreUploader(context);
98940 _background = rendererBackground(context);
98941 _features = rendererFeatures(context);
98942 _map = rendererMap(context);
98943 _photos = rendererPhotos(context);
98944 _ui = uiInit(context);
98945 } // Set up objects that might need to access properties of `context`. The order
98946 // might matter if dependents make calls to each other. Be wary of async calls.
98949 function initializeDependents() {
98950 if (context.initialHashParams.presets) {
98951 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
98954 if (context.initialHashParams.locale) {
98955 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
98956 } // kick off some async work
98959 _mainLocalizer.ensureLoaded();
98961 _background.ensureLoaded();
98963 _mainPresetIndex.ensureLoaded();
98964 Object.values(services).forEach(function (service) {
98965 if (service && typeof service.init === 'function') {
98976 if (services.maprules && context.initialHashParams.maprules) {
98977 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
98978 services.maprules.init();
98979 mapcss.forEach(function (mapcssSelector) {
98980 return services.maprules.addRule(mapcssSelector);
98982 })["catch"](function () {
98985 } // if the container isn't available, e.g. when testing, don't load the UI
98988 if (!context.container().empty()) {
98989 _ui.ensureLoaded().then(function () {
98999 // This is only done in testing because of the performance penalty.
99001 var debug = false; // Reexport just what our tests use, see #4379
99003 dispatch: dispatch,
99004 geoMercator: mercator,
99005 geoProjection: projection,
99006 polygonArea: d3_polygonArea,
99007 polygonCentroid: d3_polygonCentroid,
99009 selectAll: selectAll,
99010 timerFlush: timerFlush
99013 var iD = /*#__PURE__*/Object.freeze({
99017 actionAddEntity: actionAddEntity,
99018 actionAddMember: actionAddMember,
99019 actionAddMidpoint: actionAddMidpoint,
99020 actionAddVertex: actionAddVertex,
99021 actionChangeMember: actionChangeMember,
99022 actionChangePreset: actionChangePreset,
99023 actionChangeTags: actionChangeTags,
99024 actionCircularize: actionCircularize,
99025 actionConnect: actionConnect,
99026 actionCopyEntities: actionCopyEntities,
99027 actionDeleteMember: actionDeleteMember,
99028 actionDeleteMultiple: actionDeleteMultiple,
99029 actionDeleteNode: actionDeleteNode,
99030 actionDeleteRelation: actionDeleteRelation,
99031 actionDeleteWay: actionDeleteWay,
99032 actionDiscardTags: actionDiscardTags,
99033 actionDisconnect: actionDisconnect,
99034 actionExtract: actionExtract,
99035 actionJoin: actionJoin,
99036 actionMerge: actionMerge,
99037 actionMergeNodes: actionMergeNodes,
99038 actionMergePolygon: actionMergePolygon,
99039 actionMergeRemoteChanges: actionMergeRemoteChanges,
99040 actionMove: actionMove,
99041 actionMoveMember: actionMoveMember,
99042 actionMoveNode: actionMoveNode,
99043 actionNoop: actionNoop,
99044 actionOrthogonalize: actionOrthogonalize,
99045 actionRestrictTurn: actionRestrictTurn,
99046 actionReverse: actionReverse,
99047 actionRevert: actionRevert,
99048 actionRotate: actionRotate,
99049 actionScale: actionScale,
99050 actionSplit: actionSplit,
99051 actionStraightenNodes: actionStraightenNodes,
99052 actionStraightenWay: actionStraightenWay,
99053 actionUnrestrictTurn: actionUnrestrictTurn,
99054 actionReflect: actionReflect,
99055 actionUpgradeTags: actionUpgradeTags,
99056 behaviorAddWay: behaviorAddWay,
99057 behaviorBreathe: behaviorBreathe,
99058 behaviorDrag: behaviorDrag,
99059 behaviorDrawWay: behaviorDrawWay,
99060 behaviorDraw: behaviorDraw,
99061 behaviorEdit: behaviorEdit,
99062 behaviorHash: behaviorHash,
99063 behaviorHover: behaviorHover,
99064 behaviorLasso: behaviorLasso,
99065 behaviorOperation: behaviorOperation,
99066 behaviorPaste: behaviorPaste,
99067 behaviorSelect: behaviorSelect,
99068 coreContext: coreContext,
99069 coreFileFetcher: coreFileFetcher,
99070 fileFetcher: _mainFileFetcher,
99071 coreDifference: coreDifference,
99072 coreGraph: coreGraph,
99073 coreHistory: coreHistory,
99074 coreLocalizer: coreLocalizer,
99076 localizer: _mainLocalizer,
99077 prefs: corePreferences,
99078 coreTree: coreTree,
99079 coreUploader: coreUploader,
99080 coreValidator: coreValidator,
99081 geoExtent: geoExtent,
99082 geoLatToMeters: geoLatToMeters,
99083 geoLonToMeters: geoLonToMeters,
99084 geoMetersToLat: geoMetersToLat,
99085 geoMetersToLon: geoMetersToLon,
99086 geoMetersToOffset: geoMetersToOffset,
99087 geoOffsetToMeters: geoOffsetToMeters,
99088 geoScaleToZoom: geoScaleToZoom,
99089 geoSphericalClosestNode: geoSphericalClosestNode,
99090 geoSphericalDistance: geoSphericalDistance,
99091 geoZoomToScale: geoZoomToScale,
99092 geoAngle: geoAngle,
99093 geoChooseEdge: geoChooseEdge,
99094 geoEdgeEqual: geoEdgeEqual,
99095 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
99096 geoHasLineIntersections: geoHasLineIntersections,
99097 geoHasSelfIntersections: geoHasSelfIntersections,
99098 geoRotate: geoRotate,
99099 geoLineIntersection: geoLineIntersection,
99100 geoPathHasIntersections: geoPathHasIntersections,
99101 geoPathIntersections: geoPathIntersections,
99102 geoPathLength: geoPathLength,
99103 geoPointInPolygon: geoPointInPolygon,
99104 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
99105 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
99106 geoViewportEdge: geoViewportEdge,
99107 geoRawMercator: geoRawMercator,
99108 geoVecAdd: geoVecAdd,
99109 geoVecAngle: geoVecAngle,
99110 geoVecCross: geoVecCross,
99111 geoVecDot: geoVecDot,
99112 geoVecEqual: geoVecEqual,
99113 geoVecFloor: geoVecFloor,
99114 geoVecInterp: geoVecInterp,
99115 geoVecLength: geoVecLength,
99116 geoVecLengthSquare: geoVecLengthSquare,
99117 geoVecNormalize: geoVecNormalize,
99118 geoVecNormalizedDot: geoVecNormalizedDot,
99119 geoVecProject: geoVecProject,
99120 geoVecSubtract: geoVecSubtract,
99121 geoVecScale: geoVecScale,
99122 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
99123 geoOrthoCalcScore: geoOrthoCalcScore,
99124 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
99125 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
99126 modeAddArea: modeAddArea,
99127 modeAddLine: modeAddLine,
99128 modeAddPoint: modeAddPoint,
99129 modeAddNote: modeAddNote,
99130 modeBrowse: modeBrowse,
99131 modeDragNode: modeDragNode,
99132 modeDragNote: modeDragNote,
99133 modeDrawArea: modeDrawArea,
99134 modeDrawLine: modeDrawLine,
99135 modeMove: modeMove,
99136 modeRotate: modeRotate,
99137 modeSave: modeSave,
99138 modeSelect: modeSelect,
99139 modeSelectData: modeSelectData,
99140 modeSelectError: modeSelectError,
99141 modeSelectNote: modeSelectNote,
99142 operationCircularize: operationCircularize,
99143 operationContinue: operationContinue,
99144 operationCopy: operationCopy,
99145 operationDelete: operationDelete,
99146 operationDisconnect: operationDisconnect,
99147 operationDowngrade: operationDowngrade,
99148 operationExtract: operationExtract,
99149 operationMerge: operationMerge,
99150 operationMove: operationMove,
99151 operationOrthogonalize: operationOrthogonalize,
99152 operationPaste: operationPaste,
99153 operationReflectShort: operationReflectShort,
99154 operationReflectLong: operationReflectLong,
99155 operationReverse: operationReverse,
99156 operationRotate: operationRotate,
99157 operationSplit: operationSplit,
99158 operationStraighten: operationStraighten,
99159 osmChangeset: osmChangeset,
99160 osmEntity: osmEntity,
99163 osmRelation: osmRelation,
99166 osmIntersection: osmIntersection,
99168 osmInferRestriction: osmInferRestriction,
99169 osmLanes: osmLanes,
99170 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
99171 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
99172 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
99173 osmJoinWays: osmJoinWays,
99174 get osmAreaKeys () { return osmAreaKeys; },
99175 osmSetAreaKeys: osmSetAreaKeys,
99176 osmTagSuggestingArea: osmTagSuggestingArea,
99177 get osmPointTags () { return osmPointTags; },
99178 osmSetPointTags: osmSetPointTags,
99179 get osmVertexTags () { return osmVertexTags; },
99180 osmSetVertexTags: osmSetVertexTags,
99181 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
99182 osmOneWayTags: osmOneWayTags,
99183 osmPavedTags: osmPavedTags,
99184 osmIsInterestingTag: osmIsInterestingTag,
99185 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
99186 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
99187 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
99188 presetCategory: presetCategory,
99189 presetCollection: presetCollection,
99190 presetField: presetField,
99191 presetPreset: presetPreset,
99192 presetManager: _mainPresetIndex,
99193 presetIndex: presetIndex,
99194 rendererBackgroundSource: rendererBackgroundSource,
99195 rendererBackground: rendererBackground,
99196 rendererFeatures: rendererFeatures,
99197 rendererMap: rendererMap,
99198 rendererPhotos: rendererPhotos,
99199 rendererTileLayer: rendererTileLayer,
99200 services: services,
99201 serviceKeepRight: serviceKeepRight,
99202 serviceImproveOSM: serviceImproveOSM,
99203 serviceOsmose: serviceOsmose,
99204 serviceMapillary: serviceMapillary,
99205 serviceMapRules: serviceMapRules,
99206 serviceNominatim: serviceNominatim,
99207 serviceOpenstreetcam: serviceOpenstreetcam,
99208 serviceOsm: serviceOsm,
99209 serviceOsmWikibase: serviceOsmWikibase,
99210 serviceStreetside: serviceStreetside,
99211 serviceTaginfo: serviceTaginfo,
99212 serviceVectorTile: serviceVectorTile,
99213 serviceWikidata: serviceWikidata,
99214 serviceWikipedia: serviceWikipedia,
99215 svgAreas: svgAreas,
99217 svgDebug: svgDebug,
99219 svgKeepRight: svgKeepRight,
99221 svgGeolocate: svgGeolocate,
99222 svgLabels: svgLabels,
99223 svgLayers: svgLayers,
99224 svgLines: svgLines,
99225 svgMapillaryImages: svgMapillaryImages,
99226 svgMapillarySigns: svgMapillarySigns,
99227 svgMidpoints: svgMidpoints,
99228 svgNotes: svgNotes,
99229 svgMarkerSegments: svgMarkerSegments,
99230 svgOpenstreetcamImages: svgOpenstreetcamImages,
99232 svgPassiveVertex: svgPassiveVertex,
99234 svgPointTransform: svgPointTransform,
99235 svgPoints: svgPoints,
99236 svgRelationMemberTags: svgRelationMemberTags,
99237 svgSegmentWay: svgSegmentWay,
99238 svgStreetside: svgStreetside,
99239 svgTagClasses: svgTagClasses,
99240 svgTagPattern: svgTagPattern,
99241 svgTouch: svgTouch,
99242 svgTurns: svgTurns,
99243 svgVertices: svgVertices,
99244 uiFieldDefaultCheck: uiFieldCheck,
99245 uiFieldOnewayCheck: uiFieldCheck,
99246 uiFieldCheck: uiFieldCheck,
99247 uiFieldManyCombo: uiFieldCombo,
99248 uiFieldMultiCombo: uiFieldCombo,
99249 uiFieldNetworkCombo: uiFieldCombo,
99250 uiFieldSemiCombo: uiFieldCombo,
99251 uiFieldTypeCombo: uiFieldCombo,
99252 uiFieldCombo: uiFieldCombo,
99253 uiFieldUrl: uiFieldText,
99254 uiFieldIdentifier: uiFieldText,
99255 uiFieldNumber: uiFieldText,
99256 uiFieldTel: uiFieldText,
99257 uiFieldEmail: uiFieldText,
99258 uiFieldText: uiFieldText,
99259 uiFieldAccess: uiFieldAccess,
99260 uiFieldAddress: uiFieldAddress,
99261 uiFieldCycleway: uiFieldCycleway,
99262 uiFieldLanes: uiFieldLanes,
99263 uiFieldLocalized: uiFieldLocalized,
99264 uiFieldMaxspeed: uiFieldMaxspeed,
99265 uiFieldStructureRadio: uiFieldRadio,
99266 uiFieldRadio: uiFieldRadio,
99267 uiFieldRestrictions: uiFieldRestrictions,
99268 uiFieldTextarea: uiFieldTextarea,
99269 uiFieldWikidata: uiFieldWikidata,
99270 uiFieldWikipedia: uiFieldWikipedia,
99271 uiFields: uiFields,
99273 uiPanelBackground: uiPanelBackground,
99274 uiPanelHistory: uiPanelHistory,
99275 uiPanelLocation: uiPanelLocation,
99276 uiPanelMeasurement: uiPanelMeasurement,
99277 uiInfoPanels: uiInfoPanels,
99278 uiPaneBackground: uiPaneBackground,
99279 uiPaneHelp: uiPaneHelp,
99280 uiPaneIssues: uiPaneIssues,
99281 uiPaneMapData: uiPaneMapData,
99282 uiPanePreferences: uiPanePreferences,
99283 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
99284 uiSectionBackgroundList: uiSectionBackgroundList,
99285 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
99286 uiSectionChanges: uiSectionChanges,
99287 uiSectionDataLayers: uiSectionDataLayers,
99288 uiSectionEntityIssues: uiSectionEntityIssues,
99289 uiSectionFeatureType: uiSectionFeatureType,
99290 uiSectionMapFeatures: uiSectionMapFeatures,
99291 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
99292 uiSectionOverlayList: uiSectionOverlayList,
99293 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
99294 uiSectionPresetFields: uiSectionPresetFields,
99295 uiSectionPrivacy: uiSectionPrivacy,
99296 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
99297 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
99298 uiSectionRawTagEditor: uiSectionRawTagEditor,
99299 uiSectionSelectionList: uiSectionSelectionList,
99300 uiSectionValidationIssues: uiSectionValidationIssues,
99301 uiSectionValidationOptions: uiSectionValidationOptions,
99302 uiSectionValidationRules: uiSectionValidationRules,
99303 uiSectionValidationStatus: uiSectionValidationStatus,
99304 uiSettingsCustomBackground: uiSettingsCustomBackground,
99305 uiSettingsCustomData: uiSettingsCustomData,
99307 uiAccount: uiAccount,
99308 uiAttribution: uiAttribution,
99309 uiChangesetEditor: uiChangesetEditor,
99311 uiCombobox: uiCombobox,
99312 uiCommit: uiCommit,
99313 uiCommitWarnings: uiCommitWarnings,
99314 uiConfirm: uiConfirm,
99315 uiConflicts: uiConflicts,
99316 uiContributors: uiContributors,
99317 uiCurtain: uiCurtain,
99318 uiDataEditor: uiDataEditor,
99319 uiDataHeader: uiDataHeader,
99320 uiDisclosure: uiDisclosure,
99321 uiEditMenu: uiEditMenu,
99322 uiEntityEditor: uiEntityEditor,
99323 uiFeatureInfo: uiFeatureInfo,
99324 uiFeatureList: uiFeatureList,
99326 uiFieldHelp: uiFieldHelp,
99328 uiFormFields: uiFormFields,
99329 uiFullScreen: uiFullScreen,
99330 uiGeolocate: uiGeolocate,
99331 uiImproveOsmComments: uiImproveOsmComments,
99332 uiImproveOsmDetails: uiImproveOsmDetails,
99333 uiImproveOsmEditor: uiImproveOsmEditor,
99334 uiImproveOsmHeader: uiImproveOsmHeader,
99336 uiInspector: uiInspector,
99337 uiIssuesInfo: uiIssuesInfo,
99338 uiKeepRightDetails: uiKeepRightDetails,
99339 uiKeepRightEditor: uiKeepRightEditor,
99340 uiKeepRightHeader: uiKeepRightHeader,
99342 uiLoading: uiLoading,
99343 uiMapInMap: uiMapInMap,
99345 uiNotice: uiNotice,
99346 uiNoteComments: uiNoteComments,
99347 uiNoteEditor: uiNoteEditor,
99348 uiNoteHeader: uiNoteHeader,
99349 uiNoteReport: uiNoteReport,
99350 uiPopover: uiPopover,
99351 uiPresetIcon: uiPresetIcon,
99352 uiPresetList: uiPresetList,
99353 uiRestore: uiRestore,
99355 uiSidebar: uiSidebar,
99356 uiSourceSwitch: uiSourceSwitch,
99357 uiSpinner: uiSpinner,
99358 uiSplash: uiSplash,
99359 uiStatus: uiStatus,
99360 uiSuccess: uiSuccess,
99361 uiTagReference: uiTagReference,
99362 uiToggle: uiToggle,
99363 uiTooltip: uiTooltip,
99364 uiVersion: uiVersion,
99365 uiViewOnOSM: uiViewOnOSM,
99366 uiViewOnKeepRight: uiViewOnKeepRight,
99368 utilAesEncrypt: utilAesEncrypt,
99369 utilAesDecrypt: utilAesDecrypt,
99370 utilArrayChunk: utilArrayChunk,
99371 utilArrayDifference: utilArrayDifference,
99372 utilArrayFlatten: utilArrayFlatten,
99373 utilArrayGroupBy: utilArrayGroupBy,
99374 utilArrayIdentical: utilArrayIdentical,
99375 utilArrayIntersection: utilArrayIntersection,
99376 utilArrayUnion: utilArrayUnion,
99377 utilArrayUniq: utilArrayUniq,
99378 utilArrayUniqBy: utilArrayUniqBy,
99379 utilAsyncMap: utilAsyncMap,
99380 utilCleanTags: utilCleanTags,
99381 utilCombinedTags: utilCombinedTags,
99382 utilDeepMemberSelector: utilDeepMemberSelector,
99383 utilDetect: utilDetect,
99384 utilDisplayName: utilDisplayName,
99385 utilDisplayNameForPath: utilDisplayNameForPath,
99386 utilDisplayType: utilDisplayType,
99387 utilDisplayLabel: utilDisplayLabel,
99388 utilEntityRoot: utilEntityRoot,
99389 utilEditDistance: utilEditDistance,
99390 utilEntitySelector: utilEntitySelector,
99391 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
99392 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
99393 utilFastMouse: utilFastMouse,
99394 utilFunctor: utilFunctor,
99395 utilGetAllNodes: utilGetAllNodes,
99396 utilGetSetValue: utilGetSetValue,
99397 utilHashcode: utilHashcode,
99398 utilHighlightEntities: utilHighlightEntities,
99399 utilKeybinding: utilKeybinding,
99400 utilNoAuto: utilNoAuto,
99401 utilObjectOmit: utilObjectOmit,
99402 utilPrefixCSSProperty: utilPrefixCSSProperty,
99403 utilPrefixDOMProperty: utilPrefixDOMProperty,
99404 utilQsString: utilQsString,
99405 utilRebind: utilRebind,
99406 utilSafeClassName: utilSafeClassName,
99407 utilSetTransform: utilSetTransform,
99408 utilSessionMutex: utilSessionMutex,
99409 utilStringQs: utilStringQs,
99410 utilTagDiff: utilTagDiff,
99411 utilTagText: utilTagText,
99412 utilTiler: utilTiler,
99413 utilTotalExtent: utilTotalExtent,
99414 utilTriggerEvent: utilTriggerEvent,
99415 utilUnicodeCharsCount: utilUnicodeCharsCount,
99416 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
99417 utilUniqueDomId: utilUniqueDomId,
99418 utilWrap: utilWrap,
99419 validationAlmostJunction: validationAlmostJunction,
99420 validationCloseNodes: validationCloseNodes,
99421 validationCrossingWays: validationCrossingWays,
99422 validationDisconnectedWay: validationDisconnectedWay,
99423 validationFormatting: validationFormatting,
99424 validationHelpRequest: validationHelpRequest,
99425 validationImpossibleOneway: validationImpossibleOneway,
99426 validationIncompatibleSource: validationIncompatibleSource,
99427 validationMaprules: validationMaprules,
99428 validationMismatchedGeometry: validationMismatchedGeometry,
99429 validationMissingRole: validationMissingRole,
99430 validationMissingTag: validationMissingTag,
99431 validationOutdatedTags: validationOutdatedTags,
99432 validationPrivateData: validationPrivateData,
99433 validationSuspiciousName: validationSuspiciousName,
99434 validationUnsquareWay: validationUnsquareWay
99437 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
99438 var start = Date.now();
99439 return window.requestAnimationFrame(function () {
99442 timeRemaining: function timeRemaining() {
99443 return Math.max(0, 50 - (Date.now() - start));
99449 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
99450 window.cancelAnimationFrame(id);