3 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5 function createCommonjsModule(fn, basedir, module) {
9 require: function (path, base) {
10 return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
12 }, fn(module, module.exports), module.exports;
15 function commonjsRequire () {
16 throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
19 var check = function (it) {
20 return it && it.Math == Math && it;
23 // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
25 /* global globalThis -- safe */
26 check(typeof globalThis == 'object' && globalThis) ||
27 check(typeof window == 'object' && window) ||
28 check(typeof self == 'object' && self) ||
29 check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
30 // eslint-disable-next-line no-new-func -- fallback
31 (function () { return this; })() || Function('return this')();
33 var fails = function (exec) {
41 // Detect IE8's incomplete defineProperty implementation
42 var descriptors = !fails(function () {
43 return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
46 var nativePropertyIsEnumerable = {}.propertyIsEnumerable;
47 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
50 var NASHORN_BUG = getOwnPropertyDescriptor && !nativePropertyIsEnumerable.call({ 1: 2 }, 1);
52 // `Object.prototype.propertyIsEnumerable` method implementation
53 // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
54 var f = NASHORN_BUG ? function propertyIsEnumerable(V) {
55 var descriptor = getOwnPropertyDescriptor(this, V);
56 return !!descriptor && descriptor.enumerable;
57 } : nativePropertyIsEnumerable;
59 var objectPropertyIsEnumerable = {
63 var createPropertyDescriptor = function (bitmap, value) {
65 enumerable: !(bitmap & 1),
66 configurable: !(bitmap & 2),
67 writable: !(bitmap & 4),
72 var toString = {}.toString;
74 var classofRaw = function (it) {
75 return toString.call(it).slice(8, -1);
80 // fallback for non-array-like ES3 and non-enumerable old V8 strings
81 var indexedObject = fails(function () {
82 // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
83 // eslint-disable-next-line no-prototype-builtins -- safe
84 return !Object('z').propertyIsEnumerable(0);
86 return classofRaw(it) == 'String' ? split.call(it, '') : Object(it);
89 // `RequireObjectCoercible` abstract operation
90 // https://tc39.es/ecma262/#sec-requireobjectcoercible
91 var requireObjectCoercible = function (it) {
92 if (it == undefined) throw TypeError("Can't call method on " + it);
96 // toObject with fallback for non-array-like ES3 strings
100 var toIndexedObject = function (it) {
101 return indexedObject(requireObjectCoercible(it));
104 var isObject = function (it) {
105 return typeof it === 'object' ? it !== null : typeof it === 'function';
108 // `ToPrimitive` abstract operation
109 // https://tc39.es/ecma262/#sec-toprimitive
110 // instead of the ES6 spec version, we didn't implement @@toPrimitive case
111 // and the second argument - flag - preferred type is a string
112 var toPrimitive = function (input, PREFERRED_STRING) {
113 if (!isObject(input)) return input;
115 if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
116 if (typeof (fn = input.valueOf) == 'function' && !isObject(val = fn.call(input))) return val;
117 if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
118 throw TypeError("Can't convert object to primitive value");
121 var hasOwnProperty = {}.hasOwnProperty;
123 var has = function (it, key) {
124 return hasOwnProperty.call(it, key);
127 var document$1 = global_1.document;
128 // typeof document.createElement is 'object' in old IE
129 var EXISTS = isObject(document$1) && isObject(document$1.createElement);
131 var documentCreateElement = function (it) {
132 return EXISTS ? document$1.createElement(it) : {};
135 // Thank's IE8 for his funny defineProperty
136 var ie8DomDefine = !descriptors && !fails(function () {
137 return Object.defineProperty(documentCreateElement('div'), 'a', {
138 get: function () { return 7; }
142 var nativeGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
144 // `Object.getOwnPropertyDescriptor` method
145 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
146 var f$1 = descriptors ? nativeGetOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {
147 O = toIndexedObject(O);
148 P = toPrimitive(P, true);
149 if (ie8DomDefine) try {
150 return nativeGetOwnPropertyDescriptor(O, P);
151 } catch (error) { /* empty */ }
152 if (has(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
155 var objectGetOwnPropertyDescriptor = {
159 var anObject = function (it) {
161 throw TypeError(String(it) + ' is not an object');
165 var nativeDefineProperty = Object.defineProperty;
167 // `Object.defineProperty` method
168 // https://tc39.es/ecma262/#sec-object.defineproperty
169 var f$2 = descriptors ? nativeDefineProperty : function defineProperty(O, P, Attributes) {
171 P = toPrimitive(P, true);
172 anObject(Attributes);
173 if (ie8DomDefine) try {
174 return nativeDefineProperty(O, P, Attributes);
175 } catch (error) { /* empty */ }
176 if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
177 if ('value' in Attributes) O[P] = Attributes.value;
181 var objectDefineProperty = {
185 var createNonEnumerableProperty = descriptors ? function (object, key, value) {
186 return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
187 } : function (object, key, value) {
192 var setGlobal = function (key, value) {
194 createNonEnumerableProperty(global_1, key, value);
196 global_1[key] = value;
200 var SHARED = '__core-js_shared__';
201 var store = global_1[SHARED] || setGlobal(SHARED, {});
203 var sharedStore = store;
205 var functionToString = Function.toString;
207 // this helper broken in `3.4.1-3.4.4`, so we can't use `shared` helper
208 if (typeof sharedStore.inspectSource != 'function') {
209 sharedStore.inspectSource = function (it) {
210 return functionToString.call(it);
214 var inspectSource = sharedStore.inspectSource;
216 var WeakMap = global_1.WeakMap;
218 var nativeWeakMap = typeof WeakMap === 'function' && /native code/.test(inspectSource(WeakMap));
222 var shared = createCommonjsModule(function (module) {
223 (module.exports = function (key, value) {
224 return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
225 })('versions', []).push({
228 copyright: '© 2021 Denis Pushkarev (zloirock.ru)'
233 var postfix = Math.random();
235 var uid = function (key) {
236 return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36);
239 var keys = shared('keys');
241 var sharedKey = function (key) {
242 return keys[key] || (keys[key] = uid(key));
247 var WeakMap$1 = global_1.WeakMap;
250 var enforce = function (it) {
251 return has$1(it) ? get(it) : set(it, {});
254 var getterFor = function (TYPE) {
255 return function (it) {
257 if (!isObject(it) || (state = get(it)).type !== TYPE) {
258 throw TypeError('Incompatible receiver, ' + TYPE + ' required');
264 var store$1 = sharedStore.state || (sharedStore.state = new WeakMap$1());
265 var wmget = store$1.get;
266 var wmhas = store$1.has;
267 var wmset = store$1.set;
268 set = function (it, metadata) {
269 metadata.facade = it;
270 wmset.call(store$1, it, metadata);
273 get = function (it) {
274 return wmget.call(store$1, it) || {};
276 has$1 = function (it) {
277 return wmhas.call(store$1, it);
280 var STATE = sharedKey('state');
281 hiddenKeys[STATE] = true;
282 set = function (it, metadata) {
283 metadata.facade = it;
284 createNonEnumerableProperty(it, STATE, metadata);
287 get = function (it) {
288 return has(it, STATE) ? it[STATE] : {};
290 has$1 = function (it) {
291 return has(it, STATE);
295 var internalState = {
303 var redefine = createCommonjsModule(function (module) {
304 var getInternalState = internalState.get;
305 var enforceInternalState = internalState.enforce;
306 var TEMPLATE = String(String).split('String');
308 (module.exports = function (O, key, value, options) {
309 var unsafe = options ? !!options.unsafe : false;
310 var simple = options ? !!options.enumerable : false;
311 var noTargetGet = options ? !!options.noTargetGet : false;
313 if (typeof value == 'function') {
314 if (typeof key == 'string' && !has(value, 'name')) {
315 createNonEnumerableProperty(value, 'name', key);
317 state = enforceInternalState(value);
319 state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
322 if (O === global_1) {
323 if (simple) O[key] = value;
324 else setGlobal(key, value);
326 } else if (!unsafe) {
328 } else if (!noTargetGet && O[key]) {
331 if (simple) O[key] = value;
332 else createNonEnumerableProperty(O, key, value);
333 // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
334 })(Function.prototype, 'toString', function toString() {
335 return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
341 var aFunction = function (variable) {
342 return typeof variable == 'function' ? variable : undefined;
345 var getBuiltIn = function (namespace, method) {
346 return arguments.length < 2 ? aFunction(path[namespace]) || aFunction(global_1[namespace])
347 : path[namespace] && path[namespace][method] || global_1[namespace] && global_1[namespace][method];
350 var ceil = Math.ceil;
351 var floor = Math.floor;
353 // `ToInteger` abstract operation
354 // https://tc39.es/ecma262/#sec-tointeger
355 var toInteger = function (argument) {
356 return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
361 // `ToLength` abstract operation
362 // https://tc39.es/ecma262/#sec-tolength
363 var toLength = function (argument) {
364 return argument > 0 ? min(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
368 var min$1 = Math.min;
370 // Helper for a popular repeating case of the spec:
371 // Let integer be ? ToInteger(index).
372 // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
373 var toAbsoluteIndex = function (index, length) {
374 var integer = toInteger(index);
375 return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
378 // `Array.prototype.{ indexOf, includes }` methods implementation
379 var createMethod = function (IS_INCLUDES) {
380 return function ($this, el, fromIndex) {
381 var O = toIndexedObject($this);
382 var length = toLength(O.length);
383 var index = toAbsoluteIndex(fromIndex, length);
385 // Array#includes uses SameValueZero equality algorithm
386 // eslint-disable-next-line no-self-compare -- NaN check
387 if (IS_INCLUDES && el != el) while (length > index) {
389 // eslint-disable-next-line no-self-compare -- NaN check
390 if (value != value) return true;
391 // Array#indexOf ignores holes, Array#includes - not
392 } else for (;length > index; index++) {
393 if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
394 } return !IS_INCLUDES && -1;
398 var arrayIncludes = {
399 // `Array.prototype.includes` method
400 // https://tc39.es/ecma262/#sec-array.prototype.includes
401 includes: createMethod(true),
402 // `Array.prototype.indexOf` method
403 // https://tc39.es/ecma262/#sec-array.prototype.indexof
404 indexOf: createMethod(false)
407 var indexOf = arrayIncludes.indexOf;
410 var objectKeysInternal = function (object, names) {
411 var O = toIndexedObject(object);
415 for (key in O) !has(hiddenKeys, key) && has(O, key) && result.push(key);
416 // Don't enum bug & hidden keys
417 while (names.length > i) if (has(O, key = names[i++])) {
418 ~indexOf(result, key) || result.push(key);
423 // IE8- don't enum bug keys
428 'propertyIsEnumerable',
434 var hiddenKeys$1 = enumBugKeys.concat('length', 'prototype');
436 // `Object.getOwnPropertyNames` method
437 // https://tc39.es/ecma262/#sec-object.getownpropertynames
438 var f$3 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
439 return objectKeysInternal(O, hiddenKeys$1);
442 var objectGetOwnPropertyNames = {
446 var f$4 = Object.getOwnPropertySymbols;
448 var objectGetOwnPropertySymbols = {
452 // all object keys, includes non-enumerable and symbols
453 var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
454 var keys = objectGetOwnPropertyNames.f(anObject(it));
455 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
456 return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
459 var copyConstructorProperties = function (target, source) {
460 var keys = ownKeys(source);
461 var defineProperty = objectDefineProperty.f;
462 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
463 for (var i = 0; i < keys.length; i++) {
465 if (!has(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
469 var replacement = /#|\.prototype\./;
471 var isForced = function (feature, detection) {
472 var value = data[normalize(feature)];
473 return value == POLYFILL ? true
474 : value == NATIVE ? false
475 : typeof detection == 'function' ? fails(detection)
479 var normalize = isForced.normalize = function (string) {
480 return String(string).replace(replacement, '.').toLowerCase();
483 var data = isForced.data = {};
484 var NATIVE = isForced.NATIVE = 'N';
485 var POLYFILL = isForced.POLYFILL = 'P';
487 var isForced_1 = isForced;
489 var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
497 options.target - name of the target object
498 options.global - target is the global object
499 options.stat - export as static methods of target
500 options.proto - export as prototype methods of target
501 options.real - real prototype method for the `pure` version
502 options.forced - export even if the native feature is available
503 options.bind - bind methods to the target, required for the `pure` version
504 options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
505 options.unsafe - use the simple assignment of property instead of delete + defineProperty
506 options.sham - add a flag to not completely full polyfills
507 options.enumerable - export as enumerable property
508 options.noTargetGet - prevent calling a getter on target
510 var _export = function (options, source) {
511 var TARGET = options.target;
512 var GLOBAL = options.global;
513 var STATIC = options.stat;
514 var FORCED, target, key, targetProperty, sourceProperty, descriptor;
518 target = global_1[TARGET] || setGlobal(TARGET, {});
520 target = (global_1[TARGET] || {}).prototype;
522 if (target) for (key in source) {
523 sourceProperty = source[key];
524 if (options.noTargetGet) {
525 descriptor = getOwnPropertyDescriptor$1(target, key);
526 targetProperty = descriptor && descriptor.value;
527 } else targetProperty = target[key];
528 FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
529 // contained in target
530 if (!FORCED && targetProperty !== undefined) {
531 if (typeof sourceProperty === typeof targetProperty) continue;
532 copyConstructorProperties(sourceProperty, targetProperty);
534 // add a flag to not completely full polyfills
535 if (options.sham || (targetProperty && targetProperty.sham)) {
536 createNonEnumerableProperty(sourceProperty, 'sham', true);
539 redefine(target, key, sourceProperty, options);
544 // https://tc39.es/ecma262/#sec-date.now
545 _export({ target: 'Date', stat: true }, {
546 now: function now() {
547 return new Date().getTime();
551 var DatePrototype = Date.prototype;
552 var INVALID_DATE = 'Invalid Date';
553 var TO_STRING = 'toString';
554 var nativeDateToString = DatePrototype[TO_STRING];
555 var getTime = DatePrototype.getTime;
557 // `Date.prototype.toString` method
558 // https://tc39.es/ecma262/#sec-date.prototype.tostring
559 if (new Date(NaN) + '' != INVALID_DATE) {
560 redefine(DatePrototype, TO_STRING, function toString() {
561 var value = getTime.call(this);
562 // eslint-disable-next-line no-self-compare -- NaN check
563 return value === value ? nativeDateToString.call(this) : INVALID_DATE;
567 function _typeof(obj) {
568 "@babel/helpers - typeof";
570 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
571 _typeof = function (obj) {
575 _typeof = function (obj) {
576 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
583 function _classCallCheck(instance, Constructor) {
584 if (!(instance instanceof Constructor)) {
585 throw new TypeError("Cannot call a class as a function");
589 function _defineProperties(target, props) {
590 for (var i = 0; i < props.length; i++) {
591 var descriptor = props[i];
592 descriptor.enumerable = descriptor.enumerable || false;
593 descriptor.configurable = true;
594 if ("value" in descriptor) descriptor.writable = true;
595 Object.defineProperty(target, descriptor.key, descriptor);
599 function _createClass(Constructor, protoProps, staticProps) {
600 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
601 if (staticProps) _defineProperties(Constructor, staticProps);
605 function _defineProperty(obj, key, value) {
607 Object.defineProperty(obj, key, {
620 function _slicedToArray(arr, i) {
621 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
624 function _toConsumableArray(arr) {
625 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
628 function _arrayWithoutHoles(arr) {
629 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
632 function _arrayWithHoles(arr) {
633 if (Array.isArray(arr)) return arr;
636 function _iterableToArray(iter) {
637 if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
640 function _iterableToArrayLimit(arr, i) {
641 if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
648 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
651 if (i && _arr.length === i) break;
658 if (!_n && _i["return"] != null) _i["return"]();
667 function _unsupportedIterableToArray(o, minLen) {
669 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
670 var n = Object.prototype.toString.call(o).slice(8, -1);
671 if (n === "Object" && o.constructor) n = o.constructor.name;
672 if (n === "Map" || n === "Set") return Array.from(o);
673 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
676 function _arrayLikeToArray(arr, len) {
677 if (len == null || len > arr.length) len = arr.length;
679 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
684 function _nonIterableSpread() {
685 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
688 function _nonIterableRest() {
689 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
692 function _createForOfIteratorHelper(o, allowArrayLike) {
695 if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
696 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
700 var F = function () {};
705 if (i >= o.length) return {
720 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
723 var normalCompletion = true,
728 it = o[Symbol.iterator]();
731 var step = it.next();
732 normalCompletion = step.done;
741 if (!normalCompletion && it.return != null) it.return();
743 if (didErr) throw err;
749 var engineIsNode = classofRaw(global_1.process) == 'process';
751 var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
753 var process$1 = global_1.process;
754 var versions = process$1 && process$1.versions;
755 var v8 = versions && versions.v8;
759 match = v8.split('.');
760 version = match[0] + match[1];
761 } else if (engineUserAgent) {
762 match = engineUserAgent.match(/Edge\/(\d+)/);
763 if (!match || match[1] >= 74) {
764 match = engineUserAgent.match(/Chrome\/(\d+)/);
765 if (match) version = match[1];
769 var engineV8Version = version && +version;
771 var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
772 /* global Symbol -- required for testing */
773 return !Symbol.sham &&
774 // Chrome 38 Symbol has incorrect toString conversion
775 // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
776 (engineIsNode ? engineV8Version === 38 : engineV8Version > 37 && engineV8Version < 41);
779 var useSymbolAsUid = nativeSymbol
780 /* global Symbol -- safe */
782 && typeof Symbol.iterator == 'symbol';
784 var WellKnownSymbolsStore = shared('wks');
785 var Symbol$1 = global_1.Symbol;
786 var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
788 var wellKnownSymbol = function (name) {
789 if (!has(WellKnownSymbolsStore, name) || !(nativeSymbol || typeof WellKnownSymbolsStore[name] == 'string')) {
790 if (nativeSymbol && has(Symbol$1, name)) {
791 WellKnownSymbolsStore[name] = Symbol$1[name];
793 WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name);
795 } return WellKnownSymbolsStore[name];
798 var f$5 = wellKnownSymbol;
800 var wellKnownSymbolWrapped = {
804 var defineProperty = objectDefineProperty.f;
806 var defineWellKnownSymbol = function (NAME) {
807 var Symbol = path.Symbol || (path.Symbol = {});
808 if (!has(Symbol, NAME)) defineProperty(Symbol, NAME, {
809 value: wellKnownSymbolWrapped.f(NAME)
813 // `Symbol.iterator` well-known symbol
814 // https://tc39.es/ecma262/#sec-symbol.iterator
815 defineWellKnownSymbol('iterator');
817 var TO_STRING_TAG = wellKnownSymbol('toStringTag');
820 test[TO_STRING_TAG] = 'z';
822 var toStringTagSupport = String(test) === '[object z]';
824 var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
826 var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
828 // fallback for IE11 Script Access Denied error
829 var tryGet = function (it, key) {
832 } catch (error) { /* empty */ }
835 // getting tag from ES6+ `Object.prototype.toString`
836 var classof = toStringTagSupport ? classofRaw : function (it) {
838 return it === undefined ? 'Undefined' : it === null ? 'Null'
839 // @@toStringTag case
840 : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$1)) == 'string' ? tag
842 : CORRECT_ARGUMENTS ? classofRaw(O)
843 // ES3 arguments fallback
844 : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
847 // `Object.prototype.toString` method implementation
848 // https://tc39.es/ecma262/#sec-object.prototype.tostring
849 var objectToString = toStringTagSupport ? {}.toString : function toString() {
850 return '[object ' + classof(this) + ']';
853 // `Object.prototype.toString` method
854 // https://tc39.es/ecma262/#sec-object.prototype.tostring
855 if (!toStringTagSupport) {
856 redefine(Object.prototype, 'toString', objectToString, { unsafe: true });
859 // `String.prototype.{ codePointAt, at }` methods implementation
860 var createMethod$1 = function (CONVERT_TO_STRING) {
861 return function ($this, pos) {
862 var S = String(requireObjectCoercible($this));
863 var position = toInteger(pos);
866 if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
867 first = S.charCodeAt(position);
868 return first < 0xD800 || first > 0xDBFF || position + 1 === size
869 || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
870 ? CONVERT_TO_STRING ? S.charAt(position) : first
871 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
875 var stringMultibyte = {
876 // `String.prototype.codePointAt` method
877 // https://tc39.es/ecma262/#sec-string.prototype.codepointat
878 codeAt: createMethod$1(false),
879 // `String.prototype.at` method
880 // https://github.com/mathiasbynens/String.prototype.at
881 charAt: createMethod$1(true)
884 // `ToObject` abstract operation
885 // https://tc39.es/ecma262/#sec-toobject
886 var toObject = function (argument) {
887 return Object(requireObjectCoercible(argument));
890 var correctPrototypeGetter = !fails(function () {
891 function F() { /* empty */ }
892 F.prototype.constructor = null;
893 return Object.getPrototypeOf(new F()) !== F.prototype;
896 var IE_PROTO = sharedKey('IE_PROTO');
897 var ObjectPrototype = Object.prototype;
899 // `Object.getPrototypeOf` method
900 // https://tc39.es/ecma262/#sec-object.getprototypeof
901 var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
903 if (has(O, IE_PROTO)) return O[IE_PROTO];
904 if (typeof O.constructor == 'function' && O instanceof O.constructor) {
905 return O.constructor.prototype;
906 } return O instanceof Object ? ObjectPrototype : null;
909 var ITERATOR = wellKnownSymbol('iterator');
910 var BUGGY_SAFARI_ITERATORS = false;
912 var returnThis = function () { return this; };
914 // `%IteratorPrototype%` object
915 // https://tc39.es/ecma262/#sec-%iteratorprototype%-object
916 var IteratorPrototype, PrototypeOfArrayIteratorPrototype, arrayIterator;
919 arrayIterator = [].keys();
920 // Safari 8 has buggy iterators w/o `next`
921 if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS = true;
923 PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
924 if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype = PrototypeOfArrayIteratorPrototype;
928 var NEW_ITERATOR_PROTOTYPE = IteratorPrototype == undefined || fails(function () {
930 // FF44- legacy iterators case
931 return IteratorPrototype[ITERATOR].call(test) !== test;
934 if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype = {};
936 // 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
937 if ( !has(IteratorPrototype, ITERATOR)) {
938 createNonEnumerableProperty(IteratorPrototype, ITERATOR, returnThis);
941 var iteratorsCore = {
942 IteratorPrototype: IteratorPrototype,
943 BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS
946 // `Object.keys` method
947 // https://tc39.es/ecma262/#sec-object.keys
948 var objectKeys = Object.keys || function keys(O) {
949 return objectKeysInternal(O, enumBugKeys);
952 // `Object.defineProperties` method
953 // https://tc39.es/ecma262/#sec-object.defineproperties
954 var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
956 var keys = objectKeys(Properties);
957 var length = keys.length;
960 while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
964 var html = getBuiltIn('document', 'documentElement');
968 var PROTOTYPE = 'prototype';
969 var SCRIPT = 'script';
970 var IE_PROTO$1 = sharedKey('IE_PROTO');
972 var EmptyConstructor = function () { /* empty */ };
974 var scriptTag = function (content) {
975 return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
978 // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
979 var NullProtoObjectViaActiveX = function (activeXDocument) {
980 activeXDocument.write(scriptTag(''));
981 activeXDocument.close();
982 var temp = activeXDocument.parentWindow.Object;
983 activeXDocument = null; // avoid memory leak
987 // Create object with fake `null` prototype: use iframe Object with cleared prototype
988 var NullProtoObjectViaIFrame = function () {
989 // Thrash, waste and sodomy: IE GC bug
990 var iframe = documentCreateElement('iframe');
991 var JS = 'java' + SCRIPT + ':';
993 iframe.style.display = 'none';
994 html.appendChild(iframe);
995 // https://github.com/zloirock/core-js/issues/475
996 iframe.src = String(JS);
997 iframeDocument = iframe.contentWindow.document;
998 iframeDocument.open();
999 iframeDocument.write(scriptTag('document.F=Object'));
1000 iframeDocument.close();
1001 return iframeDocument.F;
1004 // Check for document.domain and active x support
1005 // No need to use active x approach when document.domain is not set
1006 // see https://github.com/es-shims/es5-shim/issues/150
1007 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
1009 var activeXDocument;
1010 var NullProtoObject = function () {
1012 /* global ActiveXObject -- old IE */
1013 activeXDocument = document.domain && new ActiveXObject('htmlfile');
1014 } catch (error) { /* ignore */ }
1015 NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
1016 var length = enumBugKeys.length;
1017 while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]];
1018 return NullProtoObject();
1021 hiddenKeys[IE_PROTO$1] = true;
1023 // `Object.create` method
1024 // https://tc39.es/ecma262/#sec-object.create
1025 var objectCreate = Object.create || function create(O, Properties) {
1028 EmptyConstructor[PROTOTYPE] = anObject(O);
1029 result = new EmptyConstructor();
1030 EmptyConstructor[PROTOTYPE] = null;
1031 // add "__proto__" for Object.getPrototypeOf polyfill
1032 result[IE_PROTO$1] = O;
1033 } else result = NullProtoObject();
1034 return Properties === undefined ? result : objectDefineProperties(result, Properties);
1037 var defineProperty$1 = objectDefineProperty.f;
1041 var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1043 var setToStringTag = function (it, TAG, STATIC) {
1044 if (it && !has(it = STATIC ? it : it.prototype, TO_STRING_TAG$2)) {
1045 defineProperty$1(it, TO_STRING_TAG$2, { configurable: true, value: TAG });
1051 var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1057 var returnThis$1 = function () { return this; };
1059 var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1060 var TO_STRING_TAG = NAME + ' Iterator';
1061 IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1062 setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1063 iterators[TO_STRING_TAG] = returnThis$1;
1064 return IteratorConstructor;
1067 var aPossiblePrototype = function (it) {
1068 if (!isObject(it) && it !== null) {
1069 throw TypeError("Can't set " + String(it) + ' as a prototype');
1073 /* eslint-disable no-proto -- safe */
1077 // `Object.setPrototypeOf` method
1078 // https://tc39.es/ecma262/#sec-object.setprototypeof
1079 // Works with __proto__ only. Old v8 can't work with null proto objects.
1080 var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1081 var CORRECT_SETTER = false;
1085 setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1086 setter.call(test, []);
1087 CORRECT_SETTER = test instanceof Array;
1088 } catch (error) { /* empty */ }
1089 return function setPrototypeOf(O, proto) {
1091 aPossiblePrototype(proto);
1092 if (CORRECT_SETTER) setter.call(O, proto);
1093 else O.__proto__ = proto;
1098 var IteratorPrototype$2 = iteratorsCore.IteratorPrototype;
1099 var BUGGY_SAFARI_ITERATORS$1 = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1100 var ITERATOR$1 = wellKnownSymbol('iterator');
1102 var VALUES = 'values';
1103 var ENTRIES = 'entries';
1105 var returnThis$2 = function () { return this; };
1107 var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1108 createIteratorConstructor(IteratorConstructor, NAME, next);
1110 var getIterationMethod = function (KIND) {
1111 if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1112 if (!BUGGY_SAFARI_ITERATORS$1 && KIND in IterablePrototype) return IterablePrototype[KIND];
1114 case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1115 case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1116 case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1117 } return function () { return new IteratorConstructor(this); };
1120 var TO_STRING_TAG = NAME + ' Iterator';
1121 var INCORRECT_VALUES_NAME = false;
1122 var IterablePrototype = Iterable.prototype;
1123 var nativeIterator = IterablePrototype[ITERATOR$1]
1124 || IterablePrototype['@@iterator']
1125 || DEFAULT && IterablePrototype[DEFAULT];
1126 var defaultIterator = !BUGGY_SAFARI_ITERATORS$1 && nativeIterator || getIterationMethod(DEFAULT);
1127 var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1128 var CurrentIteratorPrototype, methods, KEY;
1131 if (anyNativeIterator) {
1132 CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1133 if (IteratorPrototype$2 !== Object.prototype && CurrentIteratorPrototype.next) {
1134 if ( objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype$2) {
1135 if (objectSetPrototypeOf) {
1136 objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype$2);
1137 } else if (typeof CurrentIteratorPrototype[ITERATOR$1] != 'function') {
1138 createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$1, returnThis$2);
1141 // Set @@toStringTag to native iterators
1142 setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1146 // fix Array#{values, @@iterator}.name in V8 / FF
1147 if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1148 INCORRECT_VALUES_NAME = true;
1149 defaultIterator = function values() { return nativeIterator.call(this); };
1153 if ( IterablePrototype[ITERATOR$1] !== defaultIterator) {
1154 createNonEnumerableProperty(IterablePrototype, ITERATOR$1, defaultIterator);
1156 iterators[NAME] = defaultIterator;
1158 // export additional methods
1161 values: getIterationMethod(VALUES),
1162 keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1163 entries: getIterationMethod(ENTRIES)
1165 if (FORCED) for (KEY in methods) {
1166 if (BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1167 redefine(IterablePrototype, KEY, methods[KEY]);
1169 } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME }, methods);
1175 var charAt = stringMultibyte.charAt;
1179 var STRING_ITERATOR = 'String Iterator';
1180 var setInternalState = internalState.set;
1181 var getInternalState = internalState.getterFor(STRING_ITERATOR);
1183 // `String.prototype[@@iterator]` method
1184 // https://tc39.es/ecma262/#sec-string.prototype-@@iterator
1185 defineIterator(String, 'String', function (iterated) {
1186 setInternalState(this, {
1187 type: STRING_ITERATOR,
1188 string: String(iterated),
1191 // `%StringIteratorPrototype%.next` method
1192 // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next
1193 }, function next() {
1194 var state = getInternalState(this);
1195 var string = state.string;
1196 var index = state.index;
1198 if (index >= string.length) return { value: undefined, done: true };
1199 point = charAt(string, index);
1200 state.index += point.length;
1201 return { value: point, done: false };
1204 var UNSCOPABLES = wellKnownSymbol('unscopables');
1205 var ArrayPrototype = Array.prototype;
1207 // Array.prototype[@@unscopables]
1208 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
1209 if (ArrayPrototype[UNSCOPABLES] == undefined) {
1210 objectDefineProperty.f(ArrayPrototype, UNSCOPABLES, {
1212 value: objectCreate(null)
1216 // add a key to Array.prototype[@@unscopables]
1217 var addToUnscopables = function (key) {
1218 ArrayPrototype[UNSCOPABLES][key] = true;
1221 var ARRAY_ITERATOR = 'Array Iterator';
1222 var setInternalState$1 = internalState.set;
1223 var getInternalState$1 = internalState.getterFor(ARRAY_ITERATOR);
1225 // `Array.prototype.entries` method
1226 // https://tc39.es/ecma262/#sec-array.prototype.entries
1227 // `Array.prototype.keys` method
1228 // https://tc39.es/ecma262/#sec-array.prototype.keys
1229 // `Array.prototype.values` method
1230 // https://tc39.es/ecma262/#sec-array.prototype.values
1231 // `Array.prototype[@@iterator]` method
1232 // https://tc39.es/ecma262/#sec-array.prototype-@@iterator
1233 // `CreateArrayIterator` internal method
1234 // https://tc39.es/ecma262/#sec-createarrayiterator
1235 var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1236 setInternalState$1(this, {
1237 type: ARRAY_ITERATOR,
1238 target: toIndexedObject(iterated), // target
1239 index: 0, // next index
1242 // `%ArrayIteratorPrototype%.next` method
1243 // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
1245 var state = getInternalState$1(this);
1246 var target = state.target;
1247 var kind = state.kind;
1248 var index = state.index++;
1249 if (!target || index >= target.length) {
1250 state.target = undefined;
1251 return { value: undefined, done: true };
1253 if (kind == 'keys') return { value: index, done: false };
1254 if (kind == 'values') return { value: target[index], done: false };
1255 return { value: [index, target[index]], done: false };
1258 // argumentsList[@@iterator] is %ArrayProto_values%
1259 // https://tc39.es/ecma262/#sec-createunmappedargumentsobject
1260 // https://tc39.es/ecma262/#sec-createmappedargumentsobject
1261 iterators.Arguments = iterators.Array;
1263 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
1264 addToUnscopables('keys');
1265 addToUnscopables('values');
1266 addToUnscopables('entries');
1268 // iterable DOM collections
1269 // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
1270 var domIterables = {
1272 CSSStyleDeclaration: 0,
1278 DataTransferItemList: 0,
1280 HTMLAllCollection: 0,
1283 HTMLSelectElement: 0,
1288 PaintRequestList: 0,
1296 SVGTransformList: 0,
1297 SourceBufferList: 0,
1299 TextTrackCueList: 0,
1304 var ITERATOR$2 = wellKnownSymbol('iterator');
1305 var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
1306 var ArrayValues = es_array_iterator.values;
1308 for (var COLLECTION_NAME in domIterables) {
1309 var Collection = global_1[COLLECTION_NAME];
1310 var CollectionPrototype = Collection && Collection.prototype;
1311 if (CollectionPrototype) {
1312 // some Chrome versions have non-configurable methods on DOMTokenList
1313 if (CollectionPrototype[ITERATOR$2] !== ArrayValues) try {
1314 createNonEnumerableProperty(CollectionPrototype, ITERATOR$2, ArrayValues);
1316 CollectionPrototype[ITERATOR$2] = ArrayValues;
1318 if (!CollectionPrototype[TO_STRING_TAG$3]) {
1319 createNonEnumerableProperty(CollectionPrototype, TO_STRING_TAG$3, COLLECTION_NAME);
1321 if (domIterables[COLLECTION_NAME]) for (var METHOD_NAME in es_array_iterator) {
1322 // some Chrome versions have non-configurable methods on DOMTokenList
1323 if (CollectionPrototype[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
1324 createNonEnumerableProperty(CollectionPrototype, METHOD_NAME, es_array_iterator[METHOD_NAME]);
1326 CollectionPrototype[METHOD_NAME] = es_array_iterator[METHOD_NAME];
1332 // `IsArray` abstract operation
1333 // https://tc39.es/ecma262/#sec-isarray
1334 var isArray = Array.isArray || function isArray(arg) {
1335 return classofRaw(arg) == 'Array';
1338 var nativeGetOwnPropertyNames = objectGetOwnPropertyNames.f;
1340 var toString$1 = {}.toString;
1342 var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
1343 ? Object.getOwnPropertyNames(window) : [];
1345 var getWindowNames = function (it) {
1347 return nativeGetOwnPropertyNames(it);
1349 return windowNames.slice();
1353 // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
1354 var f$6 = function getOwnPropertyNames(it) {
1355 return windowNames && toString$1.call(it) == '[object Window]'
1356 ? getWindowNames(it)
1357 : nativeGetOwnPropertyNames(toIndexedObject(it));
1360 var objectGetOwnPropertyNamesExternal = {
1364 var aFunction$1 = function (it) {
1365 if (typeof it != 'function') {
1366 throw TypeError(String(it) + ' is not a function');
1370 // optional / simple context binding
1371 var functionBindContext = function (fn, that, length) {
1373 if (that === undefined) return fn;
1375 case 0: return function () {
1376 return fn.call(that);
1378 case 1: return function (a) {
1379 return fn.call(that, a);
1381 case 2: return function (a, b) {
1382 return fn.call(that, a, b);
1384 case 3: return function (a, b, c) {
1385 return fn.call(that, a, b, c);
1388 return function (/* ...args */) {
1389 return fn.apply(that, arguments);
1393 var SPECIES = wellKnownSymbol('species');
1395 // `ArraySpeciesCreate` abstract operation
1396 // https://tc39.es/ecma262/#sec-arrayspeciescreate
1397 var arraySpeciesCreate = function (originalArray, length) {
1399 if (isArray(originalArray)) {
1400 C = originalArray.constructor;
1401 // cross-realm fallback
1402 if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
1403 else if (isObject(C)) {
1405 if (C === null) C = undefined;
1407 } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
1412 // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterOut }` methods implementation
1413 var createMethod$2 = function (TYPE) {
1414 var IS_MAP = TYPE == 1;
1415 var IS_FILTER = TYPE == 2;
1416 var IS_SOME = TYPE == 3;
1417 var IS_EVERY = TYPE == 4;
1418 var IS_FIND_INDEX = TYPE == 6;
1419 var IS_FILTER_OUT = TYPE == 7;
1420 var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
1421 return function ($this, callbackfn, that, specificCreate) {
1422 var O = toObject($this);
1423 var self = indexedObject(O);
1424 var boundFunction = functionBindContext(callbackfn, that, 3);
1425 var length = toLength(self.length);
1427 var create = specificCreate || arraySpeciesCreate;
1428 var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_OUT ? create($this, 0) : undefined;
1430 for (;length > index; index++) if (NO_HOLES || index in self) {
1431 value = self[index];
1432 result = boundFunction(value, index, O);
1434 if (IS_MAP) target[index] = result; // map
1435 else if (result) switch (TYPE) {
1436 case 3: return true; // some
1437 case 5: return value; // find
1438 case 6: return index; // findIndex
1439 case 2: push.call(target, value); // filter
1440 } else switch (TYPE) {
1441 case 4: return false; // every
1442 case 7: push.call(target, value); // filterOut
1446 return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
1450 var arrayIteration = {
1451 // `Array.prototype.forEach` method
1452 // https://tc39.es/ecma262/#sec-array.prototype.foreach
1453 forEach: createMethod$2(0),
1454 // `Array.prototype.map` method
1455 // https://tc39.es/ecma262/#sec-array.prototype.map
1456 map: createMethod$2(1),
1457 // `Array.prototype.filter` method
1458 // https://tc39.es/ecma262/#sec-array.prototype.filter
1459 filter: createMethod$2(2),
1460 // `Array.prototype.some` method
1461 // https://tc39.es/ecma262/#sec-array.prototype.some
1462 some: createMethod$2(3),
1463 // `Array.prototype.every` method
1464 // https://tc39.es/ecma262/#sec-array.prototype.every
1465 every: createMethod$2(4),
1466 // `Array.prototype.find` method
1467 // https://tc39.es/ecma262/#sec-array.prototype.find
1468 find: createMethod$2(5),
1469 // `Array.prototype.findIndex` method
1470 // https://tc39.es/ecma262/#sec-array.prototype.findIndex
1471 findIndex: createMethod$2(6),
1472 // `Array.prototype.filterOut` method
1473 // https://github.com/tc39/proposal-array-filtering
1474 filterOut: createMethod$2(7)
1477 var $forEach = arrayIteration.forEach;
1479 var HIDDEN = sharedKey('hidden');
1480 var SYMBOL = 'Symbol';
1481 var PROTOTYPE$1 = 'prototype';
1482 var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
1483 var setInternalState$2 = internalState.set;
1484 var getInternalState$2 = internalState.getterFor(SYMBOL);
1485 var ObjectPrototype$1 = Object[PROTOTYPE$1];
1486 var $Symbol = global_1.Symbol;
1487 var $stringify = getBuiltIn('JSON', 'stringify');
1488 var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
1489 var nativeDefineProperty$1 = objectDefineProperty.f;
1490 var nativeGetOwnPropertyNames$1 = objectGetOwnPropertyNamesExternal.f;
1491 var nativePropertyIsEnumerable$1 = objectPropertyIsEnumerable.f;
1492 var AllSymbols = shared('symbols');
1493 var ObjectPrototypeSymbols = shared('op-symbols');
1494 var StringToSymbolRegistry = shared('string-to-symbol-registry');
1495 var SymbolToStringRegistry = shared('symbol-to-string-registry');
1496 var WellKnownSymbolsStore$1 = shared('wks');
1497 var QObject = global_1.QObject;
1498 // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
1499 var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
1501 // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
1502 var setSymbolDescriptor = descriptors && fails(function () {
1503 return objectCreate(nativeDefineProperty$1({}, 'a', {
1504 get: function () { return nativeDefineProperty$1(this, 'a', { value: 7 }).a; }
1506 }) ? function (O, P, Attributes) {
1507 var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$1, P);
1508 if (ObjectPrototypeDescriptor) delete ObjectPrototype$1[P];
1509 nativeDefineProperty$1(O, P, Attributes);
1510 if (ObjectPrototypeDescriptor && O !== ObjectPrototype$1) {
1511 nativeDefineProperty$1(ObjectPrototype$1, P, ObjectPrototypeDescriptor);
1513 } : nativeDefineProperty$1;
1515 var wrap = function (tag, description) {
1516 var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
1517 setInternalState$2(symbol, {
1520 description: description
1522 if (!descriptors) symbol.description = description;
1526 var isSymbol = useSymbolAsUid ? function (it) {
1527 return typeof it == 'symbol';
1529 return Object(it) instanceof $Symbol;
1532 var $defineProperty = function defineProperty(O, P, Attributes) {
1533 if (O === ObjectPrototype$1) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
1535 var key = toPrimitive(P, true);
1536 anObject(Attributes);
1537 if (has(AllSymbols, key)) {
1538 if (!Attributes.enumerable) {
1539 if (!has(O, HIDDEN)) nativeDefineProperty$1(O, HIDDEN, createPropertyDescriptor(1, {}));
1540 O[HIDDEN][key] = true;
1542 if (has(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
1543 Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
1544 } return setSymbolDescriptor(O, key, Attributes);
1545 } return nativeDefineProperty$1(O, key, Attributes);
1548 var $defineProperties = function defineProperties(O, Properties) {
1550 var properties = toIndexedObject(Properties);
1551 var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
1552 $forEach(keys, function (key) {
1553 if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
1558 var $create = function create(O, Properties) {
1559 return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
1562 var $propertyIsEnumerable = function propertyIsEnumerable(V) {
1563 var P = toPrimitive(V, true);
1564 var enumerable = nativePropertyIsEnumerable$1.call(this, P);
1565 if (this === ObjectPrototype$1 && has(AllSymbols, P) && !has(ObjectPrototypeSymbols, P)) return false;
1566 return enumerable || !has(this, P) || !has(AllSymbols, P) || has(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
1569 var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
1570 var it = toIndexedObject(O);
1571 var key = toPrimitive(P, true);
1572 if (it === ObjectPrototype$1 && has(AllSymbols, key) && !has(ObjectPrototypeSymbols, key)) return;
1573 var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
1574 if (descriptor && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) {
1575 descriptor.enumerable = true;
1580 var $getOwnPropertyNames = function getOwnPropertyNames(O) {
1581 var names = nativeGetOwnPropertyNames$1(toIndexedObject(O));
1583 $forEach(names, function (key) {
1584 if (!has(AllSymbols, key) && !has(hiddenKeys, key)) result.push(key);
1589 var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
1590 var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$1;
1591 var names = nativeGetOwnPropertyNames$1(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
1593 $forEach(names, function (key) {
1594 if (has(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has(ObjectPrototype$1, key))) {
1595 result.push(AllSymbols[key]);
1601 // `Symbol` constructor
1602 // https://tc39.es/ecma262/#sec-symbol-constructor
1603 if (!nativeSymbol) {
1604 $Symbol = function Symbol() {
1605 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
1606 var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
1607 var tag = uid(description);
1608 var setter = function (value) {
1609 if (this === ObjectPrototype$1) setter.call(ObjectPrototypeSymbols, value);
1610 if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
1611 setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
1613 if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype$1, tag, { configurable: true, set: setter });
1614 return wrap(tag, description);
1617 redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
1618 return getInternalState$2(this).tag;
1621 redefine($Symbol, 'withoutSetter', function (description) {
1622 return wrap(uid(description), description);
1625 objectPropertyIsEnumerable.f = $propertyIsEnumerable;
1626 objectDefineProperty.f = $defineProperty;
1627 objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
1628 objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1629 objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1631 wellKnownSymbolWrapped.f = function (name) {
1632 return wrap(wellKnownSymbol(name), name);
1636 // https://github.com/tc39/proposal-Symbol-description
1637 nativeDefineProperty$1($Symbol[PROTOTYPE$1], 'description', {
1639 get: function description() {
1640 return getInternalState$2(this).description;
1644 redefine(ObjectPrototype$1, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1649 _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1653 $forEach(objectKeys(WellKnownSymbolsStore$1), function (name) {
1654 defineWellKnownSymbol(name);
1657 _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1658 // `Symbol.for` method
1659 // https://tc39.es/ecma262/#sec-symbol.for
1660 'for': function (key) {
1661 var string = String(key);
1662 if (has(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1663 var symbol = $Symbol(string);
1664 StringToSymbolRegistry[string] = symbol;
1665 SymbolToStringRegistry[symbol] = string;
1668 // `Symbol.keyFor` method
1669 // https://tc39.es/ecma262/#sec-symbol.keyfor
1670 keyFor: function keyFor(sym) {
1671 if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol');
1672 if (has(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1674 useSetter: function () { USE_SETTER = true; },
1675 useSimple: function () { USE_SETTER = false; }
1678 _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1679 // `Object.create` method
1680 // https://tc39.es/ecma262/#sec-object.create
1682 // `Object.defineProperty` method
1683 // https://tc39.es/ecma262/#sec-object.defineproperty
1684 defineProperty: $defineProperty,
1685 // `Object.defineProperties` method
1686 // https://tc39.es/ecma262/#sec-object.defineproperties
1687 defineProperties: $defineProperties,
1688 // `Object.getOwnPropertyDescriptor` method
1689 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors
1690 getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1693 _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1694 // `Object.getOwnPropertyNames` method
1695 // https://tc39.es/ecma262/#sec-object.getownpropertynames
1696 getOwnPropertyNames: $getOwnPropertyNames,
1697 // `Object.getOwnPropertySymbols` method
1698 // https://tc39.es/ecma262/#sec-object.getownpropertysymbols
1699 getOwnPropertySymbols: $getOwnPropertySymbols
1702 // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1703 // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1704 _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1705 getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1706 return objectGetOwnPropertySymbols.f(toObject(it));
1710 // `JSON.stringify` method behavior with symbols
1711 // https://tc39.es/ecma262/#sec-json.stringify
1713 var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1714 var symbol = $Symbol();
1715 // MS Edge converts symbol values to JSON as {}
1716 return $stringify([symbol]) != '[null]'
1717 // WebKit converts symbol values to JSON as null
1718 || $stringify({ a: symbol }) != '{}'
1719 // V8 throws on boxed symbols
1720 || $stringify(Object(symbol)) != '{}';
1723 _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1724 // eslint-disable-next-line no-unused-vars -- required for `.length`
1725 stringify: function stringify(it, replacer, space) {
1729 while (arguments.length > index) args.push(arguments[index++]);
1730 $replacer = replacer;
1731 if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
1732 if (!isArray(replacer)) replacer = function (key, value) {
1733 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1734 if (!isSymbol(value)) return value;
1737 return $stringify.apply(null, args);
1742 // `Symbol.prototype[@@toPrimitive]` method
1743 // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive
1744 if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1745 createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1747 // `Symbol.prototype[@@toStringTag]` property
1748 // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag
1749 setToStringTag($Symbol, SYMBOL);
1751 hiddenKeys[HIDDEN] = true;
1753 var defineProperty$2 = objectDefineProperty.f;
1756 var NativeSymbol = global_1.Symbol;
1758 if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1760 NativeSymbol().description !== undefined
1762 var EmptyStringDescriptionStore = {};
1763 // wrap Symbol constructor for correct work with undefined description
1764 var SymbolWrapper = function Symbol() {
1765 var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1766 var result = this instanceof SymbolWrapper
1767 ? new NativeSymbol(description)
1768 // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1769 : description === undefined ? NativeSymbol() : NativeSymbol(description);
1770 if (description === '') EmptyStringDescriptionStore[result] = true;
1773 copyConstructorProperties(SymbolWrapper, NativeSymbol);
1774 var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1775 symbolPrototype.constructor = SymbolWrapper;
1777 var symbolToString = symbolPrototype.toString;
1778 var native = String(NativeSymbol('test')) == 'Symbol(test)';
1779 var regexp = /^Symbol\((.*)\)[^)]+$/;
1780 defineProperty$2(symbolPrototype, 'description', {
1782 get: function description() {
1783 var symbol = isObject(this) ? this.valueOf() : this;
1784 var string = symbolToString.call(symbol);
1785 if (has(EmptyStringDescriptionStore, symbol)) return '';
1786 var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1787 return desc === '' ? undefined : desc;
1791 _export({ global: true, forced: true }, {
1792 Symbol: SymbolWrapper
1796 var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1798 var redefineAll = function (target, src, options) {
1799 for (var key in src) redefine(target, key, src[key], options);
1803 var anInstance = function (it, Constructor, name) {
1804 if (!(it instanceof Constructor)) {
1805 throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1809 // `ToIndex` abstract operation
1810 // https://tc39.es/ecma262/#sec-toindex
1811 var toIndex = function (it) {
1812 if (it === undefined) return 0;
1813 var number = toInteger(it);
1814 var length = toLength(number);
1815 if (number !== length) throw RangeError('Wrong length or index');
1819 // IEEE754 conversions based on https://github.com/feross/ieee754
1822 var floor$1 = Math.floor;
1826 var pack = function (number, mantissaLength, bytes) {
1827 var buffer = new Array(bytes);
1828 var exponentLength = bytes * 8 - mantissaLength - 1;
1829 var eMax = (1 << exponentLength) - 1;
1830 var eBias = eMax >> 1;
1831 var rt = mantissaLength === 23 ? pow(2, -24) - pow(2, -77) : 0;
1832 var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1834 var exponent, mantissa, c;
1835 number = abs(number);
1836 // eslint-disable-next-line no-self-compare -- NaN check
1837 if (number != number || number === Infinity) {
1838 // eslint-disable-next-line no-self-compare -- NaN check
1839 mantissa = number != number ? 1 : 0;
1842 exponent = floor$1(log(number) / LN2);
1843 if (number * (c = pow(2, -exponent)) < 1) {
1847 if (exponent + eBias >= 1) {
1850 number += rt * pow(2, 1 - eBias);
1852 if (number * c >= 2) {
1856 if (exponent + eBias >= eMax) {
1859 } else if (exponent + eBias >= 1) {
1860 mantissa = (number * c - 1) * pow(2, mantissaLength);
1861 exponent = exponent + eBias;
1863 mantissa = number * pow(2, eBias - 1) * pow(2, mantissaLength);
1867 for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1868 exponent = exponent << mantissaLength | mantissa;
1869 exponentLength += mantissaLength;
1870 for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1871 buffer[--index] |= sign * 128;
1875 var unpack = function (buffer, mantissaLength) {
1876 var bytes = buffer.length;
1877 var exponentLength = bytes * 8 - mantissaLength - 1;
1878 var eMax = (1 << exponentLength) - 1;
1879 var eBias = eMax >> 1;
1880 var nBits = exponentLength - 7;
1881 var index = bytes - 1;
1882 var sign = buffer[index--];
1883 var exponent = sign & 127;
1886 for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1887 mantissa = exponent & (1 << -nBits) - 1;
1888 exponent >>= -nBits;
1889 nBits += mantissaLength;
1890 for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1891 if (exponent === 0) {
1892 exponent = 1 - eBias;
1893 } else if (exponent === eMax) {
1894 return mantissa ? NaN : sign ? -Infinity : Infinity;
1896 mantissa = mantissa + pow(2, mantissaLength);
1897 exponent = exponent - eBias;
1898 } return (sign ? -1 : 1) * mantissa * pow(2, exponent - mantissaLength);
1906 // `Array.prototype.fill` method implementation
1907 // https://tc39.es/ecma262/#sec-array.prototype.fill
1908 var arrayFill = function fill(value /* , start = 0, end = @length */) {
1909 var O = toObject(this);
1910 var length = toLength(O.length);
1911 var argumentsLength = arguments.length;
1912 var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1913 var end = argumentsLength > 2 ? arguments[2] : undefined;
1914 var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1915 while (endPos > index) O[index++] = value;
1919 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
1920 var defineProperty$3 = objectDefineProperty.f;
1925 var getInternalState$3 = internalState.get;
1926 var setInternalState$3 = internalState.set;
1927 var ARRAY_BUFFER = 'ArrayBuffer';
1928 var DATA_VIEW = 'DataView';
1929 var PROTOTYPE$2 = 'prototype';
1930 var WRONG_LENGTH = 'Wrong length';
1931 var WRONG_INDEX = 'Wrong index';
1932 var NativeArrayBuffer = global_1[ARRAY_BUFFER];
1933 var $ArrayBuffer = NativeArrayBuffer;
1934 var $DataView = global_1[DATA_VIEW];
1935 var $DataViewPrototype = $DataView && $DataView[PROTOTYPE$2];
1936 var ObjectPrototype$2 = Object.prototype;
1937 var RangeError$1 = global_1.RangeError;
1939 var packIEEE754 = ieee754.pack;
1940 var unpackIEEE754 = ieee754.unpack;
1942 var packInt8 = function (number) {
1943 return [number & 0xFF];
1946 var packInt16 = function (number) {
1947 return [number & 0xFF, number >> 8 & 0xFF];
1950 var packInt32 = function (number) {
1951 return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1954 var unpackInt32 = function (buffer) {
1955 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1958 var packFloat32 = function (number) {
1959 return packIEEE754(number, 23, 4);
1962 var packFloat64 = function (number) {
1963 return packIEEE754(number, 52, 8);
1966 var addGetter = function (Constructor, key) {
1967 defineProperty$3(Constructor[PROTOTYPE$2], key, { get: function () { return getInternalState$3(this)[key]; } });
1970 var get$1 = function (view, count, index, isLittleEndian) {
1971 var intIndex = toIndex(index);
1972 var store = getInternalState$3(view);
1973 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1974 var bytes = getInternalState$3(store.buffer).bytes;
1975 var start = intIndex + store.byteOffset;
1976 var pack = bytes.slice(start, start + count);
1977 return isLittleEndian ? pack : pack.reverse();
1980 var set$1 = function (view, count, index, conversion, value, isLittleEndian) {
1981 var intIndex = toIndex(index);
1982 var store = getInternalState$3(view);
1983 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1984 var bytes = getInternalState$3(store.buffer).bytes;
1985 var start = intIndex + store.byteOffset;
1986 var pack = conversion(+value);
1987 for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
1990 if (!arrayBufferNative) {
1991 $ArrayBuffer = function ArrayBuffer(length) {
1992 anInstance(this, $ArrayBuffer, ARRAY_BUFFER);
1993 var byteLength = toIndex(length);
1994 setInternalState$3(this, {
1995 bytes: arrayFill.call(new Array(byteLength), 0),
1996 byteLength: byteLength
1998 if (!descriptors) this.byteLength = byteLength;
2001 $DataView = function DataView(buffer, byteOffset, byteLength) {
2002 anInstance(this, $DataView, DATA_VIEW);
2003 anInstance(buffer, $ArrayBuffer, DATA_VIEW);
2004 var bufferLength = getInternalState$3(buffer).byteLength;
2005 var offset = toInteger(byteOffset);
2006 if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
2007 byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
2008 if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
2009 setInternalState$3(this, {
2011 byteLength: byteLength,
2015 this.buffer = buffer;
2016 this.byteLength = byteLength;
2017 this.byteOffset = offset;
2022 addGetter($ArrayBuffer, 'byteLength');
2023 addGetter($DataView, 'buffer');
2024 addGetter($DataView, 'byteLength');
2025 addGetter($DataView, 'byteOffset');
2028 redefineAll($DataView[PROTOTYPE$2], {
2029 getInt8: function getInt8(byteOffset) {
2030 return get$1(this, 1, byteOffset)[0] << 24 >> 24;
2032 getUint8: function getUint8(byteOffset) {
2033 return get$1(this, 1, byteOffset)[0];
2035 getInt16: function getInt16(byteOffset /* , littleEndian */) {
2036 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2037 return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
2039 getUint16: function getUint16(byteOffset /* , littleEndian */) {
2040 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2041 return bytes[1] << 8 | bytes[0];
2043 getInt32: function getInt32(byteOffset /* , littleEndian */) {
2044 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
2046 getUint32: function getUint32(byteOffset /* , littleEndian */) {
2047 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
2049 getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
2050 return unpackIEEE754(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
2052 getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
2053 return unpackIEEE754(get$1(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
2055 setInt8: function setInt8(byteOffset, value) {
2056 set$1(this, 1, byteOffset, packInt8, value);
2058 setUint8: function setUint8(byteOffset, value) {
2059 set$1(this, 1, byteOffset, packInt8, value);
2061 setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
2062 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2064 setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
2065 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2067 setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
2068 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2070 setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
2071 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2073 setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
2074 set$1(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
2076 setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
2077 set$1(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
2081 /* eslint-disable no-new -- required for testing */
2082 if (!fails(function () {
2083 NativeArrayBuffer(1);
2084 }) || !fails(function () {
2085 new NativeArrayBuffer(-1);
2086 }) || fails(function () {
2087 new NativeArrayBuffer();
2088 new NativeArrayBuffer(1.5);
2089 new NativeArrayBuffer(NaN);
2090 return NativeArrayBuffer.name != ARRAY_BUFFER;
2092 /* eslint-enable no-new -- required for testing */
2093 $ArrayBuffer = function ArrayBuffer(length) {
2094 anInstance(this, $ArrayBuffer);
2095 return new NativeArrayBuffer(toIndex(length));
2097 var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE$2] = NativeArrayBuffer[PROTOTYPE$2];
2098 for (var keys$1 = getOwnPropertyNames(NativeArrayBuffer), j = 0, key; keys$1.length > j;) {
2099 if (!((key = keys$1[j++]) in $ArrayBuffer)) {
2100 createNonEnumerableProperty($ArrayBuffer, key, NativeArrayBuffer[key]);
2103 ArrayBufferPrototype.constructor = $ArrayBuffer;
2106 // WebKit bug - the same parent prototype for typed arrays and data view
2107 if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$2) {
2108 objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$2);
2111 // iOS Safari 7.x bug
2112 var testView = new $DataView(new $ArrayBuffer(2));
2113 var nativeSetInt8 = $DataViewPrototype.setInt8;
2114 testView.setInt8(0, 2147483648);
2115 testView.setInt8(1, 2147483649);
2116 if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
2117 setInt8: function setInt8(byteOffset, value) {
2118 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
2120 setUint8: function setUint8(byteOffset, value) {
2121 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
2123 }, { unsafe: true });
2126 setToStringTag($ArrayBuffer, ARRAY_BUFFER);
2127 setToStringTag($DataView, DATA_VIEW);
2130 ArrayBuffer: $ArrayBuffer,
2134 // `DataView` constructor
2135 // https://tc39.es/ecma262/#sec-dataview-constructor
2136 _export({ global: true, forced: !arrayBufferNative }, {
2137 DataView: arrayBuffer.DataView
2140 var SPECIES$1 = wellKnownSymbol('species');
2142 // `SpeciesConstructor` abstract operation
2143 // https://tc39.es/ecma262/#sec-speciesconstructor
2144 var speciesConstructor = function (O, defaultConstructor) {
2145 var C = anObject(O).constructor;
2147 return C === undefined || (S = anObject(C)[SPECIES$1]) == undefined ? defaultConstructor : aFunction$1(S);
2150 var ArrayBuffer$1 = arrayBuffer.ArrayBuffer;
2151 var DataView$1 = arrayBuffer.DataView;
2152 var nativeArrayBufferSlice = ArrayBuffer$1.prototype.slice;
2154 var INCORRECT_SLICE = fails(function () {
2155 return !new ArrayBuffer$1(2).slice(1, undefined).byteLength;
2158 // `ArrayBuffer.prototype.slice` method
2159 // https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
2160 _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2161 slice: function slice(start, end) {
2162 if (nativeArrayBufferSlice !== undefined && end === undefined) {
2163 return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2165 var length = anObject(this).byteLength;
2166 var first = toAbsoluteIndex(start, length);
2167 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2168 var result = new (speciesConstructor(this, ArrayBuffer$1))(toLength(fin - first));
2169 var viewSource = new DataView$1(this);
2170 var viewTarget = new DataView$1(result);
2172 while (first < fin) {
2173 viewTarget.setUint8(index++, viewSource.getUint8(first++));
2178 var defineProperty$4 = objectDefineProperty.f;
2184 var Int8Array$1 = global_1.Int8Array;
2185 var Int8ArrayPrototype = Int8Array$1 && Int8Array$1.prototype;
2186 var Uint8ClampedArray = global_1.Uint8ClampedArray;
2187 var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2188 var TypedArray = Int8Array$1 && objectGetPrototypeOf(Int8Array$1);
2189 var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2190 var ObjectPrototype$3 = Object.prototype;
2191 var isPrototypeOf = ObjectPrototype$3.isPrototypeOf;
2193 var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
2194 var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2195 // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2196 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferNative && !!objectSetPrototypeOf && classof(global_1.opera) !== 'Opera';
2197 var TYPED_ARRAY_TAG_REQIRED = false;
2200 var TypedArrayConstructorsList = {
2203 Uint8ClampedArray: 1,
2212 var BigIntArrayConstructorsList = {
2217 var isView = function isView(it) {
2218 if (!isObject(it)) return false;
2219 var klass = classof(it);
2220 return klass === 'DataView'
2221 || has(TypedArrayConstructorsList, klass)
2222 || has(BigIntArrayConstructorsList, klass);
2225 var isTypedArray = function (it) {
2226 if (!isObject(it)) return false;
2227 var klass = classof(it);
2228 return has(TypedArrayConstructorsList, klass)
2229 || has(BigIntArrayConstructorsList, klass);
2232 var aTypedArray = function (it) {
2233 if (isTypedArray(it)) return it;
2234 throw TypeError('Target is not a typed array');
2237 var aTypedArrayConstructor = function (C) {
2238 if (objectSetPrototypeOf) {
2239 if (isPrototypeOf.call(TypedArray, C)) return C;
2240 } else for (var ARRAY in TypedArrayConstructorsList) if (has(TypedArrayConstructorsList, NAME)) {
2241 var TypedArrayConstructor = global_1[ARRAY];
2242 if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2245 } throw TypeError('Target is not a typed array constructor');
2248 var exportTypedArrayMethod = function (KEY, property, forced) {
2249 if (!descriptors) return;
2250 if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2251 var TypedArrayConstructor = global_1[ARRAY];
2252 if (TypedArrayConstructor && has(TypedArrayConstructor.prototype, KEY)) {
2253 delete TypedArrayConstructor.prototype[KEY];
2256 if (!TypedArrayPrototype[KEY] || forced) {
2257 redefine(TypedArrayPrototype, KEY, forced ? property
2258 : NATIVE_ARRAY_BUFFER_VIEWS && Int8ArrayPrototype[KEY] || property);
2262 var exportTypedArrayStaticMethod = function (KEY, property, forced) {
2263 var ARRAY, TypedArrayConstructor;
2264 if (!descriptors) return;
2265 if (objectSetPrototypeOf) {
2266 if (forced) for (ARRAY in TypedArrayConstructorsList) {
2267 TypedArrayConstructor = global_1[ARRAY];
2268 if (TypedArrayConstructor && has(TypedArrayConstructor, KEY)) {
2269 delete TypedArrayConstructor[KEY];
2272 if (!TypedArray[KEY] || forced) {
2273 // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2275 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && Int8Array$1[KEY] || property);
2276 } catch (error) { /* empty */ }
2279 for (ARRAY in TypedArrayConstructorsList) {
2280 TypedArrayConstructor = global_1[ARRAY];
2281 if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2282 redefine(TypedArrayConstructor, KEY, property);
2287 for (NAME in TypedArrayConstructorsList) {
2288 if (!global_1[NAME]) NATIVE_ARRAY_BUFFER_VIEWS = false;
2291 // WebKit bug - typed arrays constructors prototype is Object.prototype
2292 if (!NATIVE_ARRAY_BUFFER_VIEWS || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2293 // eslint-disable-next-line no-shadow -- safe
2294 TypedArray = function TypedArray() {
2295 throw TypeError('Incorrect invocation');
2297 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2298 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME], TypedArray);
2302 if (!NATIVE_ARRAY_BUFFER_VIEWS || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype$3) {
2303 TypedArrayPrototype = TypedArray.prototype;
2304 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2305 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME].prototype, TypedArrayPrototype);
2309 // WebKit bug - one more object in Uint8ClampedArray prototype chain
2310 if (NATIVE_ARRAY_BUFFER_VIEWS && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2311 objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2314 if (descriptors && !has(TypedArrayPrototype, TO_STRING_TAG$4)) {
2315 TYPED_ARRAY_TAG_REQIRED = true;
2316 defineProperty$4(TypedArrayPrototype, TO_STRING_TAG$4, { get: function () {
2317 return isObject(this) ? this[TYPED_ARRAY_TAG] : undefined;
2319 for (NAME in TypedArrayConstructorsList) if (global_1[NAME]) {
2320 createNonEnumerableProperty(global_1[NAME], TYPED_ARRAY_TAG, NAME);
2324 var arrayBufferViewCore = {
2325 NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS,
2326 TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2327 aTypedArray: aTypedArray,
2328 aTypedArrayConstructor: aTypedArrayConstructor,
2329 exportTypedArrayMethod: exportTypedArrayMethod,
2330 exportTypedArrayStaticMethod: exportTypedArrayStaticMethod,
2332 isTypedArray: isTypedArray,
2333 TypedArray: TypedArray,
2334 TypedArrayPrototype: TypedArrayPrototype
2337 var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2339 // `ArrayBuffer.isView` method
2340 // https://tc39.es/ecma262/#sec-arraybuffer.isview
2341 _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2342 isView: arrayBufferViewCore.isView
2345 var SPECIES$2 = wellKnownSymbol('species');
2347 var setSpecies = function (CONSTRUCTOR_NAME) {
2348 var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
2349 var defineProperty = objectDefineProperty.f;
2351 if (descriptors && Constructor && !Constructor[SPECIES$2]) {
2352 defineProperty(Constructor, SPECIES$2, {
2354 get: function () { return this; }
2359 var ARRAY_BUFFER$1 = 'ArrayBuffer';
2360 var ArrayBuffer$2 = arrayBuffer[ARRAY_BUFFER$1];
2361 var NativeArrayBuffer$1 = global_1[ARRAY_BUFFER$1];
2363 // `ArrayBuffer` constructor
2364 // https://tc39.es/ecma262/#sec-arraybuffer-constructor
2365 _export({ global: true, forced: NativeArrayBuffer$1 !== ArrayBuffer$2 }, {
2366 ArrayBuffer: ArrayBuffer$2
2369 setSpecies(ARRAY_BUFFER$1);
2371 var arrayMethodIsStrict = function (METHOD_NAME, argument) {
2372 var method = [][METHOD_NAME];
2373 return !!method && fails(function () {
2374 // eslint-disable-next-line no-useless-call,no-throw-literal -- required for testing
2375 method.call(null, argument || function () { throw 1; }, 1);
2379 var $indexOf = arrayIncludes.indexOf;
2382 var nativeIndexOf = [].indexOf;
2384 var NEGATIVE_ZERO = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
2385 var STRICT_METHOD = arrayMethodIsStrict('indexOf');
2387 // `Array.prototype.indexOf` method
2388 // https://tc39.es/ecma262/#sec-array.prototype.indexof
2389 _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO || !STRICT_METHOD }, {
2390 indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
2391 return NEGATIVE_ZERO
2393 ? nativeIndexOf.apply(this, arguments) || 0
2394 : $indexOf(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
2398 var SPECIES$3 = wellKnownSymbol('species');
2400 var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
2401 // We can't use this feature detection in V8 since it causes
2402 // deoptimization and serious performance degradation
2403 // https://github.com/zloirock/core-js/issues/677
2404 return engineV8Version >= 51 || !fails(function () {
2406 var constructor = array.constructor = {};
2407 constructor[SPECIES$3] = function () {
2410 return array[METHOD_NAME](Boolean).foo !== 1;
2414 var $map = arrayIteration.map;
2417 var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('map');
2419 // `Array.prototype.map` method
2420 // https://tc39.es/ecma262/#sec-array.prototype.map
2421 // with adding support of @@species
2422 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, {
2423 map: function map(callbackfn /* , thisArg */) {
2424 return $map(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2428 var $forEach$1 = arrayIteration.forEach;
2431 var STRICT_METHOD$1 = arrayMethodIsStrict('forEach');
2433 // `Array.prototype.forEach` method implementation
2434 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2435 var arrayForEach = !STRICT_METHOD$1 ? function forEach(callbackfn /* , thisArg */) {
2436 return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2439 // `Array.prototype.forEach` method
2440 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2441 _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
2442 forEach: arrayForEach
2445 for (var COLLECTION_NAME$1 in domIterables) {
2446 var Collection$1 = global_1[COLLECTION_NAME$1];
2447 var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
2448 // some Chrome versions have non-configurable methods on DOMTokenList
2449 if (CollectionPrototype$1 && CollectionPrototype$1.forEach !== arrayForEach) try {
2450 createNonEnumerableProperty(CollectionPrototype$1, 'forEach', arrayForEach);
2452 CollectionPrototype$1.forEach = arrayForEach;
2456 // `Array.isArray` method
2457 // https://tc39.es/ecma262/#sec-array.isarray
2458 _export({ target: 'Array', stat: true }, {
2462 var nativeGetOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2464 var FAILS_ON_PRIMITIVES = fails(function () { return !Object.getOwnPropertyNames(1); });
2466 // `Object.getOwnPropertyNames` method
2467 // https://tc39.es/ecma262/#sec-object.getownpropertynames
2468 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, {
2469 getOwnPropertyNames: nativeGetOwnPropertyNames$2
2472 var nativePromiseConstructor = global_1.Promise;
2474 var ITERATOR$3 = wellKnownSymbol('iterator');
2475 var ArrayPrototype$1 = Array.prototype;
2477 // check on default Array iterator
2478 var isArrayIteratorMethod = function (it) {
2479 return it !== undefined && (iterators.Array === it || ArrayPrototype$1[ITERATOR$3] === it);
2482 var ITERATOR$4 = wellKnownSymbol('iterator');
2484 var getIteratorMethod = function (it) {
2485 if (it != undefined) return it[ITERATOR$4]
2487 || iterators[classof(it)];
2490 var iteratorClose = function (iterator) {
2491 var returnMethod = iterator['return'];
2492 if (returnMethod !== undefined) {
2493 return anObject(returnMethod.call(iterator)).value;
2497 var Result = function (stopped, result) {
2498 this.stopped = stopped;
2499 this.result = result;
2502 var iterate = function (iterable, unboundFunction, options) {
2503 var that = options && options.that;
2504 var AS_ENTRIES = !!(options && options.AS_ENTRIES);
2505 var IS_ITERATOR = !!(options && options.IS_ITERATOR);
2506 var INTERRUPTED = !!(options && options.INTERRUPTED);
2507 var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
2508 var iterator, iterFn, index, length, result, next, step;
2510 var stop = function (condition) {
2511 if (iterator) iteratorClose(iterator);
2512 return new Result(true, condition);
2515 var callFn = function (value) {
2518 return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
2519 } return INTERRUPTED ? fn(value, stop) : fn(value);
2523 iterator = iterable;
2525 iterFn = getIteratorMethod(iterable);
2526 if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2527 // optimisation for array iterators
2528 if (isArrayIteratorMethod(iterFn)) {
2529 for (index = 0, length = toLength(iterable.length); length > index; index++) {
2530 result = callFn(iterable[index]);
2531 if (result && result instanceof Result) return result;
2532 } return new Result(false);
2534 iterator = iterFn.call(iterable);
2537 next = iterator.next;
2538 while (!(step = next.call(iterator)).done) {
2540 result = callFn(step.value);
2542 iteratorClose(iterator);
2545 if (typeof result == 'object' && result && result instanceof Result) return result;
2546 } return new Result(false);
2549 var ITERATOR$5 = wellKnownSymbol('iterator');
2550 var SAFE_CLOSING = false;
2554 var iteratorWithReturn = {
2556 return { done: !!called++ };
2558 'return': function () {
2559 SAFE_CLOSING = true;
2562 iteratorWithReturn[ITERATOR$5] = function () {
2565 // eslint-disable-next-line no-throw-literal -- required for testing
2566 Array.from(iteratorWithReturn, function () { throw 2; });
2567 } catch (error) { /* empty */ }
2569 var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2570 if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2571 var ITERATION_SUPPORT = false;
2574 object[ITERATOR$5] = function () {
2577 return { done: ITERATION_SUPPORT = true };
2582 } catch (error) { /* empty */ }
2583 return ITERATION_SUPPORT;
2586 var engineIsIos = /(iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2588 var location$1 = global_1.location;
2589 var set$2 = global_1.setImmediate;
2590 var clear = global_1.clearImmediate;
2591 var process$2 = global_1.process;
2592 var MessageChannel = global_1.MessageChannel;
2593 var Dispatch = global_1.Dispatch;
2596 var ONREADYSTATECHANGE = 'onreadystatechange';
2597 var defer, channel, port;
2599 var run = function (id) {
2600 // eslint-disable-next-line no-prototype-builtins -- safe
2601 if (queue.hasOwnProperty(id)) {
2608 var runner = function (id) {
2609 return function () {
2614 var listener = function (event) {
2618 var post = function (id) {
2619 // old engines have not location.origin
2620 global_1.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2623 // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2624 if (!set$2 || !clear) {
2625 set$2 = function setImmediate(fn) {
2628 while (arguments.length > i) args.push(arguments[i++]);
2629 queue[++counter] = function () {
2630 // eslint-disable-next-line no-new-func -- spec requirement
2631 (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2636 clear = function clearImmediate(id) {
2641 defer = function (id) {
2642 process$2.nextTick(runner(id));
2644 // Sphere (JS game engine) Dispatch API
2645 } else if (Dispatch && Dispatch.now) {
2646 defer = function (id) {
2647 Dispatch.now(runner(id));
2649 // Browsers with MessageChannel, includes WebWorkers
2650 // except iOS - https://github.com/zloirock/core-js/issues/624
2651 } else if (MessageChannel && !engineIsIos) {
2652 channel = new MessageChannel();
2653 port = channel.port2;
2654 channel.port1.onmessage = listener;
2655 defer = functionBindContext(port.postMessage, port, 1);
2656 // Browsers with postMessage, skip WebWorkers
2657 // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2659 global_1.addEventListener &&
2660 typeof postMessage == 'function' &&
2661 !global_1.importScripts &&
2662 location$1 && location$1.protocol !== 'file:' &&
2666 global_1.addEventListener('message', listener, false);
2668 } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2669 defer = function (id) {
2670 html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2671 html.removeChild(this);
2675 // Rest old browsers
2677 defer = function (id) {
2678 setTimeout(runner(id), 0);
2688 var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(engineUserAgent);
2690 var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
2691 var macrotask = task.set;
2696 var MutationObserver = global_1.MutationObserver || global_1.WebKitMutationObserver;
2697 var document$2 = global_1.document;
2698 var process$3 = global_1.process;
2699 var Promise$1 = global_1.Promise;
2700 // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2701 var queueMicrotaskDescriptor = getOwnPropertyDescriptor$2(global_1, 'queueMicrotask');
2702 var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2704 var flush, head, last, notify, toggle, node, promise, then;
2706 // modern engines have queueMicrotask method
2707 if (!queueMicrotask) {
2708 flush = function () {
2710 if (engineIsNode && (parent = process$3.domain)) parent.exit();
2718 else last = undefined;
2722 if (parent) parent.enter();
2725 // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2726 // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898
2727 if (!engineIsIos && !engineIsNode && !engineIsWebosWebkit && MutationObserver && document$2) {
2729 node = document$2.createTextNode('');
2730 new MutationObserver(flush).observe(node, { characterData: true });
2731 notify = function () {
2732 node.data = toggle = !toggle;
2734 // environments with maybe non-completely correct, but existent Promise
2735 } else if (Promise$1 && Promise$1.resolve) {
2736 // Promise.resolve without an argument throws an error in LG WebOS 2
2737 promise = Promise$1.resolve(undefined);
2738 then = promise.then;
2739 notify = function () {
2740 then.call(promise, flush);
2742 // Node.js without promises
2743 } else if (engineIsNode) {
2744 notify = function () {
2745 process$3.nextTick(flush);
2747 // for other environments - macrotask based on:
2750 // - window.postMessag
2751 // - onreadystatechange
2754 notify = function () {
2755 // strange IE + webpack dev server bug - use .call(global)
2756 macrotask.call(global_1, flush);
2761 var microtask = queueMicrotask || function (fn) {
2762 var task = { fn: fn, next: undefined };
2763 if (last) last.next = task;
2770 var PromiseCapability = function (C) {
2771 var resolve, reject;
2772 this.promise = new C(function ($$resolve, $$reject) {
2773 if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2774 resolve = $$resolve;
2777 this.resolve = aFunction$1(resolve);
2778 this.reject = aFunction$1(reject);
2781 // 25.4.1.5 NewPromiseCapability(C)
2782 var f$7 = function (C) {
2783 return new PromiseCapability(C);
2786 var newPromiseCapability = {
2790 var promiseResolve = function (C, x) {
2792 if (isObject(x) && x.constructor === C) return x;
2793 var promiseCapability = newPromiseCapability.f(C);
2794 var resolve = promiseCapability.resolve;
2796 return promiseCapability.promise;
2799 var hostReportErrors = function (a, b) {
2800 var console = global_1.console;
2801 if (console && console.error) {
2802 arguments.length === 1 ? console.error(a) : console.error(a, b);
2806 var perform = function (exec) {
2808 return { error: false, value: exec() };
2810 return { error: true, value: error };
2814 var task$1 = task.set;
2826 var SPECIES$4 = wellKnownSymbol('species');
2827 var PROMISE = 'Promise';
2828 var getInternalState$4 = internalState.get;
2829 var setInternalState$4 = internalState.set;
2830 var getInternalPromiseState = internalState.getterFor(PROMISE);
2831 var PromiseConstructor = nativePromiseConstructor;
2832 var TypeError$1 = global_1.TypeError;
2833 var document$3 = global_1.document;
2834 var process$4 = global_1.process;
2835 var $fetch = getBuiltIn('fetch');
2836 var newPromiseCapability$1 = newPromiseCapability.f;
2837 var newGenericPromiseCapability = newPromiseCapability$1;
2838 var DISPATCH_EVENT = !!(document$3 && document$3.createEvent && global_1.dispatchEvent);
2839 var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
2840 var UNHANDLED_REJECTION = 'unhandledrejection';
2841 var REJECTION_HANDLED = 'rejectionhandled';
2847 var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2849 var FORCED = isForced_1(PROMISE, function () {
2850 var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor);
2851 if (!GLOBAL_CORE_JS_PROMISE) {
2852 // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2853 // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2854 // We can't detect it synchronously, so just check versions
2855 if (engineV8Version === 66) return true;
2856 // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2857 if (!engineIsNode && !NATIVE_REJECTION_EVENT) return true;
2859 // We can't use @@species feature detection in V8 since it causes
2860 // deoptimization and performance degradation
2861 // https://github.com/zloirock/core-js/issues/679
2862 if (engineV8Version >= 51 && /native code/.test(PromiseConstructor)) return false;
2863 // Detect correctness of subclassing with @@species support
2864 var promise = PromiseConstructor.resolve(1);
2865 var FakePromise = function (exec) {
2866 exec(function () { /* empty */ }, function () { /* empty */ });
2868 var constructor = promise.constructor = {};
2869 constructor[SPECIES$4] = FakePromise;
2870 return !(promise.then(function () { /* empty */ }) instanceof FakePromise);
2873 var INCORRECT_ITERATION = FORCED || !checkCorrectnessOfIteration(function (iterable) {
2874 PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2878 var isThenable = function (it) {
2880 return isObject(it) && typeof (then = it.then) == 'function' ? then : false;
2883 var notify$1 = function (state, isReject) {
2884 if (state.notified) return;
2885 state.notified = true;
2886 var chain = state.reactions;
2887 microtask(function () {
2888 var value = state.value;
2889 var ok = state.state == FULFILLED;
2891 // variable length - can't use forEach
2892 while (chain.length > index) {
2893 var reaction = chain[index++];
2894 var handler = ok ? reaction.ok : reaction.fail;
2895 var resolve = reaction.resolve;
2896 var reject = reaction.reject;
2897 var domain = reaction.domain;
2898 var result, then, exited;
2902 if (state.rejection === UNHANDLED) onHandleUnhandled(state);
2903 state.rejection = HANDLED;
2905 if (handler === true) result = value;
2907 if (domain) domain.enter();
2908 result = handler(value); // can throw
2914 if (result === reaction.promise) {
2915 reject(TypeError$1('Promise-chain cycle'));
2916 } else if (then = isThenable(result)) {
2917 then.call(result, resolve, reject);
2918 } else resolve(result);
2919 } else reject(value);
2921 if (domain && !exited) domain.exit();
2925 state.reactions = [];
2926 state.notified = false;
2927 if (isReject && !state.rejection) onUnhandled(state);
2931 var dispatchEvent = function (name, promise, reason) {
2933 if (DISPATCH_EVENT) {
2934 event = document$3.createEvent('Event');
2935 event.promise = promise;
2936 event.reason = reason;
2937 event.initEvent(name, false, true);
2938 global_1.dispatchEvent(event);
2939 } else event = { promise: promise, reason: reason };
2940 if (!NATIVE_REJECTION_EVENT && (handler = global_1['on' + name])) handler(event);
2941 else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2944 var onUnhandled = function (state) {
2945 task$1.call(global_1, function () {
2946 var promise = state.facade;
2947 var value = state.value;
2948 var IS_UNHANDLED = isUnhandled(state);
2951 result = perform(function () {
2953 process$4.emit('unhandledRejection', value, promise);
2954 } else dispatchEvent(UNHANDLED_REJECTION, promise, value);
2956 // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2957 state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
2958 if (result.error) throw result.value;
2963 var isUnhandled = function (state) {
2964 return state.rejection !== HANDLED && !state.parent;
2967 var onHandleUnhandled = function (state) {
2968 task$1.call(global_1, function () {
2969 var promise = state.facade;
2971 process$4.emit('rejectionHandled', promise);
2972 } else dispatchEvent(REJECTION_HANDLED, promise, state.value);
2976 var bind = function (fn, state, unwrap) {
2977 return function (value) {
2978 fn(state, value, unwrap);
2982 var internalReject = function (state, value, unwrap) {
2983 if (state.done) return;
2985 if (unwrap) state = unwrap;
2986 state.value = value;
2987 state.state = REJECTED;
2988 notify$1(state, true);
2991 var internalResolve = function (state, value, unwrap) {
2992 if (state.done) return;
2994 if (unwrap) state = unwrap;
2996 if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
2997 var then = isThenable(value);
2999 microtask(function () {
3000 var wrapper = { done: false };
3003 bind(internalResolve, wrapper, state),
3004 bind(internalReject, wrapper, state)
3007 internalReject(wrapper, error, state);
3011 state.value = value;
3012 state.state = FULFILLED;
3013 notify$1(state, false);
3016 internalReject({ done: false }, error, state);
3020 // constructor polyfill
3022 // 25.4.3.1 Promise(executor)
3023 PromiseConstructor = function Promise(executor) {
3024 anInstance(this, PromiseConstructor, PROMISE);
3025 aFunction$1(executor);
3026 Internal.call(this);
3027 var state = getInternalState$4(this);
3029 executor(bind(internalResolve, state), bind(internalReject, state));
3031 internalReject(state, error);
3034 // eslint-disable-next-line no-unused-vars -- required for `.length`
3035 Internal = function Promise(executor) {
3036 setInternalState$4(this, {
3047 Internal.prototype = redefineAll(PromiseConstructor.prototype, {
3048 // `Promise.prototype.then` method
3049 // https://tc39.es/ecma262/#sec-promise.prototype.then
3050 then: function then(onFulfilled, onRejected) {
3051 var state = getInternalPromiseState(this);
3052 var reaction = newPromiseCapability$1(speciesConstructor(this, PromiseConstructor));
3053 reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
3054 reaction.fail = typeof onRejected == 'function' && onRejected;
3055 reaction.domain = engineIsNode ? process$4.domain : undefined;
3056 state.parent = true;
3057 state.reactions.push(reaction);
3058 if (state.state != PENDING) notify$1(state, false);
3059 return reaction.promise;
3061 // `Promise.prototype.catch` method
3062 // https://tc39.es/ecma262/#sec-promise.prototype.catch
3063 'catch': function (onRejected) {
3064 return this.then(undefined, onRejected);
3067 OwnPromiseCapability = function () {
3068 var promise = new Internal();
3069 var state = getInternalState$4(promise);
3070 this.promise = promise;
3071 this.resolve = bind(internalResolve, state);
3072 this.reject = bind(internalReject, state);
3074 newPromiseCapability.f = newPromiseCapability$1 = function (C) {
3075 return C === PromiseConstructor || C === PromiseWrapper
3076 ? new OwnPromiseCapability(C)
3077 : newGenericPromiseCapability(C);
3080 if ( typeof nativePromiseConstructor == 'function') {
3081 nativeThen = nativePromiseConstructor.prototype.then;
3083 // wrap native Promise#then for native async functions
3084 redefine(nativePromiseConstructor.prototype, 'then', function then(onFulfilled, onRejected) {
3086 return new PromiseConstructor(function (resolve, reject) {
3087 nativeThen.call(that, resolve, reject);
3088 }).then(onFulfilled, onRejected);
3089 // https://github.com/zloirock/core-js/issues/640
3090 }, { unsafe: true });
3092 // wrap fetch result
3093 if (typeof $fetch == 'function') _export({ global: true, enumerable: true, forced: true }, {
3094 // eslint-disable-next-line no-unused-vars -- required for `.length`
3095 fetch: function fetch(input /* , init */) {
3096 return promiseResolve(PromiseConstructor, $fetch.apply(global_1, arguments));
3102 _export({ global: true, wrap: true, forced: FORCED }, {
3103 Promise: PromiseConstructor
3106 setToStringTag(PromiseConstructor, PROMISE, false);
3107 setSpecies(PROMISE);
3109 PromiseWrapper = getBuiltIn(PROMISE);
3112 _export({ target: PROMISE, stat: true, forced: FORCED }, {
3113 // `Promise.reject` method
3114 // https://tc39.es/ecma262/#sec-promise.reject
3115 reject: function reject(r) {
3116 var capability = newPromiseCapability$1(this);
3117 capability.reject.call(undefined, r);
3118 return capability.promise;
3122 _export({ target: PROMISE, stat: true, forced: FORCED }, {
3123 // `Promise.resolve` method
3124 // https://tc39.es/ecma262/#sec-promise.resolve
3125 resolve: function resolve(x) {
3126 return promiseResolve( this, x);
3130 _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION }, {
3131 // `Promise.all` method
3132 // https://tc39.es/ecma262/#sec-promise.all
3133 all: function all(iterable) {
3135 var capability = newPromiseCapability$1(C);
3136 var resolve = capability.resolve;
3137 var reject = capability.reject;
3138 var result = perform(function () {
3139 var $promiseResolve = aFunction$1(C.resolve);
3143 iterate(iterable, function (promise) {
3144 var index = counter++;
3145 var alreadyCalled = false;
3146 values.push(undefined);
3148 $promiseResolve.call(C, promise).then(function (value) {
3149 if (alreadyCalled) return;
3150 alreadyCalled = true;
3151 values[index] = value;
3152 --remaining || resolve(values);
3155 --remaining || resolve(values);
3157 if (result.error) reject(result.value);
3158 return capability.promise;
3160 // `Promise.race` method
3161 // https://tc39.es/ecma262/#sec-promise.race
3162 race: function race(iterable) {
3164 var capability = newPromiseCapability$1(C);
3165 var reject = capability.reject;
3166 var result = perform(function () {
3167 var $promiseResolve = aFunction$1(C.resolve);
3168 iterate(iterable, function (promise) {
3169 $promiseResolve.call(C, promise).then(capability.resolve, reject);
3172 if (result.error) reject(result.value);
3173 return capability.promise;
3177 /* eslint-disable no-new -- required for testing */
3181 var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3183 var ArrayBuffer$3 = global_1.ArrayBuffer;
3184 var Int8Array$2 = global_1.Int8Array;
3186 var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS$2 || !fails(function () {
3188 }) || !fails(function () {
3189 new Int8Array$2(-1);
3190 }) || !checkCorrectnessOfIteration(function (iterable) {
3192 new Int8Array$2(null);
3193 new Int8Array$2(1.5);
3194 new Int8Array$2(iterable);
3195 }, true) || fails(function () {
3196 // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3197 return new Int8Array$2(new ArrayBuffer$3(2), 1, undefined).length !== 1;
3200 var toPositiveInteger = function (it) {
3201 var result = toInteger(it);
3202 if (result < 0) throw RangeError("The argument can't be less than 0");
3206 var toOffset = function (it, BYTES) {
3207 var offset = toPositiveInteger(it);
3208 if (offset % BYTES) throw RangeError('Wrong offset');
3212 var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3214 var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3215 var O = toObject(source);
3216 var argumentsLength = arguments.length;
3217 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3218 var mapping = mapfn !== undefined;
3219 var iteratorMethod = getIteratorMethod(O);
3220 var i, length, result, step, iterator, next;
3221 if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3222 iterator = iteratorMethod.call(O);
3223 next = iterator.next;
3225 while (!(step = next.call(iterator)).done) {
3229 if (mapping && argumentsLength > 2) {
3230 mapfn = functionBindContext(mapfn, arguments[2], 2);
3232 length = toLength(O.length);
3233 result = new (aTypedArrayConstructor$1(this))(length);
3234 for (i = 0; length > i; i++) {
3235 result[i] = mapping ? mapfn(O[i], i) : O[i];
3240 // makes subclassing work correct for wrapped built-ins
3241 var inheritIfRequired = function ($this, dummy, Wrapper) {
3242 var NewTarget, NewTargetPrototype;
3244 // it can work only with native `setPrototypeOf`
3245 objectSetPrototypeOf &&
3246 // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3247 typeof (NewTarget = dummy.constructor) == 'function' &&
3248 NewTarget !== Wrapper &&
3249 isObject(NewTargetPrototype = NewTarget.prototype) &&
3250 NewTargetPrototype !== Wrapper.prototype
3251 ) objectSetPrototypeOf($this, NewTargetPrototype);
3255 var typedArrayConstructor = createCommonjsModule(function (module) {
3274 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3276 var forEach = arrayIteration.forEach;
3283 var getInternalState = internalState.get;
3284 var setInternalState = internalState.set;
3285 var nativeDefineProperty = objectDefineProperty.f;
3286 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3287 var round = Math.round;
3288 var RangeError = global_1.RangeError;
3289 var ArrayBuffer = arrayBuffer.ArrayBuffer;
3290 var DataView = arrayBuffer.DataView;
3291 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3292 var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3293 var TypedArray = arrayBufferViewCore.TypedArray;
3294 var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3295 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3296 var isTypedArray = arrayBufferViewCore.isTypedArray;
3297 var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3298 var WRONG_LENGTH = 'Wrong length';
3300 var fromList = function (C, list) {
3302 var length = list.length;
3303 var result = new (aTypedArrayConstructor(C))(length);
3304 while (length > index) result[index] = list[index++];
3308 var addGetter = function (it, key) {
3309 nativeDefineProperty(it, key, { get: function () {
3310 return getInternalState(this)[key];
3314 var isArrayBuffer = function (it) {
3316 return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3319 var isTypedArrayIndex = function (target, key) {
3320 return isTypedArray(target)
3321 && typeof key != 'symbol'
3323 && String(+key) == String(key);
3326 var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3327 return isTypedArrayIndex(target, key = toPrimitive(key, true))
3328 ? createPropertyDescriptor(2, target[key])
3329 : nativeGetOwnPropertyDescriptor(target, key);
3332 var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3333 if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3334 && isObject(descriptor)
3335 && has(descriptor, 'value')
3336 && !has(descriptor, 'get')
3337 && !has(descriptor, 'set')
3338 // TODO: add validation descriptor w/o calling accessors
3339 && !descriptor.configurable
3340 && (!has(descriptor, 'writable') || descriptor.writable)
3341 && (!has(descriptor, 'enumerable') || descriptor.enumerable)
3343 target[key] = descriptor.value;
3345 } return nativeDefineProperty(target, key, descriptor);
3349 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3350 objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3351 objectDefineProperty.f = wrappedDefineProperty;
3352 addGetter(TypedArrayPrototype, 'buffer');
3353 addGetter(TypedArrayPrototype, 'byteOffset');
3354 addGetter(TypedArrayPrototype, 'byteLength');
3355 addGetter(TypedArrayPrototype, 'length');
3358 _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3359 getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3360 defineProperty: wrappedDefineProperty
3363 module.exports = function (TYPE, wrapper, CLAMPED) {
3364 var BYTES = TYPE.match(/\d+$/)[0] / 8;
3365 var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3366 var GETTER = 'get' + TYPE;
3367 var SETTER = 'set' + TYPE;
3368 var NativeTypedArrayConstructor = global_1[CONSTRUCTOR_NAME];
3369 var TypedArrayConstructor = NativeTypedArrayConstructor;
3370 var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3373 var getter = function (that, index) {
3374 var data = getInternalState(that);
3375 return data.view[GETTER](index * BYTES + data.byteOffset, true);
3378 var setter = function (that, index, value) {
3379 var data = getInternalState(that);
3380 if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3381 data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3384 var addElement = function (that, index) {
3385 nativeDefineProperty(that, index, {
3387 return getter(this, index);
3389 set: function (value) {
3390 return setter(this, index, value);
3396 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3397 TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3398 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3401 var buffer, byteLength, length;
3402 if (!isObject(data)) {
3403 length = toIndex(data);
3404 byteLength = length * BYTES;
3405 buffer = new ArrayBuffer(byteLength);
3406 } else if (isArrayBuffer(data)) {
3408 byteOffset = toOffset(offset, BYTES);
3409 var $len = data.byteLength;
3410 if ($length === undefined) {
3411 if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3412 byteLength = $len - byteOffset;
3413 if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3415 byteLength = toLength($length) * BYTES;
3416 if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3418 length = byteLength / BYTES;
3419 } else if (isTypedArray(data)) {
3420 return fromList(TypedArrayConstructor, data);
3422 return typedArrayFrom.call(TypedArrayConstructor, data);
3424 setInternalState(that, {
3426 byteOffset: byteOffset,
3427 byteLength: byteLength,
3429 view: new DataView(buffer)
3431 while (index < length) addElement(that, index++);
3434 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3435 TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3436 } else if (typedArrayConstructorsRequireWrappers) {
3437 TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3438 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3439 return inheritIfRequired(function () {
3440 if (!isObject(data)) return new NativeTypedArrayConstructor(toIndex(data));
3441 if (isArrayBuffer(data)) return $length !== undefined
3442 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3443 : typedArrayOffset !== undefined
3444 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3445 : new NativeTypedArrayConstructor(data);
3446 if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3447 return typedArrayFrom.call(TypedArrayConstructor, data);
3448 }(), dummy, TypedArrayConstructor);
3451 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3452 forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3453 if (!(key in TypedArrayConstructor)) {
3454 createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3457 TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3460 if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3461 createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3464 if (TYPED_ARRAY_TAG) {
3465 createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3468 exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3471 global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3474 if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3475 createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3478 if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3479 createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3482 setSpecies(CONSTRUCTOR_NAME);
3484 } else module.exports = function () { /* empty */ };
3487 // `Uint8Array` constructor
3488 // https://tc39.es/ecma262/#sec-typedarray-objects
3489 typedArrayConstructor('Uint8', function (init) {
3490 return function Uint8Array(data, byteOffset, length) {
3491 return init(this, data, byteOffset, length);
3495 var min$2 = Math.min;
3497 // `Array.prototype.copyWithin` method implementation
3498 // https://tc39.es/ecma262/#sec-array.prototype.copywithin
3499 var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3500 var O = toObject(this);
3501 var len = toLength(O.length);
3502 var to = toAbsoluteIndex(target, len);
3503 var from = toAbsoluteIndex(start, len);
3504 var end = arguments.length > 2 ? arguments[2] : undefined;
3505 var count = min$2((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3507 if (from < to && to < from + count) {
3512 while (count-- > 0) {
3513 if (from in O) O[to] = O[from];
3520 var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
3521 var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
3523 // `%TypedArray%.prototype.copyWithin` method
3524 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
3525 exportTypedArrayMethod$1('copyWithin', function copyWithin(target, start /* , end */) {
3526 return arrayCopyWithin.call(aTypedArray$1(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3529 var $every = arrayIteration.every;
3531 var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3532 var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
3534 // `%TypedArray%.prototype.every` method
3535 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.every
3536 exportTypedArrayMethod$2('every', function every(callbackfn /* , thisArg */) {
3537 return $every(aTypedArray$2(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3540 var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3541 var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3543 // `%TypedArray%.prototype.fill` method
3544 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill
3545 // eslint-disable-next-line no-unused-vars -- required for `.length`
3546 exportTypedArrayMethod$3('fill', function fill(value /* , start, end */) {
3547 return arrayFill.apply(aTypedArray$3(this), arguments);
3550 var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3553 var typedArrayFromSpeciesAndList = function (instance, list) {
3554 var C = speciesConstructor(instance, instance.constructor);
3556 var length = list.length;
3557 var result = new (aTypedArrayConstructor$2(C))(length);
3558 while (length > index) result[index] = list[index++];
3562 var $filter = arrayIteration.filter;
3565 var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3566 var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3568 // `%TypedArray%.prototype.filter` method
3569 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter
3570 exportTypedArrayMethod$4('filter', function filter(callbackfn /* , thisArg */) {
3571 var list = $filter(aTypedArray$4(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3572 return typedArrayFromSpeciesAndList(this, list);
3575 var $find = arrayIteration.find;
3577 var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
3578 var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
3580 // `%TypedArray%.prototype.find` method
3581 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.find
3582 exportTypedArrayMethod$5('find', function find(predicate /* , thisArg */) {
3583 return $find(aTypedArray$5(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3586 var $findIndex = arrayIteration.findIndex;
3588 var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
3589 var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
3591 // `%TypedArray%.prototype.findIndex` method
3592 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex
3593 exportTypedArrayMethod$6('findIndex', function findIndex(predicate /* , thisArg */) {
3594 return $findIndex(aTypedArray$6(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3597 var $forEach$2 = arrayIteration.forEach;
3599 var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
3600 var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
3602 // `%TypedArray%.prototype.forEach` method
3603 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
3604 exportTypedArrayMethod$7('forEach', function forEach(callbackfn /* , thisArg */) {
3605 $forEach$2(aTypedArray$7(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3608 var $includes = arrayIncludes.includes;
3610 var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
3611 var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
3613 // `%TypedArray%.prototype.includes` method
3614 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes
3615 exportTypedArrayMethod$8('includes', function includes(searchElement /* , fromIndex */) {
3616 return $includes(aTypedArray$8(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3619 var $indexOf$1 = arrayIncludes.indexOf;
3621 var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
3622 var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
3624 // `%TypedArray%.prototype.indexOf` method
3625 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof
3626 exportTypedArrayMethod$9('indexOf', function indexOf(searchElement /* , fromIndex */) {
3627 return $indexOf$1(aTypedArray$9(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3630 var ITERATOR$6 = wellKnownSymbol('iterator');
3631 var Uint8Array$1 = global_1.Uint8Array;
3632 var arrayValues = es_array_iterator.values;
3633 var arrayKeys = es_array_iterator.keys;
3634 var arrayEntries = es_array_iterator.entries;
3635 var aTypedArray$a = arrayBufferViewCore.aTypedArray;
3636 var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
3637 var nativeTypedArrayIterator = Uint8Array$1 && Uint8Array$1.prototype[ITERATOR$6];
3639 var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
3640 && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
3642 var typedArrayValues = function values() {
3643 return arrayValues.call(aTypedArray$a(this));
3646 // `%TypedArray%.prototype.entries` method
3647 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries
3648 exportTypedArrayMethod$a('entries', function entries() {
3649 return arrayEntries.call(aTypedArray$a(this));
3651 // `%TypedArray%.prototype.keys` method
3652 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys
3653 exportTypedArrayMethod$a('keys', function keys() {
3654 return arrayKeys.call(aTypedArray$a(this));
3656 // `%TypedArray%.prototype.values` method
3657 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
3658 exportTypedArrayMethod$a('values', typedArrayValues, !CORRECT_ITER_NAME);
3659 // `%TypedArray%.prototype[@@iterator]` method
3660 // https://tc39.es/ecma262/#sec-%typedarray%.prototype-@@iterator
3661 exportTypedArrayMethod$a(ITERATOR$6, typedArrayValues, !CORRECT_ITER_NAME);
3663 var aTypedArray$b = arrayBufferViewCore.aTypedArray;
3664 var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
3665 var $join = [].join;
3667 // `%TypedArray%.prototype.join` method
3668 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.join
3669 // eslint-disable-next-line no-unused-vars -- required for `.length`
3670 exportTypedArrayMethod$b('join', function join(separator) {
3671 return $join.apply(aTypedArray$b(this), arguments);
3674 var min$3 = Math.min;
3675 var nativeLastIndexOf = [].lastIndexOf;
3676 var NEGATIVE_ZERO$1 = !!nativeLastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
3677 var STRICT_METHOD$2 = arrayMethodIsStrict('lastIndexOf');
3678 var FORCED$1 = NEGATIVE_ZERO$1 || !STRICT_METHOD$2;
3680 // `Array.prototype.lastIndexOf` method implementation
3681 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
3682 var arrayLastIndexOf = FORCED$1 ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
3684 if (NEGATIVE_ZERO$1) return nativeLastIndexOf.apply(this, arguments) || 0;
3685 var O = toIndexedObject(this);
3686 var length = toLength(O.length);
3687 var index = length - 1;
3688 if (arguments.length > 1) index = min$3(index, toInteger(arguments[1]));
3689 if (index < 0) index = length + index;
3690 for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
3692 } : nativeLastIndexOf;
3694 var aTypedArray$c = arrayBufferViewCore.aTypedArray;
3695 var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
3697 // `%TypedArray%.prototype.lastIndexOf` method
3698 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof
3699 // eslint-disable-next-line no-unused-vars -- required for `.length`
3700 exportTypedArrayMethod$c('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
3701 return arrayLastIndexOf.apply(aTypedArray$c(this), arguments);
3704 var $map$1 = arrayIteration.map;
3707 var aTypedArray$d = arrayBufferViewCore.aTypedArray;
3708 var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
3709 var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
3711 // `%TypedArray%.prototype.map` method
3712 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
3713 exportTypedArrayMethod$d('map', function map(mapfn /* , thisArg */) {
3714 return $map$1(aTypedArray$d(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
3715 return new (aTypedArrayConstructor$3(speciesConstructor(O, O.constructor)))(length);
3719 // `Array.prototype.{ reduce, reduceRight }` methods implementation
3720 var createMethod$3 = function (IS_RIGHT) {
3721 return function (that, callbackfn, argumentsLength, memo) {
3722 aFunction$1(callbackfn);
3723 var O = toObject(that);
3724 var self = indexedObject(O);
3725 var length = toLength(O.length);
3726 var index = IS_RIGHT ? length - 1 : 0;
3727 var i = IS_RIGHT ? -1 : 1;
3728 if (argumentsLength < 2) while (true) {
3729 if (index in self) {
3735 if (IS_RIGHT ? index < 0 : length <= index) {
3736 throw TypeError('Reduce of empty array with no initial value');
3739 for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
3740 memo = callbackfn(memo, self[index], index, O);
3747 // `Array.prototype.reduce` method
3748 // https://tc39.es/ecma262/#sec-array.prototype.reduce
3749 left: createMethod$3(false),
3750 // `Array.prototype.reduceRight` method
3751 // https://tc39.es/ecma262/#sec-array.prototype.reduceright
3752 right: createMethod$3(true)
3755 var $reduce = arrayReduce.left;
3757 var aTypedArray$e = arrayBufferViewCore.aTypedArray;
3758 var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
3760 // `%TypedArray%.prototype.reduce` method
3761 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce
3762 exportTypedArrayMethod$e('reduce', function reduce(callbackfn /* , initialValue */) {
3763 return $reduce(aTypedArray$e(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3766 var $reduceRight = arrayReduce.right;
3768 var aTypedArray$f = arrayBufferViewCore.aTypedArray;
3769 var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
3771 // `%TypedArray%.prototype.reduceRicht` method
3772 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright
3773 exportTypedArrayMethod$f('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
3774 return $reduceRight(aTypedArray$f(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3777 var aTypedArray$g = arrayBufferViewCore.aTypedArray;
3778 var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
3779 var floor$2 = Math.floor;
3781 // `%TypedArray%.prototype.reverse` method
3782 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse
3783 exportTypedArrayMethod$g('reverse', function reverse() {
3785 var length = aTypedArray$g(that).length;
3786 var middle = floor$2(length / 2);
3789 while (index < middle) {
3790 value = that[index];
3791 that[index++] = that[--length];
3792 that[length] = value;
3796 var aTypedArray$h = arrayBufferViewCore.aTypedArray;
3797 var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
3799 var FORCED$2 = fails(function () {
3800 /* global Int8Array -- safe */
3801 new Int8Array(1).set({});
3804 // `%TypedArray%.prototype.set` method
3805 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
3806 exportTypedArrayMethod$h('set', function set(arrayLike /* , offset */) {
3807 aTypedArray$h(this);
3808 var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
3809 var length = this.length;
3810 var src = toObject(arrayLike);
3811 var len = toLength(src.length);
3813 if (len + offset > length) throw RangeError('Wrong length');
3814 while (index < len) this[offset + index] = src[index++];
3817 var aTypedArray$i = arrayBufferViewCore.aTypedArray;
3818 var aTypedArrayConstructor$4 = arrayBufferViewCore.aTypedArrayConstructor;
3819 var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
3820 var $slice = [].slice;
3822 var FORCED$3 = fails(function () {
3823 /* global Int8Array -- safe */
3824 new Int8Array(1).slice();
3827 // `%TypedArray%.prototype.slice` method
3828 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice
3829 exportTypedArrayMethod$i('slice', function slice(start, end) {
3830 var list = $slice.call(aTypedArray$i(this), start, end);
3831 var C = speciesConstructor(this, this.constructor);
3833 var length = list.length;
3834 var result = new (aTypedArrayConstructor$4(C))(length);
3835 while (length > index) result[index] = list[index++];
3839 var $some = arrayIteration.some;
3841 var aTypedArray$j = arrayBufferViewCore.aTypedArray;
3842 var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
3844 // `%TypedArray%.prototype.some` method
3845 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
3846 exportTypedArrayMethod$j('some', function some(callbackfn /* , thisArg */) {
3847 return $some(aTypedArray$j(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3850 var aTypedArray$k = arrayBufferViewCore.aTypedArray;
3851 var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
3852 var $sort = [].sort;
3854 // `%TypedArray%.prototype.sort` method
3855 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
3856 exportTypedArrayMethod$k('sort', function sort(comparefn) {
3857 return $sort.call(aTypedArray$k(this), comparefn);
3860 var aTypedArray$l = arrayBufferViewCore.aTypedArray;
3861 var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
3863 // `%TypedArray%.prototype.subarray` method
3864 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray
3865 exportTypedArrayMethod$l('subarray', function subarray(begin, end) {
3866 var O = aTypedArray$l(this);
3867 var length = O.length;
3868 var beginIndex = toAbsoluteIndex(begin, length);
3869 return new (speciesConstructor(O, O.constructor))(
3871 O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
3872 toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
3876 var Int8Array$3 = global_1.Int8Array;
3877 var aTypedArray$m = arrayBufferViewCore.aTypedArray;
3878 var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
3879 var $toLocaleString = [].toLocaleString;
3880 var $slice$1 = [].slice;
3882 // iOS Safari 6.x fails here
3883 var TO_LOCALE_STRING_BUG = !!Int8Array$3 && fails(function () {
3884 $toLocaleString.call(new Int8Array$3(1));
3887 var FORCED$4 = fails(function () {
3888 return [1, 2].toLocaleString() != new Int8Array$3([1, 2]).toLocaleString();
3889 }) || !fails(function () {
3890 Int8Array$3.prototype.toLocaleString.call([1, 2]);
3893 // `%TypedArray%.prototype.toLocaleString` method
3894 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
3895 exportTypedArrayMethod$m('toLocaleString', function toLocaleString() {
3896 return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice$1.call(aTypedArray$m(this)) : aTypedArray$m(this), arguments);
3899 var exportTypedArrayMethod$n = arrayBufferViewCore.exportTypedArrayMethod;
3903 var Uint8Array$2 = global_1.Uint8Array;
3904 var Uint8ArrayPrototype = Uint8Array$2 && Uint8Array$2.prototype || {};
3905 var arrayToString = [].toString;
3906 var arrayJoin = [].join;
3908 if (fails(function () { arrayToString.call({}); })) {
3909 arrayToString = function toString() {
3910 return arrayJoin.call(this);
3914 var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
3916 // `%TypedArray%.prototype.toString` method
3917 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tostring
3918 exportTypedArrayMethod$n('toString', arrayToString, IS_NOT_ARRAY_METHOD);
3920 var nativeJoin = [].join;
3922 var ES3_STRINGS = indexedObject != Object;
3923 var STRICT_METHOD$3 = arrayMethodIsStrict('join', ',');
3925 // `Array.prototype.join` method
3926 // https://tc39.es/ecma262/#sec-array.prototype.join
3927 _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$3 }, {
3928 join: function join(separator) {
3929 return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
3933 var createProperty = function (object, key, value) {
3934 var propertyKey = toPrimitive(key);
3935 if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
3936 else object[propertyKey] = value;
3939 var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('slice');
3941 var SPECIES$5 = wellKnownSymbol('species');
3942 var nativeSlice = [].slice;
3943 var max$1 = Math.max;
3945 // `Array.prototype.slice` method
3946 // https://tc39.es/ecma262/#sec-array.prototype.slice
3947 // fallback for not array-like ES3 strings and DOM objects
3948 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, {
3949 slice: function slice(start, end) {
3950 var O = toIndexedObject(this);
3951 var length = toLength(O.length);
3952 var k = toAbsoluteIndex(start, length);
3953 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
3954 // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
3955 var Constructor, result, n;
3957 Constructor = O.constructor;
3958 // cross-realm fallback
3959 if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
3960 Constructor = undefined;
3961 } else if (isObject(Constructor)) {
3962 Constructor = Constructor[SPECIES$5];
3963 if (Constructor === null) Constructor = undefined;
3965 if (Constructor === Array || Constructor === undefined) {
3966 return nativeSlice.call(O, k, fin);
3969 result = new (Constructor === undefined ? Array : Constructor)(max$1(fin - k, 0));
3970 for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
3976 var ITERATOR$7 = wellKnownSymbol('iterator');
3978 var nativeUrl = !fails(function () {
3979 var url = new URL('b?a=1&b=2&c=3', 'http://a');
3980 var searchParams = url.searchParams;
3982 url.pathname = 'c%20d';
3983 searchParams.forEach(function (value, key) {
3984 searchParams['delete']('b');
3985 result += key + value;
3987 return (isPure && !url.toJSON)
3988 || !searchParams.sort
3989 || url.href !== 'http://a/c%20d?a=1&c=3'
3990 || searchParams.get('c') !== '3'
3991 || String(new URLSearchParams('?a=1')) !== 'a=1'
3992 || !searchParams[ITERATOR$7]
3994 || new URL('https://a@b').username !== 'a'
3995 || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
3996 // not punycoded in Edge
3997 || new URL('http://тест').host !== 'xn--e1aybc'
3998 // not escaped in Chrome 62-
3999 || new URL('http://a#б').hash !== '#%D0%B1'
4000 // fails in Chrome 66-
4001 || result !== 'a1c3'
4003 || new URL('http://x', undefined).host !== 'x';
4006 var nativeAssign = Object.assign;
4007 var defineProperty$5 = Object.defineProperty;
4009 // `Object.assign` method
4010 // https://tc39.es/ecma262/#sec-object.assign
4011 var objectAssign = !nativeAssign || fails(function () {
4012 // should have correct order of operations (Edge bug)
4013 if (descriptors && nativeAssign({ b: 1 }, nativeAssign(defineProperty$5({}, 'a', {
4016 defineProperty$5(this, 'b', {
4021 }), { b: 2 })).b !== 1) return true;
4022 // should work with symbols and should have deterministic property order (V8 bug)
4025 /* global Symbol -- required for testing */
4026 var symbol = Symbol();
4027 var alphabet = 'abcdefghijklmnopqrst';
4029 alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4030 return nativeAssign({}, A)[symbol] != 7 || objectKeys(nativeAssign({}, B)).join('') != alphabet;
4031 }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length`
4032 var T = toObject(target);
4033 var argumentsLength = arguments.length;
4035 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4036 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4037 while (argumentsLength > index) {
4038 var S = indexedObject(arguments[index++]);
4039 var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4040 var length = keys.length;
4043 while (length > j) {
4045 if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4050 // call something on iterator step with safe closing on error
4051 var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
4053 return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
4054 // 7.4.6 IteratorClose(iterator, completion)
4056 iteratorClose(iterator);
4061 // `Array.from` method implementation
4062 // https://tc39.es/ecma262/#sec-array.from
4063 var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4064 var O = toObject(arrayLike);
4065 var C = typeof this == 'function' ? this : Array;
4066 var argumentsLength = arguments.length;
4067 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4068 var mapping = mapfn !== undefined;
4069 var iteratorMethod = getIteratorMethod(O);
4071 var length, result, step, iterator, next, value;
4072 if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4073 // if the target is not iterable or it's an array with the default iterator - use a simple case
4074 if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4075 iterator = iteratorMethod.call(O);
4076 next = iterator.next;
4078 for (;!(step = next.call(iterator)).done; index++) {
4079 value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4080 createProperty(result, index, value);
4083 length = toLength(O.length);
4084 result = new C(length);
4085 for (;length > index; index++) {
4086 value = mapping ? mapfn(O[index], index) : O[index];
4087 createProperty(result, index, value);
4090 result.length = index;
4094 // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4095 var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4101 var initialBias = 72;
4102 var initialN = 128; // 0x80
4103 var delimiter = '-'; // '\x2D'
4104 var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4105 var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4106 var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4107 var baseMinusTMin = base - tMin;
4108 var floor$3 = Math.floor;
4109 var stringFromCharCode = String.fromCharCode;
4112 * Creates an array containing the numeric code points of each Unicode
4113 * character in the string. While JavaScript uses UCS-2 internally,
4114 * this function will convert a pair of surrogate halves (each of which
4115 * UCS-2 exposes as separate characters) into a single code point,
4118 var ucs2decode = function (string) {
4121 var length = string.length;
4122 while (counter < length) {
4123 var value = string.charCodeAt(counter++);
4124 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4125 // It's a high surrogate, and there is a next character.
4126 var extra = string.charCodeAt(counter++);
4127 if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4128 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4130 // It's an unmatched surrogate; only append this code unit, in case the
4131 // next code unit is the high surrogate of a surrogate pair.
4143 * Converts a digit/integer into a basic code point.
4145 var digitToBasic = function (digit) {
4146 // 0..25 map to ASCII a..z or A..Z
4147 // 26..35 map to ASCII 0..9
4148 return digit + 22 + 75 * (digit < 26);
4152 * Bias adaptation function as per section 3.4 of RFC 3492.
4153 * https://tools.ietf.org/html/rfc3492#section-3.4
4155 var adapt = function (delta, numPoints, firstTime) {
4157 delta = firstTime ? floor$3(delta / damp) : delta >> 1;
4158 delta += floor$3(delta / numPoints);
4159 for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4160 delta = floor$3(delta / baseMinusTMin);
4162 return floor$3(k + (baseMinusTMin + 1) * delta / (delta + skew));
4166 * Converts a string of Unicode symbols (e.g. a domain name label) to a
4167 * Punycode string of ASCII-only symbols.
4169 // eslint-disable-next-line max-statements -- TODO
4170 var encode = function (input) {
4173 // Convert the input in UCS-2 to an array of Unicode code points.
4174 input = ucs2decode(input);
4176 // Cache the length.
4177 var inputLength = input.length;
4179 // Initialize the state.
4182 var bias = initialBias;
4183 var i, currentValue;
4185 // Handle the basic code points.
4186 for (i = 0; i < input.length; i++) {
4187 currentValue = input[i];
4188 if (currentValue < 0x80) {
4189 output.push(stringFromCharCode(currentValue));
4193 var basicLength = output.length; // number of basic code points.
4194 var handledCPCount = basicLength; // number of code points that have been handled;
4196 // Finish the basic string with a delimiter unless it's empty.
4198 output.push(delimiter);
4201 // Main encoding loop:
4202 while (handledCPCount < inputLength) {
4203 // All non-basic code points < n have been handled already. Find the next larger one:
4205 for (i = 0; i < input.length; i++) {
4206 currentValue = input[i];
4207 if (currentValue >= n && currentValue < m) {
4212 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4213 var handledCPCountPlusOne = handledCPCount + 1;
4214 if (m - n > floor$3((maxInt - delta) / handledCPCountPlusOne)) {
4215 throw RangeError(OVERFLOW_ERROR);
4218 delta += (m - n) * handledCPCountPlusOne;
4221 for (i = 0; i < input.length; i++) {
4222 currentValue = input[i];
4223 if (currentValue < n && ++delta > maxInt) {
4224 throw RangeError(OVERFLOW_ERROR);
4226 if (currentValue == n) {
4227 // Represent delta as a generalized variable-length integer.
4229 for (var k = base; /* no condition */; k += base) {
4230 var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4232 var qMinusT = q - t;
4233 var baseMinusT = base - t;
4234 output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4235 q = floor$3(qMinusT / baseMinusT);
4238 output.push(stringFromCharCode(digitToBasic(q)));
4239 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4248 return output.join('');
4251 var stringPunycodeToAscii = function (input) {
4253 var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4255 for (i = 0; i < labels.length; i++) {
4257 encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4259 return encoded.join('.');
4262 var getIterator = function (it) {
4263 var iteratorMethod = getIteratorMethod(it);
4264 if (typeof iteratorMethod != 'function') {
4265 throw TypeError(String(it) + ' is not iterable');
4266 } return anObject(iteratorMethod.call(it));
4269 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4291 var $fetch$1 = getBuiltIn('fetch');
4292 var Headers = getBuiltIn('Headers');
4293 var ITERATOR$8 = wellKnownSymbol('iterator');
4294 var URL_SEARCH_PARAMS = 'URLSearchParams';
4295 var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4296 var setInternalState$5 = internalState.set;
4297 var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4298 var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4301 var sequences = Array(4);
4303 var percentSequence = function (bytes) {
4304 return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4307 var percentDecode = function (sequence) {
4309 return decodeURIComponent(sequence);
4315 var deserialize = function (it) {
4316 var result = it.replace(plus, ' ');
4319 return decodeURIComponent(result);
4322 result = result.replace(percentSequence(bytes--), percentDecode);
4328 var find = /[!'()~]|%20/g;
4339 var replacer = function (match) {
4340 return replace[match];
4343 var serialize = function (it) {
4344 return encodeURIComponent(it).replace(find, replacer);
4347 var parseSearchParams = function (result, query) {
4349 var attributes = query.split('&');
4351 var attribute, entry;
4352 while (index < attributes.length) {
4353 attribute = attributes[index++];
4354 if (attribute.length) {
4355 entry = attribute.split('=');
4357 key: deserialize(entry.shift()),
4358 value: deserialize(entry.join('='))
4365 var updateSearchParams = function (query) {
4366 this.entries.length = 0;
4367 parseSearchParams(this.entries, query);
4370 var validateArgumentsLength = function (passed, required) {
4371 if (passed < required) throw TypeError('Not enough arguments');
4374 var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4375 setInternalState$5(this, {
4376 type: URL_SEARCH_PARAMS_ITERATOR,
4377 iterator: getIterator(getInternalParamsState(params).entries),
4380 }, 'Iterator', function next() {
4381 var state = getInternalIteratorState(this);
4382 var kind = state.kind;
4383 var step = state.iterator.next();
4384 var entry = step.value;
4386 step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4390 // `URLSearchParams` constructor
4391 // https://url.spec.whatwg.org/#interface-urlsearchparams
4392 var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4393 anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4394 var init = arguments.length > 0 ? arguments[0] : undefined;
4397 var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4399 setInternalState$5(that, {
4400 type: URL_SEARCH_PARAMS,
4402 updateURL: function () { /* empty */ },
4403 updateSearchParams: updateSearchParams
4406 if (init !== undefined) {
4407 if (isObject(init)) {
4408 iteratorMethod = getIteratorMethod(init);
4409 if (typeof iteratorMethod === 'function') {
4410 iterator = iteratorMethod.call(init);
4411 next = iterator.next;
4412 while (!(step = next.call(iterator)).done) {
4413 entryIterator = getIterator(anObject(step.value));
4414 entryNext = entryIterator.next;
4416 (first = entryNext.call(entryIterator)).done ||
4417 (second = entryNext.call(entryIterator)).done ||
4418 !entryNext.call(entryIterator).done
4419 ) throw TypeError('Expected sequence with length 2');
4420 entries.push({ key: first.value + '', value: second.value + '' });
4422 } else for (key in init) if (has(init, key)) entries.push({ key: key, value: init[key] + '' });
4424 parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4429 var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4431 redefineAll(URLSearchParamsPrototype, {
4432 // `URLSearchParams.prototype.append` method
4433 // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4434 append: function append(name, value) {
4435 validateArgumentsLength(arguments.length, 2);
4436 var state = getInternalParamsState(this);
4437 state.entries.push({ key: name + '', value: value + '' });
4440 // `URLSearchParams.prototype.delete` method
4441 // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4442 'delete': function (name) {
4443 validateArgumentsLength(arguments.length, 1);
4444 var state = getInternalParamsState(this);
4445 var entries = state.entries;
4446 var key = name + '';
4448 while (index < entries.length) {
4449 if (entries[index].key === key) entries.splice(index, 1);
4454 // `URLSearchParams.prototype.get` method
4455 // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4456 get: function get(name) {
4457 validateArgumentsLength(arguments.length, 1);
4458 var entries = getInternalParamsState(this).entries;
4459 var key = name + '';
4461 for (; index < entries.length; index++) {
4462 if (entries[index].key === key) return entries[index].value;
4466 // `URLSearchParams.prototype.getAll` method
4467 // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4468 getAll: function getAll(name) {
4469 validateArgumentsLength(arguments.length, 1);
4470 var entries = getInternalParamsState(this).entries;
4471 var key = name + '';
4474 for (; index < entries.length; index++) {
4475 if (entries[index].key === key) result.push(entries[index].value);
4479 // `URLSearchParams.prototype.has` method
4480 // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4481 has: function has(name) {
4482 validateArgumentsLength(arguments.length, 1);
4483 var entries = getInternalParamsState(this).entries;
4484 var key = name + '';
4486 while (index < entries.length) {
4487 if (entries[index++].key === key) return true;
4491 // `URLSearchParams.prototype.set` method
4492 // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4493 set: function set(name, value) {
4494 validateArgumentsLength(arguments.length, 1);
4495 var state = getInternalParamsState(this);
4496 var entries = state.entries;
4498 var key = name + '';
4499 var val = value + '';
4502 for (; index < entries.length; index++) {
4503 entry = entries[index];
4504 if (entry.key === key) {
4505 if (found) entries.splice(index--, 1);
4512 if (!found) entries.push({ key: key, value: val });
4515 // `URLSearchParams.prototype.sort` method
4516 // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4517 sort: function sort() {
4518 var state = getInternalParamsState(this);
4519 var entries = state.entries;
4520 // Array#sort is not stable in some engines
4521 var slice = entries.slice();
4522 var entry, entriesIndex, sliceIndex;
4524 for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
4525 entry = slice[sliceIndex];
4526 for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
4527 if (entries[entriesIndex].key > entry.key) {
4528 entries.splice(entriesIndex, 0, entry);
4532 if (entriesIndex === sliceIndex) entries.push(entry);
4536 // `URLSearchParams.prototype.forEach` method
4537 forEach: function forEach(callback /* , thisArg */) {
4538 var entries = getInternalParamsState(this).entries;
4539 var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
4542 while (index < entries.length) {
4543 entry = entries[index++];
4544 boundFunction(entry.value, entry.key, this);
4547 // `URLSearchParams.prototype.keys` method
4548 keys: function keys() {
4549 return new URLSearchParamsIterator(this, 'keys');
4551 // `URLSearchParams.prototype.values` method
4552 values: function values() {
4553 return new URLSearchParamsIterator(this, 'values');
4555 // `URLSearchParams.prototype.entries` method
4556 entries: function entries() {
4557 return new URLSearchParamsIterator(this, 'entries');
4559 }, { enumerable: true });
4561 // `URLSearchParams.prototype[@@iterator]` method
4562 redefine(URLSearchParamsPrototype, ITERATOR$8, URLSearchParamsPrototype.entries);
4564 // `URLSearchParams.prototype.toString` method
4565 // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
4566 redefine(URLSearchParamsPrototype, 'toString', function toString() {
4567 var entries = getInternalParamsState(this).entries;
4571 while (index < entries.length) {
4572 entry = entries[index++];
4573 result.push(serialize(entry.key) + '=' + serialize(entry.value));
4574 } return result.join('&');
4575 }, { enumerable: true });
4577 setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4579 _export({ global: true, forced: !nativeUrl }, {
4580 URLSearchParams: URLSearchParamsConstructor
4583 // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
4584 // https://github.com/zloirock/core-js/issues/674
4585 if (!nativeUrl && typeof $fetch$1 == 'function' && typeof Headers == 'function') {
4586 _export({ global: true, enumerable: true, forced: true }, {
4587 fetch: function fetch(input /* , init */) {
4589 var init, body, headers;
4590 if (arguments.length > 1) {
4591 init = arguments[1];
4592 if (isObject(init)) {
4594 if (classof(body) === URL_SEARCH_PARAMS) {
4595 headers = init.headers ? new Headers(init.headers) : new Headers();
4596 if (!headers.has('content-type')) {
4597 headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
4599 init = objectCreate(init, {
4600 body: createPropertyDescriptor(0, String(body)),
4601 headers: createPropertyDescriptor(0, headers)
4606 } return $fetch$1.apply(this, args);
4611 var web_urlSearchParams = {
4612 URLSearchParams: URLSearchParamsConstructor,
4613 getState: getInternalParamsState
4616 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4628 var codeAt = stringMultibyte.codeAt;
4634 var NativeURL = global_1.URL;
4635 var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
4636 var getInternalSearchParamsState = web_urlSearchParams.getState;
4637 var setInternalState$6 = internalState.set;
4638 var getInternalURLState = internalState.getterFor('URL');
4639 var floor$4 = Math.floor;
4640 var pow$1 = Math.pow;
4642 var INVALID_AUTHORITY = 'Invalid authority';
4643 var INVALID_SCHEME = 'Invalid scheme';
4644 var INVALID_HOST = 'Invalid host';
4645 var INVALID_PORT = 'Invalid port';
4647 var ALPHA = /[A-Za-z]/;
4648 var ALPHANUMERIC = /[\d+-.A-Za-z]/;
4650 var HEX_START = /^(0x|0X)/;
4651 var OCT = /^[0-7]+$/;
4653 var HEX = /^[\dA-Fa-f]+$/;
4654 /* eslint-disable no-control-regex -- safe */
4655 var FORBIDDEN_HOST_CODE_POINT = /[\u0000\t\u000A\u000D #%/:?@[\\]]/;
4656 var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\u0000\t\u000A\u000D #/:?@[\\]]/;
4657 var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
4658 var TAB_AND_NEW_LINE = /[\t\u000A\u000D]/g;
4659 /* eslint-enable no-control-regex -- safe */
4662 var parseHost = function (url, input) {
4663 var result, codePoints, index;
4664 if (input.charAt(0) == '[') {
4665 if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
4666 result = parseIPv6(input.slice(1, -1));
4667 if (!result) return INVALID_HOST;
4670 } else if (!isSpecial(url)) {
4671 if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
4673 codePoints = arrayFrom(input);
4674 for (index = 0; index < codePoints.length; index++) {
4675 result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
4679 input = stringPunycodeToAscii(input);
4680 if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
4681 result = parseIPv4(input);
4682 if (result === null) return INVALID_HOST;
4687 var parseIPv4 = function (input) {
4688 var parts = input.split('.');
4689 var partsLength, numbers, index, part, radix, number, ipv4;
4690 if (parts.length && parts[parts.length - 1] == '') {
4693 partsLength = parts.length;
4694 if (partsLength > 4) return input;
4696 for (index = 0; index < partsLength; index++) {
4697 part = parts[index];
4698 if (part == '') return input;
4700 if (part.length > 1 && part.charAt(0) == '0') {
4701 radix = HEX_START.test(part) ? 16 : 8;
4702 part = part.slice(radix == 8 ? 1 : 2);
4707 if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
4708 number = parseInt(part, radix);
4710 numbers.push(number);
4712 for (index = 0; index < partsLength; index++) {
4713 number = numbers[index];
4714 if (index == partsLength - 1) {
4715 if (number >= pow$1(256, 5 - partsLength)) return null;
4716 } else if (number > 255) return null;
4718 ipv4 = numbers.pop();
4719 for (index = 0; index < numbers.length; index++) {
4720 ipv4 += numbers[index] * pow$1(256, 3 - index);
4725 // eslint-disable-next-line max-statements -- TODO
4726 var parseIPv6 = function (input) {
4727 var address = [0, 0, 0, 0, 0, 0, 0, 0];
4729 var compress = null;
4731 var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
4733 var char = function () {
4734 return input.charAt(pointer);
4737 if (char() == ':') {
4738 if (input.charAt(1) != ':') return;
4741 compress = pieceIndex;
4744 if (pieceIndex == 8) return;
4745 if (char() == ':') {
4746 if (compress !== null) return;
4749 compress = pieceIndex;
4753 while (length < 4 && HEX.test(char())) {
4754 value = value * 16 + parseInt(char(), 16);
4758 if (char() == '.') {
4759 if (length == 0) return;
4761 if (pieceIndex > 6) return;
4765 if (numbersSeen > 0) {
4766 if (char() == '.' && numbersSeen < 4) pointer++;
4769 if (!DIGIT.test(char())) return;
4770 while (DIGIT.test(char())) {
4771 number = parseInt(char(), 10);
4772 if (ipv4Piece === null) ipv4Piece = number;
4773 else if (ipv4Piece == 0) return;
4774 else ipv4Piece = ipv4Piece * 10 + number;
4775 if (ipv4Piece > 255) return;
4778 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
4780 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
4782 if (numbersSeen != 4) return;
4784 } else if (char() == ':') {
4786 if (!char()) return;
4787 } else if (char()) return;
4788 address[pieceIndex++] = value;
4790 if (compress !== null) {
4791 swaps = pieceIndex - compress;
4793 while (pieceIndex != 0 && swaps > 0) {
4794 swap = address[pieceIndex];
4795 address[pieceIndex--] = address[compress + swaps - 1];
4796 address[compress + --swaps] = swap;
4798 } else if (pieceIndex != 8) return;
4802 var findLongestZeroSequence = function (ipv6) {
4803 var maxIndex = null;
4805 var currStart = null;
4808 for (; index < 8; index++) {
4809 if (ipv6[index] !== 0) {
4810 if (currLength > maxLength) {
4811 maxIndex = currStart;
4812 maxLength = currLength;
4817 if (currStart === null) currStart = index;
4821 if (currLength > maxLength) {
4822 maxIndex = currStart;
4823 maxLength = currLength;
4828 var serializeHost = function (host) {
4829 var result, index, compress, ignore0;
4831 if (typeof host == 'number') {
4833 for (index = 0; index < 4; index++) {
4834 result.unshift(host % 256);
4835 host = floor$4(host / 256);
4836 } return result.join('.');
4838 } else if (typeof host == 'object') {
4840 compress = findLongestZeroSequence(host);
4841 for (index = 0; index < 8; index++) {
4842 if (ignore0 && host[index] === 0) continue;
4843 if (ignore0) ignore0 = false;
4844 if (compress === index) {
4845 result += index ? ':' : '::';
4848 result += host[index].toString(16);
4849 if (index < 7) result += ':';
4852 return '[' + result + ']';
4856 var C0ControlPercentEncodeSet = {};
4857 var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
4858 ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
4860 var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
4861 '#': 1, '?': 1, '{': 1, '}': 1
4863 var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
4864 '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
4867 var percentEncode = function (char, set) {
4868 var code = codeAt(char, 0);
4869 return code > 0x20 && code < 0x7F && !has(set, char) ? char : encodeURIComponent(char);
4872 var specialSchemes = {
4881 var isSpecial = function (url) {
4882 return has(specialSchemes, url.scheme);
4885 var includesCredentials = function (url) {
4886 return url.username != '' || url.password != '';
4889 var cannotHaveUsernamePasswordPort = function (url) {
4890 return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
4893 var isWindowsDriveLetter = function (string, normalized) {
4895 return string.length == 2 && ALPHA.test(string.charAt(0))
4896 && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
4899 var startsWithWindowsDriveLetter = function (string) {
4901 return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
4902 string.length == 2 ||
4903 ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
4907 var shortenURLsPath = function (url) {
4908 var path = url.path;
4909 var pathSize = path.length;
4910 if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
4915 var isSingleDot = function (segment) {
4916 return segment === '.' || segment.toLowerCase() === '%2e';
4919 var isDoubleDot = function (segment) {
4920 segment = segment.toLowerCase();
4921 return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
4925 var SCHEME_START = {};
4928 var SPECIAL_RELATIVE_OR_AUTHORITY = {};
4929 var PATH_OR_AUTHORITY = {};
4931 var RELATIVE_SLASH = {};
4932 var SPECIAL_AUTHORITY_SLASHES = {};
4933 var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
4939 var FILE_SLASH = {};
4941 var PATH_START = {};
4943 var CANNOT_BE_A_BASE_URL_PATH = {};
4947 // eslint-disable-next-line max-statements -- TODO
4948 var parseURL = function (url, input, stateOverride, base) {
4949 var state = stateOverride || SCHEME_START;
4953 var seenBracket = false;
4954 var seenPasswordToken = false;
4955 var codePoints, char, bufferCodePoints, failure;
4957 if (!stateOverride) {
4965 url.fragment = null;
4966 url.cannotBeABaseURL = false;
4967 input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
4970 input = input.replace(TAB_AND_NEW_LINE, '');
4972 codePoints = arrayFrom(input);
4974 while (pointer <= codePoints.length) {
4975 char = codePoints[pointer];
4978 if (char && ALPHA.test(char)) {
4979 buffer += char.toLowerCase();
4981 } else if (!stateOverride) {
4984 } else return INVALID_SCHEME;
4988 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
4989 buffer += char.toLowerCase();
4990 } else if (char == ':') {
4991 if (stateOverride && (
4992 (isSpecial(url) != has(specialSchemes, buffer)) ||
4993 (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
4994 (url.scheme == 'file' && !url.host)
4996 url.scheme = buffer;
4997 if (stateOverride) {
4998 if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5002 if (url.scheme == 'file') {
5004 } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5005 state = SPECIAL_RELATIVE_OR_AUTHORITY;
5006 } else if (isSpecial(url)) {
5007 state = SPECIAL_AUTHORITY_SLASHES;
5008 } else if (codePoints[pointer + 1] == '/') {
5009 state = PATH_OR_AUTHORITY;
5012 url.cannotBeABaseURL = true;
5014 state = CANNOT_BE_A_BASE_URL_PATH;
5016 } else if (!stateOverride) {
5021 } else return INVALID_SCHEME;
5025 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5026 if (base.cannotBeABaseURL && char == '#') {
5027 url.scheme = base.scheme;
5028 url.path = base.path.slice();
5029 url.query = base.query;
5031 url.cannotBeABaseURL = true;
5035 state = base.scheme == 'file' ? FILE : RELATIVE;
5038 case SPECIAL_RELATIVE_OR_AUTHORITY:
5039 if (char == '/' && codePoints[pointer + 1] == '/') {
5040 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5047 case PATH_OR_AUTHORITY:
5057 url.scheme = base.scheme;
5059 url.username = base.username;
5060 url.password = base.password;
5061 url.host = base.host;
5062 url.port = base.port;
5063 url.path = base.path.slice();
5064 url.query = base.query;
5065 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5066 state = RELATIVE_SLASH;
5067 } else if (char == '?') {
5068 url.username = base.username;
5069 url.password = base.password;
5070 url.host = base.host;
5071 url.port = base.port;
5072 url.path = base.path.slice();
5075 } else if (char == '#') {
5076 url.username = base.username;
5077 url.password = base.password;
5078 url.host = base.host;
5079 url.port = base.port;
5080 url.path = base.path.slice();
5081 url.query = base.query;
5085 url.username = base.username;
5086 url.password = base.password;
5087 url.host = base.host;
5088 url.port = base.port;
5089 url.path = base.path.slice();
5095 case RELATIVE_SLASH:
5096 if (isSpecial(url) && (char == '/' || char == '\\')) {
5097 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5098 } else if (char == '/') {
5101 url.username = base.username;
5102 url.password = base.password;
5103 url.host = base.host;
5104 url.port = base.port;
5109 case SPECIAL_AUTHORITY_SLASHES:
5110 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5111 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5115 case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5116 if (char != '/' && char != '\\') {
5123 if (seenAt) buffer = '%40' + buffer;
5125 bufferCodePoints = arrayFrom(buffer);
5126 for (var i = 0; i < bufferCodePoints.length; i++) {
5127 var codePoint = bufferCodePoints[i];
5128 if (codePoint == ':' && !seenPasswordToken) {
5129 seenPasswordToken = true;
5132 var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5133 if (seenPasswordToken) url.password += encodedCodePoints;
5134 else url.username += encodedCodePoints;
5138 char == EOF || char == '/' || char == '?' || char == '#' ||
5139 (char == '\\' && isSpecial(url))
5141 if (seenAt && buffer == '') return INVALID_AUTHORITY;
5142 pointer -= arrayFrom(buffer).length + 1;
5145 } else buffer += char;
5150 if (stateOverride && url.scheme == 'file') {
5153 } else if (char == ':' && !seenBracket) {
5154 if (buffer == '') return INVALID_HOST;
5155 failure = parseHost(url, buffer);
5156 if (failure) return failure;
5159 if (stateOverride == HOSTNAME) return;
5161 char == EOF || char == '/' || char == '?' || char == '#' ||
5162 (char == '\\' && isSpecial(url))
5164 if (isSpecial(url) && buffer == '') return INVALID_HOST;
5165 if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5166 failure = parseHost(url, buffer);
5167 if (failure) return failure;
5170 if (stateOverride) return;
5173 if (char == '[') seenBracket = true;
5174 else if (char == ']') seenBracket = false;
5179 if (DIGIT.test(char)) {
5182 char == EOF || char == '/' || char == '?' || char == '#' ||
5183 (char == '\\' && isSpecial(url)) ||
5187 var port = parseInt(buffer, 10);
5188 if (port > 0xFFFF) return INVALID_PORT;
5189 url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5192 if (stateOverride) return;
5195 } else return INVALID_PORT;
5199 url.scheme = 'file';
5200 if (char == '/' || char == '\\') state = FILE_SLASH;
5201 else if (base && base.scheme == 'file') {
5203 url.host = base.host;
5204 url.path = base.path.slice();
5205 url.query = base.query;
5206 } else if (char == '?') {
5207 url.host = base.host;
5208 url.path = base.path.slice();
5211 } else if (char == '#') {
5212 url.host = base.host;
5213 url.path = base.path.slice();
5214 url.query = base.query;
5218 if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5219 url.host = base.host;
5220 url.path = base.path.slice();
5221 shortenURLsPath(url);
5232 if (char == '/' || char == '\\') {
5236 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5237 if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5238 else url.host = base.host;
5244 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5245 if (!stateOverride && isWindowsDriveLetter(buffer)) {
5247 } else if (buffer == '') {
5249 if (stateOverride) return;
5252 failure = parseHost(url, buffer);
5253 if (failure) return failure;
5254 if (url.host == 'localhost') url.host = '';
5255 if (stateOverride) return;
5259 } else buffer += char;
5263 if (isSpecial(url)) {
5265 if (char != '/' && char != '\\') continue;
5266 } else if (!stateOverride && char == '?') {
5269 } else if (!stateOverride && char == '#') {
5272 } else if (char != EOF) {
5274 if (char != '/') continue;
5279 char == EOF || char == '/' ||
5280 (char == '\\' && isSpecial(url)) ||
5281 (!stateOverride && (char == '?' || char == '#'))
5283 if (isDoubleDot(buffer)) {
5284 shortenURLsPath(url);
5285 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5288 } else if (isSingleDot(buffer)) {
5289 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5293 if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5294 if (url.host) url.host = '';
5295 buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5297 url.path.push(buffer);
5300 if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5301 while (url.path.length > 1 && url.path[0] === '') {
5308 } else if (char == '#') {
5313 buffer += percentEncode(char, pathPercentEncodeSet);
5316 case CANNOT_BE_A_BASE_URL_PATH:
5320 } else if (char == '#') {
5323 } else if (char != EOF) {
5324 url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5328 if (!stateOverride && char == '#') {
5331 } else if (char != EOF) {
5332 if (char == "'" && isSpecial(url)) url.query += '%27';
5333 else if (char == '#') url.query += '%23';
5334 else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5338 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5346 // `URL` constructor
5347 // https://url.spec.whatwg.org/#url-class
5348 var URLConstructor = function URL(url /* , base */) {
5349 var that = anInstance(this, URLConstructor, 'URL');
5350 var base = arguments.length > 1 ? arguments[1] : undefined;
5351 var urlString = String(url);
5352 var state = setInternalState$6(that, { type: 'URL' });
5353 var baseState, failure;
5354 if (base !== undefined) {
5355 if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5357 failure = parseURL(baseState = {}, String(base));
5358 if (failure) throw TypeError(failure);
5361 failure = parseURL(state, urlString, null, baseState);
5362 if (failure) throw TypeError(failure);
5363 var searchParams = state.searchParams = new URLSearchParams$1();
5364 var searchParamsState = getInternalSearchParamsState(searchParams);
5365 searchParamsState.updateSearchParams(state.query);
5366 searchParamsState.updateURL = function () {
5367 state.query = String(searchParams) || null;
5370 that.href = serializeURL.call(that);
5371 that.origin = getOrigin.call(that);
5372 that.protocol = getProtocol.call(that);
5373 that.username = getUsername.call(that);
5374 that.password = getPassword.call(that);
5375 that.host = getHost.call(that);
5376 that.hostname = getHostname.call(that);
5377 that.port = getPort.call(that);
5378 that.pathname = getPathname.call(that);
5379 that.search = getSearch.call(that);
5380 that.searchParams = getSearchParams.call(that);
5381 that.hash = getHash.call(that);
5385 var URLPrototype = URLConstructor.prototype;
5387 var serializeURL = function () {
5388 var url = getInternalURLState(this);
5389 var scheme = url.scheme;
5390 var username = url.username;
5391 var password = url.password;
5392 var host = url.host;
5393 var port = url.port;
5394 var path = url.path;
5395 var query = url.query;
5396 var fragment = url.fragment;
5397 var output = scheme + ':';
5398 if (host !== null) {
5400 if (includesCredentials(url)) {
5401 output += username + (password ? ':' + password : '') + '@';
5403 output += serializeHost(host);
5404 if (port !== null) output += ':' + port;
5405 } else if (scheme == 'file') output += '//';
5406 output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5407 if (query !== null) output += '?' + query;
5408 if (fragment !== null) output += '#' + fragment;
5412 var getOrigin = function () {
5413 var url = getInternalURLState(this);
5414 var scheme = url.scheme;
5415 var port = url.port;
5416 if (scheme == 'blob') try {
5417 return new URL(scheme.path[0]).origin;
5421 if (scheme == 'file' || !isSpecial(url)) return 'null';
5422 return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5425 var getProtocol = function () {
5426 return getInternalURLState(this).scheme + ':';
5429 var getUsername = function () {
5430 return getInternalURLState(this).username;
5433 var getPassword = function () {
5434 return getInternalURLState(this).password;
5437 var getHost = function () {
5438 var url = getInternalURLState(this);
5439 var host = url.host;
5440 var port = url.port;
5441 return host === null ? ''
5442 : port === null ? serializeHost(host)
5443 : serializeHost(host) + ':' + port;
5446 var getHostname = function () {
5447 var host = getInternalURLState(this).host;
5448 return host === null ? '' : serializeHost(host);
5451 var getPort = function () {
5452 var port = getInternalURLState(this).port;
5453 return port === null ? '' : String(port);
5456 var getPathname = function () {
5457 var url = getInternalURLState(this);
5458 var path = url.path;
5459 return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5462 var getSearch = function () {
5463 var query = getInternalURLState(this).query;
5464 return query ? '?' + query : '';
5467 var getSearchParams = function () {
5468 return getInternalURLState(this).searchParams;
5471 var getHash = function () {
5472 var fragment = getInternalURLState(this).fragment;
5473 return fragment ? '#' + fragment : '';
5476 var accessorDescriptor = function (getter, setter) {
5477 return { get: getter, set: setter, configurable: true, enumerable: true };
5481 objectDefineProperties(URLPrototype, {
5482 // `URL.prototype.href` accessors pair
5483 // https://url.spec.whatwg.org/#dom-url-href
5484 href: accessorDescriptor(serializeURL, function (href) {
5485 var url = getInternalURLState(this);
5486 var urlString = String(href);
5487 var failure = parseURL(url, urlString);
5488 if (failure) throw TypeError(failure);
5489 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5491 // `URL.prototype.origin` getter
5492 // https://url.spec.whatwg.org/#dom-url-origin
5493 origin: accessorDescriptor(getOrigin),
5494 // `URL.prototype.protocol` accessors pair
5495 // https://url.spec.whatwg.org/#dom-url-protocol
5496 protocol: accessorDescriptor(getProtocol, function (protocol) {
5497 var url = getInternalURLState(this);
5498 parseURL(url, String(protocol) + ':', SCHEME_START);
5500 // `URL.prototype.username` accessors pair
5501 // https://url.spec.whatwg.org/#dom-url-username
5502 username: accessorDescriptor(getUsername, function (username) {
5503 var url = getInternalURLState(this);
5504 var codePoints = arrayFrom(String(username));
5505 if (cannotHaveUsernamePasswordPort(url)) return;
5507 for (var i = 0; i < codePoints.length; i++) {
5508 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5511 // `URL.prototype.password` accessors pair
5512 // https://url.spec.whatwg.org/#dom-url-password
5513 password: accessorDescriptor(getPassword, function (password) {
5514 var url = getInternalURLState(this);
5515 var codePoints = arrayFrom(String(password));
5516 if (cannotHaveUsernamePasswordPort(url)) return;
5518 for (var i = 0; i < codePoints.length; i++) {
5519 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5522 // `URL.prototype.host` accessors pair
5523 // https://url.spec.whatwg.org/#dom-url-host
5524 host: accessorDescriptor(getHost, function (host) {
5525 var url = getInternalURLState(this);
5526 if (url.cannotBeABaseURL) return;
5527 parseURL(url, String(host), HOST);
5529 // `URL.prototype.hostname` accessors pair
5530 // https://url.spec.whatwg.org/#dom-url-hostname
5531 hostname: accessorDescriptor(getHostname, function (hostname) {
5532 var url = getInternalURLState(this);
5533 if (url.cannotBeABaseURL) return;
5534 parseURL(url, String(hostname), HOSTNAME);
5536 // `URL.prototype.port` accessors pair
5537 // https://url.spec.whatwg.org/#dom-url-port
5538 port: accessorDescriptor(getPort, function (port) {
5539 var url = getInternalURLState(this);
5540 if (cannotHaveUsernamePasswordPort(url)) return;
5541 port = String(port);
5542 if (port == '') url.port = null;
5543 else parseURL(url, port, PORT);
5545 // `URL.prototype.pathname` accessors pair
5546 // https://url.spec.whatwg.org/#dom-url-pathname
5547 pathname: accessorDescriptor(getPathname, function (pathname) {
5548 var url = getInternalURLState(this);
5549 if (url.cannotBeABaseURL) return;
5551 parseURL(url, pathname + '', PATH_START);
5553 // `URL.prototype.search` accessors pair
5554 // https://url.spec.whatwg.org/#dom-url-search
5555 search: accessorDescriptor(getSearch, function (search) {
5556 var url = getInternalURLState(this);
5557 search = String(search);
5561 if ('?' == search.charAt(0)) search = search.slice(1);
5563 parseURL(url, search, QUERY);
5565 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5567 // `URL.prototype.searchParams` getter
5568 // https://url.spec.whatwg.org/#dom-url-searchparams
5569 searchParams: accessorDescriptor(getSearchParams),
5570 // `URL.prototype.hash` accessors pair
5571 // https://url.spec.whatwg.org/#dom-url-hash
5572 hash: accessorDescriptor(getHash, function (hash) {
5573 var url = getInternalURLState(this);
5574 hash = String(hash);
5576 url.fragment = null;
5579 if ('#' == hash.charAt(0)) hash = hash.slice(1);
5581 parseURL(url, hash, FRAGMENT);
5586 // `URL.prototype.toJSON` method
5587 // https://url.spec.whatwg.org/#dom-url-tojson
5588 redefine(URLPrototype, 'toJSON', function toJSON() {
5589 return serializeURL.call(this);
5590 }, { enumerable: true });
5592 // `URL.prototype.toString` method
5593 // https://url.spec.whatwg.org/#URL-stringification-behavior
5594 redefine(URLPrototype, 'toString', function toString() {
5595 return serializeURL.call(this);
5596 }, { enumerable: true });
5599 var nativeCreateObjectURL = NativeURL.createObjectURL;
5600 var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
5601 // `URL.createObjectURL` method
5602 // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
5603 // eslint-disable-next-line no-unused-vars -- required for `.length`
5604 if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
5605 return nativeCreateObjectURL.apply(NativeURL, arguments);
5607 // `URL.revokeObjectURL` method
5608 // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
5609 // eslint-disable-next-line no-unused-vars -- required for `.length`
5610 if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
5611 return nativeRevokeObjectURL.apply(NativeURL, arguments);
5615 setToStringTag(URLConstructor, 'URL');
5617 _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
5621 // `RegExp.prototype.flags` getter implementation
5622 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
5623 var regexpFlags = function () {
5624 var that = anObject(this);
5626 if (that.global) result += 'g';
5627 if (that.ignoreCase) result += 'i';
5628 if (that.multiline) result += 'm';
5629 if (that.dotAll) result += 's';
5630 if (that.unicode) result += 'u';
5631 if (that.sticky) result += 'y';
5635 var TO_STRING$1 = 'toString';
5636 var RegExpPrototype = RegExp.prototype;
5637 var nativeToString = RegExpPrototype[TO_STRING$1];
5639 var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
5640 // FF44- RegExp#toString has a wrong name
5641 var INCORRECT_NAME = nativeToString.name != TO_STRING$1;
5643 // `RegExp.prototype.toString` method
5644 // https://tc39.es/ecma262/#sec-regexp.prototype.tostring
5645 if (NOT_GENERIC || INCORRECT_NAME) {
5646 redefine(RegExp.prototype, TO_STRING$1, function toString() {
5647 var R = anObject(this);
5648 var p = String(R.source);
5650 var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype) ? regexpFlags.call(R) : rf);
5651 return '/' + p + '/' + f;
5652 }, { unsafe: true });
5655 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
5656 // so we use an intermediate function.
5658 return RegExp(s, f);
5661 var UNSUPPORTED_Y = fails(function () {
5662 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError
5663 var re = RE('a', 'y');
5665 return re.exec('abcd') != null;
5668 var BROKEN_CARET = fails(function () {
5669 // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
5670 var re = RE('^r', 'gy');
5672 return re.exec('str') != null;
5675 var regexpStickyHelpers = {
5676 UNSUPPORTED_Y: UNSUPPORTED_Y,
5677 BROKEN_CARET: BROKEN_CARET
5680 var nativeExec = RegExp.prototype.exec;
5681 // This always refers to the native implementation, because the
5682 // String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,
5683 // which loads this file before patching the method.
5684 var nativeReplace = String.prototype.replace;
5686 var patchedExec = nativeExec;
5688 var UPDATES_LAST_INDEX_WRONG = (function () {
5691 nativeExec.call(re1, 'a');
5692 nativeExec.call(re2, 'a');
5693 return re1.lastIndex !== 0 || re2.lastIndex !== 0;
5696 var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
5698 // nonparticipating capturing group, copied from es5-shim's String#split patch.
5699 // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing
5700 var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
5702 var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$1;
5705 patchedExec = function exec(str) {
5707 var lastIndex, reCopy, match, i;
5708 var sticky = UNSUPPORTED_Y$1 && re.sticky;
5709 var flags = regexpFlags.call(re);
5710 var source = re.source;
5715 flags = flags.replace('y', '');
5716 if (flags.indexOf('g') === -1) {
5720 strCopy = String(str).slice(re.lastIndex);
5721 // Support anchored sticky behavior.
5722 if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
5723 source = '(?: ' + source + ')';
5724 strCopy = ' ' + strCopy;
5727 // ^(? + rx + ) is needed, in combination with some str slicing, to
5728 // simulate the 'y' flag.
5729 reCopy = new RegExp('^(?:' + source + ')', flags);
5732 if (NPCG_INCLUDED) {
5733 reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
5735 if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
5737 match = nativeExec.call(sticky ? reCopy : re, strCopy);
5741 match.input = match.input.slice(charsAdded);
5742 match[0] = match[0].slice(charsAdded);
5743 match.index = re.lastIndex;
5744 re.lastIndex += match[0].length;
5745 } else re.lastIndex = 0;
5746 } else if (UPDATES_LAST_INDEX_WRONG && match) {
5747 re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
5749 if (NPCG_INCLUDED && match && match.length > 1) {
5750 // Fix browsers whose `exec` methods don't consistently return `undefined`
5751 // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
5752 nativeReplace.call(match[0], reCopy, function () {
5753 for (i = 1; i < arguments.length - 2; i++) {
5754 if (arguments[i] === undefined) match[i] = undefined;
5763 var regexpExec = patchedExec;
5765 // `RegExp.prototype.exec` method
5766 // https://tc39.es/ecma262/#sec-regexp.prototype.exec
5767 _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
5771 // TODO: Remove from `core-js@4` since it's moved to entry points
5779 var SPECIES$6 = wellKnownSymbol('species');
5781 var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
5782 // #replace needs built-in support for named groups.
5783 // #match works fine because it just return the exec results, even if it has
5784 // a "grops" property.
5786 re.exec = function () {
5788 result.groups = { a: '7' };
5791 return ''.replace(re, '$<a>') !== '7';
5794 // IE <= 11 replaces $0 with the whole match, as if it was $&
5795 // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
5796 var REPLACE_KEEPS_$0 = (function () {
5797 return 'a'.replace(/./, '$0') === '$0';
5800 var REPLACE = wellKnownSymbol('replace');
5801 // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
5802 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
5804 return /./[REPLACE]('a', '$0') === '';
5809 // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
5810 // Weex JS has frozen built-in prototypes, so use try / catch wrapper
5811 var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
5812 // eslint-disable-next-line regexp/no-empty-group -- required for testing
5814 var originalExec = re.exec;
5815 re.exec = function () { return originalExec.apply(this, arguments); };
5816 var result = 'ab'.split(re);
5817 return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
5820 var fixRegexpWellKnownSymbolLogic = function (KEY, length, exec, sham) {
5821 var SYMBOL = wellKnownSymbol(KEY);
5823 var DELEGATES_TO_SYMBOL = !fails(function () {
5824 // String methods call symbol-named RegEp methods
5826 O[SYMBOL] = function () { return 7; };
5827 return ''[KEY](O) != 7;
5830 var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
5831 // Symbol-named RegExp methods call .exec
5832 var execCalled = false;
5835 if (KEY === 'split') {
5836 // We can't use real regex here since it causes deoptimization
5837 // and serious performance degradation in V8
5838 // https://github.com/zloirock/core-js/issues/306
5840 // RegExp[@@split] doesn't call the regex's exec method, but first creates
5841 // a new one. We need to return the patched regex when creating the new one.
5842 re.constructor = {};
5843 re.constructor[SPECIES$6] = function () { return re; };
5845 re[SYMBOL] = /./[SYMBOL];
5848 re.exec = function () { execCalled = true; return null; };
5855 !DELEGATES_TO_SYMBOL ||
5856 !DELEGATES_TO_EXEC ||
5857 (KEY === 'replace' && !(
5858 REPLACE_SUPPORTS_NAMED_GROUPS &&
5860 !REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
5862 (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC)
5864 var nativeRegExpMethod = /./[SYMBOL];
5865 var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
5866 if (regexp.exec === regexpExec) {
5867 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
5868 // The native String method already delegates to @@method (this
5869 // polyfilled function), leasing to infinite recursion.
5870 // We avoid it by directly calling the native @@method method.
5871 return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
5873 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
5875 return { done: false };
5877 REPLACE_KEEPS_$0: REPLACE_KEEPS_$0,
5878 REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
5880 var stringMethod = methods[0];
5881 var regexMethod = methods[1];
5883 redefine(String.prototype, KEY, stringMethod);
5884 redefine(RegExp.prototype, SYMBOL, length == 2
5885 // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
5886 // 21.2.5.11 RegExp.prototype[@@split](string, limit)
5887 ? function (string, arg) { return regexMethod.call(string, this, arg); }
5888 // 21.2.5.6 RegExp.prototype[@@match](string)
5889 // 21.2.5.9 RegExp.prototype[@@search](string)
5890 : function (string) { return regexMethod.call(string, this); }
5894 if (sham) createNonEnumerableProperty(RegExp.prototype[SYMBOL], 'sham', true);
5897 var charAt$1 = stringMultibyte.charAt;
5899 // `AdvanceStringIndex` abstract operation
5900 // https://tc39.es/ecma262/#sec-advancestringindex
5901 var advanceStringIndex = function (S, index, unicode) {
5902 return index + (unicode ? charAt$1(S, index).length : 1);
5905 var floor$5 = Math.floor;
5906 var replace$1 = ''.replace;
5907 var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d{1,2}|<[^>]*>)/g;
5908 var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d{1,2})/g;
5910 // https://tc39.es/ecma262/#sec-getsubstitution
5911 var getSubstitution = function (matched, str, position, captures, namedCaptures, replacement) {
5912 var tailPos = position + matched.length;
5913 var m = captures.length;
5914 var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
5915 if (namedCaptures !== undefined) {
5916 namedCaptures = toObject(namedCaptures);
5917 symbols = SUBSTITUTION_SYMBOLS;
5919 return replace$1.call(replacement, symbols, function (match, ch) {
5921 switch (ch.charAt(0)) {
5922 case '$': return '$';
5923 case '&': return matched;
5924 case '`': return str.slice(0, position);
5925 case "'": return str.slice(tailPos);
5927 capture = namedCaptures[ch.slice(1, -1)];
5931 if (n === 0) return match;
5933 var f = floor$5(n / 10);
5934 if (f === 0) return match;
5935 if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
5938 capture = captures[n - 1];
5940 return capture === undefined ? '' : capture;
5944 // `RegExpExec` abstract operation
5945 // https://tc39.es/ecma262/#sec-regexpexec
5946 var regexpExecAbstract = function (R, S) {
5948 if (typeof exec === 'function') {
5949 var result = exec.call(R, S);
5950 if (typeof result !== 'object') {
5951 throw TypeError('RegExp exec method returned something other than an Object or null');
5956 if (classofRaw(R) !== 'RegExp') {
5957 throw TypeError('RegExp#exec called on incompatible receiver');
5960 return regexpExec.call(R, S);
5963 var max$2 = Math.max;
5964 var min$4 = Math.min;
5966 var maybeToString = function (it) {
5967 return it === undefined ? it : String(it);
5971 fixRegexpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) {
5972 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
5973 var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
5974 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
5977 // `String.prototype.replace` method
5978 // https://tc39.es/ecma262/#sec-string.prototype.replace
5979 function replace(searchValue, replaceValue) {
5980 var O = requireObjectCoercible(this);
5981 var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
5982 return replacer !== undefined
5983 ? replacer.call(searchValue, O, replaceValue)
5984 : nativeReplace.call(String(O), searchValue, replaceValue);
5986 // `RegExp.prototype[@@replace]` method
5987 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
5988 function (regexp, replaceValue) {
5990 (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) ||
5991 (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1)
5993 var res = maybeCallNative(nativeReplace, regexp, this, replaceValue);
5994 if (res.done) return res.value;
5997 var rx = anObject(regexp);
5998 var S = String(this);
6000 var functionalReplace = typeof replaceValue === 'function';
6001 if (!functionalReplace) replaceValue = String(replaceValue);
6003 var global = rx.global;
6005 var fullUnicode = rx.unicode;
6010 var result = regexpExecAbstract(rx, S);
6011 if (result === null) break;
6013 results.push(result);
6016 var matchStr = String(result[0]);
6017 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
6020 var accumulatedResult = '';
6021 var nextSourcePosition = 0;
6022 for (var i = 0; i < results.length; i++) {
6023 result = results[i];
6025 var matched = String(result[0]);
6026 var position = max$2(min$4(toInteger(result.index), S.length), 0);
6028 // NOTE: This is equivalent to
6029 // captures = result.slice(1).map(maybeToString)
6030 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
6031 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
6032 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
6033 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
6034 var namedCaptures = result.groups;
6035 if (functionalReplace) {
6036 var replacerArgs = [matched].concat(captures, position, S);
6037 if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
6038 var replacement = String(replaceValue.apply(undefined, replacerArgs));
6040 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
6042 if (position >= nextSourcePosition) {
6043 accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
6044 nextSourcePosition = position + matched.length;
6047 return accumulatedResult + S.slice(nextSourcePosition);
6052 var MATCH = wellKnownSymbol('match');
6054 // `IsRegExp` abstract operation
6055 // https://tc39.es/ecma262/#sec-isregexp
6056 var isRegexp = function (it) {
6058 return isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
6061 var arrayPush = [].push;
6062 var min$5 = Math.min;
6063 var MAX_UINT32 = 0xFFFFFFFF;
6065 // babel-minify transpiles RegExp('x', 'y') -> /x/y and it causes SyntaxError
6066 var SUPPORTS_Y = !fails(function () { return !RegExp(MAX_UINT32, 'y'); });
6069 fixRegexpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCallNative) {
6072 'abbc'.split(/(b)*/)[1] == 'c' ||
6073 // eslint-disable-next-line regexp/no-empty-group -- required for testing
6074 'test'.split(/(?:)/, -1).length != 4 ||
6075 'ab'.split(/(?:ab)*/).length != 2 ||
6076 '.'.split(/(.?)(.?)/).length != 4 ||
6077 // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing
6078 '.'.split(/()()/).length > 1 ||
6079 ''.split(/.?/).length
6081 // based on es5-shim implementation, need to rework it
6082 internalSplit = function (separator, limit) {
6083 var string = String(requireObjectCoercible(this));
6084 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6085 if (lim === 0) return [];
6086 if (separator === undefined) return [string];
6087 // If `separator` is not a regex, use native split
6088 if (!isRegexp(separator)) {
6089 return nativeSplit.call(string, separator, lim);
6092 var flags = (separator.ignoreCase ? 'i' : '') +
6093 (separator.multiline ? 'm' : '') +
6094 (separator.unicode ? 'u' : '') +
6095 (separator.sticky ? 'y' : '');
6096 var lastLastIndex = 0;
6097 // Make `global` and avoid `lastIndex` issues by working with a copy
6098 var separatorCopy = new RegExp(separator.source, flags + 'g');
6099 var match, lastIndex, lastLength;
6100 while (match = regexpExec.call(separatorCopy, string)) {
6101 lastIndex = separatorCopy.lastIndex;
6102 if (lastIndex > lastLastIndex) {
6103 output.push(string.slice(lastLastIndex, match.index));
6104 if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
6105 lastLength = match[0].length;
6106 lastLastIndex = lastIndex;
6107 if (output.length >= lim) break;
6109 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
6111 if (lastLastIndex === string.length) {
6112 if (lastLength || !separatorCopy.test('')) output.push('');
6113 } else output.push(string.slice(lastLastIndex));
6114 return output.length > lim ? output.slice(0, lim) : output;
6117 } else if ('0'.split(undefined, 0).length) {
6118 internalSplit = function (separator, limit) {
6119 return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
6121 } else internalSplit = nativeSplit;
6124 // `String.prototype.split` method
6125 // https://tc39.es/ecma262/#sec-string.prototype.split
6126 function split(separator, limit) {
6127 var O = requireObjectCoercible(this);
6128 var splitter = separator == undefined ? undefined : separator[SPLIT];
6129 return splitter !== undefined
6130 ? splitter.call(separator, O, limit)
6131 : internalSplit.call(String(O), separator, limit);
6133 // `RegExp.prototype[@@split]` method
6134 // https://tc39.es/ecma262/#sec-regexp.prototype-@@split
6136 // NOTE: This cannot be properly polyfilled in engines that don't support
6138 function (regexp, limit) {
6139 var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit);
6140 if (res.done) return res.value;
6142 var rx = anObject(regexp);
6143 var S = String(this);
6144 var C = speciesConstructor(rx, RegExp);
6146 var unicodeMatching = rx.unicode;
6147 var flags = (rx.ignoreCase ? 'i' : '') +
6148 (rx.multiline ? 'm' : '') +
6149 (rx.unicode ? 'u' : '') +
6150 (SUPPORTS_Y ? 'y' : 'g');
6152 // ^(? + rx + ) is needed, in combination with some S slicing, to
6153 // simulate the 'y' flag.
6154 var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags);
6155 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6156 if (lim === 0) return [];
6157 if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
6161 while (q < S.length) {
6162 splitter.lastIndex = SUPPORTS_Y ? q : 0;
6163 var z = regexpExecAbstract(splitter, SUPPORTS_Y ? S : S.slice(q));
6167 (e = min$5(toLength(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p
6169 q = advanceStringIndex(S, q, unicodeMatching);
6171 A.push(S.slice(p, q));
6172 if (A.length === lim) return A;
6173 for (var i = 1; i <= z.length - 1; i++) {
6175 if (A.length === lim) return A;
6186 // a string of all valid unicode whitespaces
6187 var whitespaces = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
6188 '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
6190 var whitespace = '[' + whitespaces + ']';
6191 var ltrim = RegExp('^' + whitespace + whitespace + '*');
6192 var rtrim = RegExp(whitespace + whitespace + '*$');
6194 // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
6195 var createMethod$4 = function (TYPE) {
6196 return function ($this) {
6197 var string = String(requireObjectCoercible($this));
6198 if (TYPE & 1) string = string.replace(ltrim, '');
6199 if (TYPE & 2) string = string.replace(rtrim, '');
6205 // `String.prototype.{ trimLeft, trimStart }` methods
6206 // https://tc39.es/ecma262/#sec-string.prototype.trimstart
6207 start: createMethod$4(1),
6208 // `String.prototype.{ trimRight, trimEnd }` methods
6209 // https://tc39.es/ecma262/#sec-string.prototype.trimend
6210 end: createMethod$4(2),
6211 // `String.prototype.trim` method
6212 // https://tc39.es/ecma262/#sec-string.prototype.trim
6213 trim: createMethod$4(3)
6216 var non = '\u200B\u0085\u180E';
6218 // check that a method works with the correct list
6219 // of whitespaces and has a correct name
6220 var stringTrimForced = function (METHOD_NAME) {
6221 return fails(function () {
6222 return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
6226 var $trim = stringTrim.trim;
6229 // `String.prototype.trim` method
6230 // https://tc39.es/ecma262/#sec-string.prototype.trim
6231 _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
6232 trim: function trim() {
6237 var defineProperty$6 = objectDefineProperty.f;
6239 var FunctionPrototype = Function.prototype;
6240 var FunctionPrototypeToString = FunctionPrototype.toString;
6241 var nameRE = /^\s*function ([^ (]*)/;
6242 var NAME$1 = 'name';
6244 // Function instances `.name` property
6245 // https://tc39.es/ecma262/#sec-function-instances-name
6246 if (descriptors && !(NAME$1 in FunctionPrototype)) {
6247 defineProperty$6(FunctionPrototype, NAME$1, {
6251 return FunctionPrototypeToString.call(this).match(nameRE)[1];
6259 // `Object.create` method
6260 // https://tc39.es/ecma262/#sec-object.create
6261 _export({ target: 'Object', stat: true, sham: !descriptors }, {
6262 create: objectCreate
6265 var slice = [].slice;
6266 var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
6268 var wrap$1 = function (scheduler) {
6269 return function (handler, timeout /* , ...arguments */) {
6270 var boundArgs = arguments.length > 2;
6271 var args = boundArgs ? slice.call(arguments, 2) : undefined;
6272 return scheduler(boundArgs ? function () {
6273 // eslint-disable-next-line no-new-func -- spec requirement
6274 (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
6275 } : handler, timeout);
6279 // ie9- setTimeout & setInterval additional parameters fix
6280 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
6281 _export({ global: true, bind: true, forced: MSIE }, {
6282 // `setTimeout` method
6283 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
6284 setTimeout: wrap$1(global_1.setTimeout),
6285 // `setInterval` method
6286 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
6287 setInterval: wrap$1(global_1.setInterval)
6290 var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6292 searchParams: 'URLSearchParams' in global$1,
6293 iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6294 blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6302 formData: 'FormData' in global$1,
6303 arrayBuffer: 'ArrayBuffer' in global$1
6306 function isDataView(obj) {
6307 return obj && DataView.prototype.isPrototypeOf(obj);
6310 if (support.arrayBuffer) {
6311 var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6313 var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6314 return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6318 function normalizeName(name) {
6319 if (typeof name !== 'string') {
6320 name = String(name);
6323 if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6324 throw new TypeError('Invalid character in header field name: "' + name + '"');
6327 return name.toLowerCase();
6330 function normalizeValue(value) {
6331 if (typeof value !== 'string') {
6332 value = String(value);
6336 } // Build a destructive iterator for the value list
6339 function iteratorFor(items) {
6341 next: function next() {
6342 var value = items.shift();
6344 done: value === undefined,
6350 if (support.iterable) {
6351 iterator[Symbol.iterator] = function () {
6359 function Headers$1(headers) {
6362 if (headers instanceof Headers$1) {
6363 headers.forEach(function (value, name) {
6364 this.append(name, value);
6366 } else if (Array.isArray(headers)) {
6367 headers.forEach(function (header) {
6368 this.append(header[0], header[1]);
6370 } else if (headers) {
6371 Object.getOwnPropertyNames(headers).forEach(function (name) {
6372 this.append(name, headers[name]);
6377 Headers$1.prototype.append = function (name, value) {
6378 name = normalizeName(name);
6379 value = normalizeValue(value);
6380 var oldValue = this.map[name];
6381 this.map[name] = oldValue ? oldValue + ', ' + value : value;
6384 Headers$1.prototype['delete'] = function (name) {
6385 delete this.map[normalizeName(name)];
6388 Headers$1.prototype.get = function (name) {
6389 name = normalizeName(name);
6390 return this.has(name) ? this.map[name] : null;
6393 Headers$1.prototype.has = function (name) {
6394 return this.map.hasOwnProperty(normalizeName(name));
6397 Headers$1.prototype.set = function (name, value) {
6398 this.map[normalizeName(name)] = normalizeValue(value);
6401 Headers$1.prototype.forEach = function (callback, thisArg) {
6402 for (var name in this.map) {
6403 if (this.map.hasOwnProperty(name)) {
6404 callback.call(thisArg, this.map[name], name, this);
6409 Headers$1.prototype.keys = function () {
6411 this.forEach(function (value, name) {
6414 return iteratorFor(items);
6417 Headers$1.prototype.values = function () {
6419 this.forEach(function (value) {
6422 return iteratorFor(items);
6425 Headers$1.prototype.entries = function () {
6427 this.forEach(function (value, name) {
6428 items.push([name, value]);
6430 return iteratorFor(items);
6433 if (support.iterable) {
6434 Headers$1.prototype[Symbol.iterator] = Headers$1.prototype.entries;
6437 function consumed(body) {
6438 if (body.bodyUsed) {
6439 return Promise.reject(new TypeError('Already read'));
6442 body.bodyUsed = true;
6445 function fileReaderReady(reader) {
6446 return new Promise(function (resolve, reject) {
6447 reader.onload = function () {
6448 resolve(reader.result);
6451 reader.onerror = function () {
6452 reject(reader.error);
6457 function readBlobAsArrayBuffer(blob) {
6458 var reader = new FileReader();
6459 var promise = fileReaderReady(reader);
6460 reader.readAsArrayBuffer(blob);
6464 function readBlobAsText(blob) {
6465 var reader = new FileReader();
6466 var promise = fileReaderReady(reader);
6467 reader.readAsText(blob);
6471 function readArrayBufferAsText(buf) {
6472 var view = new Uint8Array(buf);
6473 var chars = new Array(view.length);
6475 for (var i = 0; i < view.length; i++) {
6476 chars[i] = String.fromCharCode(view[i]);
6479 return chars.join('');
6482 function bufferClone(buf) {
6484 return buf.slice(0);
6486 var view = new Uint8Array(buf.byteLength);
6487 view.set(new Uint8Array(buf));
6493 this.bodyUsed = false;
6495 this._initBody = function (body) {
6497 fetch-mock wraps the Response object in an ES6 Proxy to
6498 provide useful test harness features such as flush. However, on
6499 ES5 browsers without fetch or Proxy support pollyfills must be used;
6500 the proxy-pollyfill is unable to proxy an attribute unless it exists
6501 on the object before the Proxy is created. This change ensures
6502 Response.bodyUsed exists on the instance, while maintaining the
6503 semantic of setting Request.bodyUsed in the constructor before
6504 _initBody is called.
6506 this.bodyUsed = this.bodyUsed;
6507 this._bodyInit = body;
6510 this._bodyText = '';
6511 } else if (typeof body === 'string') {
6512 this._bodyText = body;
6513 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6514 this._bodyBlob = body;
6515 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6516 this._bodyFormData = body;
6517 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6518 this._bodyText = body.toString();
6519 } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6520 this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6522 this._bodyInit = new Blob([this._bodyArrayBuffer]);
6523 } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6524 this._bodyArrayBuffer = bufferClone(body);
6526 this._bodyText = body = Object.prototype.toString.call(body);
6529 if (!this.headers.get('content-type')) {
6530 if (typeof body === 'string') {
6531 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6532 } else if (this._bodyBlob && this._bodyBlob.type) {
6533 this.headers.set('content-type', this._bodyBlob.type);
6534 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6535 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6541 this.blob = function () {
6542 var rejected = consumed(this);
6548 if (this._bodyBlob) {
6549 return Promise.resolve(this._bodyBlob);
6550 } else if (this._bodyArrayBuffer) {
6551 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6552 } else if (this._bodyFormData) {
6553 throw new Error('could not read FormData body as blob');
6555 return Promise.resolve(new Blob([this._bodyText]));
6559 this.arrayBuffer = function () {
6560 if (this._bodyArrayBuffer) {
6561 var isConsumed = consumed(this);
6567 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6568 return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6570 return Promise.resolve(this._bodyArrayBuffer);
6573 return this.blob().then(readBlobAsArrayBuffer);
6578 this.text = function () {
6579 var rejected = consumed(this);
6585 if (this._bodyBlob) {
6586 return readBlobAsText(this._bodyBlob);
6587 } else if (this._bodyArrayBuffer) {
6588 return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6589 } else if (this._bodyFormData) {
6590 throw new Error('could not read FormData body as text');
6592 return Promise.resolve(this._bodyText);
6596 if (support.formData) {
6597 this.formData = function () {
6598 return this.text().then(decode);
6602 this.json = function () {
6603 return this.text().then(JSON.parse);
6607 } // HTTP methods whose capitalization should be normalized
6610 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6612 function normalizeMethod(method) {
6613 var upcased = method.toUpperCase();
6614 return methods.indexOf(upcased) > -1 ? upcased : method;
6617 function Request(input, options) {
6618 if (!(this instanceof Request)) {
6619 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6622 options = options || {};
6623 var body = options.body;
6625 if (input instanceof Request) {
6626 if (input.bodyUsed) {
6627 throw new TypeError('Already read');
6630 this.url = input.url;
6631 this.credentials = input.credentials;
6633 if (!options.headers) {
6634 this.headers = new Headers$1(input.headers);
6637 this.method = input.method;
6638 this.mode = input.mode;
6639 this.signal = input.signal;
6641 if (!body && input._bodyInit != null) {
6642 body = input._bodyInit;
6643 input.bodyUsed = true;
6646 this.url = String(input);
6649 this.credentials = options.credentials || this.credentials || 'same-origin';
6651 if (options.headers || !this.headers) {
6652 this.headers = new Headers$1(options.headers);
6655 this.method = normalizeMethod(options.method || this.method || 'GET');
6656 this.mode = options.mode || this.mode || null;
6657 this.signal = options.signal || this.signal;
6658 this.referrer = null;
6660 if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6661 throw new TypeError('Body not allowed for GET or HEAD requests');
6664 this._initBody(body);
6666 if (this.method === 'GET' || this.method === 'HEAD') {
6667 if (options.cache === 'no-store' || options.cache === 'no-cache') {
6668 // Search for a '_' parameter in the query string
6669 var reParamSearch = /([?&])_=[^&]*/;
6671 if (reParamSearch.test(this.url)) {
6672 // If it already exists then set the value with the current time
6673 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6675 // Otherwise add a new '_' parameter to the end with the current time
6676 var reQueryString = /\?/;
6677 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6683 Request.prototype.clone = function () {
6684 return new Request(this, {
6685 body: this._bodyInit
6689 function decode(body) {
6690 var form = new FormData();
6691 body.trim().split('&').forEach(function (bytes) {
6693 var split = bytes.split('=');
6694 var name = split.shift().replace(/\+/g, ' ');
6695 var value = split.join('=').replace(/\+/g, ' ');
6696 form.append(decodeURIComponent(name), decodeURIComponent(value));
6702 function parseHeaders(rawHeaders) {
6703 var headers = new Headers$1(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6704 // https://tools.ietf.org/html/rfc7230#section-3.2
6706 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
6707 // https://github.com/github/fetch/issues/748
6708 // https://github.com/zloirock/core-js/issues/751
6710 preProcessedHeaders.split('\r').map(function (header) {
6711 return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
6712 }).forEach(function (line) {
6713 var parts = line.split(':');
6714 var key = parts.shift().trim();
6717 var value = parts.join(':').trim();
6718 headers.append(key, value);
6724 Body.call(Request.prototype);
6725 function Response(bodyInit, options) {
6726 if (!(this instanceof Response)) {
6727 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6734 this.type = 'default';
6735 this.status = options.status === undefined ? 200 : options.status;
6736 this.ok = this.status >= 200 && this.status < 300;
6737 this.statusText = options.statusText === undefined ? '' : '' + options.statusText;
6738 this.headers = new Headers$1(options.headers);
6739 this.url = options.url || '';
6741 this._initBody(bodyInit);
6743 Body.call(Response.prototype);
6745 Response.prototype.clone = function () {
6746 return new Response(this._bodyInit, {
6747 status: this.status,
6748 statusText: this.statusText,
6749 headers: new Headers$1(this.headers),
6754 Response.error = function () {
6755 var response = new Response(null, {
6759 response.type = 'error';
6763 var redirectStatuses = [301, 302, 303, 307, 308];
6765 Response.redirect = function (url, status) {
6766 if (redirectStatuses.indexOf(status) === -1) {
6767 throw new RangeError('Invalid status code');
6770 return new Response(null, {
6778 var DOMException$1 = global$1.DOMException;
6781 new DOMException$1();
6783 DOMException$1 = function DOMException(message, name) {
6784 this.message = message;
6786 var error = Error(message);
6787 this.stack = error.stack;
6790 DOMException$1.prototype = Object.create(Error.prototype);
6791 DOMException$1.prototype.constructor = DOMException$1;
6794 function fetch$1(input, init) {
6795 return new Promise(function (resolve, reject) {
6796 var request = new Request(input, init);
6798 if (request.signal && request.signal.aborted) {
6799 return reject(new DOMException$1('Aborted', 'AbortError'));
6802 var xhr = new XMLHttpRequest();
6804 function abortXhr() {
6808 xhr.onload = function () {
6811 statusText: xhr.statusText,
6812 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
6814 options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
6815 var body = 'response' in xhr ? xhr.response : xhr.responseText;
6816 setTimeout(function () {
6817 resolve(new Response(body, options));
6821 xhr.onerror = function () {
6822 setTimeout(function () {
6823 reject(new TypeError('Network request failed'));
6827 xhr.ontimeout = function () {
6828 setTimeout(function () {
6829 reject(new TypeError('Network request failed'));
6833 xhr.onabort = function () {
6834 setTimeout(function () {
6835 reject(new DOMException$1('Aborted', 'AbortError'));
6839 function fixUrl(url) {
6841 return url === '' && global$1.location.href ? global$1.location.href : url;
6847 xhr.open(request.method, fixUrl(request.url), true);
6849 if (request.credentials === 'include') {
6850 xhr.withCredentials = true;
6851 } else if (request.credentials === 'omit') {
6852 xhr.withCredentials = false;
6855 if ('responseType' in xhr) {
6857 xhr.responseType = 'blob';
6858 } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
6859 xhr.responseType = 'arraybuffer';
6863 if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers$1)) {
6864 Object.getOwnPropertyNames(init.headers).forEach(function (name) {
6865 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
6868 request.headers.forEach(function (value, name) {
6869 xhr.setRequestHeader(name, value);
6873 if (request.signal) {
6874 request.signal.addEventListener('abort', abortXhr);
6876 xhr.onreadystatechange = function () {
6877 // DONE (success or failure)
6878 if (xhr.readyState === 4) {
6879 request.signal.removeEventListener('abort', abortXhr);
6884 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
6887 fetch$1.polyfill = true;
6889 if (!global$1.fetch) {
6890 global$1.fetch = fetch$1;
6891 global$1.Headers = Headers$1;
6892 global$1.Request = Request;
6893 global$1.Response = Response;
6896 // `Object.defineProperty` method
6897 // https://tc39.es/ecma262/#sec-object.defineproperty
6898 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
6899 defineProperty: objectDefineProperty.f
6902 // `Object.setPrototypeOf` method
6903 // https://tc39.es/ecma262/#sec-object.setprototypeof
6904 _export({ target: 'Object', stat: true }, {
6905 setPrototypeOf: objectSetPrototypeOf
6908 var FAILS_ON_PRIMITIVES$1 = fails(function () { objectGetPrototypeOf(1); });
6910 // `Object.getPrototypeOf` method
6911 // https://tc39.es/ecma262/#sec-object.getprototypeof
6912 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1, sham: !correctPrototypeGetter }, {
6913 getPrototypeOf: function getPrototypeOf(it) {
6914 return objectGetPrototypeOf(toObject(it));
6918 var slice$1 = [].slice;
6921 var construct = function (C, argsLength, args) {
6922 if (!(argsLength in factories)) {
6923 for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
6924 // eslint-disable-next-line no-new-func -- we have no proper alternatives, IE8- only
6925 factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
6926 } return factories[argsLength](C, args);
6929 // `Function.prototype.bind` method implementation
6930 // https://tc39.es/ecma262/#sec-function.prototype.bind
6931 var functionBind = Function.bind || function bind(that /* , ...args */) {
6932 var fn = aFunction$1(this);
6933 var partArgs = slice$1.call(arguments, 1);
6934 var boundFunction = function bound(/* args... */) {
6935 var args = partArgs.concat(slice$1.call(arguments));
6936 return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
6938 if (isObject(fn.prototype)) boundFunction.prototype = fn.prototype;
6939 return boundFunction;
6942 var nativeConstruct = getBuiltIn('Reflect', 'construct');
6944 // `Reflect.construct` method
6945 // https://tc39.es/ecma262/#sec-reflect.construct
6946 // MS Edge supports only 2 arguments and argumentsList argument is optional
6947 // FF Nightly sets third argument as `new.target`, but does not create `this` from it
6948 var NEW_TARGET_BUG = fails(function () {
6949 function F() { /* empty */ }
6950 return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
6952 var ARGS_BUG = !fails(function () {
6953 nativeConstruct(function () { /* empty */ });
6955 var FORCED$5 = NEW_TARGET_BUG || ARGS_BUG;
6957 _export({ target: 'Reflect', stat: true, forced: FORCED$5, sham: FORCED$5 }, {
6958 construct: function construct(Target, args /* , newTarget */) {
6959 aFunction$1(Target);
6961 var newTarget = arguments.length < 3 ? Target : aFunction$1(arguments[2]);
6962 if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
6963 if (Target == newTarget) {
6964 // w/o altered newTarget, optimization for 0-4 arguments
6965 switch (args.length) {
6966 case 0: return new Target();
6967 case 1: return new Target(args[0]);
6968 case 2: return new Target(args[0], args[1]);
6969 case 3: return new Target(args[0], args[1], args[2]);
6970 case 4: return new Target(args[0], args[1], args[2], args[3]);
6972 // w/o altered newTarget, lot of arguments case
6974 $args.push.apply($args, args);
6975 return new (functionBind.apply(Target, $args))();
6977 // with altered newTarget, not support built-in constructors
6978 var proto = newTarget.prototype;
6979 var instance = objectCreate(isObject(proto) ? proto : Object.prototype);
6980 var result = Function.apply.call(Target, instance, args);
6981 return isObject(result) ? result : instance;
6985 // `Reflect.get` method
6986 // https://tc39.es/ecma262/#sec-reflect.get
6987 function get$2(target, propertyKey /* , receiver */) {
6988 var receiver = arguments.length < 3 ? target : arguments[2];
6989 var descriptor, prototype;
6990 if (anObject(target) === receiver) return target[propertyKey];
6991 if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has(descriptor, 'value')
6993 : descriptor.get === undefined
6995 : descriptor.get.call(receiver);
6996 if (isObject(prototype = objectGetPrototypeOf(target))) return get$2(prototype, propertyKey, receiver);
6999 _export({ target: 'Reflect', stat: true }, {
7003 var nativeGetOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
7006 var FAILS_ON_PRIMITIVES$2 = fails(function () { nativeGetOwnPropertyDescriptor$2(1); });
7007 var FORCED$6 = !descriptors || FAILS_ON_PRIMITIVES$2;
7009 // `Object.getOwnPropertyDescriptor` method
7010 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
7011 _export({ target: 'Object', stat: true, forced: FORCED$6, sham: !descriptors }, {
7012 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
7013 return nativeGetOwnPropertyDescriptor$2(toIndexedObject(it), key);
7017 var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('splice');
7019 var max$3 = Math.max;
7020 var min$6 = Math.min;
7021 var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
7022 var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
7024 // `Array.prototype.splice` method
7025 // https://tc39.es/ecma262/#sec-array.prototype.splice
7026 // with adding support of @@species
7027 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, {
7028 splice: function splice(start, deleteCount /* , ...items */) {
7029 var O = toObject(this);
7030 var len = toLength(O.length);
7031 var actualStart = toAbsoluteIndex(start, len);
7032 var argumentsLength = arguments.length;
7033 var insertCount, actualDeleteCount, A, k, from, to;
7034 if (argumentsLength === 0) {
7035 insertCount = actualDeleteCount = 0;
7036 } else if (argumentsLength === 1) {
7038 actualDeleteCount = len - actualStart;
7040 insertCount = argumentsLength - 2;
7041 actualDeleteCount = min$6(max$3(toInteger(deleteCount), 0), len - actualStart);
7043 if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER) {
7044 throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
7046 A = arraySpeciesCreate(O, actualDeleteCount);
7047 for (k = 0; k < actualDeleteCount; k++) {
7048 from = actualStart + k;
7049 if (from in O) createProperty(A, k, O[from]);
7051 A.length = actualDeleteCount;
7052 if (insertCount < actualDeleteCount) {
7053 for (k = actualStart; k < len - actualDeleteCount; k++) {
7054 from = k + actualDeleteCount;
7055 to = k + insertCount;
7056 if (from in O) O[to] = O[from];
7059 for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
7060 } else if (insertCount > actualDeleteCount) {
7061 for (k = len - actualDeleteCount; k > actualStart; k--) {
7062 from = k + actualDeleteCount - 1;
7063 to = k + insertCount - 1;
7064 if (from in O) O[to] = O[from];
7068 for (k = 0; k < insertCount; k++) {
7069 O[k + actualStart] = arguments[k + 2];
7071 O.length = len - actualDeleteCount + insertCount;
7076 // `Symbol.toStringTag` well-known symbol
7077 // https://tc39.es/ecma262/#sec-symbol.tostringtag
7078 defineWellKnownSymbol('toStringTag');
7080 // Math[@@toStringTag] property
7081 // https://tc39.es/ecma262/#sec-math-@@tostringtag
7082 setToStringTag(Math, 'Math', true);
7084 // JSON[@@toStringTag] property
7085 // https://tc39.es/ecma262/#sec-json-@@tostringtag
7086 setToStringTag(global_1.JSON, 'JSON', true);
7088 (function (factory) {
7092 function _classCallCheck(instance, Constructor) {
7093 if (!(instance instanceof Constructor)) {
7094 throw new TypeError("Cannot call a class as a function");
7098 function _defineProperties(target, props) {
7099 for (var i = 0; i < props.length; i++) {
7100 var descriptor = props[i];
7101 descriptor.enumerable = descriptor.enumerable || false;
7102 descriptor.configurable = true;
7103 if ("value" in descriptor) descriptor.writable = true;
7104 Object.defineProperty(target, descriptor.key, descriptor);
7108 function _createClass(Constructor, protoProps, staticProps) {
7109 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7110 if (staticProps) _defineProperties(Constructor, staticProps);
7114 function _inherits(subClass, superClass) {
7115 if (typeof superClass !== "function" && superClass !== null) {
7116 throw new TypeError("Super expression must either be null or a function");
7119 subClass.prototype = Object.create(superClass && superClass.prototype, {
7126 if (superClass) _setPrototypeOf(subClass, superClass);
7129 function _getPrototypeOf(o) {
7130 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7131 return o.__proto__ || Object.getPrototypeOf(o);
7133 return _getPrototypeOf(o);
7136 function _setPrototypeOf(o, p) {
7137 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7142 return _setPrototypeOf(o, p);
7145 function _isNativeReflectConstruct() {
7146 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7147 if (Reflect.construct.sham) return false;
7148 if (typeof Proxy === "function") return true;
7151 Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
7158 function _assertThisInitialized(self) {
7159 if (self === void 0) {
7160 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7166 function _possibleConstructorReturn(self, call) {
7167 if (call && (_typeof(call) === "object" || typeof call === "function")) {
7171 return _assertThisInitialized(self);
7174 function _createSuper(Derived) {
7175 var hasNativeReflectConstruct = _isNativeReflectConstruct();
7177 return function _createSuperInternal() {
7178 var Super = _getPrototypeOf(Derived),
7181 if (hasNativeReflectConstruct) {
7182 var NewTarget = _getPrototypeOf(this).constructor;
7184 result = Reflect.construct(Super, arguments, NewTarget);
7186 result = Super.apply(this, arguments);
7189 return _possibleConstructorReturn(this, result);
7193 function _superPropBase(object, property) {
7194 while (!Object.prototype.hasOwnProperty.call(object, property)) {
7195 object = _getPrototypeOf(object);
7196 if (object === null) break;
7202 function _get(target, property, receiver) {
7203 if (typeof Reflect !== "undefined" && Reflect.get) {
7206 _get = function _get(target, property, receiver) {
7207 var base = _superPropBase(target, property);
7210 var desc = Object.getOwnPropertyDescriptor(base, property);
7213 return desc.get.call(receiver);
7220 return _get(target, property, receiver || target);
7223 var Emitter = /*#__PURE__*/function () {
7224 function Emitter() {
7225 _classCallCheck(this, Emitter);
7227 Object.defineProperty(this, 'listeners', {
7234 _createClass(Emitter, [{
7235 key: "addEventListener",
7236 value: function addEventListener(type, callback, options) {
7237 if (!(type in this.listeners)) {
7238 this.listeners[type] = [];
7241 this.listeners[type].push({
7247 key: "removeEventListener",
7248 value: function removeEventListener(type, callback) {
7249 if (!(type in this.listeners)) {
7253 var stack = this.listeners[type];
7255 for (var i = 0, l = stack.length; i < l; i++) {
7256 if (stack[i].callback === callback) {
7263 key: "dispatchEvent",
7264 value: function dispatchEvent(event) {
7265 if (!(event.type in this.listeners)) {
7269 var stack = this.listeners[event.type];
7270 var stackToCall = stack.slice();
7272 for (var i = 0, l = stackToCall.length; i < l; i++) {
7273 var listener = stackToCall[i];
7276 listener.callback.call(this, event);
7278 Promise.resolve().then(function () {
7283 if (listener.options && listener.options.once) {
7284 this.removeEventListener(event.type, listener.callback);
7288 return !event.defaultPrevented;
7295 var AbortSignal = /*#__PURE__*/function (_Emitter) {
7296 _inherits(AbortSignal, _Emitter);
7298 var _super = _createSuper(AbortSignal);
7300 function AbortSignal() {
7303 _classCallCheck(this, AbortSignal);
7305 _this = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7306 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7307 // the parent constructor directly instead as a workaround. For general details, see babel bug:
7308 // https://github.com/babel/babel/issues/3041
7309 // This hack was added as a fix for the issue described here:
7310 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7312 if (!_this.listeners) {
7313 Emitter.call(_assertThisInitialized(_this));
7314 } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7315 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7318 Object.defineProperty(_assertThisInitialized(_this), 'aborted', {
7323 Object.defineProperty(_assertThisInitialized(_this), 'onabort', {
7331 _createClass(AbortSignal, [{
7333 value: function toString() {
7334 return '[object AbortSignal]';
7337 key: "dispatchEvent",
7338 value: function dispatchEvent(event) {
7339 if (event.type === 'abort') {
7340 this.aborted = true;
7342 if (typeof this.onabort === 'function') {
7343 this.onabort.call(this, event);
7347 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7354 var AbortController = /*#__PURE__*/function () {
7355 function AbortController() {
7356 _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7357 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7360 Object.defineProperty(this, 'signal', {
7361 value: new AbortSignal(),
7367 _createClass(AbortController, [{
7369 value: function abort() {
7373 event = new Event('abort');
7375 if (typeof document !== 'undefined') {
7376 if (!document.createEvent) {
7377 // For Internet Explorer 8:
7378 event = document.createEventObject();
7379 event.type = 'abort';
7381 // For Internet Explorer 11:
7382 event = document.createEvent('Event');
7383 event.initEvent('abort', false, false);
7386 // Fallback where document isn't available:
7395 this.signal.dispatchEvent(event);
7399 value: function toString() {
7400 return '[object AbortController]';
7404 return AbortController;
7407 if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7408 // These are necessary to make sure that we get correct output for:
7409 // Object.prototype.toString.call(new AbortController())
7410 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7411 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7414 function polyfillNeeded(self) {
7415 if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7416 console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7418 } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7419 // defining window.Request, and this polyfill need to work on top of unfetch
7420 // so the below feature detection needs the !self.AbortController part.
7421 // The Request.prototype check is also needed because Safari versions 11.1.2
7422 // up to and including 12.1.x has a window.AbortController present but still
7423 // does NOT correctly implement abortable fetch:
7424 // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7427 return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7430 * Note: the "fetch.Request" default value is available for fetch imported from
7431 * the "node-fetch" package and not in browsers. This is OK since browsers
7432 * will be importing umd-polyfill.js from that path "self" is passed the
7433 * decorator so the default value will not be used (because browsers that define
7434 * fetch also has Request). One quirky setup where self.fetch exists but
7435 * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7436 * on top of IE11; for this case the browser will try to use the fetch.Request
7437 * default value which in turn will be undefined but then then "if (Request)"
7438 * will ensure that you get a patched fetch but still no Request (as expected).
7439 * @param {fetch, Request = fetch.Request}
7440 * @returns {fetch: abortableFetch, Request: AbortableRequest}
7444 function abortableFetchDecorator(patchTargets) {
7445 if ('function' === typeof patchTargets) {
7451 var _patchTargets = patchTargets,
7452 fetch = _patchTargets.fetch,
7453 _patchTargets$Request = _patchTargets.Request,
7454 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7455 NativeAbortController = _patchTargets.AbortController,
7456 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7457 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7459 if (!polyfillNeeded({
7461 Request: NativeRequest,
7462 AbortController: NativeAbortController,
7463 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7471 var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7472 // defining window.Request, and this polyfill need to work on top of unfetch
7473 // hence we only patch it if it's available. Also we don't patch it if signal
7474 // is already available on the Request prototype because in this case support
7475 // is present and the patching below can cause a crash since it assigns to
7476 // request.signal which is technically a read-only property. This latter error
7477 // happens when you run the main5.js node-fetch example in the repo
7478 // "abortcontroller-polyfill-examples". The exact error is:
7479 // request.signal = init.signal;
7481 // TypeError: Cannot set property signal of #<Request> which has only a getter
7483 if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7484 Request = function Request(input, init) {
7487 if (init && init.signal) {
7488 signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7489 // been installed because if we're running on top of a browser with a
7490 // working native AbortController (i.e. the polyfill was installed due to
7491 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7492 // fake AbortSignal to the native fetch will trigger:
7493 // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7498 var request = new NativeRequest(input, init);
7501 Object.defineProperty(request, 'signal', {
7512 Request.prototype = NativeRequest.prototype;
7515 var realFetch = fetch;
7517 var abortableFetch = function abortableFetch(input, init) {
7518 var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7524 abortError = new DOMException('Aborted', 'AbortError');
7526 // IE 11 does not support calling the DOMException constructor, use a
7527 // regular error object on it instead.
7528 abortError = new Error('Aborted');
7529 abortError.name = 'AbortError';
7530 } // Return early if already aborted, thus avoiding making an HTTP request
7533 if (signal.aborted) {
7534 return Promise.reject(abortError);
7535 } // Turn an event into a promise, reject it once `abort` is dispatched
7538 var cancellation = new Promise(function (_, reject) {
7539 signal.addEventListener('abort', function () {
7540 return reject(abortError);
7546 if (init && init.signal) {
7547 // Never pass .signal to the native implementation when the polyfill has
7548 // been installed because if we're running on top of a browser with a
7549 // working native AbortController (i.e. the polyfill was installed due to
7550 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7551 // fake AbortSignal to the native fetch will trigger:
7552 // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7554 } // Return the fastest promise (don't need to wait for request to finish)
7557 return Promise.race([cancellation, realFetch(input, init)]);
7560 return realFetch(input, init);
7564 fetch: abortableFetch,
7570 if (!polyfillNeeded(self)) {
7575 console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7579 var _abortableFetch = abortableFetchDecorator(self),
7580 fetch = _abortableFetch.fetch,
7581 Request = _abortableFetch.Request;
7584 self.Request = Request;
7585 Object.defineProperty(self, 'AbortController', {
7589 value: AbortController
7591 Object.defineProperty(self, 'AbortSignal', {
7597 })(typeof self !== 'undefined' ? self : commonjsGlobal);
7600 function actionAddEntity(way) {
7601 return function (graph) {
7602 return graph.replace(way);
7606 var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7607 var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7608 var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7610 // We can't use this feature detection in V8 since it causes
7611 // deoptimization and serious performance degradation
7612 // https://github.com/zloirock/core-js/issues/679
7613 var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7615 array[IS_CONCAT_SPREADABLE] = false;
7616 return array.concat()[0] !== array;
7619 var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7621 var isConcatSpreadable = function (O) {
7622 if (!isObject(O)) return false;
7623 var spreadable = O[IS_CONCAT_SPREADABLE];
7624 return spreadable !== undefined ? !!spreadable : isArray(O);
7627 var FORCED$7 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7629 // `Array.prototype.concat` method
7630 // https://tc39.es/ecma262/#sec-array.prototype.concat
7631 // with adding support of @@isConcatSpreadable and @@species
7632 _export({ target: 'Array', proto: true, forced: FORCED$7 }, {
7633 // eslint-disable-next-line no-unused-vars -- required for `.length`
7634 concat: function concat(arg) {
7635 var O = toObject(this);
7636 var A = arraySpeciesCreate(O, 0);
7638 var i, k, length, len, E;
7639 for (i = -1, length = arguments.length; i < length; i++) {
7640 E = i === -1 ? O : arguments[i];
7641 if (isConcatSpreadable(E)) {
7642 len = toLength(E.length);
7643 if (n + len > MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7644 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7646 if (n >= MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7647 createProperty(A, n++, E);
7655 // `Object.assign` method
7656 // https://tc39.es/ecma262/#sec-object.assign
7657 _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7658 assign: objectAssign
7661 var $filter$1 = arrayIteration.filter;
7664 var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('filter');
7666 // `Array.prototype.filter` method
7667 // https://tc39.es/ecma262/#sec-array.prototype.filter
7668 // with adding support of @@species
7669 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, {
7670 filter: function filter(callbackfn /* , thisArg */) {
7671 return $filter$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7675 var FAILS_ON_PRIMITIVES$3 = fails(function () { objectKeys(1); });
7677 // `Object.keys` method
7678 // https://tc39.es/ecma262/#sec-object.keys
7679 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3 }, {
7680 keys: function keys(it) {
7681 return objectKeys(toObject(it));
7685 var nativeReverse = [].reverse;
7686 var test$1 = [1, 2];
7688 // `Array.prototype.reverse` method
7689 // https://tc39.es/ecma262/#sec-array.prototype.reverse
7690 // fix for Safari 12.0 bug
7691 // https://bugs.webkit.org/show_bug.cgi?id=188794
7692 _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7693 reverse: function reverse() {
7694 // eslint-disable-next-line no-self-assign -- dirty hack
7695 if (isArray(this)) this.length = this.length;
7696 return nativeReverse.call(this);
7700 var trim = stringTrim.trim;
7703 var $parseFloat = global_1.parseFloat;
7704 var FORCED$8 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7706 // `parseFloat` method
7707 // https://tc39.es/ecma262/#sec-parsefloat-string
7708 var numberParseFloat = FORCED$8 ? function parseFloat(string) {
7709 var trimmedString = trim(String(string));
7710 var result = $parseFloat(trimmedString);
7711 return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7714 // `parseFloat` method
7715 // https://tc39.es/ecma262/#sec-parsefloat-string
7716 _export({ global: true, forced: parseFloat != numberParseFloat }, {
7717 parseFloat: numberParseFloat
7721 Order the nodes of a way in reverse order and reverse any direction dependent tags
7722 other than `oneway`. (We assume that correcting a backwards oneway is the primary
7723 reason for reversing a way.)
7725 In addition, numeric-valued `incline` tags are negated.
7727 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7728 or adjusted tags that don't seem to be used in practice were omitted.
7731 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7732 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7733 http://wiki.openstreetmap.org/wiki/Key:incline
7734 http://wiki.openstreetmap.org/wiki/Route#Members
7735 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7736 http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7737 http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7739 function actionReverse(entityID, options) {
7740 var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7741 var numeric = /^([+\-]?)(?=[\d.])/;
7742 var directionKey = /direction$/;
7743 var turn_lanes = /^turn:lanes:?/;
7744 var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7745 var valueReplacements = {
7750 forward: 'backward',
7751 backward: 'forward',
7752 forwards: 'backward',
7753 backwards: 'forward'
7755 var roleReplacements = {
7756 forward: 'backward',
7757 backward: 'forward',
7758 forwards: 'backward',
7759 backwards: 'forward'
7761 var onewayReplacements = {
7766 var compassReplacements = {
7785 function reverseKey(key) {
7786 for (var i = 0; i < keyReplacements.length; ++i) {
7787 var replacement = keyReplacements[i];
7789 if (replacement[0].test(key)) {
7790 return key.replace(replacement[0], replacement[1]);
7797 function reverseValue(key, value, includeAbsolute) {
7798 if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7800 if (turn_lanes.test(key)) {
7802 } else if (key === 'incline' && numeric.test(value)) {
7803 return value.replace(numeric, function (_, sign) {
7804 return sign === '-' ? '' : '-';
7806 } else if (options && options.reverseOneway && key === 'oneway') {
7807 return onewayReplacements[value] || value;
7808 } else if (includeAbsolute && directionKey.test(key)) {
7809 if (compassReplacements[value]) return compassReplacements[value];
7810 var degrees = parseFloat(value);
7812 if (typeof degrees === 'number' && !isNaN(degrees)) {
7813 if (degrees < 180) {
7819 return degrees.toString();
7823 return valueReplacements[value] || value;
7824 } // Reverse the direction of tags attached to the nodes - #3076
7827 function reverseNodeTags(graph, nodeIDs) {
7828 for (var i = 0; i < nodeIDs.length; i++) {
7829 var node = graph.hasEntity(nodeIDs[i]);
7830 if (!node || !Object.keys(node.tags).length) continue;
7833 for (var key in node.tags) {
7834 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
7837 graph = graph.replace(node.update({
7845 function reverseWay(graph, way) {
7846 var nodes = way.nodes.slice().reverse();
7850 for (var key in way.tags) {
7851 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
7854 graph.parentRelations(way).forEach(function (relation) {
7855 relation.members.forEach(function (member, index) {
7856 if (member.id === way.id && (role = roleReplacements[member.role])) {
7857 relation = relation.updateMember({
7860 graph = graph.replace(relation);
7863 }); // Reverse any associated directions on nodes on the way and then replace
7864 // the way itself with the reversed node ids and updated way tags
7866 return reverseNodeTags(graph, nodes).replace(way.update({
7872 var action = function action(graph) {
7873 var entity = graph.entity(entityID);
7875 if (entity.type === 'way') {
7876 return reverseWay(graph, entity);
7879 return reverseNodeTags(graph, [entityID]);
7882 action.disabled = function (graph) {
7883 var entity = graph.hasEntity(entityID);
7884 if (!entity || entity.type === 'way') return false;
7886 for (var key in entity.tags) {
7887 var value = entity.tags[key];
7889 if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
7894 return 'nondirectional_node';
7897 action.entityID = function () {
7904 function osmIsInterestingTag(key) {
7905 return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
7906 key.indexOf('tiger:') !== 0;
7908 var osmAreaKeys = {};
7909 function osmSetAreaKeys(value) {
7910 osmAreaKeys = value;
7911 } // returns an object with the tag from `tags` that implies an area geometry, if any
7913 function osmTagSuggestingArea(tags) {
7914 if (tags.area === 'yes') return {
7917 if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
7918 // are a few exceptions that should be treated as areas, even in the
7919 // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
7934 var returnTags = {};
7936 for (var key in tags) {
7937 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
7938 returnTags[key] = tags[key];
7942 if (key in lineKeys && tags[key] in lineKeys[key]) {
7943 returnTags[key] = tags[key];
7949 } // Tags that indicate a node can be a standalone point
7950 // e.g. { amenity: { bar: true, parking: true, ... } ... }
7952 var osmPointTags = {};
7953 function osmSetPointTags(value) {
7954 osmPointTags = value;
7955 } // Tags that indicate a node can be part of a way
7956 // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
7958 var osmVertexTags = {};
7959 function osmSetVertexTags(value) {
7960 osmVertexTags = value;
7962 function osmNodeGeometriesForTags(nodeTags) {
7963 var geometries = {};
7965 for (var key in nodeTags) {
7966 if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
7967 geometries.point = true;
7970 if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
7971 geometries.vertex = true;
7972 } // break early if both are already supported
7975 if (geometries.point && geometries.vertex) break;
7980 var osmOneWayTags = {
7985 'magic_carpet': true,
8000 'goods_conveyor': true,
8001 'piste:halfpipe': true
8015 'tidal_channel': true
8017 }; // solid and smooth surfaces akin to the assumed default road surface in OSM
8019 var osmPavedTags = {
8024 'concrete:lanes': true,
8025 'concrete:plates': true
8030 }; // solid, if somewhat uncommon surfaces with a high range of smoothness
8032 var osmSemipavedTags = {
8034 'cobblestone': true,
8035 'cobblestone:flattened': true,
8036 'unhewn_cobblestone': true,
8038 'paving_stones': true,
8043 var osmRightSideIsInsideTags = {
8046 'coastline': 'coastline'
8049 'retaining_wall': true,
8060 }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8061 // (does not include `raceway`)
8063 var osmRoutableHighwayTagValues = {
8070 motorway_link: true,
8073 secondary_link: true,
8074 tertiary_link: true,
8079 living_street: true,
8088 }; // "highway" tag values that generally do not allow motor vehicles
8090 var osmPathHighwayTagValues = {
8098 }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8100 var osmRailwayTrackTagValues = {
8111 }; // "waterway" tag values for line features representing water flow
8113 var osmFlowingWaterwayTagValues = {
8123 var trim$1 = stringTrim.trim;
8126 var $parseInt = global_1.parseInt;
8127 var hex$1 = /^[+-]?0[Xx]/;
8128 var FORCED$9 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8130 // `parseInt` method
8131 // https://tc39.es/ecma262/#sec-parseint-string-radix
8132 var numberParseInt = FORCED$9 ? function parseInt(string, radix) {
8133 var S = trim$1(String(string));
8134 return $parseInt(S, (radix >>> 0) || (hex$1.test(S) ? 16 : 10));
8137 // `parseInt` method
8138 // https://tc39.es/ecma262/#sec-parseint-string-radix
8139 _export({ global: true, forced: parseInt != numberParseInt }, {
8140 parseInt: numberParseInt
8143 var freezing = !fails(function () {
8144 return Object.isExtensible(Object.preventExtensions({}));
8147 var internalMetadata = createCommonjsModule(function (module) {
8148 var defineProperty = objectDefineProperty.f;
8152 var METADATA = uid('meta');
8155 var isExtensible = Object.isExtensible || function () {
8159 var setMetadata = function (it) {
8160 defineProperty(it, METADATA, { value: {
8161 objectID: 'O' + ++id, // object ID
8162 weakData: {} // weak collections IDs
8166 var fastKey = function (it, create) {
8167 // return a primitive with prefix
8168 if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8169 if (!has(it, METADATA)) {
8170 // can't set metadata to uncaught frozen object
8171 if (!isExtensible(it)) return 'F';
8172 // not necessary to add metadata
8173 if (!create) return 'E';
8174 // add missing metadata
8177 } return it[METADATA].objectID;
8180 var getWeakData = function (it, create) {
8181 if (!has(it, METADATA)) {
8182 // can't set metadata to uncaught frozen object
8183 if (!isExtensible(it)) return true;
8184 // not necessary to add metadata
8185 if (!create) return false;
8186 // add missing metadata
8188 // return the store of weak collections IDs
8189 } return it[METADATA].weakData;
8192 // add metadata on freeze-family methods calling
8193 var onFreeze = function (it) {
8194 if (freezing && meta.REQUIRED && isExtensible(it) && !has(it, METADATA)) setMetadata(it);
8198 var meta = module.exports = {
8201 getWeakData: getWeakData,
8205 hiddenKeys[METADATA] = true;
8208 var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8209 var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8210 var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8211 var ADDER = IS_MAP ? 'set' : 'add';
8212 var NativeConstructor = global_1[CONSTRUCTOR_NAME];
8213 var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8214 var Constructor = NativeConstructor;
8217 var fixMethod = function (KEY) {
8218 var nativeMethod = NativePrototype[KEY];
8219 redefine(NativePrototype, KEY,
8220 KEY == 'add' ? function add(value) {
8221 nativeMethod.call(this, value === 0 ? 0 : value);
8223 } : KEY == 'delete' ? function (key) {
8224 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8225 } : KEY == 'get' ? function get(key) {
8226 return IS_WEAK && !isObject(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8227 } : KEY == 'has' ? function has(key) {
8228 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8229 } : function set(key, value) {
8230 nativeMethod.call(this, key === 0 ? 0 : key, value);
8236 var REPLACE = isForced_1(
8238 typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8239 new NativeConstructor().entries().next();
8244 // create collection constructor
8245 Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8246 internalMetadata.REQUIRED = true;
8247 } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8248 var instance = new Constructor();
8249 // early implementations not supports chaining
8250 var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8251 // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8252 var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8253 // most early implementations doesn't supports iterables, most modern - not close it correctly
8254 // eslint-disable-next-line no-new -- required for testing
8255 var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8256 // for early implementations -0 and +0 not the same
8257 var BUGGY_ZERO = !IS_WEAK && fails(function () {
8258 // V8 ~ Chromium 42- fails only with 5+ elements
8259 var $instance = new NativeConstructor();
8261 while (index--) $instance[ADDER](index, index);
8262 return !$instance.has(-0);
8265 if (!ACCEPT_ITERABLES) {
8266 Constructor = wrapper(function (dummy, iterable) {
8267 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8268 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8269 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8272 Constructor.prototype = NativePrototype;
8273 NativePrototype.constructor = Constructor;
8276 if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8277 fixMethod('delete');
8279 IS_MAP && fixMethod('get');
8282 if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8284 // weak collections should not contains .clear method
8285 if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8288 exported[CONSTRUCTOR_NAME] = Constructor;
8289 _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8291 setToStringTag(Constructor, CONSTRUCTOR_NAME);
8293 if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8298 var defineProperty$7 = objectDefineProperty.f;
8307 var fastKey = internalMetadata.fastKey;
8310 var setInternalState$7 = internalState.set;
8311 var internalStateGetterFor = internalState.getterFor;
8313 var collectionStrong = {
8314 getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8315 var C = wrapper(function (that, iterable) {
8316 anInstance(that, C, CONSTRUCTOR_NAME);
8317 setInternalState$7(that, {
8318 type: CONSTRUCTOR_NAME,
8319 index: objectCreate(null),
8324 if (!descriptors) that.size = 0;
8325 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8328 var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8330 var define = function (that, key, value) {
8331 var state = getInternalState(that);
8332 var entry = getEntry(that, key);
8333 var previous, index;
8334 // change existing entry
8336 entry.value = value;
8339 state.last = entry = {
8340 index: index = fastKey(key, true),
8343 previous: previous = state.last,
8347 if (!state.first) state.first = entry;
8348 if (previous) previous.next = entry;
8349 if (descriptors) state.size++;
8352 if (index !== 'F') state.index[index] = entry;
8356 var getEntry = function (that, key) {
8357 var state = getInternalState(that);
8359 var index = fastKey(key);
8361 if (index !== 'F') return state.index[index];
8362 // frozen object case
8363 for (entry = state.first; entry; entry = entry.next) {
8364 if (entry.key == key) return entry;
8368 redefineAll(C.prototype, {
8369 // 23.1.3.1 Map.prototype.clear()
8370 // 23.2.3.2 Set.prototype.clear()
8371 clear: function clear() {
8373 var state = getInternalState(that);
8374 var data = state.index;
8375 var entry = state.first;
8377 entry.removed = true;
8378 if (entry.previous) entry.previous = entry.previous.next = undefined;
8379 delete data[entry.index];
8382 state.first = state.last = undefined;
8383 if (descriptors) state.size = 0;
8386 // 23.1.3.3 Map.prototype.delete(key)
8387 // 23.2.3.4 Set.prototype.delete(value)
8388 'delete': function (key) {
8390 var state = getInternalState(that);
8391 var entry = getEntry(that, key);
8393 var next = entry.next;
8394 var prev = entry.previous;
8395 delete state.index[entry.index];
8396 entry.removed = true;
8397 if (prev) prev.next = next;
8398 if (next) next.previous = prev;
8399 if (state.first == entry) state.first = next;
8400 if (state.last == entry) state.last = prev;
8401 if (descriptors) state.size--;
8405 // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
8406 // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
8407 forEach: function forEach(callbackfn /* , that = undefined */) {
8408 var state = getInternalState(this);
8409 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8411 while (entry = entry ? entry.next : state.first) {
8412 boundFunction(entry.value, entry.key, this);
8413 // revert to the last existing entry
8414 while (entry && entry.removed) entry = entry.previous;
8417 // 23.1.3.7 Map.prototype.has(key)
8418 // 23.2.3.7 Set.prototype.has(value)
8419 has: function has(key) {
8420 return !!getEntry(this, key);
8424 redefineAll(C.prototype, IS_MAP ? {
8425 // 23.1.3.6 Map.prototype.get(key)
8426 get: function get(key) {
8427 var entry = getEntry(this, key);
8428 return entry && entry.value;
8430 // 23.1.3.9 Map.prototype.set(key, value)
8431 set: function set(key, value) {
8432 return define(this, key === 0 ? 0 : key, value);
8435 // 23.2.3.1 Set.prototype.add(value)
8436 add: function add(value) {
8437 return define(this, value = value === 0 ? 0 : value, value);
8440 if (descriptors) defineProperty$7(C.prototype, 'size', {
8442 return getInternalState(this).size;
8447 setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8448 var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8449 var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8450 var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8451 // add .keys, .values, .entries, [@@iterator]
8452 // 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
8453 defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8454 setInternalState$7(this, {
8455 type: ITERATOR_NAME,
8457 state: getInternalCollectionState(iterated),
8462 var state = getInternalIteratorState(this);
8463 var kind = state.kind;
8464 var entry = state.last;
8465 // revert to the last existing entry
8466 while (entry && entry.removed) entry = entry.previous;
8468 if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8469 // or finish the iteration
8470 state.target = undefined;
8471 return { value: undefined, done: true };
8473 // return step by kind
8474 if (kind == 'keys') return { value: entry.key, done: false };
8475 if (kind == 'values') return { value: entry.value, done: false };
8476 return { value: [entry.key, entry.value], done: false };
8477 }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8479 // add [@@species], 23.1.2.2, 23.2.2.2
8480 setSpecies(CONSTRUCTOR_NAME);
8484 // `Set` constructor
8485 // https://tc39.es/ecma262/#sec-set-objects
8486 var es_set = collection('Set', function (init) {
8487 return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8488 }, collectionStrong);
8490 function d3_ascending (a, b) {
8491 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8494 function d3_bisector (f) {
8498 if (f.length === 1) {
8499 delta = function delta(d, x) {
8503 compare = ascendingComparator(f);
8506 function left(a, x, lo, hi) {
8507 if (lo == null) lo = 0;
8508 if (hi == null) hi = a.length;
8511 var mid = lo + hi >>> 1;
8512 if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8518 function right(a, x, lo, hi) {
8519 if (lo == null) lo = 0;
8520 if (hi == null) hi = a.length;
8523 var mid = lo + hi >>> 1;
8524 if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8530 function center(a, x, lo, hi) {
8531 if (lo == null) lo = 0;
8532 if (hi == null) hi = a.length;
8533 var i = left(a, x, lo, hi - 1);
8534 return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8544 function ascendingComparator(f) {
8545 return function (d, x) {
8546 return d3_ascending(f(d), x);
8550 // `Symbol.asyncIterator` well-known symbol
8551 // https://tc39.es/ecma262/#sec-symbol.asynciterator
8552 defineWellKnownSymbol('asyncIterator');
8554 var runtime_1 = createCommonjsModule(function (module) {
8556 * Copyright (c) 2014-present, Facebook, Inc.
8558 * This source code is licensed under the MIT license found in the
8559 * LICENSE file in the root directory of this source tree.
8561 var runtime = function (exports) {
8563 var Op = Object.prototype;
8564 var hasOwn = Op.hasOwnProperty;
8565 var undefined$1; // More compressible than void 0.
8567 var $Symbol = typeof Symbol === "function" ? Symbol : {};
8568 var iteratorSymbol = $Symbol.iterator || "@@iterator";
8569 var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8570 var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8572 function define(obj, key, value) {
8573 Object.defineProperty(obj, key, {
8583 // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8586 define = function define(obj, key, value) {
8587 return obj[key] = value;
8591 function wrap(innerFn, outerFn, self, tryLocsList) {
8592 // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8593 var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8594 var generator = Object.create(protoGenerator.prototype);
8595 var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8596 // .throw, and .return methods.
8598 generator._invoke = makeInvokeMethod(innerFn, self, context);
8602 exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8603 // record like context.tryEntries[i].completion. This interface could
8604 // have been (and was previously) designed to take a closure to be
8605 // invoked without arguments, but in all the cases we care about we
8606 // already have an existing method we want to call, so there's no need
8607 // to create a new function object. We can even get away with assuming
8608 // the method takes exactly one argument, since that happens to be true
8609 // in every case, so we don't have to touch the arguments object. The
8610 // only additional allocation required is the completion record, which
8611 // has a stable shape and so hopefully should be cheap to allocate.
8613 function tryCatch(fn, obj, arg) {
8617 arg: fn.call(obj, arg)
8627 var GenStateSuspendedStart = "suspendedStart";
8628 var GenStateSuspendedYield = "suspendedYield";
8629 var GenStateExecuting = "executing";
8630 var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8631 // breaking out of the dispatch switch statement.
8633 var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8634 // .constructor.prototype properties for functions that return Generator
8635 // objects. For full spec compliance, you may wish to configure your
8636 // minifier not to mangle the names of these two functions.
8638 function Generator() {}
8640 function GeneratorFunction() {}
8642 function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8643 // don't natively support it.
8646 var IteratorPrototype = {};
8648 IteratorPrototype[iteratorSymbol] = function () {
8652 var getProto = Object.getPrototypeOf;
8653 var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8655 if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8656 // This environment has a native %IteratorPrototype%; use it instead
8658 IteratorPrototype = NativeIteratorPrototype;
8661 var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8662 GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8663 GeneratorFunctionPrototype.constructor = GeneratorFunction;
8664 GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8665 // Iterator interface in terms of a single ._invoke method.
8667 function defineIteratorMethods(prototype) {
8668 ["next", "throw", "return"].forEach(function (method) {
8669 define(prototype, method, function (arg) {
8670 return this._invoke(method, arg);
8675 exports.isGeneratorFunction = function (genFun) {
8676 var ctor = typeof genFun === "function" && genFun.constructor;
8677 return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8678 // do is to check its .name property.
8679 (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8682 exports.mark = function (genFun) {
8683 if (Object.setPrototypeOf) {
8684 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8686 genFun.__proto__ = GeneratorFunctionPrototype;
8687 define(genFun, toStringTagSymbol, "GeneratorFunction");
8690 genFun.prototype = Object.create(Gp);
8692 }; // Within the body of any async function, `await x` is transformed to
8693 // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8694 // `hasOwn.call(value, "__await")` to determine if the yielded value is
8695 // meant to be awaited.
8698 exports.awrap = function (arg) {
8704 function AsyncIterator(generator, PromiseImpl) {
8705 function invoke(method, arg, resolve, reject) {
8706 var record = tryCatch(generator[method], generator, arg);
8708 if (record.type === "throw") {
8711 var result = record.arg;
8712 var value = result.value;
8714 if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8715 return PromiseImpl.resolve(value.__await).then(function (value) {
8716 invoke("next", value, resolve, reject);
8718 invoke("throw", err, resolve, reject);
8722 return PromiseImpl.resolve(value).then(function (unwrapped) {
8723 // When a yielded Promise is resolved, its final value becomes
8724 // the .value of the Promise<{value,done}> result for the
8725 // current iteration.
8726 result.value = unwrapped;
8728 }, function (error) {
8729 // If a rejected Promise was yielded, throw the rejection back
8730 // into the async generator function so it can be handled there.
8731 return invoke("throw", error, resolve, reject);
8736 var previousPromise;
8738 function enqueue(method, arg) {
8739 function callInvokeWithMethodAndArg() {
8740 return new PromiseImpl(function (resolve, reject) {
8741 invoke(method, arg, resolve, reject);
8745 return previousPromise = // If enqueue has been called before, then we want to wait until
8746 // all previous Promises have been resolved before calling invoke,
8747 // so that results are always delivered in the correct order. If
8748 // enqueue has not been called before, then it is important to
8749 // call invoke immediately, without waiting on a callback to fire,
8750 // so that the async generator function has the opportunity to do
8751 // any necessary setup in a predictable way. This predictability
8752 // is why the Promise constructor synchronously invokes its
8753 // executor callback, and why async functions synchronously
8754 // execute code before the first await. Since we implement simple
8755 // async functions in terms of async generators, it is especially
8756 // important to get this right, even though it requires care.
8757 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8758 // invocations of the iterator.
8759 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8760 } // Define the unified helper method that is used to implement .next,
8761 // .throw, and .return (see defineIteratorMethods).
8764 this._invoke = enqueue;
8767 defineIteratorMethods(AsyncIterator.prototype);
8769 AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8773 exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8774 // AsyncIterator objects; they just return a Promise for the value of
8775 // the final result produced by the iterator.
8777 exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8778 if (PromiseImpl === void 0) PromiseImpl = Promise;
8779 var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8780 return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8781 : iter.next().then(function (result) {
8782 return result.done ? result.value : iter.next();
8786 function makeInvokeMethod(innerFn, self, context) {
8787 var state = GenStateSuspendedStart;
8788 return function invoke(method, arg) {
8789 if (state === GenStateExecuting) {
8790 throw new Error("Generator is already running");
8793 if (state === GenStateCompleted) {
8794 if (method === "throw") {
8796 } // Be forgiving, per 25.3.3.3.3 of the spec:
8797 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
8800 return doneResult();
8803 context.method = method;
8807 var delegate = context.delegate;
8810 var delegateResult = maybeInvokeDelegate(delegate, context);
8812 if (delegateResult) {
8813 if (delegateResult === ContinueSentinel) continue;
8814 return delegateResult;
8818 if (context.method === "next") {
8819 // Setting context._sent for legacy support of Babel's
8820 // function.sent implementation.
8821 context.sent = context._sent = context.arg;
8822 } else if (context.method === "throw") {
8823 if (state === GenStateSuspendedStart) {
8824 state = GenStateCompleted;
8828 context.dispatchException(context.arg);
8829 } else if (context.method === "return") {
8830 context.abrupt("return", context.arg);
8833 state = GenStateExecuting;
8834 var record = tryCatch(innerFn, self, context);
8836 if (record.type === "normal") {
8837 // If an exception is thrown from innerFn, we leave state ===
8838 // GenStateExecuting and loop back for another invocation.
8839 state = context.done ? GenStateCompleted : GenStateSuspendedYield;
8841 if (record.arg === ContinueSentinel) {
8849 } else if (record.type === "throw") {
8850 state = GenStateCompleted; // Dispatch the exception by looping back around to the
8851 // context.dispatchException(context.arg) call above.
8853 context.method = "throw";
8854 context.arg = record.arg;
8858 } // Call delegate.iterator[context.method](context.arg) and handle the
8859 // result, either by returning a { value, done } result from the
8860 // delegate iterator, or by modifying context.method and context.arg,
8861 // setting context.delegate to null, and returning the ContinueSentinel.
8864 function maybeInvokeDelegate(delegate, context) {
8865 var method = delegate.iterator[context.method];
8867 if (method === undefined$1) {
8868 // A .throw or .return when the delegate iterator has no .throw
8869 // method always terminates the yield* loop.
8870 context.delegate = null;
8872 if (context.method === "throw") {
8873 // Note: ["return"] must be used for ES3 parsing compatibility.
8874 if (delegate.iterator["return"]) {
8875 // If the delegate iterator has a return method, give it a
8876 // chance to clean up.
8877 context.method = "return";
8878 context.arg = undefined$1;
8879 maybeInvokeDelegate(delegate, context);
8881 if (context.method === "throw") {
8882 // If maybeInvokeDelegate(context) changed context.method from
8883 // "return" to "throw", let that override the TypeError below.
8884 return ContinueSentinel;
8888 context.method = "throw";
8889 context.arg = new TypeError("The iterator does not provide a 'throw' method");
8892 return ContinueSentinel;
8895 var record = tryCatch(method, delegate.iterator, context.arg);
8897 if (record.type === "throw") {
8898 context.method = "throw";
8899 context.arg = record.arg;
8900 context.delegate = null;
8901 return ContinueSentinel;
8904 var info = record.arg;
8907 context.method = "throw";
8908 context.arg = new TypeError("iterator result is not an object");
8909 context.delegate = null;
8910 return ContinueSentinel;
8914 // Assign the result of the finished delegate to the temporary
8915 // variable specified by delegate.resultName (see delegateYield).
8916 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
8918 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
8919 // exception, let the outer generator proceed normally. If
8920 // context.method was "next", forget context.arg since it has been
8921 // "consumed" by the delegate iterator. If context.method was
8922 // "return", allow the original .return call to continue in the
8925 if (context.method !== "return") {
8926 context.method = "next";
8927 context.arg = undefined$1;
8930 // Re-yield the result returned by the delegate method.
8932 } // The delegate iterator is finished, so forget it and continue with
8933 // the outer generator.
8936 context.delegate = null;
8937 return ContinueSentinel;
8938 } // Define Generator.prototype.{next,throw,return} in terms of the
8939 // unified ._invoke helper method.
8942 defineIteratorMethods(Gp);
8943 define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
8944 // @@iterator function is called on it. Some browsers' implementations of the
8945 // iterator prototype chain incorrectly implement this, causing the Generator
8946 // object to not be returned from this call. This ensures that doesn't happen.
8947 // See https://github.com/facebook/regenerator/issues/274 for more details.
8949 Gp[iteratorSymbol] = function () {
8953 Gp.toString = function () {
8954 return "[object Generator]";
8957 function pushTryEntry(locs) {
8963 entry.catchLoc = locs[1];
8967 entry.finallyLoc = locs[2];
8968 entry.afterLoc = locs[3];
8971 this.tryEntries.push(entry);
8974 function resetTryEntry(entry) {
8975 var record = entry.completion || {};
8976 record.type = "normal";
8978 entry.completion = record;
8981 function Context(tryLocsList) {
8982 // The root entry object (effectively a try statement without a catch
8983 // or a finally block) gives us a place to store values thrown from
8984 // locations where there is no enclosing try statement.
8985 this.tryEntries = [{
8988 tryLocsList.forEach(pushTryEntry, this);
8992 exports.keys = function (object) {
8995 for (var key in object) {
8999 keys.reverse(); // Rather than returning an object with a next method, we keep
9000 // things simple and return the next function itself.
9002 return function next() {
9003 while (keys.length) {
9004 var key = keys.pop();
9006 if (key in object) {
9011 } // To avoid creating an additional object, we just hang the .value
9012 // and .done properties off the next function object itself. This
9013 // also ensures that the minifier will not anonymize the function.
9021 function values(iterable) {
9023 var iteratorMethod = iterable[iteratorSymbol];
9025 if (iteratorMethod) {
9026 return iteratorMethod.call(iterable);
9029 if (typeof iterable.next === "function") {
9033 if (!isNaN(iterable.length)) {
9035 next = function next() {
9036 while (++i < iterable.length) {
9037 if (hasOwn.call(iterable, i)) {
9038 next.value = iterable[i];
9044 next.value = undefined$1;
9049 return next.next = next;
9051 } // Return an iterator with no values.
9059 exports.values = values;
9061 function doneResult() {
9068 Context.prototype = {
9069 constructor: Context,
9070 reset: function reset(skipTempReset) {
9072 this.next = 0; // Resetting context._sent for legacy support of Babel's
9073 // function.sent implementation.
9075 this.sent = this._sent = undefined$1;
9077 this.delegate = null;
9078 this.method = "next";
9079 this.arg = undefined$1;
9080 this.tryEntries.forEach(resetTryEntry);
9082 if (!skipTempReset) {
9083 for (var name in this) {
9084 // Not sure about the optimal order of these conditions:
9085 if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9086 this[name] = undefined$1;
9091 stop: function stop() {
9093 var rootEntry = this.tryEntries[0];
9094 var rootRecord = rootEntry.completion;
9096 if (rootRecord.type === "throw") {
9097 throw rootRecord.arg;
9102 dispatchException: function dispatchException(exception) {
9109 function handle(loc, caught) {
9110 record.type = "throw";
9111 record.arg = exception;
9115 // If the dispatched exception was caught by a catch block,
9116 // then let that catch block handle the exception normally.
9117 context.method = "next";
9118 context.arg = undefined$1;
9124 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9125 var entry = this.tryEntries[i];
9126 var record = entry.completion;
9128 if (entry.tryLoc === "root") {
9129 // Exception thrown outside of any try block that could handle
9130 // it, so set the completion value of the entire function to
9131 // throw the exception.
9132 return handle("end");
9135 if (entry.tryLoc <= this.prev) {
9136 var hasCatch = hasOwn.call(entry, "catchLoc");
9137 var hasFinally = hasOwn.call(entry, "finallyLoc");
9139 if (hasCatch && hasFinally) {
9140 if (this.prev < entry.catchLoc) {
9141 return handle(entry.catchLoc, true);
9142 } else if (this.prev < entry.finallyLoc) {
9143 return handle(entry.finallyLoc);
9145 } else if (hasCatch) {
9146 if (this.prev < entry.catchLoc) {
9147 return handle(entry.catchLoc, true);
9149 } else if (hasFinally) {
9150 if (this.prev < entry.finallyLoc) {
9151 return handle(entry.finallyLoc);
9154 throw new Error("try statement without catch or finally");
9159 abrupt: function abrupt(type, arg) {
9160 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9161 var entry = this.tryEntries[i];
9163 if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9164 var finallyEntry = entry;
9169 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9170 // Ignore the finally entry if control is not jumping to a
9171 // location outside the try/catch block.
9172 finallyEntry = null;
9175 var record = finallyEntry ? finallyEntry.completion : {};
9180 this.method = "next";
9181 this.next = finallyEntry.finallyLoc;
9182 return ContinueSentinel;
9185 return this.complete(record);
9187 complete: function complete(record, afterLoc) {
9188 if (record.type === "throw") {
9192 if (record.type === "break" || record.type === "continue") {
9193 this.next = record.arg;
9194 } else if (record.type === "return") {
9195 this.rval = this.arg = record.arg;
9196 this.method = "return";
9198 } else if (record.type === "normal" && afterLoc) {
9199 this.next = afterLoc;
9202 return ContinueSentinel;
9204 finish: function finish(finallyLoc) {
9205 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9206 var entry = this.tryEntries[i];
9208 if (entry.finallyLoc === finallyLoc) {
9209 this.complete(entry.completion, entry.afterLoc);
9210 resetTryEntry(entry);
9211 return ContinueSentinel;
9215 "catch": function _catch(tryLoc) {
9216 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9217 var entry = this.tryEntries[i];
9219 if (entry.tryLoc === tryLoc) {
9220 var record = entry.completion;
9222 if (record.type === "throw") {
9223 var thrown = record.arg;
9224 resetTryEntry(entry);
9229 } // The context.catch method must only be called with a location
9230 // argument that corresponds to a known catch block.
9233 throw new Error("illegal catch attempt");
9235 delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9237 iterator: values(iterable),
9238 resultName: resultName,
9242 if (this.method === "next") {
9243 // Deliberately forget the last sent value so that we don't
9244 // accidentally pass it on to the delegate.
9245 this.arg = undefined$1;
9248 return ContinueSentinel;
9250 }; // Regardless of whether this script is executing as a CommonJS module
9251 // or not, return the runtime object so that we can declare the variable
9252 // regeneratorRuntime in the outer scope, which allows this module to be
9253 // injected easily by `bin/regenerator --include-runtime script.js`.
9256 }( // If this script is executing as a CommonJS module, use module.exports
9257 // as the regeneratorRuntime namespace. Otherwise create a new empty
9258 // object. Either way, the resulting object will be used to initialize
9259 // the regeneratorRuntime variable at the top of this file.
9263 regeneratorRuntime = runtime;
9264 } catch (accidentalStrictMode) {
9265 // This module should not be running in strict mode, so the above
9266 // assignment should always work unless something is misconfigured. Just
9267 // in case runtime.js accidentally runs in strict mode, we can escape
9268 // strict mode using a global Function call. This could conceivably fail
9269 // if a Content Security Policy forbids using Function, but in that case
9270 // the proper solution is to fix the accidental strict mode problem. If
9271 // you've misconfigured your bundler to force strict mode and applied a
9272 // CSP to forbid Function, and you're not willing to fix either of those
9273 // problems, please detail your unique predicament in a GitHub issue.
9274 Function("r", "regeneratorRuntime = r")(runtime);
9278 var _marked = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9280 function number (x) {
9281 return x === null ? NaN : +x;
9283 function numbers(values, valueof) {
9284 var _iterator, _step, value, index, _iterator2, _step2, _value;
9286 return regeneratorRuntime.wrap(function numbers$(_context) {
9288 switch (_context.prev = _context.next) {
9290 if (!(valueof === undefined)) {
9295 _iterator = _createForOfIteratorHelper(values);
9301 if ((_step = _iterator.n()).done) {
9306 value = _step.value;
9308 if (!(value != null && (value = +value) >= value)) {
9326 _context.t0 = _context["catch"](2);
9328 _iterator.e(_context.t0);
9335 return _context.finish(16);
9343 _iterator2 = _createForOfIteratorHelper(values);
9349 if ((_step2 = _iterator2.n()).done) {
9354 _value = _step2.value;
9356 if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9374 _context.t1 = _context["catch"](23);
9376 _iterator2.e(_context.t1);
9383 return _context.finish(37);
9387 return _context.stop();
9390 }, _marked, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9393 var ascendingBisect = d3_bisector(d3_ascending);
9394 var bisectRight = ascendingBisect.right;
9395 var bisectCenter = d3_bisector(number).center;
9397 var INCORRECT_ITERATION$1 = !checkCorrectnessOfIteration(function (iterable) {
9398 Array.from(iterable);
9401 // `Array.from` method
9402 // https://tc39.es/ecma262/#sec-array.from
9403 _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION$1 }, {
9407 // `Array.prototype.fill` method
9408 // https://tc39.es/ecma262/#sec-array.prototype.fill
9409 _export({ target: 'Array', proto: true }, {
9413 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
9414 addToUnscopables('fill');
9416 var $some$1 = arrayIteration.some;
9419 var STRICT_METHOD$4 = arrayMethodIsStrict('some');
9421 // `Array.prototype.some` method
9422 // https://tc39.es/ecma262/#sec-array.prototype.some
9423 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$4 }, {
9424 some: function some(callbackfn /* , thisArg */) {
9425 return $some$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9429 var exportTypedArrayStaticMethod$1 = arrayBufferViewCore.exportTypedArrayStaticMethod;
9432 // `%TypedArray%.from` method
9433 // https://tc39.es/ecma262/#sec-%typedarray%.from
9434 exportTypedArrayStaticMethod$1('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9436 // `Float64Array` constructor
9437 // https://tc39.es/ecma262/#sec-typedarray-objects
9438 typedArrayConstructor('Float64', function (init) {
9439 return function Float64Array(data, byteOffset, length) {
9440 return init(this, data, byteOffset, length);
9444 function d3_descending (a, b) {
9445 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9448 // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9449 var Adder = /*#__PURE__*/function () {
9451 _classCallCheck(this, Adder);
9453 this._partials = new Float64Array(32);
9457 _createClass(Adder, [{
9459 value: function add(x) {
9460 var p = this._partials;
9463 for (var j = 0; j < this._n && j < 32; j++) {
9466 lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9467 if (lo) p[i++] = lo;
9477 value: function valueOf() {
9478 var p = this._partials;
9496 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9499 if (y == x - hi) hi = x;
9510 // `Object.defineProperties` method
9511 // https://tc39.es/ecma262/#sec-object.defineproperties
9512 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
9513 defineProperties: objectDefineProperties
9516 // `Map` constructor
9517 // https://tc39.es/ecma262/#sec-map-objects
9518 var es_map = collection('Map', function (init) {
9519 return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9520 }, collectionStrong);
9523 var nativeSort = test$2.sort;
9526 var FAILS_ON_UNDEFINED = fails(function () {
9527 test$2.sort(undefined);
9530 var FAILS_ON_NULL = fails(function () {
9534 var STRICT_METHOD$5 = arrayMethodIsStrict('sort');
9536 var FORCED$a = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$5;
9538 // `Array.prototype.sort` method
9539 // https://tc39.es/ecma262/#sec-array.prototype.sort
9540 _export({ target: 'Array', proto: true, forced: FORCED$a }, {
9541 sort: function sort(comparefn) {
9542 return comparefn === undefined
9543 ? nativeSort.call(toObject(this))
9544 : nativeSort.call(toObject(this), aFunction$1(comparefn));
9548 var e10 = Math.sqrt(50),
9551 function ticks (start, stop, count) {
9557 stop = +stop, start = +start, count = +count;
9558 if (start === stop && count > 0) return [start];
9559 if (reverse = stop < start) n = start, start = stop, stop = n;
9560 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9563 start = Math.ceil(start / step);
9564 stop = Math.floor(stop / step);
9565 ticks = new Array(n = Math.ceil(stop - start + 1));
9568 ticks[i] = (start + i) * step;
9572 start = Math.ceil(start * step);
9573 stop = Math.floor(stop * step);
9574 ticks = new Array(n = Math.ceil(stop - start + 1));
9577 ticks[i] = (start + i) / step;
9581 if (reverse) ticks.reverse();
9584 function tickIncrement(start, stop, count) {
9585 var step = (stop - start) / Math.max(0, count),
9586 power = Math.floor(Math.log(step) / Math.LN10),
9587 error = step / Math.pow(10, power);
9588 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);
9590 function tickStep(start, stop, count) {
9591 var step0 = Math.abs(stop - start) / Math.max(0, count),
9592 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9593 error = step0 / step1;
9594 if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9595 return stop < start ? -step1 : step1;
9598 function max$4(values, valueof) {
9601 if (valueof === undefined) {
9602 var _iterator = _createForOfIteratorHelper(values),
9606 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9607 var value = _step.value;
9609 if (value != null && (max < value || max === undefined && value >= value)) {
9621 var _iterator2 = _createForOfIteratorHelper(values),
9625 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9626 var _value = _step2.value;
9628 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9642 function min$7(values, valueof) {
9645 if (valueof === undefined) {
9646 var _iterator = _createForOfIteratorHelper(values),
9650 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9651 var value = _step.value;
9653 if (value != null && (min > value || min === undefined && value >= value)) {
9665 var _iterator2 = _createForOfIteratorHelper(values),
9669 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9670 var _value = _step2.value;
9672 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9686 // ISC license, Copyright 2018 Vladimir Agafonkin.
9688 function quickselect(array, k) {
9689 var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9690 var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9691 var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9693 while (right > left) {
9694 if (right - left > 600) {
9695 var n = right - left + 1;
9696 var m = k - left + 1;
9697 var z = Math.log(n);
9698 var s = 0.5 * Math.exp(2 * z / 3);
9699 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9700 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9701 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9702 quickselect(array, k, newLeft, newRight, compare);
9708 swap(array, left, k);
9709 if (compare(array[right], t) > 0) swap(array, left, right);
9712 swap(array, i, j), ++i, --j;
9714 while (compare(array[i], t) < 0) {
9718 while (compare(array[j], t) > 0) {
9723 if (compare(array[left], t) === 0) swap(array, left, j);else ++j, swap(array, j, right);
9724 if (j <= k) left = j + 1;
9725 if (k <= j) right = j - 1;
9731 function swap(array, i, j) {
9733 array[i] = array[j];
9737 function quantile(values, p, valueof) {
9738 values = Float64Array.from(numbers(values, valueof));
9739 if (!(n = values.length)) return;
9740 if ((p = +p) <= 0 || n < 2) return min$7(values);
9741 if (p >= 1) return max$4(values);
9745 value0 = max$4(quickselect(values, i0).subarray(0, i0 + 1)),
9746 value1 = min$7(values.subarray(i0 + 1));
9747 return value0 + (value1 - value0) * (i - i0);
9750 function d3_median (values, valueof) {
9751 return quantile(values, 0.5, valueof);
9754 var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
9756 function flatten(arrays) {
9757 var _iterator, _step, array;
9759 return regeneratorRuntime.wrap(function flatten$(_context) {
9761 switch (_context.prev = _context.next) {
9763 _iterator = _createForOfIteratorHelper(arrays);
9769 if ((_step = _iterator.n()).done) {
9774 array = _step.value;
9775 return _context.delegateYield(array, "t0", 6);
9787 _context.t1 = _context["catch"](1);
9789 _iterator.e(_context.t1);
9796 return _context.finish(13);
9800 return _context.stop();
9803 }, _marked$1, null, [[1, 10, 13, 16]]);
9806 function merge(arrays) {
9807 return Array.from(flatten(arrays));
9810 function range (start, stop, step) {
9811 start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
9813 n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
9814 range = new Array(n);
9817 range[i] = start + i * step;
9823 // `SameValue` abstract operation
9824 // https://tc39.es/ecma262/#sec-samevalue
9825 var sameValue = Object.is || function is(x, y) {
9826 // eslint-disable-next-line no-self-compare -- NaN check
9827 return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
9830 var $hypot = Math.hypot;
9831 var abs$1 = Math.abs;
9832 var sqrt = Math.sqrt;
9835 // https://bugs.chromium.org/p/v8/issues/detail?id=9546
9836 var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
9838 // `Math.hypot` method
9839 // https://tc39.es/ecma262/#sec-math.hypot
9840 _export({ target: 'Math', stat: true, forced: BUGGY }, {
9841 // eslint-disable-next-line no-unused-vars -- required for `.length`
9842 hypot: function hypot(value1, value2) {
9845 var aLen = arguments.length;
9849 arg = abs$1(arguments[i++]);
9852 sum = sum * div * div + 1;
9854 } else if (arg > 0) {
9859 return larg === Infinity ? Infinity : larg * sqrt(sum);
9863 // `Math.sign` method implementation
9864 // https://tc39.es/ecma262/#sec-math.sign
9865 var mathSign = Math.sign || function sign(x) {
9866 // eslint-disable-next-line no-self-compare -- NaN check
9867 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
9870 // `Math.sign` method
9871 // https://tc39.es/ecma262/#sec-math.sign
9872 _export({ target: 'Math', stat: true }, {
9877 var epsilon2 = 1e-12;
9879 var halfPi = pi / 2;
9880 var quarterPi = pi / 4;
9882 var degrees = 180 / pi;
9883 var radians = pi / 180;
9884 var abs$2 = Math.abs;
9885 var atan = Math.atan;
9886 var atan2 = Math.atan2;
9889 var hypot = Math.hypot;
9890 var log$1 = Math.log;
9892 var sign = Math.sign || function (x) {
9893 return x > 0 ? 1 : x < 0 ? -1 : 0;
9895 var sqrt$1 = Math.sqrt;
9898 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
9901 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
9906 function streamGeometry(geometry, stream) {
9907 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
9908 streamGeometryType[geometry.type](geometry, stream);
9912 var streamObjectType = {
9913 Feature: function Feature(object, stream) {
9914 streamGeometry(object.geometry, stream);
9916 FeatureCollection: function FeatureCollection(object, stream) {
9917 var features = object.features,
9919 n = features.length;
9922 streamGeometry(features[i].geometry, stream);
9926 var streamGeometryType = {
9927 Sphere: function Sphere(object, stream) {
9930 Point: function Point(object, stream) {
9931 object = object.coordinates;
9932 stream.point(object[0], object[1], object[2]);
9934 MultiPoint: function MultiPoint(object, stream) {
9935 var coordinates = object.coordinates,
9937 n = coordinates.length;
9940 object = coordinates[i], stream.point(object[0], object[1], object[2]);
9943 LineString: function LineString(object, stream) {
9944 streamLine(object.coordinates, stream, 0);
9946 MultiLineString: function MultiLineString(object, stream) {
9947 var coordinates = object.coordinates,
9949 n = coordinates.length;
9952 streamLine(coordinates[i], stream, 0);
9955 Polygon: function Polygon(object, stream) {
9956 streamPolygon(object.coordinates, stream);
9958 MultiPolygon: function MultiPolygon(object, stream) {
9959 var coordinates = object.coordinates,
9961 n = coordinates.length;
9964 streamPolygon(coordinates[i], stream);
9967 GeometryCollection: function GeometryCollection(object, stream) {
9968 var geometries = object.geometries,
9970 n = geometries.length;
9973 streamGeometry(geometries[i], stream);
9978 function streamLine(coordinates, stream, closed) {
9980 n = coordinates.length - closed,
9985 coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
9991 function streamPolygon(coordinates, stream) {
9993 n = coordinates.length;
9994 stream.polygonStart();
9997 streamLine(coordinates[i], stream, 1);
10000 stream.polygonEnd();
10003 function d3_geoStream (object, stream) {
10004 if (object && streamObjectType.hasOwnProperty(object.type)) {
10005 streamObjectType[object.type](object, stream);
10007 streamGeometry(object, stream);
10011 var areaRingSum = new Adder(); // hello?
10013 var areaSum = new Adder(),
10023 polygonStart: function polygonStart() {
10024 areaRingSum = new Adder();
10025 areaStream.lineStart = areaRingStart;
10026 areaStream.lineEnd = areaRingEnd;
10028 polygonEnd: function polygonEnd() {
10029 var areaRing = +areaRingSum;
10030 areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
10031 this.lineStart = this.lineEnd = this.point = noop;
10033 sphere: function sphere() {
10038 function areaRingStart() {
10039 areaStream.point = areaPointFirst;
10042 function areaRingEnd() {
10043 areaPoint(lambda00, phi00);
10046 function areaPointFirst(lambda, phi) {
10047 areaStream.point = areaPoint;
10048 lambda00 = lambda, phi00 = phi;
10049 lambda *= radians, phi *= radians;
10050 lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi);
10053 function areaPoint(lambda, phi) {
10054 lambda *= radians, phi *= radians;
10055 phi = phi / 2 + quarterPi; // half the angular distance from south pole
10056 // Spherical excess E for a spherical triangle with vertices: south pole,
10057 // previous point, current point. Uses a formula derived from Cagnoli’s
10058 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10060 var dLambda = lambda - lambda0,
10061 sdLambda = dLambda >= 0 ? 1 : -1,
10062 adLambda = sdLambda * dLambda,
10065 k = sinPhi0 * sinPhi,
10066 u = cosPhi0 * cosPhi + k * cos(adLambda),
10067 v = k * sdLambda * sin(adLambda);
10068 areaRingSum.add(atan2(v, u)); // Advance the previous points.
10070 lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
10073 function d3_geoArea (object) {
10074 areaSum = new Adder();
10075 d3_geoStream(object, areaStream);
10076 return areaSum * 2;
10079 function spherical(cartesian) {
10080 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10082 function cartesian(spherical) {
10083 var lambda = spherical[0],
10084 phi = spherical[1],
10086 return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10088 function cartesianDot(a, b) {
10089 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10091 function cartesianCross(a, b) {
10092 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]];
10095 function cartesianAddInPlace(a, b) {
10096 a[0] += b[0], a[1] += b[1], a[2] += b[2];
10098 function cartesianScale(vector, k) {
10099 return [vector[0] * k, vector[1] * k, vector[2] * k];
10102 function cartesianNormalizeInPlace(d) {
10103 var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10104 d[0] /= l, d[1] /= l, d[2] /= l;
10107 var lambda0$1, phi0, lambda1, phi1, // bounds
10108 lambda2, // previous lambda-coordinate
10109 lambda00$1, phi00$1, // first point
10110 p0, // previous 3D point
10111 deltaSum, ranges, range$1;
10112 var boundsStream = {
10113 point: boundsPoint,
10114 lineStart: boundsLineStart,
10115 lineEnd: boundsLineEnd,
10116 polygonStart: function polygonStart() {
10117 boundsStream.point = boundsRingPoint;
10118 boundsStream.lineStart = boundsRingStart;
10119 boundsStream.lineEnd = boundsRingEnd;
10120 deltaSum = new Adder();
10121 areaStream.polygonStart();
10123 polygonEnd: function polygonEnd() {
10124 areaStream.polygonEnd();
10125 boundsStream.point = boundsPoint;
10126 boundsStream.lineStart = boundsLineStart;
10127 boundsStream.lineEnd = boundsLineEnd;
10128 if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon) phi1 = 90;else if (deltaSum < -epsilon) phi0 = -90;
10129 range$1[0] = lambda0$1, range$1[1] = lambda1;
10131 sphere: function sphere() {
10132 lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10136 function boundsPoint(lambda, phi) {
10137 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10138 if (phi < phi0) phi0 = phi;
10139 if (phi > phi1) phi1 = phi;
10142 function linePoint(lambda, phi) {
10143 var p = cartesian([lambda * radians, phi * radians]);
10146 var normal = cartesianCross(p0, p),
10147 equatorial = [normal[1], -normal[0], 0],
10148 inflection = cartesianCross(equatorial, normal);
10149 cartesianNormalizeInPlace(inflection);
10150 inflection = spherical(inflection);
10151 var delta = lambda - lambda2,
10152 sign = delta > 0 ? 1 : -1,
10153 lambdai = inflection[0] * degrees * sign,
10155 antimeridian = abs$2(delta) > 180;
10157 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10158 phii = inflection[1] * degrees;
10159 if (phii > phi1) phi1 = phii;
10160 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10161 phii = -inflection[1] * degrees;
10162 if (phii < phi0) phi0 = phii;
10164 if (phi < phi0) phi0 = phi;
10165 if (phi > phi1) phi1 = phi;
10168 if (antimeridian) {
10169 if (lambda < lambda2) {
10170 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10172 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10175 if (lambda1 >= lambda0$1) {
10176 if (lambda < lambda0$1) lambda0$1 = lambda;
10177 if (lambda > lambda1) lambda1 = lambda;
10179 if (lambda > lambda2) {
10180 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10182 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10187 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10190 if (phi < phi0) phi0 = phi;
10191 if (phi > phi1) phi1 = phi;
10192 p0 = p, lambda2 = lambda;
10195 function boundsLineStart() {
10196 boundsStream.point = linePoint;
10199 function boundsLineEnd() {
10200 range$1[0] = lambda0$1, range$1[1] = lambda1;
10201 boundsStream.point = boundsPoint;
10205 function boundsRingPoint(lambda, phi) {
10207 var delta = lambda - lambda2;
10208 deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10210 lambda00$1 = lambda, phi00$1 = phi;
10213 areaStream.point(lambda, phi);
10214 linePoint(lambda, phi);
10217 function boundsRingStart() {
10218 areaStream.lineStart();
10221 function boundsRingEnd() {
10222 boundsRingPoint(lambda00$1, phi00$1);
10223 areaStream.lineEnd();
10224 if (abs$2(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180);
10225 range$1[0] = lambda0$1, range$1[1] = lambda1;
10227 } // Finds the left-right distance between two longitudes.
10228 // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10229 // the distance between ±180° to be 360°.
10232 function angle(lambda0, lambda1) {
10233 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10236 function rangeCompare(a, b) {
10237 return a[0] - b[0];
10240 function rangeContains(range, x) {
10241 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10244 function d3_geoBounds (feature) {
10245 var i, n, a, b, merged, deltaMax, delta;
10246 phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10248 d3_geoStream(feature, boundsStream); // First, sort ranges by their minimum longitudes.
10250 if (n = ranges.length) {
10251 ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10253 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10256 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10257 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10258 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10260 merged.push(a = b);
10262 } // Finally, find the largest gap between the merged ranges.
10263 // The final bounding box will be the inverse of this gap.
10266 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10268 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10272 ranges = range$1 = null;
10273 return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10276 var W0, W1, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, lambda00$2, phi00$2, // first point
10277 x0, y0, z0; // previous point
10279 var centroidStream = {
10281 point: centroidPoint,
10282 lineStart: centroidLineStart,
10283 lineEnd: centroidLineEnd,
10284 polygonStart: function polygonStart() {
10285 centroidStream.lineStart = centroidRingStart;
10286 centroidStream.lineEnd = centroidRingEnd;
10288 polygonEnd: function polygonEnd() {
10289 centroidStream.lineStart = centroidLineStart;
10290 centroidStream.lineEnd = centroidLineEnd;
10292 }; // Arithmetic mean of Cartesian vectors.
10294 function centroidPoint(lambda, phi) {
10295 lambda *= radians, phi *= radians;
10296 var cosPhi = cos(phi);
10297 centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
10300 function centroidPointCartesian(x, y, z) {
10302 X0 += (x - X0) / W0;
10303 Y0 += (y - Y0) / W0;
10304 Z0 += (z - Z0) / W0;
10307 function centroidLineStart() {
10308 centroidStream.point = centroidLinePointFirst;
10311 function centroidLinePointFirst(lambda, phi) {
10312 lambda *= radians, phi *= radians;
10313 var cosPhi = cos(phi);
10314 x0 = cosPhi * cos(lambda);
10315 y0 = cosPhi * sin(lambda);
10317 centroidStream.point = centroidLinePoint;
10318 centroidPointCartesian(x0, y0, z0);
10321 function centroidLinePoint(lambda, phi) {
10322 lambda *= radians, phi *= radians;
10323 var cosPhi = cos(phi),
10324 x = cosPhi * cos(lambda),
10325 y = cosPhi * sin(lambda),
10327 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);
10329 X1 += w * (x0 + (x0 = x));
10330 Y1 += w * (y0 + (y0 = y));
10331 Z1 += w * (z0 + (z0 = z));
10332 centroidPointCartesian(x0, y0, z0);
10335 function centroidLineEnd() {
10336 centroidStream.point = centroidPoint;
10337 } // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
10338 // J. Applied Mechanics 42, 239 (1975).
10341 function centroidRingStart() {
10342 centroidStream.point = centroidRingPointFirst;
10345 function centroidRingEnd() {
10346 centroidRingPoint(lambda00$2, phi00$2);
10347 centroidStream.point = centroidPoint;
10350 function centroidRingPointFirst(lambda, phi) {
10351 lambda00$2 = lambda, phi00$2 = phi;
10352 lambda *= radians, phi *= radians;
10353 centroidStream.point = centroidRingPoint;
10354 var cosPhi = cos(phi);
10355 x0 = cosPhi * cos(lambda);
10356 y0 = cosPhi * sin(lambda);
10358 centroidPointCartesian(x0, y0, z0);
10361 function centroidRingPoint(lambda, phi) {
10362 lambda *= radians, phi *= radians;
10363 var cosPhi = cos(phi),
10364 x = cosPhi * cos(lambda),
10365 y = cosPhi * sin(lambda),
10367 cx = y0 * z - z0 * y,
10368 cy = z0 * x - x0 * z,
10369 cz = x0 * y - y0 * x,
10370 m = hypot(cx, cy, cz),
10372 // line weight = angle
10373 v = m && -w / m; // area weight multiplier
10379 X1 += w * (x0 + (x0 = x));
10380 Y1 += w * (y0 + (y0 = y));
10381 Z1 += w * (z0 + (z0 = z));
10382 centroidPointCartesian(x0, y0, z0);
10385 function d3_geoCentroid (object) {
10386 W0 = W1 = X0 = Y0 = Z0 = X1 = Y1 = Z1 = 0;
10390 d3_geoStream(object, centroidStream);
10394 m = hypot(x, y, z); // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
10396 if (m < epsilon2) {
10397 x = X1, y = Y1, z = Z1; // If the feature has zero length, fall back to arithmetic mean of point vectors.
10399 if (W1 < epsilon) x = X0, y = Y0, z = Z0;
10400 m = hypot(x, y, z); // If the feature still has an undefined ccentroid, then return.
10402 if (m < epsilon2) return [NaN, NaN];
10405 return [atan2(y, x) * degrees, asin(z / m) * degrees];
10408 function compose (a, b) {
10409 function compose(x, y) {
10410 return x = a(x, y), b(x[0], x[1]);
10413 if (a.invert && b.invert) compose.invert = function (x, y) {
10414 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10419 function rotationIdentity(lambda, phi) {
10420 return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10423 rotationIdentity.invert = rotationIdentity;
10424 function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10425 return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10428 function forwardRotationLambda(deltaLambda) {
10429 return function (lambda, phi) {
10430 return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10434 function rotationLambda(deltaLambda) {
10435 var rotation = forwardRotationLambda(deltaLambda);
10436 rotation.invert = forwardRotationLambda(-deltaLambda);
10440 function rotationPhiGamma(deltaPhi, deltaGamma) {
10441 var cosDeltaPhi = cos(deltaPhi),
10442 sinDeltaPhi = sin(deltaPhi),
10443 cosDeltaGamma = cos(deltaGamma),
10444 sinDeltaGamma = sin(deltaGamma);
10446 function rotation(lambda, phi) {
10447 var cosPhi = cos(phi),
10448 x = cos(lambda) * cosPhi,
10449 y = sin(lambda) * cosPhi,
10451 k = z * cosDeltaPhi + x * sinDeltaPhi;
10452 return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10455 rotation.invert = function (lambda, phi) {
10456 var cosPhi = cos(phi),
10457 x = cos(lambda) * cosPhi,
10458 y = sin(lambda) * cosPhi,
10460 k = z * cosDeltaGamma - y * sinDeltaGamma;
10461 return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10467 function rotation (rotate) {
10468 rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10470 function forward(coordinates) {
10471 coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10472 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10475 forward.invert = function (coordinates) {
10476 coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10477 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10483 function circleStream(stream, radius, delta, direction, t0, t1) {
10484 if (!delta) return;
10485 var cosRadius = cos(radius),
10486 sinRadius = sin(radius),
10487 step = direction * delta;
10490 t0 = radius + direction * tau;
10491 t1 = radius - step / 2;
10493 t0 = circleRadius(cosRadius, t0);
10494 t1 = circleRadius(cosRadius, t1);
10495 if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10498 for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10499 point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10500 stream.point(point[0], point[1]);
10502 } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10504 function circleRadius(cosRadius, point) {
10505 point = cartesian(point), point[0] -= cosRadius;
10506 cartesianNormalizeInPlace(point);
10507 var radius = acos(-point[1]);
10508 return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
10511 function clipBuffer () {
10515 point: function point(x, y, m) {
10516 line.push([x, y, m]);
10518 lineStart: function lineStart() {
10519 lines.push(line = []);
10522 rejoin: function rejoin() {
10523 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10525 result: function result() {
10526 var result = lines;
10534 function pointEqual (a, b) {
10535 return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon;
10538 function Intersection(point, points, other, entry) {
10541 this.o = other; // another intersection
10543 this.e = entry; // is an entry?
10545 this.v = false; // visited
10547 this.n = this.p = null; // next & previous
10548 } // A generalized polygon clipping algorithm: given a polygon that has been cut
10549 // into its visible line segments, and rejoins the segments by interpolating
10550 // along the clip edge.
10553 function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10558 segments.forEach(function (segment) {
10559 if ((n = segment.length - 1) <= 0) return;
10565 if (pointEqual(p0, p1)) {
10566 if (!p0[2] && !p1[2]) {
10567 stream.lineStart();
10569 for (i = 0; i < n; ++i) {
10570 stream.point((p0 = segment[i])[0], p0[1]);
10575 } // handle degenerate cases by moving the point
10578 p1[0] += 2 * epsilon;
10581 subject.push(x = new Intersection(p0, segment, null, true));
10582 clip.push(x.o = new Intersection(p0, null, x, false));
10583 subject.push(x = new Intersection(p1, segment, null, false));
10584 clip.push(x.o = new Intersection(p1, null, x, true));
10586 if (!subject.length) return;
10587 clip.sort(compareIntersection);
10591 for (i = 0, n = clip.length; i < n; ++i) {
10592 clip[i].e = startInside = !startInside;
10595 var start = subject[0],
10600 // Find first unvisited intersection.
10601 var current = start,
10604 while (current.v) {
10605 if ((current = current.n) === start) return;
10608 points = current.z;
10609 stream.lineStart();
10612 current.v = current.o.v = true;
10616 for (i = 0, n = points.length; i < n; ++i) {
10617 stream.point((point = points[i])[0], point[1]);
10620 interpolate(current.x, current.n.x, 1, stream);
10623 current = current.n;
10626 points = current.p.z;
10628 for (i = points.length - 1; i >= 0; --i) {
10629 stream.point((point = points[i])[0], point[1]);
10632 interpolate(current.x, current.p.x, -1, stream);
10635 current = current.p;
10638 current = current.o;
10639 points = current.z;
10640 isSubject = !isSubject;
10641 } while (!current.v);
10647 function link(array) {
10648 if (!(n = array.length)) return;
10655 a.n = b = array[i];
10660 a.n = b = array[0];
10664 function longitude(point) {
10665 if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10668 function polygonContains (polygon, point) {
10669 var lambda = longitude(point),
10672 normal = [sin(lambda), -cos(lambda), 0],
10675 var sum = new Adder();
10676 if (sinPhi === 1) phi = halfPi + epsilon;else if (sinPhi === -1) phi = -halfPi - epsilon;
10678 for (var i = 0, n = polygon.length; i < n; ++i) {
10679 if (!(m = (ring = polygon[i]).length)) continue;
10682 point0 = ring[m - 1],
10683 lambda0 = longitude(point0),
10684 phi0 = point0[1] / 2 + quarterPi,
10685 sinPhi0 = sin(phi0),
10686 cosPhi0 = cos(phi0);
10688 for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10689 var point1 = ring[j],
10690 lambda1 = longitude(point1),
10691 phi1 = point1[1] / 2 + quarterPi,
10692 sinPhi1 = sin(phi1),
10693 cosPhi1 = cos(phi1),
10694 delta = lambda1 - lambda0,
10695 sign = delta >= 0 ? 1 : -1,
10696 absDelta = sign * delta,
10697 antimeridian = absDelta > pi,
10698 k = sinPhi0 * sinPhi1;
10699 sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10700 angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10701 // and are the latitudes smaller than the parallel (phi)?
10703 if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10704 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10705 cartesianNormalizeInPlace(arc);
10706 var intersection = cartesianCross(normal, arc);
10707 cartesianNormalizeInPlace(intersection);
10708 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10710 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10711 winding += antimeridian ^ delta >= 0 ? 1 : -1;
10715 } // First, determine whether the South pole is inside or outside:
10717 // It is inside if:
10718 // * the polygon winds around it in a clockwise direction.
10719 // * the polygon does not (cumulatively) wind around it, but has a negative
10720 // (counter-clockwise) area.
10722 // Second, count the (signed) number of times a segment crosses a lambda
10723 // from the point to the South pole. If it is zero, then the point is the
10724 // same side as the South pole.
10727 return (angle < -epsilon || angle < epsilon && sum < -epsilon2) ^ winding & 1;
10730 function clip (pointVisible, clipLine, interpolate, start) {
10731 return function (sink) {
10732 var line = clipLine(sink),
10733 ringBuffer = clipBuffer(),
10734 ringSink = clipLine(ringBuffer),
10735 polygonStarted = false,
10741 lineStart: lineStart,
10743 polygonStart: function polygonStart() {
10744 clip.point = pointRing;
10745 clip.lineStart = ringStart;
10746 clip.lineEnd = ringEnd;
10750 polygonEnd: function polygonEnd() {
10751 clip.point = point;
10752 clip.lineStart = lineStart;
10753 clip.lineEnd = lineEnd;
10754 segments = merge(segments);
10755 var startInside = polygonContains(polygon, start);
10757 if (segments.length) {
10758 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10759 clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10760 } else if (startInside) {
10761 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10763 interpolate(null, null, 1, sink);
10767 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10768 segments = polygon = null;
10770 sphere: function sphere() {
10771 sink.polygonStart();
10773 interpolate(null, null, 1, sink);
10779 function point(lambda, phi) {
10780 if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10783 function pointLine(lambda, phi) {
10784 line.point(lambda, phi);
10787 function lineStart() {
10788 clip.point = pointLine;
10792 function lineEnd() {
10793 clip.point = point;
10797 function pointRing(lambda, phi) {
10798 ring.push([lambda, phi]);
10799 ringSink.point(lambda, phi);
10802 function ringStart() {
10803 ringSink.lineStart();
10807 function ringEnd() {
10808 pointRing(ring[0][0], ring[0][1]);
10809 ringSink.lineEnd();
10810 var clean = ringSink.clean(),
10811 ringSegments = ringBuffer.result(),
10813 n = ringSegments.length,
10818 polygon.push(ring);
10820 if (!n) return; // No intersections.
10823 segment = ringSegments[0];
10825 if ((m = segment.length - 1) > 0) {
10826 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10829 for (i = 0; i < m; ++i) {
10830 sink.point((point = segment[i])[0], point[1]);
10837 } // Rejoin connected segments.
10838 // TODO reuse ringBuffer.rejoin()?
10841 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10842 segments.push(ringSegments.filter(validSegment));
10849 function validSegment(segment) {
10850 return segment.length > 1;
10851 } // Intersections are sorted along the clip edge. For both antimeridian cutting
10852 // and circle clipping, the same comparison is used.
10855 function compareIntersection(a, b) {
10856 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]);
10859 var clipAntimeridian = clip(function () {
10861 }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
10862 // intersections or the line was empty; 1 - no intersections; 2 - there were
10863 // intersections, and the first and last segments should be rejoined.
10865 function clipAntimeridianLine(stream) {
10869 _clean; // no intersections
10873 lineStart: function lineStart() {
10874 stream.lineStart();
10877 point: function point(lambda1, phi1) {
10878 var sign1 = lambda1 > 0 ? pi : -pi,
10879 delta = abs$2(lambda1 - lambda0);
10881 if (abs$2(delta - pi) < epsilon) {
10882 // line crosses a pole
10883 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
10884 stream.point(sign0, phi0);
10886 stream.lineStart();
10887 stream.point(sign1, phi0);
10888 stream.point(lambda1, phi0);
10890 } else if (sign0 !== sign1 && delta >= pi) {
10891 // line crosses antimeridian
10892 if (abs$2(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies
10894 if (abs$2(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon;
10895 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
10896 stream.point(sign0, phi0);
10898 stream.lineStart();
10899 stream.point(sign1, phi0);
10903 stream.point(lambda0 = lambda1, phi0 = phi1);
10906 lineEnd: function lineEnd() {
10908 lambda0 = phi0 = NaN;
10910 clean: function clean() {
10911 return 2 - _clean; // if intersections, rejoin first and last segments
10916 function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
10919 sinLambda0Lambda1 = sin(lambda0 - lambda1);
10920 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;
10923 function clipAntimeridianInterpolate(from, to, direction, stream) {
10926 if (from == null) {
10927 phi = direction * halfPi;
10928 stream.point(-pi, phi);
10929 stream.point(0, phi);
10930 stream.point(pi, phi);
10931 stream.point(pi, 0);
10932 stream.point(pi, -phi);
10933 stream.point(0, -phi);
10934 stream.point(-pi, -phi);
10935 stream.point(-pi, 0);
10936 stream.point(-pi, phi);
10937 } else if (abs$2(from[0] - to[0]) > epsilon) {
10938 var lambda = from[0] < to[0] ? pi : -pi;
10939 phi = direction * lambda / 2;
10940 stream.point(-lambda, phi);
10941 stream.point(0, phi);
10942 stream.point(lambda, phi);
10944 stream.point(to[0], to[1]);
10948 function clipCircle (radius) {
10949 var cr = cos(radius),
10950 delta = 6 * radians,
10951 smallRadius = cr > 0,
10952 notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case
10954 function interpolate(from, to, direction, stream) {
10955 circleStream(stream, radius, delta, direction, from, to);
10958 function visible(lambda, phi) {
10959 return cos(lambda) * cos(phi) > cr;
10960 } // Takes a line and cuts into visible segments. Return values used for polygon
10961 // clipping: 0 - there were intersections or the line was empty; 1 - no
10962 // intersections 2 - there were intersections, and the first and last segments
10963 // should be rejoined.
10966 function clipLine(stream) {
10967 var point0, // previous point
10968 c0, // code for previous point
10969 v0, // visibility of previous point
10970 v00, // visibility of first point
10971 _clean; // no intersections
10975 lineStart: function lineStart() {
10979 point: function point(lambda, phi) {
10980 var point1 = [lambda, phi],
10982 v = visible(lambda, phi),
10983 c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
10984 if (!point0 && (v00 = v0 = v)) stream.lineStart();
10987 point2 = intersect(point0, point1);
10988 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
10995 // outside going in
10996 stream.lineStart();
10997 point2 = intersect(point1, point0);
10998 stream.point(point2[0], point2[1]);
11000 // inside going out
11001 point2 = intersect(point0, point1);
11002 stream.point(point2[0], point2[1], 2);
11007 } else if (notHemisphere && point0 && smallRadius ^ v) {
11008 var t; // If the codes for two points are different, or are both zero,
11009 // and there this segment intersects with the small circle.
11011 if (!(c & c0) && (t = intersect(point1, point0, true))) {
11015 stream.lineStart();
11016 stream.point(t[0][0], t[0][1]);
11017 stream.point(t[1][0], t[1][1]);
11020 stream.point(t[1][0], t[1][1]);
11022 stream.lineStart();
11023 stream.point(t[0][0], t[0][1], 3);
11028 if (v && (!point0 || !pointEqual(point0, point1))) {
11029 stream.point(point1[0], point1[1]);
11032 point0 = point1, v0 = v, c0 = c;
11034 lineEnd: function lineEnd() {
11035 if (v0) stream.lineEnd();
11038 // Rejoin first and last segments if there were intersections and the first
11039 // and last points were visible.
11040 clean: function clean() {
11041 return _clean | (v00 && v0) << 1;
11044 } // Intersects the great circle between a and b with the clip circle.
11047 function intersect(a, b, two) {
11048 var pa = cartesian(a),
11049 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
11050 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
11052 var n1 = [1, 0, 0],
11054 n2 = cartesianCross(pa, pb),
11055 n2n2 = cartesianDot(n2, n2),
11057 // cartesianDot(n1, n2),
11058 determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11060 if (!determinant) return !two && a;
11061 var c1 = cr * n2n2 / determinant,
11062 c2 = -cr * n1n2 / determinant,
11063 n1xn2 = cartesianCross(n1, n2),
11064 A = cartesianScale(n1, c1),
11065 B = cartesianScale(n2, c2);
11066 cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11069 w = cartesianDot(A, u),
11070 uu = cartesianDot(u, u),
11071 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11072 if (t2 < 0) return;
11073 var t = sqrt$1(t2),
11074 q = cartesianScale(u, (-w - t) / uu);
11075 cartesianAddInPlace(q, A);
11077 if (!two) return q; // Two intersection points.
11079 var lambda0 = a[0],
11084 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11085 var delta = lambda1 - lambda0,
11086 polar = abs$2(delta - pi) < epsilon,
11087 meridian = polar || delta < epsilon;
11088 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11090 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)) {
11091 var q1 = cartesianScale(u, (-w + t) / uu);
11092 cartesianAddInPlace(q1, A);
11093 return [q, spherical(q1)];
11095 } // Generates a 4-bit vector representing the location of a point relative to
11096 // the small circle's bounding box.
11099 function code(lambda, phi) {
11100 var r = smallRadius ? radius : pi - radius,
11102 if (lambda < -r) code |= 1; // left
11103 else if (lambda > r) code |= 2; // right
11105 if (phi < -r) code |= 4; // below
11106 else if (phi > r) code |= 8; // above
11111 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11114 function clipLine (a, b, x0, y0, x1, y1) {
11125 if (!dx && r > 0) return;
11129 if (r < t0) return;
11130 if (r < t1) t1 = r;
11131 } else if (dx > 0) {
11132 if (r > t1) return;
11133 if (r > t0) t0 = r;
11137 if (!dx && r < 0) return;
11141 if (r > t1) return;
11142 if (r > t0) t0 = r;
11143 } else if (dx > 0) {
11144 if (r < t0) return;
11145 if (r < t1) t1 = r;
11149 if (!dy && r > 0) return;
11153 if (r < t0) return;
11154 if (r < t1) t1 = r;
11155 } else if (dy > 0) {
11156 if (r > t1) return;
11157 if (r > t0) t0 = r;
11161 if (!dy && r < 0) return;
11165 if (r > t1) return;
11166 if (r > t0) t0 = r;
11167 } else if (dy > 0) {
11168 if (r < t0) return;
11169 if (r < t1) t1 = r;
11172 if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11173 if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11178 clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11179 // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11181 function clipRectangle(x0, y0, x1, y1) {
11182 function visible(x, y) {
11183 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11186 function interpolate(from, to, direction, stream) {
11190 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11192 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11193 } while ((a = (a + direction + 4) % 4) !== a1);
11195 stream.point(to[0], to[1]);
11199 function corner(p, direction) {
11200 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
11203 function compareIntersection(a, b) {
11204 return comparePoint(a.x, b.x);
11207 function comparePoint(a, b) {
11208 var ca = corner(a, 1),
11210 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];
11213 return function (stream) {
11214 var activeStream = stream,
11215 bufferStream = clipBuffer(),
11231 lineStart: lineStart,
11233 polygonStart: polygonStart,
11234 polygonEnd: polygonEnd
11237 function point(x, y) {
11238 if (visible(x, y)) activeStream.point(x, y);
11241 function polygonInside() {
11244 for (var i = 0, n = polygon.length; i < n; ++i) {
11245 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11246 a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11249 if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11251 if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11257 } // Buffer geometry within a polygon and then clip it en masse.
11260 function polygonStart() {
11261 activeStream = bufferStream, segments = [], polygon = [], clean = true;
11264 function polygonEnd() {
11265 var startInside = polygonInside(),
11266 cleanInside = clean && startInside,
11267 visible = (segments = merge(segments)).length;
11269 if (cleanInside || visible) {
11270 stream.polygonStart();
11273 stream.lineStart();
11274 interpolate(null, null, 1, stream);
11279 clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11282 stream.polygonEnd();
11285 activeStream = stream, segments = polygon = ring = null;
11288 function lineStart() {
11289 clipStream.point = linePoint;
11290 if (polygon) polygon.push(ring = []);
11294 } // TODO rather than special-case polygons, simply handle them separately.
11295 // Ideally, coincident intersection points should be jittered to avoid
11296 // clipping issues.
11299 function lineEnd() {
11301 linePoint(x__, y__);
11302 if (v__ && v_) bufferStream.rejoin();
11303 segments.push(bufferStream.result());
11306 clipStream.point = point;
11307 if (v_) activeStream.lineEnd();
11310 function linePoint(x, y) {
11311 var v = visible(x, y);
11312 if (polygon) ring.push([x, y]);
11315 x__ = x, y__ = y, v__ = v;
11319 activeStream.lineStart();
11320 activeStream.point(x, y);
11323 if (v && v_) activeStream.point(x, y);else {
11324 var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11325 b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11327 if (clipLine(a, b, x0, y0, x1, y1)) {
11329 activeStream.lineStart();
11330 activeStream.point(a[0], a[1]);
11333 activeStream.point(b[0], b[1]);
11334 if (!v) activeStream.lineEnd();
11337 activeStream.lineStart();
11338 activeStream.point(x, y);
11344 x_ = x, y_ = y, v_ = v;
11351 var lengthSum, lambda0$2, sinPhi0$1, cosPhi0$1;
11352 var lengthStream = {
11355 lineStart: lengthLineStart,
11357 polygonStart: noop,
11361 function lengthLineStart() {
11362 lengthStream.point = lengthPointFirst;
11363 lengthStream.lineEnd = lengthLineEnd;
11366 function lengthLineEnd() {
11367 lengthStream.point = lengthStream.lineEnd = noop;
11370 function lengthPointFirst(lambda, phi) {
11371 lambda *= radians, phi *= radians;
11372 lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi);
11373 lengthStream.point = lengthPoint;
11376 function lengthPoint(lambda, phi) {
11377 lambda *= radians, phi *= radians;
11378 var sinPhi = sin(phi),
11380 delta = abs$2(lambda - lambda0$2),
11381 cosDelta = cos(delta),
11382 sinDelta = sin(delta),
11383 x = cosPhi * sinDelta,
11384 y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
11385 z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
11386 lengthSum.add(atan2(sqrt$1(x * x + y * y), z));
11387 lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
11390 function d3_geoLength (object) {
11391 lengthSum = new Adder();
11392 d3_geoStream(object, lengthStream);
11396 var identity = (function (x) {
11400 var areaSum$1 = new Adder(),
11401 areaRingSum$1 = new Adder(),
11406 var areaStream$1 = {
11410 polygonStart: function polygonStart() {
11411 areaStream$1.lineStart = areaRingStart$1;
11412 areaStream$1.lineEnd = areaRingEnd$1;
11414 polygonEnd: function polygonEnd() {
11415 areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop;
11416 areaSum$1.add(abs$2(areaRingSum$1));
11417 areaRingSum$1 = new Adder();
11419 result: function result() {
11420 var area = areaSum$1 / 2;
11421 areaSum$1 = new Adder();
11426 function areaRingStart$1() {
11427 areaStream$1.point = areaPointFirst$1;
11430 function areaPointFirst$1(x, y) {
11431 areaStream$1.point = areaPoint$1;
11432 x00 = x0$1 = x, y00 = y0$1 = y;
11435 function areaPoint$1(x, y) {
11436 areaRingSum$1.add(y0$1 * x - x0$1 * y);
11437 x0$1 = x, y0$1 = y;
11440 function areaRingEnd$1() {
11441 areaPoint$1(x00, y00);
11444 var x0$2 = Infinity,
11448 var boundsStream$1 = {
11449 point: boundsPoint$1,
11452 polygonStart: noop,
11454 result: function result() {
11455 var bounds = [[x0$2, y0$2], [x1, y1]];
11456 x1 = y1 = -(y0$2 = x0$2 = Infinity);
11461 function boundsPoint$1(x, y) {
11462 if (x < x0$2) x0$2 = x;
11463 if (x > x1) x1 = x;
11464 if (y < y0$2) y0$2 = y;
11465 if (y > y1) y1 = y;
11481 var centroidStream$1 = {
11482 point: centroidPoint$1,
11483 lineStart: centroidLineStart$1,
11484 lineEnd: centroidLineEnd$1,
11485 polygonStart: function polygonStart() {
11486 centroidStream$1.lineStart = centroidRingStart$1;
11487 centroidStream$1.lineEnd = centroidRingEnd$1;
11489 polygonEnd: function polygonEnd() {
11490 centroidStream$1.point = centroidPoint$1;
11491 centroidStream$1.lineStart = centroidLineStart$1;
11492 centroidStream$1.lineEnd = centroidLineEnd$1;
11494 result: function result() {
11495 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];
11496 X0$1 = Y0$1 = Z0$1 = X1$1 = Y1$1 = Z1$1 = X2$1 = Y2$1 = Z2$1 = 0;
11501 function centroidPoint$1(x, y) {
11507 function centroidLineStart$1() {
11508 centroidStream$1.point = centroidPointFirstLine;
11511 function centroidPointFirstLine(x, y) {
11512 centroidStream$1.point = centroidPointLine;
11513 centroidPoint$1(x0$3 = x, y0$3 = y);
11516 function centroidPointLine(x, y) {
11519 z = sqrt$1(dx * dx + dy * dy);
11520 X1$1 += z * (x0$3 + x) / 2;
11521 Y1$1 += z * (y0$3 + y) / 2;
11523 centroidPoint$1(x0$3 = x, y0$3 = y);
11526 function centroidLineEnd$1() {
11527 centroidStream$1.point = centroidPoint$1;
11530 function centroidRingStart$1() {
11531 centroidStream$1.point = centroidPointFirstRing;
11534 function centroidRingEnd$1() {
11535 centroidPointRing(x00$1, y00$1);
11538 function centroidPointFirstRing(x, y) {
11539 centroidStream$1.point = centroidPointRing;
11540 centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
11543 function centroidPointRing(x, y) {
11546 z = sqrt$1(dx * dx + dy * dy);
11547 X1$1 += z * (x0$3 + x) / 2;
11548 Y1$1 += z * (y0$3 + y) / 2;
11550 z = y0$3 * x - x0$3 * y;
11551 X2$1 += z * (x0$3 + x);
11552 Y2$1 += z * (y0$3 + y);
11554 centroidPoint$1(x0$3 = x, y0$3 = y);
11557 function PathContext(context) {
11558 this._context = context;
11560 PathContext.prototype = {
11562 pointRadius: function pointRadius(_) {
11563 return this._radius = _, this;
11565 polygonStart: function polygonStart() {
11568 polygonEnd: function polygonEnd() {
11571 lineStart: function lineStart() {
11574 lineEnd: function lineEnd() {
11575 if (this._line === 0) this._context.closePath();
11578 point: function point(x, y) {
11579 switch (this._point) {
11582 this._context.moveTo(x, y);
11590 this._context.lineTo(x, y);
11597 this._context.moveTo(x + this._radius, y);
11599 this._context.arc(x, y, this._radius, 0, tau);
11608 var lengthSum$1 = new Adder(),
11614 var lengthStream$1 = {
11616 lineStart: function lineStart() {
11617 lengthStream$1.point = lengthPointFirst$1;
11619 lineEnd: function lineEnd() {
11620 if (lengthRing) lengthPoint$1(x00$2, y00$2);
11621 lengthStream$1.point = noop;
11623 polygonStart: function polygonStart() {
11626 polygonEnd: function polygonEnd() {
11629 result: function result() {
11630 var length = +lengthSum$1;
11631 lengthSum$1 = new Adder();
11636 function lengthPointFirst$1(x, y) {
11637 lengthStream$1.point = lengthPoint$1;
11638 x00$2 = x0$4 = x, y00$2 = y0$4 = y;
11641 function lengthPoint$1(x, y) {
11642 x0$4 -= x, y0$4 -= y;
11643 lengthSum$1.add(sqrt$1(x0$4 * x0$4 + y0$4 * y0$4));
11644 x0$4 = x, y0$4 = y;
11647 function PathString() {
11650 PathString.prototype = {
11652 _circle: circle(4.5),
11653 pointRadius: function pointRadius(_) {
11654 if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11657 polygonStart: function polygonStart() {
11660 polygonEnd: function polygonEnd() {
11663 lineStart: function lineStart() {
11666 lineEnd: function lineEnd() {
11667 if (this._line === 0) this._string.push("Z");
11670 point: function point(x, y) {
11671 switch (this._point) {
11674 this._string.push("M", x, ",", y);
11682 this._string.push("L", x, ",", y);
11689 if (this._circle == null) this._circle = circle(this._radius);
11691 this._string.push("M", x, ",", y, this._circle);
11697 result: function result() {
11698 if (this._string.length) {
11699 var result = this._string.join("");
11709 function circle(radius) {
11710 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11713 function d3_geoPath (projection, context) {
11714 var pointRadius = 4.5,
11718 function path(object) {
11720 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11721 d3_geoStream(object, projectionStream(contextStream));
11724 return contextStream.result();
11727 path.area = function (object) {
11728 d3_geoStream(object, projectionStream(areaStream$1));
11729 return areaStream$1.result();
11732 path.measure = function (object) {
11733 d3_geoStream(object, projectionStream(lengthStream$1));
11734 return lengthStream$1.result();
11737 path.bounds = function (object) {
11738 d3_geoStream(object, projectionStream(boundsStream$1));
11739 return boundsStream$1.result();
11742 path.centroid = function (object) {
11743 d3_geoStream(object, projectionStream(centroidStream$1));
11744 return centroidStream$1.result();
11747 path.projection = function (_) {
11748 return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
11751 path.context = function (_) {
11752 if (!arguments.length) return context;
11753 contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11754 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11758 path.pointRadius = function (_) {
11759 if (!arguments.length) return pointRadius;
11760 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11764 return path.projection(projection).context(context);
11767 function d3_geoTransform (methods) {
11769 stream: transformer(methods)
11772 function transformer(methods) {
11773 return function (stream) {
11774 var s = new TransformStream();
11776 for (var key in methods) {
11777 s[key] = methods[key];
11785 function TransformStream() {}
11787 TransformStream.prototype = {
11788 constructor: TransformStream,
11789 point: function point(x, y) {
11790 this.stream.point(x, y);
11792 sphere: function sphere() {
11793 this.stream.sphere();
11795 lineStart: function lineStart() {
11796 this.stream.lineStart();
11798 lineEnd: function lineEnd() {
11799 this.stream.lineEnd();
11801 polygonStart: function polygonStart() {
11802 this.stream.polygonStart();
11804 polygonEnd: function polygonEnd() {
11805 this.stream.polygonEnd();
11809 function fit(projection, fitBounds, object) {
11810 var clip = projection.clipExtent && projection.clipExtent();
11811 projection.scale(150).translate([0, 0]);
11812 if (clip != null) projection.clipExtent(null);
11813 d3_geoStream(object, projection.stream(boundsStream$1));
11814 fitBounds(boundsStream$1.result());
11815 if (clip != null) projection.clipExtent(clip);
11819 function fitExtent(projection, extent, object) {
11820 return fit(projection, function (b) {
11821 var w = extent[1][0] - extent[0][0],
11822 h = extent[1][1] - extent[0][1],
11823 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11824 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11825 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11826 projection.scale(150 * k).translate([x, y]);
11829 function fitSize(projection, size, object) {
11830 return fitExtent(projection, [[0, 0], size], object);
11832 function fitWidth(projection, width, object) {
11833 return fit(projection, function (b) {
11835 k = w / (b[1][0] - b[0][0]),
11836 x = (w - k * (b[1][0] + b[0][0])) / 2,
11838 projection.scale(150 * k).translate([x, y]);
11841 function fitHeight(projection, height, object) {
11842 return fit(projection, function (b) {
11844 k = h / (b[1][1] - b[0][1]),
11846 y = (h - k * (b[1][1] + b[0][1])) / 2;
11847 projection.scale(150 * k).translate([x, y]);
11852 // maximum depth of subdivision
11853 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11855 function resample (project, delta2) {
11856 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11859 function resampleNone(project) {
11860 return transformer({
11861 point: function point(x, y) {
11863 this.stream.point(x[0], x[1]);
11868 function resample$1(project, delta2) {
11869 function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
11872 d2 = dx * dx + dy * dy;
11874 if (d2 > 4 * delta2 && depth--) {
11878 m = sqrt$1(a * a + b * b + c * c),
11879 phi2 = asin(c /= m),
11880 lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
11881 p = project(lambda2, phi2),
11886 dz = dy * dx2 - dx * dy2;
11888 if (dz * dz / d2 > delta2 // perpendicular projected distance
11889 || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
11890 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
11891 // angular distance
11892 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
11893 stream.point(x2, y2);
11894 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
11899 return function (stream) {
11900 var lambda00, x00, y00, a00, b00, c00, // first point
11901 lambda0, x0, y0, a0, b0, c0; // previous point
11903 var resampleStream = {
11905 lineStart: lineStart,
11907 polygonStart: function polygonStart() {
11908 stream.polygonStart();
11909 resampleStream.lineStart = ringStart;
11911 polygonEnd: function polygonEnd() {
11912 stream.polygonEnd();
11913 resampleStream.lineStart = lineStart;
11917 function point(x, y) {
11919 stream.point(x[0], x[1]);
11922 function lineStart() {
11924 resampleStream.point = linePoint;
11925 stream.lineStart();
11928 function linePoint(lambda, phi) {
11929 var c = cartesian([lambda, phi]),
11930 p = project(lambda, phi);
11931 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);
11932 stream.point(x0, y0);
11935 function lineEnd() {
11936 resampleStream.point = point;
11940 function ringStart() {
11942 resampleStream.point = ringPoint;
11943 resampleStream.lineEnd = ringEnd;
11946 function ringPoint(lambda, phi) {
11947 linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
11948 resampleStream.point = linePoint;
11951 function ringEnd() {
11952 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
11953 resampleStream.lineEnd = lineEnd;
11957 return resampleStream;
11961 var transformRadians = transformer({
11962 point: function point(x, y) {
11963 this.stream.point(x * radians, y * radians);
11967 function transformRotate(rotate) {
11968 return transformer({
11969 point: function point(x, y) {
11970 var r = rotate(x, y);
11971 return this.stream.point(r[0], r[1]);
11976 function scaleTranslate(k, dx, dy, sx, sy) {
11977 function transform(x, y) {
11980 return [dx + k * x, dy - k * y];
11983 transform.invert = function (x, y) {
11984 return [(x - dx) / k * sx, (dy - y) / k * sy];
11990 function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
11991 if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
11992 var cosAlpha = cos(alpha),
11993 sinAlpha = sin(alpha),
11998 ci = (sinAlpha * dy - cosAlpha * dx) / k,
11999 fi = (sinAlpha * dx + cosAlpha * dy) / k;
12001 function transform(x, y) {
12004 return [a * x - b * y + dx, dy - b * x - a * y];
12007 transform.invert = function (x, y) {
12008 return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
12014 function projection(project) {
12015 return projectionMutator(function () {
12019 function projectionMutator(projectAt) {
12035 // post-rotate angle
12041 preclip = clipAntimeridian,
12047 postclip = identity,
12048 // post-clip extent
12053 projectRotateTransform,
12057 function projection(point) {
12058 return projectRotateTransform(point[0] * radians, point[1] * radians);
12061 function invert(point) {
12062 point = projectRotateTransform.invert(point[0], point[1]);
12063 return point && [point[0] * degrees, point[1] * degrees];
12066 projection.stream = function (stream) {
12067 return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12070 projection.preclip = function (_) {
12071 return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12074 projection.postclip = function (_) {
12075 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12078 projection.clipAngle = function (_) {
12079 return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
12082 projection.clipExtent = function (_) {
12083 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]];
12086 projection.scale = function (_) {
12087 return arguments.length ? (k = +_, recenter()) : k;
12090 projection.translate = function (_) {
12091 return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12094 projection.center = function (_) {
12095 return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
12098 projection.rotate = function (_) {
12099 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];
12102 projection.angle = function (_) {
12103 return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
12106 projection.reflectX = function (_) {
12107 return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12110 projection.reflectY = function (_) {
12111 return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12114 projection.precision = function (_) {
12115 return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2);
12118 projection.fitExtent = function (extent, object) {
12119 return fitExtent(projection, extent, object);
12122 projection.fitSize = function (size, object) {
12123 return fitSize(projection, size, object);
12126 projection.fitWidth = function (width, object) {
12127 return fitWidth(projection, width, object);
12130 projection.fitHeight = function (height, object) {
12131 return fitHeight(projection, height, object);
12134 function recenter() {
12135 var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12136 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12137 rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12138 projectTransform = compose(project, transform);
12139 projectRotateTransform = compose(rotate, projectTransform);
12140 projectResample = resample(projectTransform, delta2);
12145 cache = cacheStream = null;
12149 return function () {
12150 project = projectAt.apply(this, arguments);
12151 projection.invert = project.invert && invert;
12156 function mercatorRaw(lambda, phi) {
12157 return [lambda, log$1(tan((halfPi + phi) / 2))];
12160 mercatorRaw.invert = function (x, y) {
12161 return [x, 2 * atan(exp(y)) - halfPi];
12164 function mercator () {
12165 return mercatorProjection(mercatorRaw).scale(961 / tau);
12167 function mercatorProjection(project) {
12168 var m = projection(project),
12171 translate = m.translate,
12172 clipExtent = m.clipExtent,
12178 m.scale = function (_) {
12179 return arguments.length ? (scale(_), reclip()) : scale();
12182 m.translate = function (_) {
12183 return arguments.length ? (translate(_), reclip()) : translate();
12186 m.center = function (_) {
12187 return arguments.length ? (center(_), reclip()) : center();
12190 m.clipExtent = function (_) {
12191 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]];
12194 function reclip() {
12195 var k = pi * scale(),
12196 t = m(rotation(m.rotate()).invert([0, 0]));
12197 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)]]);
12203 function d3_geoIdentity () {
12209 // scale, translate and reflect
12221 transform = transformer({
12222 point: function point(x, y) {
12223 var p = projection([x, y]);
12224 this.stream.point(p[0], p[1]);
12227 postclip = identity,
12234 cache = cacheStream = null;
12238 function projection(p) {
12243 var t = y * ca - x * sa;
12244 x = x * ca + y * sa;
12248 return [x + tx, y + ty];
12251 projection.invert = function (p) {
12256 var t = y * ca + x * sa;
12257 x = x * ca - y * sa;
12261 return [x / kx, y / ky];
12264 projection.stream = function (stream) {
12265 return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12268 projection.postclip = function (_) {
12269 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12272 projection.clipExtent = function (_) {
12273 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]];
12276 projection.scale = function (_) {
12277 return arguments.length ? (k = +_, reset()) : k;
12280 projection.translate = function (_) {
12281 return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12284 projection.angle = function (_) {
12285 return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
12288 projection.reflectX = function (_) {
12289 return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12292 projection.reflectY = function (_) {
12293 return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12296 projection.fitExtent = function (extent, object) {
12297 return fitExtent(projection, extent, object);
12300 projection.fitSize = function (size, object) {
12301 return fitSize(projection, size, object);
12304 projection.fitWidth = function (width, object) {
12305 return fitWidth(projection, width, object);
12308 projection.fitHeight = function (height, object) {
12309 return fitHeight(projection, height, object);
12316 var TAU = 2 * Math.PI;
12317 var EQUATORIAL_RADIUS = 6356752.314245179;
12318 var POLAR_RADIUS = 6378137.0;
12319 function geoLatToMeters(dLat) {
12320 return dLat * (TAU * POLAR_RADIUS / 360);
12322 function geoLonToMeters(dLon, atLat) {
12323 return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12325 function geoMetersToLat(m) {
12326 return m / (TAU * POLAR_RADIUS / 360);
12328 function geoMetersToLon(m, atLat) {
12329 return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12331 function geoMetersToOffset(meters, tileSize) {
12332 tileSize = tileSize || 256;
12333 return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS)];
12335 function geoOffsetToMeters(offset, tileSize) {
12336 tileSize = tileSize || 256;
12337 return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS / tileSize];
12338 } // Equirectangular approximation of spherical distances on Earth
12340 function geoSphericalDistance(a, b) {
12341 var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12342 var y = geoLatToMeters(a[1] - b[1]);
12343 return Math.sqrt(x * x + y * y);
12346 function geoScaleToZoom(k, tileSize) {
12347 tileSize = tileSize || 256;
12348 var log2ts = Math.log(tileSize) * Math.LOG2E;
12349 return Math.log(k * TAU) / Math.LN2 - log2ts;
12352 function geoZoomToScale(z, tileSize) {
12353 tileSize = tileSize || 256;
12354 return tileSize * Math.pow(2, z) / TAU;
12355 } // returns info about the node from `nodes` closest to the given `point`
12357 function geoSphericalClosestNode(nodes, point) {
12358 var minDistance = Infinity,
12362 for (var i in nodes) {
12363 distance = geoSphericalDistance(nodes[i].loc, point);
12365 if (distance < minDistance) {
12366 minDistance = distance;
12371 if (indexOfMin !== undefined) {
12374 distance: minDistance,
12375 node: nodes[indexOfMin]
12382 function geoExtent(min, max) {
12383 if (!(this instanceof geoExtent)) {
12384 return new geoExtent(min, max);
12385 } else if (min instanceof geoExtent) {
12387 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12391 this[0] = min || [Infinity, Infinity];
12392 this[1] = max || min || [-Infinity, -Infinity];
12395 geoExtent.prototype = new Array(2);
12396 Object.assign(geoExtent.prototype, {
12397 equals: function equals(obj) {
12398 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];
12400 extend: function extend(obj) {
12401 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12402 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])]);
12404 _extend: function _extend(extent) {
12405 this[0][0] = Math.min(extent[0][0], this[0][0]);
12406 this[0][1] = Math.min(extent[0][1], this[0][1]);
12407 this[1][0] = Math.max(extent[1][0], this[1][0]);
12408 this[1][1] = Math.max(extent[1][1], this[1][1]);
12410 area: function area() {
12411 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12413 center: function center() {
12414 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12416 rectangle: function rectangle() {
12417 return [this[0][0], this[0][1], this[1][0], this[1][1]];
12419 bbox: function bbox() {
12427 polygon: function polygon() {
12428 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]]];
12430 contains: function contains(obj) {
12431 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12432 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];
12434 intersects: function intersects(obj) {
12435 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12436 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];
12438 intersection: function intersection(obj) {
12439 if (!this.intersects(obj)) return new geoExtent();
12440 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])]);
12442 percentContainedIn: function percentContainedIn(obj) {
12443 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12444 var a1 = this.intersection(obj).area();
12445 var a2 = this.area();
12447 if (a1 === Infinity || a2 === Infinity) {
12449 } else if (a1 === 0 || a2 === 0) {
12450 if (obj.contains(this)) {
12459 padByMeters: function padByMeters(meters) {
12460 var dLat = geoMetersToLat(meters);
12461 var dLon = geoMetersToLon(meters, this.center()[1]);
12462 return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12464 toParam: function toParam() {
12465 return this.rectangle().join(',');
12469 var $every$1 = arrayIteration.every;
12472 var STRICT_METHOD$6 = arrayMethodIsStrict('every');
12474 // `Array.prototype.every` method
12475 // https://tc39.es/ecma262/#sec-array.prototype.every
12476 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$6 }, {
12477 every: function every(callbackfn /* , thisArg */) {
12478 return $every$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12482 var $reduce$1 = arrayReduce.left;
12487 var STRICT_METHOD$7 = arrayMethodIsStrict('reduce');
12488 // Chrome 80-82 has a critical bug
12489 // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
12490 var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
12492 // `Array.prototype.reduce` method
12493 // https://tc39.es/ecma262/#sec-array.prototype.reduce
12494 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$7 || CHROME_BUG }, {
12495 reduce: function reduce(callbackfn /* , initialValue */) {
12496 return $reduce$1(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12500 function d3_polygonArea (polygon) {
12502 n = polygon.length,
12504 b = polygon[n - 1],
12510 area += a[1] * b[0] - a[0] * b[1];
12516 function d3_polygonCentroid (polygon) {
12518 n = polygon.length,
12522 b = polygon[n - 1],
12529 k += c = a[0] * b[1] - b[0] * a[1];
12530 x += (a[0] + b[0]) * c;
12531 y += (a[1] + b[1]) * c;
12534 return k *= 3, [x / k, y / k];
12537 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12538 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12539 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12540 // negative if clockwise, and zero if the points are collinear.
12541 function cross (a, b, c) {
12542 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12545 function lexicographicOrder(a, b) {
12546 return a[0] - b[0] || a[1] - b[1];
12547 } // Computes the upper convex hull per the monotone chain algorithm.
12548 // Assumes points.length >= 3, is sorted by x, unique in y.
12549 // Returns an array of indices into points in left-to-right order.
12552 function computeUpperHullIndexes(points) {
12553 var n = points.length,
12558 for (i = 2; i < n; ++i) {
12559 while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12563 indexes[size++] = i;
12566 return indexes.slice(0, size); // remove popped points
12569 function d3_polygonHull (points) {
12570 if ((n = points.length) < 3) return null;
12573 sortedPoints = new Array(n),
12574 flippedPoints = new Array(n);
12576 for (i = 0; i < n; ++i) {
12577 sortedPoints[i] = [+points[i][0], +points[i][1], i];
12580 sortedPoints.sort(lexicographicOrder);
12582 for (i = 0; i < n; ++i) {
12583 flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12586 var upperIndexes = computeUpperHullIndexes(sortedPoints),
12587 lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12589 var skipLeft = lowerIndexes[0] === upperIndexes[0],
12590 skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12591 hull = []; // Add upper hull in right-to-l order.
12592 // Then add lower hull in left-to-right order.
12594 for (i = upperIndexes.length - 1; i >= 0; --i) {
12595 hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12598 for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12599 hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12606 function geoVecEqual(a, b, epsilon) {
12608 return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12610 return a[0] === b[0] && a[1] === b[1];
12612 } // vector addition
12614 function geoVecAdd(a, b) {
12615 return [a[0] + b[0], a[1] + b[1]];
12616 } // vector subtraction
12618 function geoVecSubtract(a, b) {
12619 return [a[0] - b[0], a[1] - b[1]];
12620 } // vector scaling
12622 function geoVecScale(a, mag) {
12623 return [a[0] * mag, a[1] * mag];
12624 } // vector rounding (was: geoRoundCoordinates)
12626 function geoVecFloor(a) {
12627 return [Math.floor(a[0]), Math.floor(a[1])];
12628 } // linear interpolation
12630 function geoVecInterp(a, b, t) {
12631 return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12632 } // http://jsperf.com/id-dist-optimization
12634 function geoVecLength(a, b) {
12635 return Math.sqrt(geoVecLengthSquare(a, b));
12636 } // length of vector raised to the power two
12638 function geoVecLengthSquare(a, b) {
12640 var x = a[0] - b[0];
12641 var y = a[1] - b[1];
12642 return x * x + y * y;
12643 } // get a unit vector
12645 function geoVecNormalize(a) {
12646 var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12648 if (length !== 0) {
12649 return geoVecScale(a, 1 / length);
12653 } // Return the counterclockwise angle in the range (-pi, pi)
12654 // between the positive X axis and the line intersecting a and b.
12656 function geoVecAngle(a, b) {
12657 return Math.atan2(b[1] - a[1], b[0] - a[0]);
12660 function geoVecDot(a, b, origin) {
12661 origin = origin || [0, 0];
12662 var p = geoVecSubtract(a, origin);
12663 var q = geoVecSubtract(b, origin);
12664 return p[0] * q[0] + p[1] * q[1];
12665 } // normalized dot product
12667 function geoVecNormalizedDot(a, b, origin) {
12668 origin = origin || [0, 0];
12669 var p = geoVecNormalize(geoVecSubtract(a, origin));
12670 var q = geoVecNormalize(geoVecSubtract(b, origin));
12671 return geoVecDot(p, q);
12672 } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12673 // Returns a positive value, if OAB makes a counter-clockwise turn,
12674 // negative for clockwise turn, and zero if the points are collinear.
12676 function geoVecCross(a, b, origin) {
12677 origin = origin || [0, 0];
12678 var p = geoVecSubtract(a, origin);
12679 var q = geoVecSubtract(b, origin);
12680 return p[0] * q[1] - p[1] * q[0];
12681 } // find closest orthogonal projection of point onto points array
12683 function geoVecProject(a, points) {
12684 var min = Infinity;
12688 for (var i = 0; i < points.length - 1; i++) {
12690 var s = geoVecSubtract(points[i + 1], o);
12691 var v = geoVecSubtract(a, o);
12692 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12697 } else if (proj > 1) {
12700 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12703 var dist = geoVecLength(p, a);
12712 if (idx !== undefined) {
12723 // between the positive X axis and the line intersecting a and b.
12725 function geoAngle(a, b, projection) {
12726 return geoVecAngle(projection(a.loc), projection(b.loc));
12728 function geoEdgeEqual(a, b) {
12729 return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12730 } // Rotate all points counterclockwise around a pivot point by given angle
12732 function geoRotate(points, angle, around) {
12733 return points.map(function (point) {
12734 var radial = geoVecSubtract(point, around);
12735 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]];
12737 } // Choose the edge with the minimal distance from `point` to its orthogonal
12738 // projection onto that edge, if such a projection exists, or the distance to
12739 // the closest vertex on that edge. Returns an object with the `index` of the
12740 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12742 function geoChooseEdge(nodes, point, projection, activeID) {
12743 var dist = geoVecLength;
12744 var points = nodes.map(function (n) {
12745 return projection(n.loc);
12747 var ids = nodes.map(function (n) {
12750 var min = Infinity;
12754 for (var i = 0; i < points.length - 1; i++) {
12755 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12757 var s = geoVecSubtract(points[i + 1], o);
12758 var v = geoVecSubtract(point, o);
12759 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12764 } else if (proj > 1) {
12767 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12770 var d = dist(p, point);
12775 loc = projection.invert(p);
12779 if (idx !== undefined) {
12788 } // Test active (dragged or drawing) segments against inactive segments
12789 // This is used to test e.g. multipolygon rings that cross
12790 // `activeNodes` is the ring containing the activeID being dragged.
12791 // `inactiveNodes` is the other ring to test against
12793 function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12795 var inactives = [];
12796 var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12798 for (j = 0; j < activeNodes.length - 1; j++) {
12799 n1 = activeNodes[j];
12800 n2 = activeNodes[j + 1];
12801 segment = [n1.loc, n2.loc];
12803 if (n1.id === activeID || n2.id === activeID) {
12804 actives.push(segment);
12806 } // gather inactive segments
12809 for (j = 0; j < inactiveNodes.length - 1; j++) {
12810 n1 = inactiveNodes[j];
12811 n2 = inactiveNodes[j + 1];
12812 segment = [n1.loc, n2.loc];
12813 inactives.push(segment);
12817 for (j = 0; j < actives.length; j++) {
12818 for (k = 0; k < inactives.length; k++) {
12819 var p = actives[j];
12820 var q = inactives[k];
12821 var hit = geoLineIntersection(p, q);
12830 } // Test active (dragged or drawing) segments against inactive segments
12831 // This is used to test whether a way intersects with itself.
12833 function geoHasSelfIntersections(nodes, activeID) {
12835 var inactives = [];
12836 var j, k; // group active and passive segments along the nodes
12838 for (j = 0; j < nodes.length - 1; j++) {
12840 var n2 = nodes[j + 1];
12841 var segment = [n1.loc, n2.loc];
12843 if (n1.id === activeID || n2.id === activeID) {
12844 actives.push(segment);
12846 inactives.push(segment);
12851 for (j = 0; j < actives.length; j++) {
12852 for (k = 0; k < inactives.length; k++) {
12853 var p = actives[j];
12854 var q = inactives[k]; // skip if segments share an endpoint
12856 if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
12860 var hit = geoLineIntersection(p, q);
12863 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
12865 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
12875 } // Return the intersection point of 2 line segments.
12876 // From https://github.com/pgkelley4/line-segments-intersect
12877 // This uses the vector cross product approach described below:
12878 // http://stackoverflow.com/a/565282/786339
12880 function geoLineIntersection(a, b) {
12881 var p = [a[0][0], a[0][1]];
12882 var p2 = [a[1][0], a[1][1]];
12883 var q = [b[0][0], b[0][1]];
12884 var q2 = [b[1][0], b[1][1]];
12885 var r = geoVecSubtract(p2, p);
12886 var s = geoVecSubtract(q2, q);
12887 var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
12888 var denominator = geoVecCross(r, s);
12890 if (uNumerator && denominator) {
12891 var u = uNumerator / denominator;
12892 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
12894 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
12895 return geoVecInterp(p, p2, t);
12901 function geoPathIntersections(path1, path2) {
12902 var intersections = [];
12904 for (var i = 0; i < path1.length - 1; i++) {
12905 for (var j = 0; j < path2.length - 1; j++) {
12906 var a = [path1[i], path1[i + 1]];
12907 var b = [path2[j], path2[j + 1]];
12908 var hit = geoLineIntersection(a, b);
12911 intersections.push(hit);
12916 return intersections;
12918 function geoPathHasIntersections(path1, path2) {
12919 for (var i = 0; i < path1.length - 1; i++) {
12920 for (var j = 0; j < path2.length - 1; j++) {
12921 var a = [path1[i], path1[i + 1]];
12922 var b = [path2[j], path2[j + 1]];
12923 var hit = geoLineIntersection(a, b);
12932 } // Return whether point is contained in polygon.
12934 // `point` should be a 2-item array of coordinates.
12935 // `polygon` should be an array of 2-item arrays of coordinates.
12937 // From https://github.com/substack/point-in-polygon.
12938 // ray-casting algorithm based on
12939 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
12942 function geoPointInPolygon(point, polygon) {
12945 var inside = false;
12947 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
12948 var xi = polygon[i][0];
12949 var yi = polygon[i][1];
12950 var xj = polygon[j][0];
12951 var yj = polygon[j][1];
12952 var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
12953 if (intersect) inside = !inside;
12958 function geoPolygonContainsPolygon(outer, inner) {
12959 return inner.every(function (point) {
12960 return geoPointInPolygon(point, outer);
12963 function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
12964 function testPoints(outer, inner) {
12965 return inner.some(function (point) {
12966 return geoPointInPolygon(point, outer);
12970 return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
12971 } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
12972 // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
12974 function geoGetSmallestSurroundingRectangle(points) {
12975 var hull = d3_polygonHull(points);
12976 var centroid = d3_polygonCentroid(hull);
12977 var minArea = Infinity;
12978 var ssrExtent = [];
12982 for (var i = 0; i <= hull.length - 1; i++) {
12983 var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
12984 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
12985 var poly = geoRotate(hull, -angle, centroid);
12986 var extent = poly.reduce(function (extent, point) {
12987 return extent.extend(geoExtent(point));
12989 var area = extent.area();
12991 if (area < minArea) {
12993 ssrExtent = extent;
13001 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
13005 function geoPathLength(path) {
13008 for (var i = 0; i < path.length - 1; i++) {
13009 length += geoVecLength(path[i], path[i + 1]);
13013 } // If the given point is at the edge of the padded viewport,
13014 // return a vector that will nudge the viewport in that direction
13016 function geoViewportEdge(point, dimensions) {
13017 var pad = [80, 20, 50, 20]; // top, right, bottom, left
13021 if (point[0] > dimensions[0] - pad[1]) x = -10;
13022 if (point[0] < pad[3]) x = 10;
13023 if (point[1] > dimensions[1] - pad[2]) y = -10;
13024 if (point[1] < pad[0]) y = 10;
13034 value: function value() {}
13037 function dispatch() {
13038 for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
13039 if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
13043 return new Dispatch$1(_);
13046 function Dispatch$1(_) {
13050 function parseTypenames(typenames, types) {
13051 return typenames.trim().split(/^|\s+/).map(function (t) {
13053 i = t.indexOf(".");
13054 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13055 if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13063 Dispatch$1.prototype = dispatch.prototype = {
13064 constructor: Dispatch$1,
13065 on: function on(typename, callback) {
13067 T = parseTypenames(typename + "", _),
13070 n = T.length; // If no callback was specified, return the callback of the given type and name.
13072 if (arguments.length < 2) {
13074 if ((t = (typename = T[i]).type) && (t = get$3(_[t], typename.name))) return t;
13078 } // If a type was specified, set the callback for the given type and name.
13079 // Otherwise, if a null callback was specified, remove callbacks of the given name.
13082 if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13085 if (t = (typename = T[i]).type) _[t] = set$3(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13086 _[t] = set$3(_[t], typename.name, null);
13092 copy: function copy() {
13097 copy[t] = _[t].slice();
13100 return new Dispatch$1(copy);
13102 call: function call(type, that) {
13103 if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13104 args[i] = arguments[i + 2];
13106 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13108 for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13109 t[i].value.apply(that, args);
13112 apply: function apply(type, that, args) {
13113 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13115 for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13116 t[i].value.apply(that, args);
13121 function get$3(type, name) {
13122 for (var i = 0, n = type.length, c; i < n; ++i) {
13123 if ((c = type[i]).name === name) {
13129 function set$3(type, name, callback) {
13130 for (var i = 0, n = type.length; i < n; ++i) {
13131 if (type[i].name === name) {
13132 type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1));
13137 if (callback != null) type.push({
13144 var xhtml = "http://www.w3.org/1999/xhtml";
13146 svg: "http://www.w3.org/2000/svg",
13148 xlink: "http://www.w3.org/1999/xlink",
13149 xml: "http://www.w3.org/XML/1998/namespace",
13150 xmlns: "http://www.w3.org/2000/xmlns/"
13153 function namespace (name) {
13154 var prefix = name += "",
13155 i = prefix.indexOf(":");
13156 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13157 return namespaces.hasOwnProperty(prefix) ? {
13158 space: namespaces[prefix],
13160 } : name; // eslint-disable-line no-prototype-builtins
13163 function creatorInherit(name) {
13164 return function () {
13165 var document = this.ownerDocument,
13166 uri = this.namespaceURI;
13167 return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13171 function creatorFixed(fullname) {
13172 return function () {
13173 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13177 function creator (name) {
13178 var fullname = namespace(name);
13179 return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13184 function selector (selector) {
13185 return selector == null ? none : function () {
13186 return this.querySelector(selector);
13190 function selection_select (select) {
13191 if (typeof select !== "function") select = selector(select);
13193 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13194 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13195 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13196 if ("__data__" in node) subnode.__data__ = node.__data__;
13197 subgroup[i] = subnode;
13202 return new Selection(subgroups, this._parents);
13205 function array (x) {
13206 return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13207 : Array.from(x); // Map, Set, iterable, string, or anything else
13214 function selectorAll (selector) {
13215 return selector == null ? empty : function () {
13216 return this.querySelectorAll(selector);
13220 function arrayAll(select) {
13221 return function () {
13222 var group = select.apply(this, arguments);
13223 return group == null ? [] : array(group);
13227 function selection_selectAll (select) {
13228 if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13230 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13231 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13232 if (node = group[i]) {
13233 subgroups.push(select.call(node, node.__data__, i, group));
13234 parents.push(node);
13239 return new Selection(subgroups, parents);
13242 var $find$1 = arrayIteration.find;
13246 var SKIPS_HOLES = true;
13248 // Shouldn't skip holes
13249 if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES = false; });
13251 // `Array.prototype.find` method
13252 // https://tc39.es/ecma262/#sec-array.prototype.find
13253 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES }, {
13254 find: function find(callbackfn /* , that = undefined */) {
13255 return $find$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13259 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
13260 addToUnscopables(FIND);
13262 function matcher (selector) {
13263 return function () {
13264 return this.matches(selector);
13267 function childMatcher(selector) {
13268 return function (node) {
13269 return node.matches(selector);
13273 var find$1 = Array.prototype.find;
13275 function childFind(match) {
13276 return function () {
13277 return find$1.call(this.children, match);
13281 function childFirst() {
13282 return this.firstElementChild;
13285 function selection_selectChild (match) {
13286 return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13289 var filter = Array.prototype.filter;
13291 function children() {
13292 return this.children;
13295 function childrenFilter(match) {
13296 return function () {
13297 return filter.call(this.children, match);
13301 function selection_selectChildren (match) {
13302 return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13305 function selection_filter (match) {
13306 if (typeof match !== "function") match = matcher(match);
13308 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13309 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13310 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13311 subgroup.push(node);
13316 return new Selection(subgroups, this._parents);
13319 function sparse (update) {
13320 return new Array(update.length);
13323 function selection_enter () {
13324 return new Selection(this._enter || this._groups.map(sparse), this._parents);
13326 function EnterNode(parent, datum) {
13327 this.ownerDocument = parent.ownerDocument;
13328 this.namespaceURI = parent.namespaceURI;
13330 this._parent = parent;
13331 this.__data__ = datum;
13333 EnterNode.prototype = {
13334 constructor: EnterNode,
13335 appendChild: function appendChild(child) {
13336 return this._parent.insertBefore(child, this._next);
13338 insertBefore: function insertBefore(child, next) {
13339 return this._parent.insertBefore(child, next);
13341 querySelector: function querySelector(selector) {
13342 return this._parent.querySelector(selector);
13344 querySelectorAll: function querySelectorAll(selector) {
13345 return this._parent.querySelectorAll(selector);
13349 function constant (x) {
13350 return function () {
13355 function bindIndex(parent, group, enter, update, exit, data) {
13358 groupLength = group.length,
13359 dataLength = data.length; // Put any non-null nodes that fit into update.
13360 // Put any null nodes into enter.
13361 // Put any remaining data into enter.
13363 for (; i < dataLength; ++i) {
13364 if (node = group[i]) {
13365 node.__data__ = data[i];
13368 enter[i] = new EnterNode(parent, data[i]);
13370 } // Put any non-null nodes that don’t fit into exit.
13373 for (; i < groupLength; ++i) {
13374 if (node = group[i]) {
13380 function bindKey(parent, group, enter, update, exit, data, key) {
13383 nodeByKeyValue = new Map(),
13384 groupLength = group.length,
13385 dataLength = data.length,
13386 keyValues = new Array(groupLength),
13387 keyValue; // Compute the key for each node.
13388 // If multiple nodes have the same key, the duplicates are added to exit.
13390 for (i = 0; i < groupLength; ++i) {
13391 if (node = group[i]) {
13392 keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13394 if (nodeByKeyValue.has(keyValue)) {
13397 nodeByKeyValue.set(keyValue, node);
13400 } // Compute the key for each datum.
13401 // If there a node associated with this key, join and add it to update.
13402 // If there is not (or the key is a duplicate), add it to enter.
13405 for (i = 0; i < dataLength; ++i) {
13406 keyValue = key.call(parent, data[i], i, data) + "";
13408 if (node = nodeByKeyValue.get(keyValue)) {
13410 node.__data__ = data[i];
13411 nodeByKeyValue["delete"](keyValue);
13413 enter[i] = new EnterNode(parent, data[i]);
13415 } // Add any remaining nodes that were not bound to data to exit.
13418 for (i = 0; i < groupLength; ++i) {
13419 if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13425 function datum(node) {
13426 return node.__data__;
13429 function selection_data (value, key) {
13430 if (!arguments.length) return Array.from(this, datum);
13431 var bind = key ? bindKey : bindIndex,
13432 parents = this._parents,
13433 groups = this._groups;
13434 if (typeof value !== "function") value = constant(value);
13436 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13437 var parent = parents[j],
13439 groupLength = group.length,
13440 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13441 dataLength = data.length,
13442 enterGroup = enter[j] = new Array(dataLength),
13443 updateGroup = update[j] = new Array(dataLength),
13444 exitGroup = exit[j] = new Array(groupLength);
13445 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13446 // appendChild can insert the materialized enter node before this node,
13447 // rather than at the end of the parent node.
13449 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13450 if (previous = enterGroup[i0]) {
13451 if (i0 >= i1) i1 = i0 + 1;
13453 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13456 previous._next = next || null;
13461 update = new Selection(update, parents);
13462 update._enter = enter;
13463 update._exit = exit;
13467 function selection_exit () {
13468 return new Selection(this._exit || this._groups.map(sparse), this._parents);
13471 function selection_join (onenter, onupdate, onexit) {
13472 var enter = this.enter(),
13474 exit = this.exit();
13475 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13476 if (onupdate != null) update = onupdate(update);
13477 if (onexit == null) exit.remove();else onexit(exit);
13478 return enter && update ? enter.merge(update).order() : update;
13481 function selection_merge (selection) {
13482 if (!(selection instanceof Selection)) throw new Error("invalid merge");
13484 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) {
13485 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13486 if (node = group0[i] || group1[i]) {
13492 for (; j < m0; ++j) {
13493 merges[j] = groups0[j];
13496 return new Selection(merges, this._parents);
13499 function selection_order () {
13500 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13501 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13502 if (node = group[i]) {
13503 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13512 function selection_sort (compare) {
13513 if (!compare) compare = ascending;
13515 function compareNode(a, b) {
13516 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13519 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13520 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13521 if (node = group[i]) {
13522 sortgroup[i] = node;
13526 sortgroup.sort(compareNode);
13529 return new Selection(sortgroups, this._parents).order();
13532 function ascending(a, b) {
13533 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13536 function selection_call () {
13537 var callback = arguments[0];
13538 arguments[0] = this;
13539 callback.apply(null, arguments);
13543 function selection_nodes () {
13544 return Array.from(this);
13547 function selection_node () {
13548 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13549 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13550 var node = group[i];
13551 if (node) return node;
13558 function selection_size () {
13561 var _iterator = _createForOfIteratorHelper(this),
13565 for (_iterator.s(); !(_step = _iterator.n()).done;) {
13566 var node = _step.value;
13568 } // eslint-disable-line no-unused-vars
13579 function selection_empty () {
13580 return !this.node();
13583 function selection_each (callback) {
13584 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13585 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13586 if (node = group[i]) callback.call(node, node.__data__, i, group);
13593 function attrRemove(name) {
13594 return function () {
13595 this.removeAttribute(name);
13599 function attrRemoveNS(fullname) {
13600 return function () {
13601 this.removeAttributeNS(fullname.space, fullname.local);
13605 function attrConstant(name, value) {
13606 return function () {
13607 this.setAttribute(name, value);
13611 function attrConstantNS(fullname, value) {
13612 return function () {
13613 this.setAttributeNS(fullname.space, fullname.local, value);
13617 function attrFunction(name, value) {
13618 return function () {
13619 var v = value.apply(this, arguments);
13620 if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13624 function attrFunctionNS(fullname, value) {
13625 return function () {
13626 var v = value.apply(this, arguments);
13627 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13631 function selection_attr (name, value) {
13632 var fullname = namespace(name);
13634 if (arguments.length < 2) {
13635 var node = this.node();
13636 return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13639 return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value));
13642 function defaultView (node) {
13643 return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13644 node.document && node // node is a Window
13645 || node.defaultView; // node is a Document
13648 function styleRemove(name) {
13649 return function () {
13650 this.style.removeProperty(name);
13654 function styleConstant(name, value, priority) {
13655 return function () {
13656 this.style.setProperty(name, value, priority);
13660 function styleFunction(name, value, priority) {
13661 return function () {
13662 var v = value.apply(this, arguments);
13663 if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13667 function selection_style (name, value, priority) {
13668 return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13670 function styleValue(node, name) {
13671 return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13674 function propertyRemove(name) {
13675 return function () {
13680 function propertyConstant(name, value) {
13681 return function () {
13682 this[name] = value;
13686 function propertyFunction(name, value) {
13687 return function () {
13688 var v = value.apply(this, arguments);
13689 if (v == null) delete this[name];else this[name] = v;
13693 function selection_property (name, value) {
13694 return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13697 function classArray(string) {
13698 return string.trim().split(/^|\s+/);
13701 function classList(node) {
13702 return node.classList || new ClassList(node);
13705 function ClassList(node) {
13707 this._names = classArray(node.getAttribute("class") || "");
13710 ClassList.prototype = {
13711 add: function add(name) {
13712 var i = this._names.indexOf(name);
13715 this._names.push(name);
13717 this._node.setAttribute("class", this._names.join(" "));
13720 remove: function remove(name) {
13721 var i = this._names.indexOf(name);
13724 this._names.splice(i, 1);
13726 this._node.setAttribute("class", this._names.join(" "));
13729 contains: function contains(name) {
13730 return this._names.indexOf(name) >= 0;
13734 function classedAdd(node, names) {
13735 var list = classList(node),
13740 list.add(names[i]);
13744 function classedRemove(node, names) {
13745 var list = classList(node),
13750 list.remove(names[i]);
13754 function classedTrue(names) {
13755 return function () {
13756 classedAdd(this, names);
13760 function classedFalse(names) {
13761 return function () {
13762 classedRemove(this, names);
13766 function classedFunction(names, value) {
13767 return function () {
13768 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13772 function selection_classed (name, value) {
13773 var names = classArray(name + "");
13775 if (arguments.length < 2) {
13776 var list = classList(this.node()),
13781 if (!list.contains(names[i])) return false;
13787 return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13790 function textRemove() {
13791 this.textContent = "";
13794 function textConstant(value) {
13795 return function () {
13796 this.textContent = value;
13800 function textFunction(value) {
13801 return function () {
13802 var v = value.apply(this, arguments);
13803 this.textContent = v == null ? "" : v;
13807 function selection_text (value) {
13808 return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent;
13811 function htmlRemove() {
13812 this.innerHTML = "";
13815 function htmlConstant(value) {
13816 return function () {
13817 this.innerHTML = value;
13821 function htmlFunction(value) {
13822 return function () {
13823 var v = value.apply(this, arguments);
13824 this.innerHTML = v == null ? "" : v;
13828 function selection_html (value) {
13829 return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13833 if (this.nextSibling) this.parentNode.appendChild(this);
13836 function selection_raise () {
13837 return this.each(raise);
13841 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13844 function selection_lower () {
13845 return this.each(lower);
13848 function selection_append (name) {
13849 var create = typeof name === "function" ? name : creator(name);
13850 return this.select(function () {
13851 return this.appendChild(create.apply(this, arguments));
13855 function constantNull() {
13859 function selection_insert (name, before) {
13860 var create = typeof name === "function" ? name : creator(name),
13861 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
13862 return this.select(function () {
13863 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
13867 function remove() {
13868 var parent = this.parentNode;
13869 if (parent) parent.removeChild(this);
13872 function selection_remove () {
13873 return this.each(remove);
13876 function selection_cloneShallow() {
13877 var clone = this.cloneNode(false),
13878 parent = this.parentNode;
13879 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13882 function selection_cloneDeep() {
13883 var clone = this.cloneNode(true),
13884 parent = this.parentNode;
13885 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13888 function selection_clone (deep) {
13889 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
13892 function selection_datum (value) {
13893 return arguments.length ? this.property("__data__", value) : this.node().__data__;
13896 function contextListener(listener) {
13897 return function (event) {
13898 listener.call(this, event, this.__data__);
13902 function parseTypenames$1(typenames) {
13903 return typenames.trim().split(/^|\s+/).map(function (t) {
13905 i = t.indexOf(".");
13906 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13914 function onRemove(typename) {
13915 return function () {
13916 var on = this.__on;
13919 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
13920 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
13921 this.removeEventListener(o.type, o.listener, o.options);
13927 if (++i) on.length = i;else delete this.__on;
13931 function onAdd(typename, value, options) {
13932 return function () {
13933 var on = this.__on,
13935 listener = contextListener(value);
13936 if (on) for (var j = 0, m = on.length; j < m; ++j) {
13937 if ((o = on[j]).type === typename.type && o.name === typename.name) {
13938 this.removeEventListener(o.type, o.listener, o.options);
13939 this.addEventListener(o.type, o.listener = listener, o.options = options);
13944 this.addEventListener(typename.type, listener, options);
13946 type: typename.type,
13947 name: typename.name,
13949 listener: listener,
13952 if (!on) this.__on = [o];else on.push(o);
13956 function selection_on (typename, value, options) {
13957 var typenames = parseTypenames$1(typename + ""),
13959 n = typenames.length,
13962 if (arguments.length < 2) {
13963 var on = this.node().__on;
13965 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
13966 for (i = 0, o = on[j]; i < n; ++i) {
13967 if ((t = typenames[i]).type === o.type && t.name === o.name) {
13975 on = value ? onAdd : onRemove;
13977 for (i = 0; i < n; ++i) {
13978 this.each(on(typenames[i], value, options));
13984 function dispatchEvent$1(node, type, params) {
13985 var window = defaultView(node),
13986 event = window.CustomEvent;
13988 if (typeof event === "function") {
13989 event = new event(type, params);
13991 event = window.document.createEvent("Event");
13992 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
13995 node.dispatchEvent(event);
13998 function dispatchConstant(type, params) {
13999 return function () {
14000 return dispatchEvent$1(this, type, params);
14004 function dispatchFunction(type, params) {
14005 return function () {
14006 return dispatchEvent$1(this, type, params.apply(this, arguments));
14010 function selection_dispatch (type, params) {
14011 return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
14014 var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(_callee);
14016 function _callee() {
14017 var groups, j, m, group, i, n, node;
14018 return regeneratorRuntime.wrap(function _callee$(_context) {
14020 switch (_context.prev = _context.next) {
14022 groups = this._groups, j = 0, m = groups.length;
14026 _context.next = 13;
14030 group = groups[j], i = 0, n = group.length;
14034 _context.next = 10;
14038 if (!(node = group[i])) {
14058 return _context.stop();
14061 }, _marked$2, this);
14065 function Selection(groups, parents) {
14066 this._groups = groups;
14067 this._parents = parents;
14070 function selection() {
14071 return new Selection([[document.documentElement]], root);
14074 function selection_selection() {
14078 Selection.prototype = selection.prototype = _defineProperty({
14079 constructor: Selection,
14080 select: selection_select,
14081 selectAll: selection_selectAll,
14082 selectChild: selection_selectChild,
14083 selectChildren: selection_selectChildren,
14084 filter: selection_filter,
14085 data: selection_data,
14086 enter: selection_enter,
14087 exit: selection_exit,
14088 join: selection_join,
14089 merge: selection_merge,
14090 selection: selection_selection,
14091 order: selection_order,
14092 sort: selection_sort,
14093 call: selection_call,
14094 nodes: selection_nodes,
14095 node: selection_node,
14096 size: selection_size,
14097 empty: selection_empty,
14098 each: selection_each,
14099 attr: selection_attr,
14100 style: selection_style,
14101 property: selection_property,
14102 classed: selection_classed,
14103 text: selection_text,
14104 html: selection_html,
14105 raise: selection_raise,
14106 lower: selection_lower,
14107 append: selection_append,
14108 insert: selection_insert,
14109 remove: selection_remove,
14110 clone: selection_clone,
14111 datum: selection_datum,
14113 dispatch: selection_dispatch
14114 }, Symbol.iterator, _callee);
14116 function select (selector) {
14117 return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root);
14120 function sourceEvent (event) {
14123 while (sourceEvent = event.sourceEvent) {
14124 event = sourceEvent;
14130 function pointer (event, node) {
14131 event = sourceEvent(event);
14132 if (node === undefined) node = event.currentTarget;
14135 var svg = node.ownerSVGElement || node;
14137 if (svg.createSVGPoint) {
14138 var point = svg.createSVGPoint();
14139 point.x = event.clientX, point.y = event.clientY;
14140 point = point.matrixTransform(node.getScreenCTM().inverse());
14141 return [point.x, point.y];
14144 if (node.getBoundingClientRect) {
14145 var rect = node.getBoundingClientRect();
14146 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14150 return [event.pageX, event.pageY];
14153 function selectAll (selector) {
14154 return typeof selector === "string" ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) : new Selection([selector == null ? [] : array(selector)], root);
14157 function nopropagation(event) {
14158 event.stopImmediatePropagation();
14160 function noevent (event) {
14161 event.preventDefault();
14162 event.stopImmediatePropagation();
14165 function dragDisable (view) {
14166 var root = view.document.documentElement,
14167 selection = select(view).on("dragstart.drag", noevent, true);
14169 if ("onselectstart" in root) {
14170 selection.on("selectstart.drag", noevent, true);
14172 root.__noselect = root.style.MozUserSelect;
14173 root.style.MozUserSelect = "none";
14176 function yesdrag(view, noclick) {
14177 var root = view.document.documentElement,
14178 selection = select(view).on("dragstart.drag", null);
14181 selection.on("click.drag", noevent, true);
14182 setTimeout(function () {
14183 selection.on("click.drag", null);
14187 if ("onselectstart" in root) {
14188 selection.on("selectstart.drag", null);
14190 root.style.MozUserSelect = root.__noselect;
14191 delete root.__noselect;
14195 var constant$1 = (function (x) {
14196 return function () {
14201 function DragEvent(type, _ref) {
14202 var sourceEvent = _ref.sourceEvent,
14203 subject = _ref.subject,
14204 target = _ref.target,
14205 identifier = _ref.identifier,
14206 active = _ref.active,
14211 dispatch = _ref.dispatch;
14212 Object.defineProperties(this, {
14219 value: sourceEvent,
14269 DragEvent.prototype.on = function () {
14270 var value = this._.on.apply(this._, arguments);
14272 return value === this._ ? this : value;
14275 function defaultFilter(event) {
14276 return !event.ctrlKey && !event.button;
14279 function defaultContainer() {
14280 return this.parentNode;
14283 function defaultSubject(event, d) {
14284 return d == null ? {
14290 function defaultTouchable() {
14291 return navigator.maxTouchPoints || "ontouchstart" in this;
14294 function d3_drag () {
14295 var filter = defaultFilter,
14296 container = defaultContainer,
14297 subject = defaultSubject,
14298 touchable = defaultTouchable,
14300 listeners = dispatch("start", "drag", "end"),
14306 clickDistance2 = 0;
14308 function drag(selection) {
14309 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)");
14312 function mousedowned(event, d) {
14313 if (touchending || !filter.call(this, event, d)) return;
14314 var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14315 if (!gesture) return;
14316 select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14317 dragDisable(event.view);
14318 nopropagation(event);
14319 mousemoving = false;
14320 mousedownx = event.clientX;
14321 mousedowny = event.clientY;
14322 gesture("start", event);
14325 function mousemoved(event) {
14328 if (!mousemoving) {
14329 var dx = event.clientX - mousedownx,
14330 dy = event.clientY - mousedowny;
14331 mousemoving = dx * dx + dy * dy > clickDistance2;
14334 gestures.mouse("drag", event);
14337 function mouseupped(event) {
14338 select(event.view).on("mousemove.drag mouseup.drag", null);
14339 yesdrag(event.view, mousemoving);
14341 gestures.mouse("end", event);
14344 function touchstarted(event, d) {
14345 if (!filter.call(this, event, d)) return;
14346 var touches = event.changedTouches,
14347 c = container.call(this, event, d),
14348 n = touches.length,
14352 for (i = 0; i < n; ++i) {
14353 if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14354 nopropagation(event);
14355 gesture("start", event, touches[i]);
14360 function touchmoved(event) {
14361 var touches = event.changedTouches,
14362 n = touches.length,
14366 for (i = 0; i < n; ++i) {
14367 if (gesture = gestures[touches[i].identifier]) {
14369 gesture("drag", event, touches[i]);
14374 function touchended(event) {
14375 var touches = event.changedTouches,
14376 n = touches.length,
14379 if (touchending) clearTimeout(touchending);
14380 touchending = setTimeout(function () {
14381 touchending = null;
14382 }, 500); // Ghost clicks are delayed!
14384 for (i = 0; i < n; ++i) {
14385 if (gesture = gestures[touches[i].identifier]) {
14386 nopropagation(event);
14387 gesture("end", event, touches[i]);
14392 function beforestart(that, container, event, d, identifier, touch) {
14393 var dispatch = listeners.copy(),
14394 p = pointer(touch || event, container),
14398 if ((s = subject.call(that, new DragEvent("beforestart", {
14399 sourceEvent: event,
14401 identifier: identifier,
14408 }), d)) == null) return;
14409 dx = s.x - p[0] || 0;
14410 dy = s.y - p[1] || 0;
14411 return function gesture(type, event, touch) {
14417 gestures[identifier] = gesture, n = active++;
14421 delete gestures[identifier], --active;
14425 p = pointer(touch || event, container), n = active;
14429 dispatch.call(type, that, new DragEvent(type, {
14430 sourceEvent: event,
14433 identifier: identifier,
14444 drag.filter = function (_) {
14445 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter;
14448 drag.container = function (_) {
14449 return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container;
14452 drag.subject = function (_) {
14453 return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject;
14456 drag.touchable = function (_) {
14457 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable;
14460 drag.on = function () {
14461 var value = listeners.on.apply(listeners, arguments);
14462 return value === listeners ? drag : value;
14465 drag.clickDistance = function (_) {
14466 return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14472 var defineProperty$8 = objectDefineProperty.f;
14473 var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14479 var setInternalState$8 = internalState.set;
14483 var MATCH$1 = wellKnownSymbol('match');
14484 var NativeRegExp = global_1.RegExp;
14485 var RegExpPrototype$1 = NativeRegExp.prototype;
14489 // "new" should create a new object, old webkit bug
14490 var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14492 var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y;
14494 var FORCED$b = descriptors && isForced_1('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y$2 || fails(function () {
14495 re2[MATCH$1] = false;
14496 // RegExp constructor can alter flags and IsRegExp works correct with @@match
14497 return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14500 // `RegExp` constructor
14501 // https://tc39.es/ecma262/#sec-regexp-constructor
14503 var RegExpWrapper = function RegExp(pattern, flags) {
14504 var thisIsRegExp = this instanceof RegExpWrapper;
14505 var patternIsRegExp = isRegexp(pattern);
14506 var flagsAreUndefined = flags === undefined;
14509 if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
14514 if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;
14515 } else if (pattern instanceof RegExpWrapper) {
14516 if (flagsAreUndefined) flags = regexpFlags.call(pattern);
14517 pattern = pattern.source;
14520 if (UNSUPPORTED_Y$2) {
14521 sticky = !!flags && flags.indexOf('y') > -1;
14522 if (sticky) flags = flags.replace(/y/g, '');
14525 var result = inheritIfRequired(
14526 CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
14527 thisIsRegExp ? this : RegExpPrototype$1,
14531 if (UNSUPPORTED_Y$2 && sticky) setInternalState$8(result, { sticky: sticky });
14535 var proxy = function (key) {
14536 key in RegExpWrapper || defineProperty$8(RegExpWrapper, key, {
14537 configurable: true,
14538 get: function () { return NativeRegExp[key]; },
14539 set: function (it) { NativeRegExp[key] = it; }
14542 var keys$2 = getOwnPropertyNames$1(NativeRegExp);
14544 while (keys$2.length > index) proxy(keys$2[index++]);
14545 RegExpPrototype$1.constructor = RegExpWrapper;
14546 RegExpWrapper.prototype = RegExpPrototype$1;
14547 redefine(global_1, 'RegExp', RegExpWrapper);
14550 // https://tc39.es/ecma262/#sec-get-regexp-@@species
14551 setSpecies('RegExp');
14553 function define (constructor, factory, prototype) {
14554 constructor.prototype = factory.prototype = prototype;
14555 prototype.constructor = constructor;
14557 function extend(parent, definition) {
14558 var prototype = Object.create(parent.prototype);
14560 for (var key in definition) {
14561 prototype[key] = definition[key];
14567 function Color() {}
14570 var _brighter = 1 / _darker;
14571 var reI = "\\s*([+-]?\\d+)\\s*",
14572 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14573 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14574 reHex = /^#([0-9a-f]{3,8})$/,
14575 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14576 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14577 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14578 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14579 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14580 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14582 aliceblue: 0xf0f8ff,
14583 antiquewhite: 0xfaebd7,
14585 aquamarine: 0x7fffd4,
14590 blanchedalmond: 0xffebcd,
14592 blueviolet: 0x8a2be2,
14594 burlywood: 0xdeb887,
14595 cadetblue: 0x5f9ea0,
14596 chartreuse: 0x7fff00,
14597 chocolate: 0xd2691e,
14599 cornflowerblue: 0x6495ed,
14600 cornsilk: 0xfff8dc,
14603 darkblue: 0x00008b,
14604 darkcyan: 0x008b8b,
14605 darkgoldenrod: 0xb8860b,
14606 darkgray: 0xa9a9a9,
14607 darkgreen: 0x006400,
14608 darkgrey: 0xa9a9a9,
14609 darkkhaki: 0xbdb76b,
14610 darkmagenta: 0x8b008b,
14611 darkolivegreen: 0x556b2f,
14612 darkorange: 0xff8c00,
14613 darkorchid: 0x9932cc,
14615 darksalmon: 0xe9967a,
14616 darkseagreen: 0x8fbc8f,
14617 darkslateblue: 0x483d8b,
14618 darkslategray: 0x2f4f4f,
14619 darkslategrey: 0x2f4f4f,
14620 darkturquoise: 0x00ced1,
14621 darkviolet: 0x9400d3,
14622 deeppink: 0xff1493,
14623 deepskyblue: 0x00bfff,
14626 dodgerblue: 0x1e90ff,
14627 firebrick: 0xb22222,
14628 floralwhite: 0xfffaf0,
14629 forestgreen: 0x228b22,
14631 gainsboro: 0xdcdcdc,
14632 ghostwhite: 0xf8f8ff,
14634 goldenrod: 0xdaa520,
14637 greenyellow: 0xadff2f,
14639 honeydew: 0xf0fff0,
14641 indianred: 0xcd5c5c,
14645 lavender: 0xe6e6fa,
14646 lavenderblush: 0xfff0f5,
14647 lawngreen: 0x7cfc00,
14648 lemonchiffon: 0xfffacd,
14649 lightblue: 0xadd8e6,
14650 lightcoral: 0xf08080,
14651 lightcyan: 0xe0ffff,
14652 lightgoldenrodyellow: 0xfafad2,
14653 lightgray: 0xd3d3d3,
14654 lightgreen: 0x90ee90,
14655 lightgrey: 0xd3d3d3,
14656 lightpink: 0xffb6c1,
14657 lightsalmon: 0xffa07a,
14658 lightseagreen: 0x20b2aa,
14659 lightskyblue: 0x87cefa,
14660 lightslategray: 0x778899,
14661 lightslategrey: 0x778899,
14662 lightsteelblue: 0xb0c4de,
14663 lightyellow: 0xffffe0,
14665 limegreen: 0x32cd32,
14669 mediumaquamarine: 0x66cdaa,
14670 mediumblue: 0x0000cd,
14671 mediumorchid: 0xba55d3,
14672 mediumpurple: 0x9370db,
14673 mediumseagreen: 0x3cb371,
14674 mediumslateblue: 0x7b68ee,
14675 mediumspringgreen: 0x00fa9a,
14676 mediumturquoise: 0x48d1cc,
14677 mediumvioletred: 0xc71585,
14678 midnightblue: 0x191970,
14679 mintcream: 0xf5fffa,
14680 mistyrose: 0xffe4e1,
14681 moccasin: 0xffe4b5,
14682 navajowhite: 0xffdead,
14686 olivedrab: 0x6b8e23,
14688 orangered: 0xff4500,
14690 palegoldenrod: 0xeee8aa,
14691 palegreen: 0x98fb98,
14692 paleturquoise: 0xafeeee,
14693 palevioletred: 0xdb7093,
14694 papayawhip: 0xffefd5,
14695 peachpuff: 0xffdab9,
14699 powderblue: 0xb0e0e6,
14701 rebeccapurple: 0x663399,
14703 rosybrown: 0xbc8f8f,
14704 royalblue: 0x4169e1,
14705 saddlebrown: 0x8b4513,
14707 sandybrown: 0xf4a460,
14708 seagreen: 0x2e8b57,
14709 seashell: 0xfff5ee,
14713 slateblue: 0x6a5acd,
14714 slategray: 0x708090,
14715 slategrey: 0x708090,
14717 springgreen: 0x00ff7f,
14718 steelblue: 0x4682b4,
14723 turquoise: 0x40e0d0,
14727 whitesmoke: 0xf5f5f5,
14729 yellowgreen: 0x9acd32
14731 define(Color, color, {
14732 copy: function copy(channels) {
14733 return Object.assign(new this.constructor(), this, channels);
14735 displayable: function displayable() {
14736 return this.rgb().displayable();
14738 hex: color_formatHex,
14739 // Deprecated! Use color.formatHex.
14740 formatHex: color_formatHex,
14741 formatHsl: color_formatHsl,
14742 formatRgb: color_formatRgb,
14743 toString: color_formatRgb
14746 function color_formatHex() {
14747 return this.rgb().formatHex();
14750 function color_formatHsl() {
14751 return hslConvert(this).formatHsl();
14754 function color_formatRgb() {
14755 return this.rgb().formatRgb();
14758 function color(format) {
14760 format = (format + "").trim().toLowerCase();
14761 return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
14762 : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
14763 : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
14764 : 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
14765 : null // invalid hex
14766 ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
14767 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
14768 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
14769 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
14770 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
14771 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
14772 : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
14773 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
14777 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
14780 function rgba(r, g, b, a) {
14781 if (a <= 0) r = g = b = NaN;
14782 return new Rgb(r, g, b, a);
14785 function rgbConvert(o) {
14786 if (!(o instanceof Color)) o = color(o);
14787 if (!o) return new Rgb();
14789 return new Rgb(o.r, o.g, o.b, o.opacity);
14791 function rgb(r, g, b, opacity) {
14792 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
14794 function Rgb(r, g, b, opacity) {
14798 this.opacity = +opacity;
14800 define(Rgb, rgb, extend(Color, {
14801 brighter: function brighter(k) {
14802 k = k == null ? _brighter : Math.pow(_brighter, k);
14803 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14805 darker: function darker(k) {
14806 k = k == null ? _darker : Math.pow(_darker, k);
14807 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14809 rgb: function rgb() {
14812 displayable: function displayable() {
14813 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;
14815 hex: rgb_formatHex,
14816 // Deprecated! Use color.formatHex.
14817 formatHex: rgb_formatHex,
14818 formatRgb: rgb_formatRgb,
14819 toString: rgb_formatRgb
14822 function rgb_formatHex() {
14823 return "#" + hex$2(this.r) + hex$2(this.g) + hex$2(this.b);
14826 function rgb_formatRgb() {
14827 var a = this.opacity;
14828 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14829 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 + ")");
14832 function hex$2(value) {
14833 value = Math.max(0, Math.min(255, Math.round(value) || 0));
14834 return (value < 16 ? "0" : "") + value.toString(16);
14837 function hsla(h, s, l, a) {
14838 if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
14839 return new Hsl(h, s, l, a);
14842 function hslConvert(o) {
14843 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
14844 if (!(o instanceof Color)) o = color(o);
14845 if (!o) return new Hsl();
14846 if (o instanceof Hsl) return o;
14851 min = Math.min(r, g, b),
14852 max = Math.max(r, g, b),
14855 l = (max + min) / 2;
14858 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;
14859 s /= l < 0.5 ? max + min : 2 - max - min;
14862 s = l > 0 && l < 1 ? 0 : h;
14865 return new Hsl(h, s, l, o.opacity);
14867 function hsl(h, s, l, opacity) {
14868 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
14871 function Hsl(h, s, l, opacity) {
14875 this.opacity = +opacity;
14878 define(Hsl, hsl, extend(Color, {
14879 brighter: function brighter(k) {
14880 k = k == null ? _brighter : Math.pow(_brighter, k);
14881 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14883 darker: function darker(k) {
14884 k = k == null ? _darker : Math.pow(_darker, k);
14885 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14887 rgb: function rgb() {
14888 var h = this.h % 360 + (this.h < 0) * 360,
14889 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
14891 m2 = l + (l < 0.5 ? l : 1 - l) * s,
14893 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);
14895 displayable: function displayable() {
14896 return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
14898 formatHsl: function formatHsl() {
14899 var a = this.opacity;
14900 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14901 return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
14904 /* From FvD 13.37, CSS Color Module Level 3 */
14906 function hsl2rgb(h, m1, m2) {
14907 return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
14910 var constant$2 = (function (x) {
14911 return function () {
14916 function linear(a, d) {
14917 return function (t) {
14922 function exponential(a, b, y) {
14923 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
14924 return Math.pow(a + t * b, y);
14927 function gamma(y) {
14928 return (y = +y) === 1 ? nogamma : function (a, b) {
14929 return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a);
14932 function nogamma(a, b) {
14934 return d ? linear(a, d) : constant$2(isNaN(a) ? b : a);
14937 var d3_interpolateRgb = (function rgbGamma(y) {
14938 var color = gamma(y);
14940 function rgb$1(start, end) {
14941 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
14942 g = color(start.g, end.g),
14943 b = color(start.b, end.b),
14944 opacity = nogamma(start.opacity, end.opacity);
14945 return function (t) {
14949 start.opacity = opacity(t);
14954 rgb$1.gamma = rgbGamma;
14958 function numberArray (a, b) {
14960 var n = a ? Math.min(b.length, a.length) : 0,
14963 return function (t) {
14964 for (i = 0; i < n; ++i) {
14965 c[i] = a[i] * (1 - t) + b[i] * t;
14971 function isNumberArray(x) {
14972 return ArrayBuffer.isView(x) && !(x instanceof DataView);
14975 function genericArray(a, b) {
14976 var nb = b ? b.length : 0,
14977 na = a ? Math.min(nb, a.length) : 0,
14982 for (i = 0; i < na; ++i) {
14983 x[i] = interpolate(a[i], b[i]);
14986 for (; i < nb; ++i) {
14990 return function (t) {
14991 for (i = 0; i < na; ++i) {
14999 function date (a, b) {
15000 var d = new Date();
15001 return a = +a, b = +b, function (t) {
15002 return d.setTime(a * (1 - t) + b * t), d;
15006 function d3_interpolateNumber (a, b) {
15007 return a = +a, b = +b, function (t) {
15008 return a * (1 - t) + b * t;
15012 function object (a, b) {
15016 if (a === null || _typeof(a) !== "object") a = {};
15017 if (b === null || _typeof(b) !== "object") b = {};
15021 i[k] = interpolate(a[k], b[k]);
15027 return function (t) {
15036 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
15037 reB = new RegExp(reA.source, "g");
15040 return function () {
15046 return function (t) {
15051 function interpolateString (a, b) {
15052 var bi = reA.lastIndex = reB.lastIndex = 0,
15053 // scan index for next number in b
15055 // current match in a
15057 // current match in b
15059 // string preceding current number in b, if any
15063 // string constants and placeholders
15064 q = []; // number interpolators
15065 // Coerce inputs to strings.
15067 a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15069 while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15070 if ((bs = bm.index) > bi) {
15071 // a string precedes the next number in b
15072 bs = b.slice(bi, bs);
15073 if (s[i]) s[i] += bs; // coalesce with previous string
15077 if ((am = am[0]) === (bm = bm[0])) {
15078 // numbers in a & b match
15079 if (s[i]) s[i] += bm; // coalesce with previous string
15082 // interpolate non-matching numbers
15086 x: d3_interpolateNumber(am, bm)
15090 bi = reB.lastIndex;
15091 } // Add remains of b.
15094 if (bi < b.length) {
15096 if (s[i]) s[i] += bs; // coalesce with previous string
15098 } // Special optimization for only a single match.
15099 // Otherwise, interpolate each of the numbers and rejoin the string.
15102 return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15103 for (var i = 0, o; i < b; ++i) {
15104 s[(o = q[i]).i] = o.x(t);
15111 function interpolate (a, b) {
15112 var t = _typeof(b),
15115 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);
15118 function interpolateRound (a, b) {
15119 return a = +a, b = +b, function (t) {
15120 return Math.round(a * (1 - t) + b * t);
15124 var degrees$1 = 180 / Math.PI;
15133 function decompose (a, b, c, d, e, f) {
15134 var scaleX, scaleY, skewX;
15135 if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15136 if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15137 if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15138 if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15142 rotate: Math.atan2(b, a) * degrees$1,
15143 skewX: Math.atan(skewX) * degrees$1,
15150 /* eslint-disable no-undef */
15152 function parseCss(value) {
15153 var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15154 return m.isIdentity ? identity$1 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15156 function parseSvg(value) {
15157 if (value == null) return identity$1;
15158 if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15159 svgNode.setAttribute("transform", value);
15160 if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1;
15161 value = value.matrix;
15162 return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15165 function interpolateTransform(parse, pxComma, pxParen, degParen) {
15167 return s.length ? s.pop() + " " : "";
15170 function translate(xa, ya, xb, yb, s, q) {
15171 if (xa !== xb || ya !== yb) {
15172 var i = s.push("translate(", null, pxComma, null, pxParen);
15175 x: d3_interpolateNumber(xa, xb)
15178 x: d3_interpolateNumber(ya, yb)
15180 } else if (xb || yb) {
15181 s.push("translate(" + xb + pxComma + yb + pxParen);
15185 function rotate(a, b, s, q) {
15187 if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15190 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15191 x: d3_interpolateNumber(a, b)
15194 s.push(pop(s) + "rotate(" + b + degParen);
15198 function skewX(a, b, s, q) {
15201 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15202 x: d3_interpolateNumber(a, b)
15205 s.push(pop(s) + "skewX(" + b + degParen);
15209 function scale(xa, ya, xb, yb, s, q) {
15210 if (xa !== xb || ya !== yb) {
15211 var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15214 x: d3_interpolateNumber(xa, xb)
15217 x: d3_interpolateNumber(ya, yb)
15219 } else if (xb !== 1 || yb !== 1) {
15220 s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15224 return function (a, b) {
15226 // string constants and placeholders
15227 q = []; // number interpolators
15229 a = parse(a), b = parse(b);
15230 translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15231 rotate(a.rotate, b.rotate, s, q);
15232 skewX(a.skewX, b.skewX, s, q);
15233 scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15234 a = b = null; // gc
15236 return function (t) {
15242 s[(o = q[i]).i] = o.x(t);
15250 var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15251 var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15253 var epsilon2$1 = 1e-12;
15256 return ((x = Math.exp(x)) + 1 / x) / 2;
15260 return ((x = Math.exp(x)) - 1 / x) / 2;
15264 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15267 var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15268 // p0 = [ux0, uy0, w0]
15269 // p1 = [ux1, uy1, w1]
15270 function zoom(p0, p1) {
15279 d2 = dx * dx + dy * dy,
15281 S; // Special case for u0 ≅ u1.
15283 if (d2 < epsilon2$1) {
15284 S = Math.log(w1 / w0) / rho;
15286 i = function i(t) {
15287 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15291 var d1 = Math.sqrt(d2),
15292 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15293 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15294 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15295 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15296 S = (r1 - r0) / rho;
15298 i = function i(t) {
15301 u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15302 return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15306 i.duration = S * 1000 * rho / Math.SQRT2;
15310 zoom.rho = function (_) {
15311 var _1 = Math.max(1e-3, +_),
15315 return zoomRho(_1, _2, _4);
15319 })(Math.SQRT2, 2, 4);
15321 function d3_quantize (interpolator, n) {
15322 var samples = new Array(n);
15324 for (var i = 0; i < n; ++i) {
15325 samples[i] = interpolator(i / (n - 1));
15331 // `Function.prototype.bind` method
15332 // https://tc39.es/ecma262/#sec-function.prototype.bind
15333 _export({ target: 'Function', proto: true }, {
15338 // is an animation frame pending?
15340 // is a timeout pending?
15342 // are any timers active?
15344 // how frequently we check for clock skew
15350 clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15351 setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15355 return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15358 function clearNow() {
15363 this._call = this._time = this._next = null;
15365 Timer.prototype = timer.prototype = {
15366 constructor: Timer,
15367 restart: function restart(callback, delay, time) {
15368 if (typeof callback !== "function") throw new TypeError("callback is not a function");
15369 time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
15371 if (!this._next && taskTail !== this) {
15372 if (taskTail) taskTail._next = this;else taskHead = this;
15376 this._call = callback;
15380 stop: function stop() {
15383 this._time = Infinity;
15388 function timer(callback, delay, time) {
15389 var t = new Timer();
15390 t.restart(callback, delay, time);
15393 function timerFlush() {
15394 now(); // Get the current time, if not already set.
15396 ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15402 if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15410 clockNow = (clockLast = clock.now()) + clockSkew;
15411 frame = timeout = 0;
15423 var now = clock.now(),
15424 delay = now - clockLast;
15425 if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15436 if (time > t1._time) time = t1._time;
15437 t0 = t1, t1 = t1._next;
15439 t2 = t1._next, t1._next = null;
15440 t1 = t0 ? t0._next = t2 : taskHead = t2;
15448 function sleep(time) {
15449 if (frame) return; // Soonest alarm already set, or will be.
15451 if (timeout) timeout = clearTimeout(timeout);
15452 var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15455 if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15456 if (interval) interval = clearInterval(interval);
15458 if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15459 frame = 1, setFrame(wake);
15463 function d3_timeout (callback, delay, time) {
15464 var t = new Timer();
15465 delay = delay == null ? 0 : +delay;
15466 t.restart(function (elapsed) {
15468 callback(elapsed + delay);
15473 var emptyOn = dispatch("start", "end", "cancel", "interrupt");
15474 var emptyTween = [];
15482 function schedule (node, name, id, index, group, timing) {
15483 var schedules = node.__transition;
15484 if (!schedules) node.__transition = {};else if (id in schedules) return;
15488 // For context during callback.
15490 // For context during callback.
15494 delay: timing.delay,
15495 duration: timing.duration,
15501 function init(node, id) {
15502 var schedule = get$4(node, id);
15503 if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15506 function set$4(node, id) {
15507 var schedule = get$4(node, id);
15508 if (schedule.state > STARTED) throw new Error("too late; already running");
15511 function get$4(node, id) {
15512 var schedule = node.__transition;
15513 if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15517 function create(node, id, self) {
15518 var schedules = node.__transition,
15519 tween; // Initialize the self timer when the transition is created.
15520 // Note the actual delay is not known until the first callback!
15522 schedules[id] = self;
15523 self.timer = timer(schedule, 0, self.time);
15525 function schedule(elapsed) {
15526 self.state = SCHEDULED;
15527 self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15529 if (self.delay <= elapsed) start(elapsed - self.delay);
15532 function start(elapsed) {
15533 var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15535 if (self.state !== SCHEDULED) return stop();
15537 for (i in schedules) {
15539 if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15540 // defer starting an interrupting transition until that transition has a
15541 // chance to tick (and possibly end); see d3/d3-transition#54!
15543 if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15545 if (o.state === RUNNING) {
15548 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15549 delete schedules[i];
15550 } // Cancel any pre-empted transitions.
15551 else if (+i < id) {
15554 o.on.call("cancel", node, node.__data__, o.index, o.group);
15555 delete schedules[i];
15557 } // Defer the first tick to end of the current frame; see d3/d3#1576.
15558 // Note the transition may be canceled after start and before the first tick!
15559 // Note this must be scheduled before the start event; see d3/d3-transition#16!
15560 // Assuming this is successful, subsequent callbacks go straight to tick.
15563 d3_timeout(function () {
15564 if (self.state === STARTED) {
15565 self.state = RUNNING;
15566 self.timer.restart(tick, self.delay, self.time);
15569 }); // Dispatch the start event.
15570 // Note this must be done before the tween are initialized.
15572 self.state = STARTING;
15573 self.on.call("start", node, node.__data__, self.index, self.group);
15574 if (self.state !== STARTING) return; // interrupted
15576 self.state = STARTED; // Initialize the tween, deleting null tween.
15578 tween = new Array(n = self.tween.length);
15580 for (i = 0, j = -1; i < n; ++i) {
15581 if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15586 tween.length = j + 1;
15589 function tick(elapsed) {
15590 var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15595 tween[i].call(node, t);
15596 } // Dispatch the end event.
15599 if (self.state === ENDING) {
15600 self.on.call("end", node, node.__data__, self.index, self.group);
15606 self.state = ENDED;
15608 delete schedules[id];
15610 for (var i in schedules) {
15612 } // eslint-disable-line no-unused-vars
15615 delete node.__transition;
15619 function interrupt (node, name) {
15620 var schedules = node.__transition,
15625 if (!schedules) return;
15626 name = name == null ? null : name + "";
15628 for (i in schedules) {
15629 if ((schedule = schedules[i]).name !== name) {
15634 active = schedule.state > STARTING && schedule.state < ENDING;
15635 schedule.state = ENDED;
15636 schedule.timer.stop();
15637 schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15638 delete schedules[i];
15641 if (empty) delete node.__transition;
15644 function selection_interrupt (name) {
15645 return this.each(function () {
15646 interrupt(this, name);
15650 function tweenRemove(id, name) {
15651 var tween0, tween1;
15652 return function () {
15653 var schedule = set$4(this, id),
15654 tween = schedule.tween; // If this node shared tween with the previous node,
15655 // just assign the updated shared tween and we’re done!
15656 // Otherwise, copy-on-write.
15658 if (tween !== tween0) {
15659 tween1 = tween0 = tween;
15661 for (var i = 0, n = tween1.length; i < n; ++i) {
15662 if (tween1[i].name === name) {
15663 tween1 = tween1.slice();
15664 tween1.splice(i, 1);
15670 schedule.tween = tween1;
15674 function tweenFunction(id, name, value) {
15675 var tween0, tween1;
15676 if (typeof value !== "function") throw new Error();
15677 return function () {
15678 var schedule = set$4(this, id),
15679 tween = schedule.tween; // If this node shared tween with the previous node,
15680 // just assign the updated shared tween and we’re done!
15681 // Otherwise, copy-on-write.
15683 if (tween !== tween0) {
15684 tween1 = (tween0 = tween).slice();
15689 }, i = 0, n = tween1.length; i < n; ++i) {
15690 if (tween1[i].name === name) {
15696 if (i === n) tween1.push(t);
15699 schedule.tween = tween1;
15703 function transition_tween (name, value) {
15707 if (arguments.length < 2) {
15708 var tween = get$4(this.node(), id).tween;
15710 for (var i = 0, n = tween.length, t; i < n; ++i) {
15711 if ((t = tween[i]).name === name) {
15719 return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15721 function tweenValue(transition, name, value) {
15722 var id = transition._id;
15723 transition.each(function () {
15724 var schedule = set$4(this, id);
15725 (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15727 return function (node) {
15728 return get$4(node, id).value[name];
15732 function interpolate$1 (a, b) {
15734 return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15737 function attrRemove$1(name) {
15738 return function () {
15739 this.removeAttribute(name);
15743 function attrRemoveNS$1(fullname) {
15744 return function () {
15745 this.removeAttributeNS(fullname.space, fullname.local);
15749 function attrConstant$1(name, interpolate, value1) {
15751 string1 = value1 + "",
15753 return function () {
15754 var string0 = this.getAttribute(name);
15755 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15759 function attrConstantNS$1(fullname, interpolate, value1) {
15761 string1 = value1 + "",
15763 return function () {
15764 var string0 = this.getAttributeNS(fullname.space, fullname.local);
15765 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15769 function attrFunction$1(name, interpolate, value) {
15770 var string00, string10, interpolate0;
15771 return function () {
15773 value1 = value(this),
15775 if (value1 == null) return void this.removeAttribute(name);
15776 string0 = this.getAttribute(name);
15777 string1 = value1 + "";
15778 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15782 function attrFunctionNS$1(fullname, interpolate, value) {
15783 var string00, string10, interpolate0;
15784 return function () {
15786 value1 = value(this),
15788 if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
15789 string0 = this.getAttributeNS(fullname.space, fullname.local);
15790 string1 = value1 + "";
15791 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15795 function transition_attr (name, value) {
15796 var fullname = namespace(name),
15797 i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
15798 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));
15801 function attrInterpolate(name, i) {
15802 return function (t) {
15803 this.setAttribute(name, i.call(this, t));
15807 function attrInterpolateNS(fullname, i) {
15808 return function (t) {
15809 this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
15813 function attrTweenNS(fullname, value) {
15817 var i = value.apply(this, arguments);
15818 if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
15822 tween._value = value;
15826 function attrTween(name, value) {
15830 var i = value.apply(this, arguments);
15831 if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
15835 tween._value = value;
15839 function transition_attrTween (name, value) {
15840 var key = "attr." + name;
15841 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
15842 if (value == null) return this.tween(key, null);
15843 if (typeof value !== "function") throw new Error();
15844 var fullname = namespace(name);
15845 return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
15848 function delayFunction(id, value) {
15849 return function () {
15850 init(this, id).delay = +value.apply(this, arguments);
15854 function delayConstant(id, value) {
15855 return value = +value, function () {
15856 init(this, id).delay = value;
15860 function transition_delay (value) {
15862 return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$4(this.node(), id).delay;
15865 function durationFunction(id, value) {
15866 return function () {
15867 set$4(this, id).duration = +value.apply(this, arguments);
15871 function durationConstant(id, value) {
15872 return value = +value, function () {
15873 set$4(this, id).duration = value;
15877 function transition_duration (value) {
15879 return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$4(this.node(), id).duration;
15882 function easeConstant(id, value) {
15883 if (typeof value !== "function") throw new Error();
15884 return function () {
15885 set$4(this, id).ease = value;
15889 function transition_ease (value) {
15891 return arguments.length ? this.each(easeConstant(id, value)) : get$4(this.node(), id).ease;
15894 function easeVarying(id, value) {
15895 return function () {
15896 var v = value.apply(this, arguments);
15897 if (typeof v !== "function") throw new Error();
15898 set$4(this, id).ease = v;
15902 function transition_easeVarying (value) {
15903 if (typeof value !== "function") throw new Error();
15904 return this.each(easeVarying(this._id, value));
15907 function transition_filter (match) {
15908 if (typeof match !== "function") match = matcher(match);
15910 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15911 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
15912 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
15913 subgroup.push(node);
15918 return new Transition(subgroups, this._parents, this._name, this._id);
15921 function transition_merge (transition) {
15922 if (transition._id !== this._id) throw new Error();
15924 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) {
15925 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
15926 if (node = group0[i] || group1[i]) {
15932 for (; j < m0; ++j) {
15933 merges[j] = groups0[j];
15936 return new Transition(merges, this._parents, this._name, this._id);
15939 function start(name) {
15940 return (name + "").trim().split(/^|\s+/).every(function (t) {
15941 var i = t.indexOf(".");
15942 if (i >= 0) t = t.slice(0, i);
15943 return !t || t === "start";
15947 function onFunction(id, name, listener) {
15950 sit = start(name) ? init : set$4;
15951 return function () {
15952 var schedule = sit(this, id),
15953 on = schedule.on; // If this node shared a dispatch with the previous node,
15954 // just assign the updated shared dispatch and we’re done!
15955 // Otherwise, copy-on-write.
15957 if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
15962 function transition_on (name, listener) {
15964 return arguments.length < 2 ? get$4(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
15967 function removeFunction(id) {
15968 return function () {
15969 var parent = this.parentNode;
15971 for (var i in this.__transition) {
15972 if (+i !== id) return;
15975 if (parent) parent.removeChild(this);
15979 function transition_remove () {
15980 return this.on("end.remove", removeFunction(this._id));
15983 function transition_select (select) {
15984 var name = this._name,
15986 if (typeof select !== "function") select = selector(select);
15988 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15989 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
15990 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
15991 if ("__data__" in node) subnode.__data__ = node.__data__;
15992 subgroup[i] = subnode;
15993 schedule(subgroup[i], name, id, i, subgroup, get$4(node, id));
15998 return new Transition(subgroups, this._parents, name, id);
16001 function transition_selectAll (select) {
16002 var name = this._name,
16004 if (typeof select !== "function") select = selectorAll(select);
16006 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
16007 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16008 if (node = group[i]) {
16009 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$4(node, id), k = 0, l = children.length; k < l; ++k) {
16010 if (child = children[k]) {
16011 schedule(child, name, id, k, children, inherit);
16015 subgroups.push(children);
16016 parents.push(node);
16021 return new Transition(subgroups, parents, name, id);
16024 var Selection$1 = selection.prototype.constructor;
16025 function transition_selection () {
16026 return new Selection$1(this._groups, this._parents);
16029 function styleNull(name, interpolate) {
16030 var string00, string10, interpolate0;
16031 return function () {
16032 var string0 = styleValue(this, name),
16033 string1 = (this.style.removeProperty(name), styleValue(this, name));
16034 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
16038 function styleRemove$1(name) {
16039 return function () {
16040 this.style.removeProperty(name);
16044 function styleConstant$1(name, interpolate, value1) {
16046 string1 = value1 + "",
16048 return function () {
16049 var string0 = styleValue(this, name);
16050 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16054 function styleFunction$1(name, interpolate, value) {
16055 var string00, string10, interpolate0;
16056 return function () {
16057 var string0 = styleValue(this, name),
16058 value1 = value(this),
16059 string1 = value1 + "";
16060 if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16061 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16065 function styleMaybeRemove(id, name) {
16069 key = "style." + name,
16070 event = "end." + key,
16072 return function () {
16073 var schedule = set$4(this, id),
16075 listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined; // If this node shared a dispatch with the previous node,
16076 // just assign the updated shared dispatch and we’re done!
16077 // Otherwise, copy-on-write.
16079 if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16084 function transition_style (name, value, priority) {
16085 var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
16086 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);
16089 function styleInterpolate(name, i, priority) {
16090 return function (t) {
16091 this.style.setProperty(name, i.call(this, t), priority);
16095 function styleTween(name, value, priority) {
16099 var i = value.apply(this, arguments);
16100 if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16104 tween._value = value;
16108 function transition_styleTween (name, value, priority) {
16109 var key = "style." + (name += "");
16110 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16111 if (value == null) return this.tween(key, null);
16112 if (typeof value !== "function") throw new Error();
16113 return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16116 function textConstant$1(value) {
16117 return function () {
16118 this.textContent = value;
16122 function textFunction$1(value) {
16123 return function () {
16124 var value1 = value(this);
16125 this.textContent = value1 == null ? "" : value1;
16129 function transition_text (value) {
16130 return this.tween("text", typeof value === "function" ? textFunction$1(tweenValue(this, "text", value)) : textConstant$1(value == null ? "" : value + ""));
16133 function textInterpolate(i) {
16134 return function (t) {
16135 this.textContent = i.call(this, t);
16139 function textTween(value) {
16143 var i = value.apply(this, arguments);
16144 if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16148 tween._value = value;
16152 function transition_textTween (value) {
16154 if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16155 if (value == null) return this.tween(key, null);
16156 if (typeof value !== "function") throw new Error();
16157 return this.tween(key, textTween(value));
16160 function transition_transition () {
16161 var name = this._name,
16165 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16166 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16167 if (node = group[i]) {
16168 var inherit = get$4(node, id0);
16169 schedule(node, name, id1, i, group, {
16170 time: inherit.time + inherit.delay + inherit.duration,
16172 duration: inherit.duration,
16179 return new Transition(groups, this._parents, name, id1);
16182 function transition_end () {
16187 size = that.size();
16188 return new Promise(function (resolve, reject) {
16193 value: function value() {
16194 if (--size === 0) resolve();
16197 that.each(function () {
16198 var schedule = set$4(this, id),
16199 on = schedule.on; // If this node shared a dispatch with the previous node,
16200 // just assign the updated shared dispatch and we’re done!
16201 // Otherwise, copy-on-write.
16204 on1 = (on0 = on).copy();
16206 on1._.cancel.push(cancel);
16208 on1._.interrupt.push(cancel);
16210 on1._.end.push(end);
16214 }); // The selection was empty, resolve end immediately
16216 if (size === 0) resolve();
16221 function Transition(groups, parents, name, id) {
16222 this._groups = groups;
16223 this._parents = parents;
16227 function transition(name) {
16228 return selection().transition(name);
16233 var selection_prototype = selection.prototype;
16234 Transition.prototype = transition.prototype = _defineProperty({
16235 constructor: Transition,
16236 select: transition_select,
16237 selectAll: transition_selectAll,
16238 filter: transition_filter,
16239 merge: transition_merge,
16240 selection: transition_selection,
16241 transition: transition_transition,
16242 call: selection_prototype.call,
16243 nodes: selection_prototype.nodes,
16244 node: selection_prototype.node,
16245 size: selection_prototype.size,
16246 empty: selection_prototype.empty,
16247 each: selection_prototype.each,
16249 attr: transition_attr,
16250 attrTween: transition_attrTween,
16251 style: transition_style,
16252 styleTween: transition_styleTween,
16253 text: transition_text,
16254 textTween: transition_textTween,
16255 remove: transition_remove,
16256 tween: transition_tween,
16257 delay: transition_delay,
16258 duration: transition_duration,
16259 ease: transition_ease,
16260 easeVarying: transition_easeVarying,
16261 end: transition_end
16262 }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16264 var linear$1 = function linear(t) {
16268 function cubicInOut(t) {
16269 return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16272 var defaultTiming = {
16280 function inherit(node, id) {
16283 while (!(timing = node.__transition) || !(timing = timing[id])) {
16284 if (!(node = node.parentNode)) {
16285 throw new Error("transition ".concat(id, " not found"));
16292 function selection_transition (name) {
16295 if (name instanceof Transition) {
16296 id = name._id, name = name._name;
16298 id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
16301 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16302 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16303 if (node = group[i]) {
16304 schedule(node, name, id, i, group, timing || inherit(node, id));
16309 return new Transition(groups, this._parents, name, id);
16312 selection.prototype.interrupt = selection_interrupt;
16313 selection.prototype.transition = selection_transition;
16315 var constant$3 = (function (x) {
16316 return function () {
16321 function ZoomEvent(type, _ref) {
16322 var sourceEvent = _ref.sourceEvent,
16323 target = _ref.target,
16324 transform = _ref.transform,
16325 dispatch = _ref.dispatch;
16326 Object.defineProperties(this, {
16333 value: sourceEvent,
16353 function Transform(k, x, y) {
16358 Transform.prototype = {
16359 constructor: Transform,
16360 scale: function scale(k) {
16361 return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16363 translate: function translate(x, y) {
16364 return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16366 apply: function apply(point) {
16367 return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16369 applyX: function applyX(x) {
16370 return x * this.k + this.x;
16372 applyY: function applyY(y) {
16373 return y * this.k + this.y;
16375 invert: function invert(location) {
16376 return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16378 invertX: function invertX(x) {
16379 return (x - this.x) / this.k;
16381 invertY: function invertY(y) {
16382 return (y - this.y) / this.k;
16384 rescaleX: function rescaleX(x) {
16385 return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16387 rescaleY: function rescaleY(y) {
16388 return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16390 toString: function toString() {
16391 return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16394 var identity$2 = new Transform(1, 0, 0);
16396 function nopropagation$1(event) {
16397 event.stopImmediatePropagation();
16399 function noevent$1 (event) {
16400 event.preventDefault();
16401 event.stopImmediatePropagation();
16404 // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16406 function defaultFilter$1(event) {
16407 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16410 function defaultExtent() {
16413 if (e instanceof SVGElement) {
16414 e = e.ownerSVGElement || e;
16416 if (e.hasAttribute("viewBox")) {
16417 e = e.viewBox.baseVal;
16418 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16421 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16424 return [[0, 0], [e.clientWidth, e.clientHeight]];
16427 function defaultTransform() {
16428 return this.__zoom || identity$2;
16431 function defaultWheelDelta(event) {
16432 return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16435 function defaultTouchable$1() {
16436 return navigator.maxTouchPoints || "ontouchstart" in this;
16439 function defaultConstrain(transform, extent, translateExtent) {
16440 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16441 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16442 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16443 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16444 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));
16447 function d3_zoom () {
16448 var filter = defaultFilter$1,
16449 extent = defaultExtent,
16450 constrain = defaultConstrain,
16451 wheelDelta = defaultWheelDelta,
16452 touchable = defaultTouchable$1,
16453 scaleExtent = [0, Infinity],
16454 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16456 interpolate = interpolateZoom,
16457 listeners = dispatch("start", "zoom", "end"),
16463 clickDistance2 = 0,
16466 function zoom(selection) {
16467 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)");
16470 zoom.transform = function (collection, transform, point, event) {
16471 var selection = collection.selection ? collection.selection() : collection;
16472 selection.property("__zoom", defaultTransform);
16474 if (collection !== selection) {
16475 schedule(collection, transform, point, event);
16477 selection.interrupt().each(function () {
16478 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16483 zoom.scaleBy = function (selection, k, p, event) {
16484 zoom.scaleTo(selection, function () {
16485 var k0 = this.__zoom.k,
16486 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16491 zoom.scaleTo = function (selection, k, p, event) {
16492 zoom.transform(selection, function () {
16493 var e = extent.apply(this, arguments),
16495 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16496 p1 = t0.invert(p0),
16497 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16498 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16502 zoom.translateBy = function (selection, x, y, event) {
16503 zoom.transform(selection, function () {
16504 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);
16508 zoom.translateTo = function (selection, x, y, p, event) {
16509 zoom.transform(selection, function () {
16510 var e = extent.apply(this, arguments),
16512 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16513 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);
16517 function scale(transform, k) {
16518 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16519 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16522 function translate(transform, p0, p1) {
16523 var x = p0[0] - p1[0] * transform.k,
16524 y = p0[1] - p1[1] * transform.k;
16525 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16528 function centroid(extent) {
16529 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16532 function schedule(transition, transform, point, event) {
16533 transition.on("start.zoom", function () {
16534 gesture(this, arguments).event(event).start();
16535 }).on("interrupt.zoom end.zoom", function () {
16536 gesture(this, arguments).event(event).end();
16537 }).tween("zoom", function () {
16540 g = gesture(that, args).event(event),
16541 e = extent.apply(that, args),
16542 p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16543 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16545 b = typeof transform === "function" ? transform.apply(that, args) : transform,
16546 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16547 return function (t) {
16548 if (t === 1) t = b; // Avoid rounding error on end.
16552 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16559 function gesture(that, args, clean) {
16560 return !clean && that.__zooming || new Gesture(that, args);
16563 function Gesture(that, args) {
16567 this.sourceEvent = null;
16568 this.extent = extent.apply(that, args);
16572 Gesture.prototype = {
16573 event: function event(_event) {
16574 if (_event) this.sourceEvent = _event;
16577 start: function start() {
16578 if (++this.active === 1) {
16579 this.that.__zooming = this;
16580 this.emit("start");
16585 zoom: function zoom(key, transform) {
16586 if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16587 if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16588 if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16589 this.that.__zoom = transform;
16593 end: function end() {
16594 if (--this.active === 0) {
16595 delete this.that.__zooming;
16601 emit: function emit(type) {
16602 var d = select(this.that).datum();
16603 listeners.call(type, this.that, new ZoomEvent(type, {
16604 sourceEvent: this.sourceEvent,
16607 transform: this.that.__zoom,
16608 dispatch: listeners
16613 function wheeled(event) {
16614 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16615 args[_key - 1] = arguments[_key];
16618 if (!filter.apply(this, arguments)) return;
16619 var g = gesture(this, args).event(event),
16621 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16622 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16623 // If there were recent wheel events, reset the wheel idle timeout.
16626 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16627 g.mouse[1] = t.invert(g.mouse[0] = p);
16630 clearTimeout(g.wheel);
16631 } // If this wheel event won’t trigger a transform change, ignore it.
16632 else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16634 g.mouse = [p, t.invert(p)];
16640 g.wheel = setTimeout(wheelidled, wheelDelay);
16641 g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16643 function wheelidled() {
16649 function mousedowned(event) {
16650 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16651 args[_key2 - 1] = arguments[_key2];
16654 if (touchending || !filter.apply(this, arguments)) return;
16655 var g = gesture(this, args, true).event(event),
16656 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16657 p = pointer(event, currentTarget),
16658 currentTarget = event.currentTarget,
16659 x0 = event.clientX,
16660 y0 = event.clientY;
16661 dragDisable(event.view);
16662 nopropagation$1(event);
16663 g.mouse = [p, this.__zoom.invert(p)];
16667 function mousemoved(event) {
16671 var dx = event.clientX - x0,
16672 dy = event.clientY - y0;
16673 g.moved = dx * dx + dy * dy > clickDistance2;
16676 g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16679 function mouseupped(event) {
16680 v.on("mousemove.zoom mouseup.zoom", null);
16681 yesdrag(event.view, g.moved);
16683 g.event(event).end();
16687 function dblclicked(event) {
16688 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16689 args[_key3 - 1] = arguments[_key3];
16692 if (!filter.apply(this, arguments)) return;
16693 var t0 = this.__zoom,
16694 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16695 p1 = t0.invert(p0),
16696 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16697 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16699 if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16702 function touchstarted(event) {
16703 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16704 args[_key4 - 1] = arguments[_key4];
16707 if (!filter.apply(this, arguments)) return;
16708 var touches = event.touches,
16709 n = touches.length,
16710 g = gesture(this, args, event.changedTouches.length === n).event(event),
16715 nopropagation$1(event);
16717 for (i = 0; i < n; ++i) {
16718 t = touches[i], p = pointer(t, this);
16719 p = [p, this.__zoom.invert(p), t.identifier];
16720 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;
16723 if (touchstarting) touchstarting = clearTimeout(touchstarting);
16726 if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16727 touchstarting = null;
16734 function touchmoved(event) {
16735 if (!this.__zooming) return;
16737 for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16738 args[_key5 - 1] = arguments[_key5];
16741 var g = gesture(this, args).event(event),
16742 touches = event.changedTouches,
16743 n = touches.length,
16750 for (i = 0; i < n; ++i) {
16751 t = touches[i], p = pointer(t, this);
16752 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;
16758 var p0 = g.touch0[0],
16762 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
16763 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
16764 t = scale(t, Math.sqrt(dp / dl));
16765 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
16766 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
16767 } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
16769 g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
16772 function touchended(event) {
16773 for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
16774 args[_key6 - 1] = arguments[_key6];
16777 if (!this.__zooming) return;
16778 var g = gesture(this, args).event(event),
16779 touches = event.changedTouches,
16780 n = touches.length,
16783 nopropagation$1(event);
16784 if (touchending) clearTimeout(touchending);
16785 touchending = setTimeout(function () {
16786 touchending = null;
16789 for (i = 0; i < n; ++i) {
16791 if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
16794 if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
16795 if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
16796 g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
16798 if (g.taps === 2) {
16799 t = pointer(t, this);
16801 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
16802 var p = select(this).on("dblclick.zoom");
16803 if (p) p.apply(this, arguments);
16809 zoom.wheelDelta = function (_) {
16810 return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta;
16813 zoom.filter = function (_) {
16814 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter;
16817 zoom.touchable = function (_) {
16818 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable;
16821 zoom.extent = function (_) {
16822 return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
16825 zoom.scaleExtent = function (_) {
16826 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
16829 zoom.translateExtent = function (_) {
16830 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]]];
16833 zoom.constrain = function (_) {
16834 return arguments.length ? (constrain = _, zoom) : constrain;
16837 zoom.duration = function (_) {
16838 return arguments.length ? (duration = +_, zoom) : duration;
16841 zoom.interpolate = function (_) {
16842 return arguments.length ? (interpolate = _, zoom) : interpolate;
16845 zoom.on = function () {
16846 var value = listeners.on.apply(listeners, arguments);
16847 return value === listeners ? zoom : value;
16850 zoom.clickDistance = function (_) {
16851 return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
16854 zoom.tapDistance = function (_) {
16855 return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
16862 Bypasses features of D3's default projection stream pipeline that are unnecessary:
16863 * Antimeridian clipping
16864 * Spherical rotation
16868 function geoRawMercator() {
16869 var project = mercatorRaw;
16870 var k = 512 / Math.PI; // scale
16873 var y = 0; // translate
16875 var clipExtent = [[0, 0], [0, 0]];
16877 function projection(point) {
16878 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
16879 return [point[0] * k + x, y - point[1] * k];
16882 projection.invert = function (point) {
16883 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
16884 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
16887 projection.scale = function (_) {
16888 if (!arguments.length) return k;
16893 projection.translate = function (_) {
16894 if (!arguments.length) return [x, y];
16900 projection.clipExtent = function (_) {
16901 if (!arguments.length) return clipExtent;
16906 projection.transform = function (obj) {
16907 if (!arguments.length) return identity$2.translate(x, y).scale(k);
16914 projection.stream = d3_geoTransform({
16915 point: function point(x, y) {
16916 var vec = projection([x, y]);
16917 this.stream.point(vec[0], vec[1]);
16923 function geoOrthoNormalizedDotProduct(a, b, origin) {
16924 if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
16925 return 1; // coincident points, treat as straight and try to remove
16928 return geoVecNormalizedDot(a, b, origin);
16931 function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
16932 var val = Math.abs(dotp);
16934 if (val < epsilon) {
16935 return 0; // already orthogonal
16936 } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
16937 return 0; // straight angle, which is okay in this case
16938 } else if (val < lowerThreshold || val > upperThreshold) {
16939 return dotp; // can be adjusted
16941 return null; // ignore vertex
16945 function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
16947 var first = isClosed ? 0 : 1;
16948 var last = isClosed ? points.length : points.length - 1;
16949 var coords = points.map(function (p) {
16952 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16953 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16955 for (var i = first; i < last; i++) {
16956 var a = coords[(i - 1 + coords.length) % coords.length];
16957 var origin = coords[i];
16958 var b = coords[(i + 1) % coords.length];
16959 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
16960 if (dotp === null) continue; // ignore vertex
16962 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
16966 } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
16968 function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
16969 var max = -Infinity;
16970 var first = isClosed ? 0 : 1;
16971 var last = isClosed ? coords.length : coords.length - 1;
16973 for (var i = first; i < last; i++) {
16974 var a = coords[(i - 1 + coords.length) % coords.length];
16975 var origin = coords[i];
16976 var b = coords[(i + 1) % coords.length];
16977 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
16978 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
16979 if (angle > 45) angle = 90 - angle;
16980 if (angle >= lessThan) continue;
16981 if (angle > max) max = angle;
16984 if (max === -Infinity) return null;
16986 } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
16988 function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
16990 var first = isClosed ? 0 : 1;
16991 var last = isClosed ? coords.length : coords.length - 1;
16992 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16993 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16995 for (var i = first; i < last; i++) {
16996 var a = coords[(i - 1 + coords.length) % coords.length];
16997 var origin = coords[i];
16998 var b = coords[(i + 1) % coords.length];
16999 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
17000 if (dotp === null) continue; // ignore vertex
17002 if (Math.abs(dotp) > 0) return 1; // something to do
17004 score = 0; // already square
17010 var onFreeze = internalMetadata.onFreeze;
17012 var nativeFreeze = Object.freeze;
17013 var FAILS_ON_PRIMITIVES$4 = fails(function () { nativeFreeze(1); });
17015 // `Object.freeze` method
17016 // https://tc39.es/ecma262/#sec-object.freeze
17017 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4, sham: !freezing }, {
17018 freeze: function freeze(it) {
17019 return nativeFreeze && isObject(it) ? nativeFreeze(onFreeze(it)) : it;
17023 // Returns true if a and b have the same elements at the same indices.
17024 function utilArrayIdentical(a, b) {
17025 // an array is always identical to itself
17026 if (a === b) return true;
17028 if (i !== b.length) return false;
17031 if (a[i] !== b[i]) return false;
17035 } // http://2ality.com/2015/01/es6-set-operations.html
17036 // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
17037 // This operation is also sometimes called minus (-).
17038 // var a = [1,2,3];
17039 // var b = [4,3,2];
17040 // utilArrayDifference(a, b)
17042 // utilArrayDifference(b, a)
17045 function utilArrayDifference(a, b) {
17046 var other = new Set(b);
17047 return Array.from(new Set(a)).filter(function (v) {
17048 return !other.has(v);
17050 } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
17051 // var a = [1,2,3];
17052 // var b = [4,3,2];
17053 // utilArrayIntersection(a, b)
17056 function utilArrayIntersection(a, b) {
17057 var other = new Set(b);
17058 return Array.from(new Set(a)).filter(function (v) {
17059 return other.has(v);
17061 } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17062 // var a = [1,2,3];
17063 // var b = [4,3,2];
17064 // utilArrayUnion(a, b)
17067 function utilArrayUnion(a, b) {
17068 var result = new Set(a);
17069 b.forEach(function (v) {
17072 return Array.from(result);
17073 } // Returns an Array with all the duplicates removed
17074 // var a = [1,1,2,3,3];
17075 // utilArrayUniq(a)
17078 function utilArrayUniq(a) {
17079 return Array.from(new Set(a));
17080 } // Splits array into chunks of given chunk size
17081 // var a = [1,2,3,4,5,6,7];
17082 // utilArrayChunk(a, 3);
17083 // [[1,2,3],[4,5,6],[7]];
17085 function utilArrayChunk(a, chunkSize) {
17086 if (!chunkSize || chunkSize < 0) return [a.slice()];
17087 var result = new Array(Math.ceil(a.length / chunkSize));
17088 return Array.from(result, function (item, i) {
17089 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17091 } // Flattens two level array into a single level
17092 // var a = [[1,2,3],[4,5,6],[7]];
17093 // utilArrayFlatten(a);
17094 // [1,2,3,4,5,6,7];
17096 function utilArrayFlatten(a) {
17097 return a.reduce(function (acc, val) {
17098 return acc.concat(val);
17100 } // Groups the items of the Array according to the given key
17101 // `key` can be passed as a property or as a key function
17104 // { type: 'Dog', name: 'Spot' },
17105 // { type: 'Cat', name: 'Tiger' },
17106 // { type: 'Dog', name: 'Rover' },
17107 // { type: 'Cat', name: 'Leo' }
17110 // utilArrayGroupBy(pets, 'type')
17112 // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17113 // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17116 // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17118 // 3: [{type: 'Cat', name: 'Leo'}],
17119 // 4: [{type: 'Dog', name: 'Spot'}],
17120 // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17123 function utilArrayGroupBy(a, key) {
17124 return a.reduce(function (acc, item) {
17125 var group = typeof key === 'function' ? key(item) : item[key];
17126 (acc[group] = acc[group] || []).push(item);
17129 } // Returns an Array with all the duplicates removed
17130 // where uniqueness determined by the given key
17131 // `key` can be passed as a property or as a key function
17134 // { type: 'Dog', name: 'Spot' },
17135 // { type: 'Cat', name: 'Tiger' },
17136 // { type: 'Dog', name: 'Rover' },
17137 // { type: 'Cat', name: 'Leo' }
17140 // utilArrayUniqBy(pets, 'type')
17142 // { type: 'Dog', name: 'Spot' },
17143 // { type: 'Cat', name: 'Tiger' }
17146 // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17148 // { type: 'Dog', name: 'Spot' },
17149 // { type: 'Cat', name: 'Tiger' },
17150 // { type: 'Cat', name: 'Leo' }
17153 function utilArrayUniqBy(a, key) {
17154 var seen = new Set();
17155 return a.reduce(function (acc, item) {
17156 var val = typeof key === 'function' ? key(item) : item[key];
17158 if (val && !seen.has(val)) {
17168 fixRegexpWellKnownSymbolLogic('match', 1, function (MATCH, nativeMatch, maybeCallNative) {
17170 // `String.prototype.match` method
17171 // https://tc39.es/ecma262/#sec-string.prototype.match
17172 function match(regexp) {
17173 var O = requireObjectCoercible(this);
17174 var matcher = regexp == undefined ? undefined : regexp[MATCH];
17175 return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17177 // `RegExp.prototype[@@match]` method
17178 // https://tc39.es/ecma262/#sec-regexp.prototype-@@match
17179 function (regexp) {
17180 var res = maybeCallNative(nativeMatch, regexp, this);
17181 if (res.done) return res.value;
17183 var rx = anObject(regexp);
17184 var S = String(this);
17186 if (!rx.global) return regexpExecAbstract(rx, S);
17188 var fullUnicode = rx.unicode;
17193 while ((result = regexpExecAbstract(rx, S)) !== null) {
17194 var matchStr = String(result[0]);
17196 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17199 return n === 0 ? null : A;
17204 var remove$1 = removeDiacritics;
17205 var replacementList = [{
17213 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"
17219 chars: "\xC6\u01FC\u01E2"
17228 chars: "\uA738\uA73A"
17234 chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17237 chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17240 chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17246 chars: "\u01F1\u01C4"
17249 chars: "\u01F2\u01C5"
17252 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"
17255 chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17258 chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17261 chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17264 chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17267 chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17270 chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17273 chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17282 chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17285 chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17294 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"
17309 chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17312 chars: "\u24C6\uFF31\uA756\uA758\u024A"
17315 chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17318 chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17321 chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17330 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"
17333 chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17339 chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17342 chars: "\u24CD\uFF38\u1E8A\u1E8C"
17345 chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17348 chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17351 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"
17357 chars: "\xE6\u01FD\u01E3"
17366 chars: "\uA739\uA73B"
17372 chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17375 chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17378 chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17384 chars: "\u01F3\u01C6"
17387 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"
17390 chars: "\u24D5\uFF46\u1E1F\u0192"
17408 chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17411 chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17417 chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17420 chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17423 chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17426 chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17432 chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17435 chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17441 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"
17456 chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17459 chars: "\u24E0\uFF51\u024B\uA757\uA759"
17462 chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17465 chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17471 chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17480 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"
17483 chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17489 chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17492 chars: "\u24E7\uFF58\u1E8B\u1E8D"
17495 chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17498 chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17500 var diacriticsMap = {};
17502 for (var i = 0; i < replacementList.length; i += 1) {
17503 var chars = replacementList[i].chars;
17505 for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17506 diacriticsMap[chars[j$1]] = replacementList[i].base;
17510 function removeDiacritics(str) {
17511 return str.replace(/[^\u0000-\u007e]/g, function (c) {
17512 return diacriticsMap[c] || c;
17516 var replacementList_1 = replacementList;
17517 var diacriticsMap_1 = diacriticsMap;
17520 replacementList: replacementList_1,
17521 diacriticsMap: diacriticsMap_1
17524 var isArabic_1 = createCommonjsModule(function (module, exports) {
17526 Object.defineProperty(exports, "__esModule", {
17529 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
17532 function isArabic(_char) {
17533 if (_char.length > 1) {
17534 // allow the newer chars?
17535 throw new Error('isArabic works on only one-character strings');
17538 var code = _char.charCodeAt(0);
17540 for (var i = 0; i < arabicBlocks.length; i++) {
17541 var block = arabicBlocks[i];
17543 if (code >= block[0] && code <= block[1]) {
17551 exports.isArabic = isArabic;
17553 function isMath(_char2) {
17554 if (_char2.length > 2) {
17555 // allow the newer chars?
17556 throw new Error('isMath works on only one-character strings');
17559 var code = _char2.charCodeAt(0);
17561 return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17564 exports.isMath = isMath;
17567 var unicodeArabic = createCommonjsModule(function (module, exports) {
17569 Object.defineProperty(exports, "__esModule", {
17572 var arabicReference = {
17574 "normal": ["\u0627"],
17576 "normal": ["\u0627\u0653", "\u0622"],
17577 "isolated": "\uFE81",
17581 "normal": ["\u0627\u0654", "\u0623"],
17582 "isolated": "\uFE83",
17586 "normal": ["\u0627\u0655", "\u0625"],
17587 "isolated": "\uFE87",
17591 "normal": "\u0671",
17592 "isolated": "\uFB50",
17595 "wavy_hamza_above": ["\u0672"],
17596 "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17597 "high_hamza": ["\u0675", "\u0627\u0674"],
17598 "indic_two_above": ["\u0773"],
17599 "indic_three_above": ["\u0774"],
17601 "normal": ["\u0627\u064B"],
17603 "isolated": "\uFD3D"
17605 "isolated": "\uFE8D",
17609 "normal": ["\u0628"],
17610 "dotless": ["\u066E"],
17611 "three_dots_horizontally_below": ["\u0750"],
17612 "dot_below_three_dots_above": ["\u0751"],
17613 "three_dots_pointing_upwards_below": ["\u0752"],
17614 "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17615 "two_dots_below_dot_above": ["\u0754"],
17616 "inverted_small_v_below": ["\u0755"],
17617 "small_v": ["\u0756"],
17618 "small_v_below": ["\u08A0"],
17619 "hamza_above": ["\u08A1"],
17620 "small_meem_above": ["\u08B6"],
17621 "isolated": "\uFE8F",
17623 "initial": "\uFE91",
17627 "normal": ["\u0629"],
17628 "isolated": "\uFE93",
17632 "normal": ["\u062A"],
17633 "ring": ["\u067C"],
17634 "three_dots_above_downwards": ["\u067D"],
17635 "small_teh_above": ["\u08B8"],
17636 "isolated": "\uFE95",
17638 "initial": "\uFE97",
17642 "normal": ["\u062B"],
17643 "isolated": "\uFE99",
17645 "initial": "\uFE9B",
17649 "normal": ["\u062C"],
17650 "two_dots_above": ["\u08A2"],
17651 "isolated": "\uFE9D",
17653 "initial": "\uFE9F",
17657 "normal": ["\u062D"],
17658 "hamza_above": ["\u0681"],
17659 "two_dots_vertical_above": ["\u0682"],
17660 "three_dots_above": ["\u0685"],
17661 "two_dots_above": ["\u0757"],
17662 "three_dots_pointing_upwards_below": ["\u0758"],
17663 "small_tah_below": ["\u076E"],
17664 "small_tah_two_dots": ["\u076F"],
17665 "small_tah_above": ["\u0772"],
17666 "indic_four_below": ["\u077C"],
17667 "isolated": "\uFEA1",
17669 "initial": "\uFEA3",
17673 "normal": ["\u062E"],
17674 "isolated": "\uFEA5",
17676 "initial": "\uFEA7",
17680 "normal": ["\u062F"],
17681 "ring": ["\u0689"],
17682 "dot_below": ["\u068A"],
17683 "dot_below_small_tah": ["\u068B"],
17684 "three_dots_above_downwards": ["\u068F"],
17685 "four_dots_above": ["\u0690"],
17686 "inverted_v": ["\u06EE"],
17687 "two_dots_vertically_below_small_tah": ["\u0759"],
17688 "inverted_small_v_below": ["\u075A"],
17689 "three_dots_below": ["\u08AE"],
17690 "isolated": "\uFEA9",
17694 "normal": ["\u0630"],
17695 "isolated": "\uFEAB",
17699 "normal": ["\u0631"],
17700 "small_v": ["\u0692"],
17701 "ring": ["\u0693"],
17702 "dot_below": ["\u0694"],
17703 "small_v_below": ["\u0695"],
17704 "dot_below_dot_above": ["\u0696"],
17705 "two_dots_above": ["\u0697"],
17706 "four_dots_above": ["\u0699"],
17707 "inverted_v": ["\u06EF"],
17708 "stroke": ["\u075B"],
17709 "two_dots_vertically_above": ["\u076B"],
17710 "hamza_above": ["\u076C"],
17711 "small_tah_two_dots": ["\u0771"],
17712 "loop": ["\u08AA"],
17713 "small_noon_above": ["\u08B9"],
17714 "isolated": "\uFEAD",
17718 "normal": ["\u0632"],
17719 "inverted_v_above": ["\u08B2"],
17720 "isolated": "\uFEAF",
17724 "normal": ["\u0633"],
17725 "dot_below_dot_above": ["\u069A"],
17726 "three_dots_below": ["\u069B"],
17727 "three_dots_below_three_dots_above": ["\u069C"],
17728 "four_dots_above": ["\u075C"],
17729 "two_dots_vertically_above": ["\u076D"],
17730 "small_tah_two_dots": ["\u0770"],
17731 "indic_four_above": ["\u077D"],
17732 "inverted_v": ["\u077E"],
17733 "isolated": "\uFEB1",
17735 "initial": "\uFEB3",
17739 "normal": ["\u0634"],
17740 "dot_below": ["\u06FA"],
17741 "isolated": "\uFEB5",
17743 "initial": "\uFEB7",
17747 "normal": ["\u0635"],
17748 "two_dots_below": ["\u069D"],
17749 "three_dots_above": ["\u069E"],
17750 "three_dots_below": ["\u08AF"],
17751 "isolated": "\uFEB9",
17753 "initial": "\uFEBB",
17757 "normal": ["\u0636"],
17758 "dot_below": ["\u06FB"],
17759 "isolated": "\uFEBD",
17761 "initial": "\uFEBF",
17765 "normal": ["\u0637"],
17766 "three_dots_above": ["\u069F"],
17767 "two_dots_above": ["\u08A3"],
17768 "isolated": "\uFEC1",
17770 "initial": "\uFEC3",
17774 "normal": ["\u0638"],
17775 "isolated": "\uFEC5",
17777 "initial": "\uFEC7",
17781 "normal": ["\u0639"],
17782 "three_dots_above": ["\u06A0"],
17783 "two_dots_above": ["\u075D"],
17784 "three_dots_pointing_downwards_above": ["\u075E"],
17785 "two_dots_vertically_above": ["\u075F"],
17786 "three_dots_below": ["\u08B3"],
17787 "isolated": "\uFEC9",
17789 "initial": "\uFECB",
17793 "normal": ["\u063A"],
17794 "dot_below": ["\u06FC"],
17795 "isolated": "\uFECD",
17797 "initial": "\uFECF",
17801 "normal": ["\u0641"],
17802 "dotless": ["\u06A1"],
17803 "dot_moved_below": ["\u06A2"],
17804 "dot_below": ["\u06A3"],
17805 "three_dots_below": ["\u06A5"],
17806 "two_dots_below": ["\u0760"],
17807 "three_dots_pointing_upwards_below": ["\u0761"],
17808 "dot_below_three_dots_above": ["\u08A4"],
17809 "isolated": "\uFED1",
17811 "initial": "\uFED3",
17815 "normal": ["\u0642"],
17816 "dotless": ["\u066F"],
17817 "dot_above": ["\u06A7"],
17818 "three_dots_above": ["\u06A8"],
17819 "dot_below": ["\u08A5"],
17820 "isolated": "\uFED5",
17822 "initial": "\uFED7",
17826 "normal": ["\u0643"],
17827 "swash": ["\u06AA"],
17828 "ring": ["\u06AB"],
17829 "dot_above": ["\u06AC"],
17830 "three_dots_below": ["\u06AE"],
17831 "two_dots_above": ["\u077F"],
17832 "dot_below": ["\u08B4"],
17833 "isolated": "\uFED9",
17835 "initial": "\uFEDB",
17839 "normal": ["\u0644"],
17840 "small_v": ["\u06B5"],
17841 "dot_above": ["\u06B6"],
17842 "three_dots_above": ["\u06B7"],
17843 "three_dots_below": ["\u06B8"],
17845 "double_bar": ["\u08A6"],
17846 "isolated": "\uFEDD",
17848 "initial": "\uFEDF",
17852 "normal": ["\u0645"],
17853 "dot_above": ["\u0765"],
17854 "dot_below": ["\u0766"],
17855 "three_dots_above": ["\u08A7"],
17856 "isolated": "\uFEE1",
17858 "initial": "\uFEE3",
17862 "normal": ["\u0646"],
17863 "dot_below": ["\u06B9"],
17864 "ring": ["\u06BC"],
17865 "three_dots_above": ["\u06BD"],
17866 "two_dots_below": ["\u0767"],
17867 "small_tah": ["\u0768"],
17868 "small_v": ["\u0769"],
17869 "isolated": "\uFEE5",
17871 "initial": "\uFEE7",
17875 "normal": ["\u0647"],
17876 "isolated": "\uFEE9",
17878 "initial": "\uFEEB",
17882 "normal": ["\u0648"],
17884 "normal": ["\u0624", "\u0648\u0654"],
17885 "isolated": "\uFE85",
17888 "high_hamza": ["\u0676", "\u0648\u0674"],
17889 "ring": ["\u06C4"],
17890 "two_dots_above": ["\u06CA"],
17891 "dot_above": ["\u06CF"],
17892 "indic_two_above": ["\u0778"],
17893 "indic_three_above": ["\u0779"],
17894 "dot_within": ["\u08AB"],
17895 "isolated": "\uFEED",
17899 "normal": ["\u0649"],
17900 "hamza_above": ["\u0626", "\u064A\u0654"],
17901 "initial": "\uFBE8",
17902 "medial": "\uFBE9",
17903 "isolated": "\uFEEF",
17907 "normal": ["\u064A"],
17909 "normal": ["\u0626", "\u0649\u0654"],
17910 "isolated": "\uFE89",
17912 "initial": "\uFE8B",
17915 "two_dots_below_hamza_above": ["\u08A8"],
17916 "high_hamza": ["\u0678", "\u064A\u0674"],
17917 "tail": ["\u06CD"],
17918 "small_v": ["\u06CE"],
17919 "three_dots_below": ["\u06D1"],
17920 "two_dots_below_dot_above": ["\u08A9"],
17921 "two_dots_below_small_noon_above": ["\u08BA"],
17922 "isolated": "\uFEF1",
17924 "initial": "\uFEF3",
17928 "normal": ["\u0679"],
17929 "isolated": "\uFB66",
17931 "initial": "\uFB68",
17935 "normal": ["\u067A"],
17936 "isolated": "\uFB5E",
17938 "initial": "\uFB60",
17942 "normal": ["\u067B"],
17943 "isolated": "\uFB52",
17945 "initial": "\uFB54",
17949 "normal": ["\u067E"],
17950 "small_meem_above": ["\u08B7"],
17951 "isolated": "\uFB56",
17953 "initial": "\uFB58",
17957 "normal": ["\u067F"],
17958 "isolated": "\uFB62",
17960 "initial": "\uFB64",
17964 "normal": ["\u0680"],
17965 "isolated": "\uFB5A",
17967 "initial": "\uFB5C",
17971 "normal": ["\u0683"],
17972 "isolated": "\uFB76",
17974 "initial": "\uFB78",
17978 "normal": ["\u0684"],
17979 "isolated": "\uFB72",
17981 "initial": "\uFB74",
17985 "normal": ["\u0686"],
17986 "dot_above": ["\u06BF"],
17987 "isolated": "\uFB7A",
17989 "initial": "\uFB7C",
17993 "normal": ["\u0687"],
17994 "isolated": "\uFB7E",
17996 "initial": "\uFB80",
18000 "normal": ["\u0688"],
18001 "isolated": "\uFB88",
18005 "normal": ["\u068C"],
18006 "isolated": "\uFB84",
18010 "normal": ["\u068D"],
18011 "isolated": "\uFB82",
18015 "normal": ["\u068F", "\u068E"],
18016 "isolated": "\uFB86",
18020 "normal": ["\u0691"],
18021 "isolated": "\uFB8C",
18025 "normal": ["\u0698"],
18026 "isolated": "\uFB8A",
18030 "normal": ["\u06A4"],
18031 "isolated": "\uFB6A",
18033 "initial": "\uFB6C",
18037 "normal": ["\u06A6"],
18038 "isolated": "\uFB6E",
18040 "initial": "\uFB70",
18044 "normal": ["\u06A9"],
18045 "dot_above": ["\u0762"],
18046 "three_dots_above": ["\u0763"],
18047 "three_dots_pointing_upwards_below": ["\u0764"],
18048 "isolated": "\uFB8E",
18050 "initial": "\uFB90",
18054 "normal": ["\u06AD"],
18055 "isolated": "\uFBD3",
18057 "initial": "\uFBD5",
18061 "normal": ["\u06AF"],
18062 "ring": ["\u06B0"],
18063 "two_dots_below": ["\u06B2"],
18064 "three_dots_above": ["\u06B4"],
18065 "inverted_stroke": ["\u08B0"],
18066 "isolated": "\uFB92",
18068 "initial": "\uFB94",
18072 "normal": ["\u06B1"],
18073 "isolated": "\uFB9A",
18075 "initial": "\uFB9C",
18079 "normal": ["\u06B3"],
18080 "isolated": "\uFB96",
18082 "initial": "\uFB98",
18086 "normal": ["\u06BA"],
18087 "isolated": "\uFB9E",
18091 "normal": ["\u06BB"],
18092 "isolated": "\uFBA0",
18094 "initial": "\uFBA2",
18097 "heh doachashmee": {
18098 "normal": ["\u06BE"],
18099 "isolated": "\uFBAA",
18101 "initial": "\uFBAC",
18105 "normal": ["\u06C1"],
18106 "hamza_above": ["\u06C1\u0654", "\u06C2"],
18107 "isolated": "\uFBA6",
18109 "initial": "\uFBA8",
18112 "teh marbuta goal": {
18113 "normal": ["\u06C3"]
18116 "normal": ["\u06C5"],
18117 "isolated": "\uFBE0",
18121 "normal": ["\u06C6"],
18122 "isolated": "\uFBD9",
18126 "normal": ["\u06C7"],
18128 "normal": ["\u0677", "\u06C7\u0674"],
18129 "isolated": "\uFBDD"
18131 "isolated": "\uFBD7",
18135 "normal": ["\u06C8"],
18136 "isolated": "\uFBDB",
18140 "normal": ["\u06C9"],
18141 "isolated": "\uFBE2",
18145 "normal": ["\u06CB"],
18146 "isolated": "\uFBDE",
18150 "normal": ["\u06CC"],
18151 "indic_two_above": ["\u0775"],
18152 "indic_three_above": ["\u0776"],
18153 "indic_four_above": ["\u0777"],
18154 "isolated": "\uFBFC",
18156 "initial": "\uFBFE",
18160 "normal": ["\u06D0"],
18161 "isolated": "\uFBE4",
18163 "initial": "\uFBE6",
18167 "normal": ["\u06D2"],
18169 "normal": ["\u06D2\u0654", "\u06D3"],
18170 "isolated": "\uFBB0",
18173 "indic_two_above": ["\u077A"],
18174 "indic_three_above": ["\u077B"],
18175 "isolated": "\uFBAE",
18179 "normal": ["\u06D5"],
18180 "isolated": "\u06D5",
18183 "normal": ["\u06C0", "\u06D5\u0654"],
18184 "isolated": "\uFBA4",
18189 "normal": ["\u08AC"]
18192 "normal": ["\u08AD"]
18195 "normal": ["\u08B1"]
18198 "normal": ["\u08BB"]
18201 "normal": ["\u08BC"]
18204 "normal": ["\u08BD"]
18207 exports["default"] = arabicReference;
18210 var unicodeLigatures = createCommonjsModule(function (module, exports) {
18212 Object.defineProperty(exports, "__esModule", {
18215 var ligatureReference = {
18217 "isolated": "\uFBEA",
18221 "isolated": "\uFBEC",
18225 "isolated": "\uFBEE",
18229 "isolated": "\uFBF0",
18233 "isolated": "\uFBF2",
18237 "isolated": "\uFBF4",
18241 "isolated": "\uFBF6",
18243 "initial": "\uFBF8"
18246 "uighur_kirghiz": {
18247 "isolated": "\uFBF9",
18249 "initial": "\uFBFB"
18251 "isolated": "\uFC03",
18255 "isolated": "\uFC00",
18256 "initial": "\uFC97"
18259 "isolated": "\uFC01",
18260 "initial": "\uFC98"
18263 "isolated": "\uFC02",
18265 "initial": "\uFC9A",
18269 "isolated": "\uFC04",
18273 "isolated": "\uFC05",
18274 "initial": "\uFC9C"
18277 "isolated": "\uFC06",
18278 "initial": "\uFC9D"
18281 "isolated": "\uFC07",
18282 "initial": "\uFC9E"
18285 "isolated": "\uFC08",
18287 "initial": "\uFC9F",
18291 "isolated": "\uFC09",
18295 "isolated": "\uFC0A",
18299 "isolated": "\uFC0B",
18300 "initial": "\uFCA1"
18303 "isolated": "\uFC0C",
18304 "initial": "\uFCA2"
18307 "isolated": "\uFC0D",
18308 "initial": "\uFCA3"
18311 "isolated": "\uFC0E",
18313 "initial": "\uFCA4",
18317 "isolated": "\uFC0F",
18321 "isolated": "\uFC10",
18325 "isolated": "\uFC11"
18328 "isolated": "\uFC12",
18330 "initial": "\uFCA6",
18334 "isolated": "\uFC13",
18338 "isolated": "\uFC14"
18341 "isolated": "\uFC15",
18342 "initial": "\uFCA7"
18345 "isolated": "\uFC16",
18346 "initial": "\uFCA8"
18349 "isolated": "\uFC17",
18350 "initial": "\uFCA9"
18353 "isolated": "\uFC18",
18354 "initial": "\uFCAA"
18357 "isolated": "\uFC19",
18358 "initial": "\uFCAB"
18361 "isolated": "\uFC1A"
18364 "isolated": "\uFC1B",
18365 "initial": "\uFCAC"
18368 "isolated": "\uFC1C",
18369 "initial": "\uFCAD",
18373 "isolated": "\uFC1D",
18374 "initial": "\uFCAE",
18378 "isolated": "\uFC1E",
18379 "initial": "\uFCAF",
18383 "isolated": "\uFC1F",
18384 "initial": "\uFCB0",
18388 "isolated": "\uFC20",
18389 "initial": "\uFCB1"
18392 "isolated": "\uFC21",
18393 "initial": "\uFCB3"
18396 "isolated": "\uFC22",
18397 "initial": "\uFCB4"
18400 "isolated": "\uFC23",
18401 "initial": "\uFCB5"
18404 "isolated": "\uFC24",
18405 "initial": "\uFCB6"
18408 "isolated": "\uFC25",
18409 "initial": "\uFCB7"
18412 "isolated": "\uFC26",
18413 "initial": "\uFCB8"
18416 "isolated": "\uFC27",
18417 "initial": "\uFD33",
18421 "isolated": "\uFC28",
18422 "initial": "\uFCB9",
18426 "isolated": "\uFC29",
18427 "initial": "\uFCBA"
18430 "isolated": "\uFC2A",
18431 "initial": "\uFCBB"
18434 "isolated": "\uFC2B",
18435 "initial": "\uFCBC"
18438 "isolated": "\uFC2C",
18439 "initial": "\uFCBD"
18442 "isolated": "\uFC2D",
18443 "initial": "\uFCBE"
18446 "isolated": "\uFC2E",
18447 "initial": "\uFCBF"
18450 "isolated": "\uFC2F",
18451 "initial": "\uFCC0"
18454 "isolated": "\uFC30",
18455 "initial": "\uFCC1"
18458 "isolated": "\uFC31",
18462 "isolated": "\uFC32",
18466 "isolated": "\uFC33",
18467 "initial": "\uFCC2"
18470 "isolated": "\uFC34",
18471 "initial": "\uFCC3"
18474 "isolated": "\uFC35",
18478 "isolated": "\uFC36",
18482 "isolated": "\uFC37",
18486 "isolated": "\uFC38",
18487 "initial": "\uFCC4"
18490 "isolated": "\uFC39",
18491 "initial": "\uFCC5"
18494 "isolated": "\uFC3A",
18495 "initial": "\uFCC6"
18498 "isolated": "\uFC3B",
18500 "initial": "\uFCC7",
18504 "isolated": "\uFC3C",
18506 "initial": "\uFCC8",
18510 "isolated": "\uFC3D",
18514 "isolated": "\uFC3E",
18518 "isolated": "\uFC3F",
18519 "initial": "\uFCC9"
18522 "isolated": "\uFC40",
18523 "initial": "\uFCCA"
18526 "isolated": "\uFC41",
18527 "initial": "\uFCCB"
18530 "isolated": "\uFC42",
18532 "initial": "\uFCCC",
18536 "isolated": "\uFC43",
18540 "isolated": "\uFC44",
18544 "isolated": "\uFC45",
18545 "initial": "\uFCCE"
18548 "isolated": "\uFC46",
18549 "initial": "\uFCCF"
18552 "isolated": "\uFC47",
18553 "initial": "\uFCD0"
18556 "isolated": "\uFC48",
18558 "initial": "\uFCD1"
18561 "isolated": "\uFC49"
18564 "isolated": "\uFC4A"
18567 "isolated": "\uFC4B",
18568 "initial": "\uFCD2"
18571 "isolated": "\uFC4C",
18572 "initial": "\uFCD3"
18575 "isolated": "\uFC4D",
18576 "initial": "\uFCD4"
18579 "isolated": "\uFC4E",
18581 "initial": "\uFCD5",
18585 "isolated": "\uFC4F",
18589 "isolated": "\uFC50",
18593 "isolated": "\uFC51",
18594 "initial": "\uFCD7"
18597 "isolated": "\uFC52",
18598 "initial": "\uFCD8"
18601 "isolated": "\uFC53"
18604 "isolated": "\uFC54"
18607 "isolated": "\uFC55",
18608 "initial": "\uFCDA"
18611 "isolated": "\uFC56",
18612 "initial": "\uFCDB"
18615 "isolated": "\uFC57",
18616 "initial": "\uFCDC"
18619 "isolated": "\uFC58",
18621 "initial": "\uFCDD",
18625 "isolated": "\uFC59",
18629 "isolated": "\uFC5A",
18633 "isolated": "\uFC5B"
18636 "isolated": "\uFC5C"
18639 "isolated": "\uFC5D",
18643 "isolated": "\uFC5E"
18646 "isolated": "\uFC5F"
18649 "isolated": "\uFC60"
18652 "isolated": "\uFC61"
18655 "isolated": "\uFC62"
18658 "isolated": "\uFC63"
18721 "initial": "\uFC99"
18724 "initial": "\uFC9B",
18728 "initial": "\uFCA0",
18732 "initial": "\uFCA5",
18736 "initial": "\uFCB2"
18739 "initial": "\uFCCD"
18742 "initial": "\uFCD6",
18746 "initial": "\uFCD9"
18749 "initial": "\uFCDE",
18756 "medial": "\uFCE8",
18757 "initial": "\uFD31"
18760 "medial": "\uFCE9",
18761 "isolated": "\uFD0C",
18763 "initial": "\uFD30"
18766 "medial": "\uFCEA",
18767 "initial": "\uFD32"
18769 "\u0640\u064E\u0651": {
18772 "\u0640\u064F\u0651": {
18775 "\u0640\u0650\u0651": {
18779 "isolated": "\uFCF5",
18783 "isolated": "\uFCF6",
18787 "isolated": "\uFCF7",
18791 "isolated": "\uFCF8",
18795 "isolated": "\uFCF9",
18799 "isolated": "\uFCFA",
18803 "isolated": "\uFCFB"
18806 "isolated": "\uFCFC",
18810 "isolated": "\uFCFD",
18814 "isolated": "\uFCFE",
18818 "isolated": "\uFCFF",
18822 "isolated": "\uFD00",
18826 "isolated": "\uFD01",
18830 "isolated": "\uFD02",
18834 "isolated": "\uFD03",
18838 "isolated": "\uFD04",
18842 "isolated": "\uFD05",
18846 "isolated": "\uFD06",
18850 "isolated": "\uFD07",
18854 "isolated": "\uFD08",
18858 "isolated": "\uFD09",
18860 "initial": "\uFD2D",
18864 "isolated": "\uFD0A",
18866 "initial": "\uFD2E",
18870 "isolated": "\uFD0B",
18872 "initial": "\uFD2F",
18876 "isolated": "\uFD0D",
18880 "isolated": "\uFD0E",
18884 "isolated": "\uFD0F",
18888 "isolated": "\uFD10",
18894 "\u062A\u062C\u0645": {
18895 "initial": "\uFD50"
18897 "\u062A\u062D\u062C": {
18899 "initial": "\uFD52"
18901 "\u062A\u062D\u0645": {
18902 "initial": "\uFD53"
18904 "\u062A\u062E\u0645": {
18905 "initial": "\uFD54"
18907 "\u062A\u0645\u062C": {
18908 "initial": "\uFD55"
18910 "\u062A\u0645\u062D": {
18911 "initial": "\uFD56"
18913 "\u062A\u0645\u062E": {
18914 "initial": "\uFD57"
18916 "\u062C\u0645\u062D": {
18918 "initial": "\uFD59"
18920 "\u062D\u0645\u064A": {
18923 "\u062D\u0645\u0649": {
18926 "\u0633\u062D\u062C": {
18927 "initial": "\uFD5C"
18929 "\u0633\u062C\u062D": {
18930 "initial": "\uFD5D"
18932 "\u0633\u062C\u0649": {
18935 "\u0633\u0645\u062D": {
18937 "initial": "\uFD60"
18939 "\u0633\u0645\u062C": {
18940 "initial": "\uFD61"
18942 "\u0633\u0645\u0645": {
18944 "initial": "\uFD63"
18946 "\u0635\u062D\u062D": {
18948 "initial": "\uFD65"
18950 "\u0635\u0645\u0645": {
18952 "initial": "\uFDC5"
18954 "\u0634\u062D\u0645": {
18956 "initial": "\uFD68"
18958 "\u0634\u062C\u064A": {
18961 "\u0634\u0645\u062E": {
18963 "initial": "\uFD6B"
18965 "\u0634\u0645\u0645": {
18967 "initial": "\uFD6D"
18969 "\u0636\u062D\u0649": {
18972 "\u0636\u062E\u0645": {
18974 "initial": "\uFD70"
18976 "\u0636\u0645\u062D": {
18979 "\u0637\u0645\u062D": {
18980 "initial": "\uFD72"
18982 "\u0637\u0645\u0645": {
18983 "initial": "\uFD73"
18985 "\u0637\u0645\u064A": {
18988 "\u0639\u062C\u0645": {
18990 "initial": "\uFDC4"
18992 "\u0639\u0645\u0645": {
18994 "initial": "\uFD77"
18996 "\u0639\u0645\u0649": {
18999 "\u063A\u0645\u0645": {
19002 "\u063A\u0645\u064A": {
19005 "\u063A\u0645\u0649": {
19008 "\u0641\u062E\u0645": {
19010 "initial": "\uFD7D"
19012 "\u0642\u0645\u062D": {
19014 "initial": "\uFDB4"
19016 "\u0642\u0645\u0645": {
19019 "\u0644\u062D\u0645": {
19021 "initial": "\uFDB5"
19023 "\u0644\u062D\u064A": {
19026 "\u0644\u062D\u0649": {
19029 "\u0644\u062C\u062C": {
19030 "initial": "\uFD83",
19033 "\u0644\u062E\u0645": {
19035 "initial": "\uFD86"
19037 "\u0644\u0645\u062D": {
19039 "initial": "\uFD88"
19041 "\u0645\u062D\u062C": {
19042 "initial": "\uFD89"
19044 "\u0645\u062D\u0645": {
19045 "initial": "\uFD8A"
19047 "\u0645\u062D\u064A": {
19050 "\u0645\u062C\u062D": {
19051 "initial": "\uFD8C"
19053 "\u0645\u062C\u0645": {
19054 "initial": "\uFD8D"
19056 "\u0645\u062E\u062C": {
19057 "initial": "\uFD8E"
19059 "\u0645\u062E\u0645": {
19060 "initial": "\uFD8F"
19062 "\u0645\u062C\u062E": {
19063 "initial": "\uFD92"
19065 "\u0647\u0645\u062C": {
19066 "initial": "\uFD93"
19068 "\u0647\u0645\u0645": {
19069 "initial": "\uFD94"
19071 "\u0646\u062D\u0645": {
19072 "initial": "\uFD95"
19074 "\u0646\u062D\u0649": {
19077 "\u0646\u062C\u0645": {
19079 "initial": "\uFD98"
19081 "\u0646\u062C\u0649": {
19084 "\u0646\u0645\u064A": {
19087 "\u0646\u0645\u0649": {
19090 "\u064A\u0645\u0645": {
19092 "initial": "\uFD9D"
19094 "\u0628\u062E\u064A": {
19097 "\u062A\u062C\u064A": {
19100 "\u062A\u062C\u0649": {
19103 "\u062A\u062E\u064A": {
19106 "\u062A\u062E\u0649": {
19109 "\u062A\u0645\u064A": {
19112 "\u062A\u0645\u0649": {
19115 "\u062C\u0645\u064A": {
19118 "\u062C\u062D\u0649": {
19121 "\u062C\u0645\u0649": {
19124 "\u0633\u062E\u0649": {
19127 "\u0635\u062D\u064A": {
19130 "\u0634\u062D\u064A": {
19133 "\u0636\u062D\u064A": {
19136 "\u0644\u062C\u064A": {
19139 "\u0644\u0645\u064A": {
19142 "\u064A\u062D\u064A": {
19145 "\u064A\u062C\u064A": {
19148 "\u064A\u0645\u064A": {
19151 "\u0645\u0645\u064A": {
19154 "\u0642\u0645\u064A": {
19157 "\u0646\u062D\u064A": {
19160 "\u0639\u0645\u064A": {
19163 "\u0643\u0645\u064A": {
19166 "\u0646\u062C\u062D": {
19167 "initial": "\uFDB8",
19170 "\u0645\u062E\u064A": {
19173 "\u0644\u062C\u0645": {
19174 "initial": "\uFDBA",
19177 "\u0643\u0645\u0645": {
19179 "initial": "\uFDC3"
19181 "\u062C\u062D\u064A": {
19184 "\u062D\u062C\u064A": {
19187 "\u0645\u062C\u064A": {
19190 "\u0641\u0645\u064A": {
19193 "\u0628\u062D\u064A": {
19196 "\u0633\u062E\u064A": {
19199 "\u0646\u062C\u064A": {
19203 "isolated": "\uFEF5",
19207 "isolated": "\uFEF7",
19211 "isolated": "\uFEF9",
19215 "isolated": "\uFEFB",
19219 "\u0635\u0644\u06D2": "\uFDF0",
19220 "\u0642\u0644\u06D2": "\uFDF1",
19221 "\u0627\u0644\u0644\u0647": "\uFDF2",
19222 "\u0627\u0643\u0628\u0631": "\uFDF3",
19223 "\u0645\u062D\u0645\u062F": "\uFDF4",
19224 "\u0635\u0644\u0639\u0645": "\uFDF5",
19225 "\u0631\u0633\u0648\u0644": "\uFDF6",
19226 "\u0639\u0644\u064A\u0647": "\uFDF7",
19227 "\u0648\u0633\u0644\u0645": "\uFDF8",
19228 "\u0635\u0644\u0649": "\uFDF9",
19229 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19230 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19231 "\u0631\u06CC\u0627\u0644": "\uFDFC"
19234 exports["default"] = ligatureReference;
19237 var reference = createCommonjsModule(function (module, exports) {
19239 Object.defineProperty(exports, "__esModule", {
19242 var letterList = Object.keys(unicodeArabic["default"]);
19243 exports.letterList = letterList;
19244 var ligatureList = Object.keys(unicodeLigatures["default"]);
19245 exports.ligatureList = ligatureList;
19246 var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19247 exports.ligatureWordList = ligatureWordList;
19248 var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19249 exports.lams = lams;
19250 var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19251 exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19252 // console.log('-');
19253 // for (var a = 0; a < alefs.length; a++) {
19254 // console.log(a + ': ' + lams[l] + alefs[a]);
19258 var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19259 exports.tashkeel = tashkeel;
19261 function addToTashkeel(start, finish) {
19262 for (var i = start; i <= finish; i++) {
19263 exports.tashkeel = tashkeel += String.fromCharCode(i);
19267 addToTashkeel(0x0610, 0x061A);
19268 addToTashkeel(0x064B, 0x065F);
19269 addToTashkeel(0x06D6, 0x06DC);
19270 addToTashkeel(0x06E0, 0x06E4);
19271 addToTashkeel(0x06EA, 0x06ED);
19272 addToTashkeel(0x08D3, 0x08E1);
19273 addToTashkeel(0x08E3, 0x08FF);
19274 addToTashkeel(0xFE70, 0xFE7F);
19275 var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19276 exports.lineBreakers = lineBreakers;
19278 function addToLineBreakers(start, finish) {
19279 for (var i = start; i <= finish; i++) {
19280 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19284 addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19286 addToLineBreakers(0x0621, 0x0625);
19287 addToLineBreakers(0x062F, 0x0632);
19288 addToLineBreakers(0x0660, 0x066D); // numerals, math
19290 addToLineBreakers(0x0671, 0x0677);
19291 addToLineBreakers(0x0688, 0x0699);
19292 addToLineBreakers(0x06C3, 0x06CB);
19293 addToLineBreakers(0x06D2, 0x06F9);
19294 addToLineBreakers(0x0759, 0x075B);
19295 addToLineBreakers(0x08AA, 0x08AE);
19296 addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19297 // Presentation Forms A includes diacritics but they are meant to stand alone
19299 addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19302 addToLineBreakers(0x10E60, 0x10E7F);
19303 addToLineBreakers(0x1EC70, 0x1ECBF);
19304 addToLineBreakers(0x1EE00, 0x1EEFF);
19307 var GlyphSplitter_1 = createCommonjsModule(function (module, exports) {
19309 Object.defineProperty(exports, "__esModule", {
19313 function GlyphSplitter(word) {
19315 var lastLetter = '';
19316 word.split('').forEach(function (letter) {
19317 if (isArabic_1.isArabic(letter)) {
19318 if (reference.tashkeel.indexOf(letter) > -1) {
19319 letters[letters.length - 1] += letter;
19320 } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19322 letters[letters.length - 1] += letter;
19324 letters.push(letter);
19327 letters.push(letter);
19330 if (reference.tashkeel.indexOf(letter) === -1) {
19331 lastLetter = letter;
19337 exports.GlyphSplitter = GlyphSplitter;
19340 var BaselineSplitter_1 = createCommonjsModule(function (module, exports) {
19342 Object.defineProperty(exports, "__esModule", {
19346 function BaselineSplitter(word) {
19348 var lastLetter = '';
19349 word.split('').forEach(function (letter) {
19350 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19351 if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19352 letters[letters.length - 1] += letter;
19353 } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19354 letters.push(letter);
19356 letters[letters.length - 1] += letter;
19359 letters.push(letter);
19362 if (reference.tashkeel.indexOf(letter) === -1) {
19363 // don't allow tashkeel to hide line break
19364 lastLetter = letter;
19370 exports.BaselineSplitter = BaselineSplitter;
19373 var Normalization = createCommonjsModule(function (module, exports) {
19375 Object.defineProperty(exports, "__esModule", {
19379 function Normal(word, breakPresentationForm) {
19380 // default is to turn initial/isolated/medial/final presentation form to generic
19381 if (typeof breakPresentationForm === 'undefined') {
19382 breakPresentationForm = true;
19385 var returnable = '';
19386 word.split('').forEach(function (letter) {
19387 if (!isArabic_1.isArabic(letter)) {
19388 returnable += letter;
19392 for (var w = 0; w < reference.letterList.length; w++) {
19393 // ok so we are checking this potential lettertron
19394 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19395 var versions = Object.keys(letterForms);
19397 for (var v = 0; v < versions.length; v++) {
19398 var localVersion = letterForms[versions[v]];
19400 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19401 // look at this embedded object
19402 var embeddedForms = Object.keys(localVersion);
19404 for (var ef = 0; ef < embeddedForms.length; ef++) {
19405 var form = localVersion[embeddedForms[ef]];
19407 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19409 // console.log('embedded match');
19410 if (form === letter) {
19412 if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19413 // replace presentation form
19414 // console.log('keeping normal form of the letter');
19415 if (_typeof(localVersion['normal']) === 'object') {
19416 returnable += localVersion['normal'][0];
19418 returnable += localVersion['normal'];
19422 } // console.log('keeping this letter');
19425 returnable += letter;
19427 } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19429 returnable += form[0]; // console.log('added the first letter from the same array');
19435 } else if (localVersion === letter) {
19437 if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19438 // replace presentation form
19439 // console.log('keeping normal form of the letter');
19440 if (_typeof(letterForms['normal']) === 'object') {
19441 returnable += letterForms['normal'][0];
19443 returnable += letterForms['normal'];
19447 } // console.log('keeping this letter');
19450 returnable += letter;
19452 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19454 returnable += localVersion[0]; // console.log('added the first letter from the same array');
19462 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19463 var normalForm = reference.ligatureList[v2];
19465 if (normalForm !== 'words') {
19466 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19468 for (var f = 0; f < ligForms.length; f++) {
19469 if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19470 returnable += normalForm;
19475 } // try words ligatures
19478 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19479 var _normalForm = reference.ligatureWordList[v3];
19481 if (unicodeLigatures["default"].words[_normalForm] === letter) {
19482 returnable += _normalForm;
19487 returnable += letter; // console.log('kept the letter')
19492 exports.Normal = Normal;
19495 var CharShaper_1 = createCommonjsModule(function (module, exports) {
19497 Object.defineProperty(exports, "__esModule", {
19501 function CharShaper(letter, form) {
19502 if (!isArabic_1.isArabic(letter)) {
19504 throw new Error('Not Arabic');
19507 if (letter === "\u0621") {
19512 for (var w = 0; w < reference.letterList.length; w++) {
19513 // ok so we are checking this potential lettertron
19514 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19515 var versions = Object.keys(letterForms);
19517 for (var v = 0; v < versions.length; v++) {
19518 var localVersion = letterForms[versions[v]];
19520 if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19521 if (versions.indexOf(form) > -1) {
19522 return letterForms[form];
19524 } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19526 var embeddedVersions = Object.keys(localVersion);
19528 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19529 if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19530 if (embeddedVersions.indexOf(form) > -1) {
19531 return localVersion[form];
19540 exports.CharShaper = CharShaper;
19543 var WordShaper_1 = createCommonjsModule(function (module, exports) {
19545 Object.defineProperty(exports, "__esModule", {
19549 function WordShaper(word) {
19550 var state = 'initial';
19553 for (var w = 0; w < word.length; w++) {
19554 var nextLetter = ' ';
19556 for (var nxw = w + 1; nxw < word.length; nxw++) {
19557 if (!isArabic_1.isArabic(word[nxw])) {
19561 if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19562 nextLetter = word[nxw];
19567 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19568 // space or other non-Arabic
19571 } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19572 // tashkeel - add without changing state
19574 } else if (nextLetter === ' ' || // last Arabic letter in this word
19575 reference.lineBreakers.indexOf(word[w]) > -1) {
19576 // the current letter is known to break lines
19577 output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19579 } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19580 // LA letters - advance an additional letter after this
19581 output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19583 while (word[w] !== nextLetter) {
19589 output += CharShaper_1.CharShaper(word[w], state);
19597 exports.WordShaper = WordShaper;
19600 var ParentLetter_1 = createCommonjsModule(function (module, exports) {
19602 Object.defineProperty(exports, "__esModule", {
19606 function ParentLetter(letter) {
19607 if (!isArabic_1.isArabic(letter)) {
19608 throw new Error('Not an Arabic letter');
19611 for (var w = 0; w < reference.letterList.length; w++) {
19612 // ok so we are checking this potential lettertron
19613 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19614 var versions = Object.keys(letterForms);
19616 for (var v = 0; v < versions.length; v++) {
19617 var localVersion = letterForms[versions[v]];
19619 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19620 // look at this embedded object
19621 var embeddedForms = Object.keys(localVersion);
19623 for (var ef = 0; ef < embeddedForms.length; ef++) {
19624 var form = localVersion[embeddedForms[ef]];
19626 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19628 return localVersion;
19631 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19633 return letterForms;
19641 exports.ParentLetter = ParentLetter;
19643 function GrandparentLetter(letter) {
19644 if (!isArabic_1.isArabic(letter)) {
19645 throw new Error('Not an Arabic letter');
19648 for (var w = 0; w < reference.letterList.length; w++) {
19649 // ok so we are checking this potential lettertron
19650 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19651 var versions = Object.keys(letterForms);
19653 for (var v = 0; v < versions.length; v++) {
19654 var localVersion = letterForms[versions[v]];
19656 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19657 // look at this embedded object
19658 var embeddedForms = Object.keys(localVersion);
19660 for (var ef = 0; ef < embeddedForms.length; ef++) {
19661 var form = localVersion[embeddedForms[ef]];
19663 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19665 return letterForms;
19668 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19670 return letterForms;
19678 exports.GrandparentLetter = GrandparentLetter;
19681 var lib = createCommonjsModule(function (module, exports) {
19683 Object.defineProperty(exports, "__esModule", {
19686 exports.isArabic = isArabic_1.isArabic;
19687 exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter;
19688 exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter;
19689 exports.Normal = Normalization.Normal;
19690 exports.CharShaper = CharShaper_1.CharShaper;
19691 exports.WordShaper = WordShaper_1.WordShaper;
19692 exports.ParentLetter = ParentLetter_1.ParentLetter;
19693 exports.GrandparentLetter = ParentLetter_1.GrandparentLetter;
19696 var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19697 function fixRTLTextForSvg(inputText) {
19700 var arabicRegex = /[\u0600-\u06FF]/g;
19701 var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19702 var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19703 var thaanaVowel = /[\u07A6-\u07B0]/;
19704 var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19706 if (arabicRegex.test(inputText)) {
19707 inputText = lib.WordShaper(inputText);
19710 for (var n = 0; n < inputText.length; n++) {
19711 var c = inputText[n];
19713 if (arabicMath.test(c)) {
19714 // Arabic numbers go LTR
19715 ret += rtlBuffer.reverse().join('');
19718 if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19719 ret += rtlBuffer.reverse().join('');
19723 if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19724 rtlBuffer[rtlBuffer.length - 1] += c;
19725 } else if (rtlRegex.test(c) // include Arabic presentation forms
19726 || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19728 } else if (c === ' ' && rtlBuffer.length) {
19729 // whitespace within RTL text
19730 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19732 // non-RTL character
19733 ret += rtlBuffer.reverse().join('') + c;
19739 ret += rtlBuffer.reverse().join('');
19743 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19745 // `Object.{ entries, values }` methods implementation
19746 var createMethod$5 = function (TO_ENTRIES) {
19747 return function (it) {
19748 var O = toIndexedObject(it);
19749 var keys = objectKeys(O);
19750 var length = keys.length;
19754 while (length > i) {
19756 if (!descriptors || propertyIsEnumerable.call(O, key)) {
19757 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
19764 var objectToArray = {
19765 // `Object.entries` method
19766 // https://tc39.es/ecma262/#sec-object.entries
19767 entries: createMethod$5(true),
19768 // `Object.values` method
19769 // https://tc39.es/ecma262/#sec-object.values
19770 values: createMethod$5(false)
19773 var $values = objectToArray.values;
19775 // `Object.values` method
19776 // https://tc39.es/ecma262/#sec-object.values
19777 _export({ target: 'Object', stat: true }, {
19778 values: function values(O) {
19783 // https://github.com/openstreetmap/iD/issues/772
19784 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19788 _storage = localStorage;
19789 } catch (e) {} // eslint-disable-line no-empty
19792 _storage = _storage || function () {
19795 getItem: function getItem(k) {
19798 setItem: function setItem(k, v) {
19801 removeItem: function removeItem(k) {
19802 return delete s[k];
19806 // corePreferences is an interface for persisting basic key-value strings
19807 // within and between iD sessions on the same site.
19811 function corePreferences(k, v) {
19813 if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
19815 /* eslint-disable no-console */
19816 if (typeof console !== 'undefined') {
19817 console.error('localStorage quota exceeded');
19819 /* eslint-enable no-console */
19824 function responseText(response) {
19825 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19826 return response.text();
19829 function d3_text (input, init) {
19830 return fetch(input, init).then(responseText);
19833 function responseJson(response) {
19834 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19835 if (response.status === 204 || response.status === 205) return;
19836 return response.json();
19839 function d3_json (input, init) {
19840 return fetch(input, init).then(responseJson);
19843 function parser(type) {
19844 return function (input, init) {
19845 return d3_text(input, init).then(function (text) {
19846 return new DOMParser().parseFromString(text, type);
19851 var d3_xml = parser("application/xml");
19852 var svg = parser("image/svg+xml");
19854 var _mainFileFetcher = coreFileFetcher(); // singleton
19855 // coreFileFetcher asynchronously fetches data from JSON files
19858 function coreFileFetcher() {
19860 var _inflight = {};
19862 'address_formats': 'data/address_formats.min.json',
19863 'deprecated': 'data/deprecated.min.json',
19864 'discarded': 'data/discarded.min.json',
19865 'imagery': 'data/imagery.min.json',
19866 'intro_graph': 'data/intro_graph.min.json',
19867 'keepRight': 'data/keepRight.min.json',
19868 'languages': 'data/languages.min.json',
19869 'locales': 'data/locales.min.json',
19870 'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json',
19871 'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json',
19872 'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json',
19873 'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json',
19874 'preset_categories': 'data/preset_categories.min.json',
19875 'preset_defaults': 'data/preset_defaults.min.json',
19876 'preset_fields': 'data/preset_fields.min.json',
19877 'preset_presets': 'data/preset_presets.min.json',
19878 'phone_formats': 'data/phone_formats.min.json',
19879 'qa_data': 'data/qa_data.min.json',
19880 'shortcuts': 'data/shortcuts.min.json',
19881 'territory_languages': 'data/territory_languages.min.json',
19882 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
19884 var _cachedData = {}; // expose the cache; useful for tests
19886 _this.cache = function () {
19887 return _cachedData;
19888 }; // Returns a Promise to fetch data
19889 // (resolved with the data if we have it already)
19892 _this.get = function (which) {
19893 if (_cachedData[which]) {
19894 return Promise.resolve(_cachedData[which]);
19897 var file = _fileMap[which];
19899 var url = file && _this.asset(file);
19902 return Promise.reject("Unknown data file for \"".concat(which, "\""));
19905 var prom = _inflight[url];
19908 _inflight[url] = prom = d3_json(url).then(function (result) {
19909 delete _inflight[url];
19912 throw new Error("No data loaded for \"".concat(which, "\""));
19915 _cachedData[which] = result;
19917 })["catch"](function (err) {
19918 delete _inflight[url];
19924 }; // Accessor for the file map
19927 _this.fileMap = function (val) {
19928 if (!arguments.length) return _fileMap;
19933 var _assetPath = '';
19935 _this.assetPath = function (val) {
19936 if (!arguments.length) return _assetPath;
19941 var _assetMap = {};
19943 _this.assetMap = function (val) {
19944 if (!arguments.length) return _assetMap;
19949 _this.asset = function (val) {
19950 if (/^http(s)?:\/\//i.test(val)) return val;
19951 var filename = _assetPath + val;
19952 return _assetMap[filename] || filename;
19958 var $findIndex$1 = arrayIteration.findIndex;
19961 var FIND_INDEX = 'findIndex';
19962 var SKIPS_HOLES$1 = true;
19964 // Shouldn't skip holes
19965 if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES$1 = false; });
19967 // `Array.prototype.findIndex` method
19968 // https://tc39.es/ecma262/#sec-array.prototype.findindex
19969 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 }, {
19970 findIndex: function findIndex(callbackfn /* , that = undefined */) {
19971 return $findIndex$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
19975 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
19976 addToUnscopables(FIND_INDEX);
19978 var $includes$1 = arrayIncludes.includes;
19981 // `Array.prototype.includes` method
19982 // https://tc39.es/ecma262/#sec-array.prototype.includes
19983 _export({ target: 'Array', proto: true }, {
19984 includes: function includes(el /* , fromIndex = 0 */) {
19985 return $includes$1(this, el, arguments.length > 1 ? arguments[1] : undefined);
19989 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
19990 addToUnscopables('includes');
19992 var notARegexp = function (it) {
19993 if (isRegexp(it)) {
19994 throw TypeError("The method doesn't accept regular expressions");
19998 var MATCH$2 = wellKnownSymbol('match');
20000 var correctIsRegexpLogic = function (METHOD_NAME) {
20003 '/./'[METHOD_NAME](regexp);
20006 regexp[MATCH$2] = false;
20007 return '/./'[METHOD_NAME](regexp);
20008 } catch (error2) { /* empty */ }
20012 // `String.prototype.includes` method
20013 // https://tc39.es/ecma262/#sec-string.prototype.includes
20014 _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
20015 includes: function includes(searchString /* , position = 0 */) {
20016 return !!~String(requireObjectCoercible(this))
20017 .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
20023 function utilDetect(refresh) {
20024 if (_detected && !refresh) return _detected;
20026 var ua = navigator.userAgent;
20030 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
20033 _detected.browser = m[1];
20034 _detected.version = m[2];
20037 if (!_detected.browser) {
20038 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
20041 _detected.browser = 'msie';
20042 _detected.version = m[1];
20046 if (!_detected.browser) {
20047 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
20050 _detected.browser = 'Opera';
20051 _detected.version = m[2];
20055 if (!_detected.browser) {
20056 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20059 _detected.browser = m[1];
20060 _detected.version = m[2];
20061 m = ua.match(/version\/([\.\d]+)/i);
20062 if (m !== null) _detected.version = m[1];
20066 if (!_detected.browser) {
20067 _detected.browser = navigator.appName;
20068 _detected.version = navigator.appVersion;
20069 } // keep major.minor version only..
20072 _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
20073 // Legacy Opera has incomplete svg style support. See #715
20075 _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
20077 if (_detected.browser.toLowerCase() === 'msie') {
20078 _detected.ie = true;
20079 _detected.browser = 'Internet Explorer';
20080 _detected.support = parseFloat(_detected.version) >= 11;
20082 _detected.ie = false;
20083 _detected.support = true;
20086 _detected.filedrop = window.FileReader && 'ondrop' in window;
20087 _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20088 _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20091 if (/Win/.test(ua)) {
20092 _detected.os = 'win';
20093 _detected.platform = 'Windows';
20094 } else if (/Mac/.test(ua)) {
20095 _detected.os = 'mac';
20096 _detected.platform = 'Macintosh';
20097 } else if (/X11/.test(ua) || /Linux/.test(ua)) {
20098 _detected.os = 'linux';
20099 _detected.platform = 'Linux';
20101 _detected.os = 'win';
20102 _detected.platform = 'Unknown';
20105 _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
20106 // so assume any "mac" with multitouch is actually iOS
20107 navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
20109 // An array of locales requested by the browser in priority order.
20111 _detected.browserLocales = Array.from(new Set( // remove duplicates
20112 [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
20113 navigator.userLanguage]) // remove any undefined values
20114 .filter(Boolean)));
20117 var loc = window.top.location;
20118 var origin = loc.origin;
20121 // for unpatched IE11
20122 origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
20125 _detected.host = origin + loc.pathname;
20129 // `Number.MAX_SAFE_INTEGER` constant
20130 // https://tc39.es/ecma262/#sec-number.max_safe_integer
20131 _export({ target: 'Number', stat: true }, {
20132 MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
20135 var getOwnPropertyNames$2 = objectGetOwnPropertyNames.f;
20136 var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
20137 var defineProperty$9 = objectDefineProperty.f;
20138 var trim$2 = stringTrim.trim;
20140 var NUMBER = 'Number';
20141 var NativeNumber = global_1[NUMBER];
20142 var NumberPrototype = NativeNumber.prototype;
20144 // Opera ~12 has broken Object#toString
20145 var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20147 // `ToNumber` abstract operation
20148 // https://tc39.es/ecma262/#sec-tonumber
20149 var toNumber = function (argument) {
20150 var it = toPrimitive(argument, false);
20151 var first, third, radix, maxCode, digits, length, index, code;
20152 if (typeof it == 'string' && it.length > 2) {
20154 first = it.charCodeAt(0);
20155 if (first === 43 || first === 45) {
20156 third = it.charCodeAt(2);
20157 if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20158 } else if (first === 48) {
20159 switch (it.charCodeAt(1)) {
20160 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20161 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20162 default: return +it;
20164 digits = it.slice(2);
20165 length = digits.length;
20166 for (index = 0; index < length; index++) {
20167 code = digits.charCodeAt(index);
20168 // parseInt parses a string to a first unavailable symbol
20169 // but ToNumber should return NaN if a string contains unavailable symbols
20170 if (code < 48 || code > maxCode) return NaN;
20171 } return parseInt(digits, radix);
20176 // `Number` constructor
20177 // https://tc39.es/ecma262/#sec-number-constructor
20178 if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20179 var NumberWrapper = function Number(value) {
20180 var it = arguments.length < 1 ? 0 : value;
20182 return dummy instanceof NumberWrapper
20183 // check on 1..constructor(foo) case
20184 && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20185 ? inheritIfRequired(new NativeNumber(toNumber(it)), dummy, NumberWrapper) : toNumber(it);
20187 for (var keys$3 = descriptors ? getOwnPropertyNames$2(NativeNumber) : (
20189 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20190 // ES2015 (in case, if modules with ES2015 Number statics required before):
20191 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20192 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,' +
20195 ).split(','), j$2 = 0, key$1; keys$3.length > j$2; j$2++) {
20196 if (has(NativeNumber, key$1 = keys$3[j$2]) && !has(NumberWrapper, key$1)) {
20197 defineProperty$9(NumberWrapper, key$1, getOwnPropertyDescriptor$3(NativeNumber, key$1));
20200 NumberWrapper.prototype = NumberPrototype;
20201 NumberPrototype.constructor = NumberWrapper;
20202 redefine(global_1, NUMBER, NumberWrapper);
20205 var aesJs = createCommonjsModule(function (module, exports) {
20206 /*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
20209 function checkInt(value) {
20210 return parseInt(value) === value;
20213 function checkInts(arrayish) {
20214 if (!checkInt(arrayish.length)) {
20218 for (var i = 0; i < arrayish.length; i++) {
20219 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
20227 function coerceArray(arg, copy) {
20228 // ArrayBuffer view
20229 if (arg.buffer && arg.name === 'Uint8Array') {
20234 arg = Array.prototype.slice.call(arg);
20239 } // It's an array; check it is a valid representation of a byte
20242 if (Array.isArray(arg)) {
20243 if (!checkInts(arg)) {
20244 throw new Error('Array contains invalid value: ' + arg);
20247 return new Uint8Array(arg);
20248 } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
20251 if (checkInt(arg.length) && checkInts(arg)) {
20252 return new Uint8Array(arg);
20255 throw new Error('unsupported array-like object');
20258 function createArray(length) {
20259 return new Uint8Array(length);
20262 function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
20263 if (sourceStart != null || sourceEnd != null) {
20264 if (sourceArray.slice) {
20265 sourceArray = sourceArray.slice(sourceStart, sourceEnd);
20267 sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
20271 targetArray.set(sourceArray, targetStart);
20274 var convertUtf8 = function () {
20275 function toBytes(text) {
20278 text = encodeURI(text);
20280 while (i < text.length) {
20281 var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
20284 result.push(parseInt(text.substr(i, 2), 16));
20285 i += 2; // otherwise, just the actual byte
20291 return coerceArray(result);
20294 function fromBytes(bytes) {
20298 while (i < bytes.length) {
20302 result.push(String.fromCharCode(c));
20304 } else if (c > 191 && c < 224) {
20305 result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
20308 result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
20313 return result.join('');
20318 fromBytes: fromBytes
20322 var convertHex = function () {
20323 function toBytes(text) {
20326 for (var i = 0; i < text.length; i += 2) {
20327 result.push(parseInt(text.substr(i, 2), 16));
20331 } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
20334 var Hex = '0123456789abcdef';
20336 function fromBytes(bytes) {
20339 for (var i = 0; i < bytes.length; i++) {
20341 result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
20344 return result.join('');
20349 fromBytes: fromBytes
20351 }(); // Number of rounds by keysize
20354 var numberOfRounds = {
20358 }; // Round constant words
20360 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)
20362 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];
20363 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
20365 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];
20366 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];
20367 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];
20368 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
20370 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];
20371 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];
20372 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];
20373 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
20375 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];
20376 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];
20377 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];
20378 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];
20380 function convertToInt32(bytes) {
20383 for (var i = 0; i < bytes.length; i += 4) {
20384 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
20390 var AES = function AES(key) {
20391 if (!(this instanceof AES)) {
20392 throw Error('AES must be instanitated with `new`');
20395 Object.defineProperty(this, 'key', {
20396 value: coerceArray(key, true)
20402 AES.prototype._prepare = function () {
20403 var rounds = numberOfRounds[this.key.length];
20405 if (rounds == null) {
20406 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
20407 } // encryption round keys
20410 this._Ke = []; // decryption round keys
20414 for (var i = 0; i <= rounds; i++) {
20415 this._Ke.push([0, 0, 0, 0]);
20417 this._Kd.push([0, 0, 0, 0]);
20420 var roundKeyCount = (rounds + 1) * 4;
20421 var KC = this.key.length / 4; // convert the key into ints
20423 var tk = convertToInt32(this.key); // copy values into round key arrays
20427 for (var i = 0; i < KC; i++) {
20429 this._Ke[index][i % 4] = tk[i];
20430 this._Kd[rounds - index][i % 4] = tk[i];
20431 } // key expansion (fips-197 section 5.2)
20434 var rconpointer = 0;
20438 while (t < roundKeyCount) {
20440 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
20441 rconpointer += 1; // key expansion (for non-256 bit)
20444 for (var i = 1; i < KC; i++) {
20445 tk[i] ^= tk[i - 1];
20446 } // key expansion for 256-bit keys is "slightly different" (fips-197)
20449 for (var i = 1; i < KC / 2; i++) {
20450 tk[i] ^= tk[i - 1];
20453 tt = tk[KC / 2 - 1];
20454 tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
20456 for (var i = KC / 2 + 1; i < KC; i++) {
20457 tk[i] ^= tk[i - 1];
20459 } // copy values into round key arrays
20466 while (i < KC && t < roundKeyCount) {
20469 this._Ke[r][c] = tk[i];
20470 this._Kd[rounds - r][c] = tk[i++];
20473 } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
20476 for (var r = 1; r < rounds; r++) {
20477 for (var c = 0; c < 4; c++) {
20478 tt = this._Kd[r][c];
20479 this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
20484 AES.prototype.encrypt = function (plaintext) {
20485 if (plaintext.length != 16) {
20486 throw new Error('invalid plaintext size (must be 16 bytes)');
20489 var rounds = this._Ke.length - 1;
20490 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20492 var t = convertToInt32(plaintext);
20494 for (var i = 0; i < 4; i++) {
20495 t[i] ^= this._Ke[0][i];
20496 } // apply round transforms
20499 for (var r = 1; r < rounds; r++) {
20500 for (var i = 0; i < 4; i++) {
20501 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];
20505 } // the last round is special
20508 var result = createArray(16),
20511 for (var i = 0; i < 4; i++) {
20512 tt = this._Ke[rounds][i];
20513 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20514 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20515 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20516 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
20522 AES.prototype.decrypt = function (ciphertext) {
20523 if (ciphertext.length != 16) {
20524 throw new Error('invalid ciphertext size (must be 16 bytes)');
20527 var rounds = this._Kd.length - 1;
20528 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20530 var t = convertToInt32(ciphertext);
20532 for (var i = 0; i < 4; i++) {
20533 t[i] ^= this._Kd[0][i];
20534 } // apply round transforms
20537 for (var r = 1; r < rounds; r++) {
20538 for (var i = 0; i < 4; i++) {
20539 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];
20543 } // the last round is special
20546 var result = createArray(16),
20549 for (var i = 0; i < 4; i++) {
20550 tt = this._Kd[rounds][i];
20551 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20552 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20553 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20554 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
20560 * Mode Of Operation - Electonic Codebook (ECB)
20564 var ModeOfOperationECB = function ModeOfOperationECB(key) {
20565 if (!(this instanceof ModeOfOperationECB)) {
20566 throw Error('AES must be instanitated with `new`');
20569 this.description = "Electronic Code Block";
20571 this._aes = new AES(key);
20574 ModeOfOperationECB.prototype.encrypt = function (plaintext) {
20575 plaintext = coerceArray(plaintext);
20577 if (plaintext.length % 16 !== 0) {
20578 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20581 var ciphertext = createArray(plaintext.length);
20582 var block = createArray(16);
20584 for (var i = 0; i < plaintext.length; i += 16) {
20585 copyArray(plaintext, block, 0, i, i + 16);
20586 block = this._aes.encrypt(block);
20587 copyArray(block, ciphertext, i);
20593 ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
20594 ciphertext = coerceArray(ciphertext);
20596 if (ciphertext.length % 16 !== 0) {
20597 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20600 var plaintext = createArray(ciphertext.length);
20601 var block = createArray(16);
20603 for (var i = 0; i < ciphertext.length; i += 16) {
20604 copyArray(ciphertext, block, 0, i, i + 16);
20605 block = this._aes.decrypt(block);
20606 copyArray(block, plaintext, i);
20612 * Mode Of Operation - Cipher Block Chaining (CBC)
20616 var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
20617 if (!(this instanceof ModeOfOperationCBC)) {
20618 throw Error('AES must be instanitated with `new`');
20621 this.description = "Cipher Block Chaining";
20625 iv = createArray(16);
20626 } else if (iv.length != 16) {
20627 throw new Error('invalid initialation vector size (must be 16 bytes)');
20630 this._lastCipherblock = coerceArray(iv, true);
20631 this._aes = new AES(key);
20634 ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
20635 plaintext = coerceArray(plaintext);
20637 if (plaintext.length % 16 !== 0) {
20638 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20641 var ciphertext = createArray(plaintext.length);
20642 var block = createArray(16);
20644 for (var i = 0; i < plaintext.length; i += 16) {
20645 copyArray(plaintext, block, 0, i, i + 16);
20647 for (var j = 0; j < 16; j++) {
20648 block[j] ^= this._lastCipherblock[j];
20651 this._lastCipherblock = this._aes.encrypt(block);
20652 copyArray(this._lastCipherblock, ciphertext, i);
20658 ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
20659 ciphertext = coerceArray(ciphertext);
20661 if (ciphertext.length % 16 !== 0) {
20662 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20665 var plaintext = createArray(ciphertext.length);
20666 var block = createArray(16);
20668 for (var i = 0; i < ciphertext.length; i += 16) {
20669 copyArray(ciphertext, block, 0, i, i + 16);
20670 block = this._aes.decrypt(block);
20672 for (var j = 0; j < 16; j++) {
20673 plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
20676 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
20682 * Mode Of Operation - Cipher Feedback (CFB)
20686 var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
20687 if (!(this instanceof ModeOfOperationCFB)) {
20688 throw Error('AES must be instanitated with `new`');
20691 this.description = "Cipher Feedback";
20695 iv = createArray(16);
20696 } else if (iv.length != 16) {
20697 throw new Error('invalid initialation vector size (must be 16 size)');
20700 if (!segmentSize) {
20704 this.segmentSize = segmentSize;
20705 this._shiftRegister = coerceArray(iv, true);
20706 this._aes = new AES(key);
20709 ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
20710 if (plaintext.length % this.segmentSize != 0) {
20711 throw new Error('invalid plaintext size (must be segmentSize bytes)');
20714 var encrypted = coerceArray(plaintext, true);
20717 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
20718 xorSegment = this._aes.encrypt(this._shiftRegister);
20720 for (var j = 0; j < this.segmentSize; j++) {
20721 encrypted[i + j] ^= xorSegment[j];
20722 } // Shift the register
20725 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20726 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20732 ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
20733 if (ciphertext.length % this.segmentSize != 0) {
20734 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
20737 var plaintext = coerceArray(ciphertext, true);
20740 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
20741 xorSegment = this._aes.encrypt(this._shiftRegister);
20743 for (var j = 0; j < this.segmentSize; j++) {
20744 plaintext[i + j] ^= xorSegment[j];
20745 } // Shift the register
20748 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20749 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20755 * Mode Of Operation - Output Feedback (OFB)
20759 var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
20760 if (!(this instanceof ModeOfOperationOFB)) {
20761 throw Error('AES must be instanitated with `new`');
20764 this.description = "Output Feedback";
20768 iv = createArray(16);
20769 } else if (iv.length != 16) {
20770 throw new Error('invalid initialation vector size (must be 16 bytes)');
20773 this._lastPrecipher = coerceArray(iv, true);
20774 this._lastPrecipherIndex = 16;
20775 this._aes = new AES(key);
20778 ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
20779 var encrypted = coerceArray(plaintext, true);
20781 for (var i = 0; i < encrypted.length; i++) {
20782 if (this._lastPrecipherIndex === 16) {
20783 this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
20784 this._lastPrecipherIndex = 0;
20787 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
20791 }; // Decryption is symetric
20794 ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
20796 * Counter object for CTR common mode of operation
20799 var Counter = function Counter(initialValue) {
20800 if (!(this instanceof Counter)) {
20801 throw Error('Counter must be instanitated with `new`');
20802 } // We allow 0, but anything false-ish uses the default 1
20805 if (initialValue !== 0 && !initialValue) {
20809 if (typeof initialValue === 'number') {
20810 this._counter = createArray(16);
20811 this.setValue(initialValue);
20813 this.setBytes(initialValue);
20817 Counter.prototype.setValue = function (value) {
20818 if (typeof value !== 'number' || parseInt(value) != value) {
20819 throw new Error('invalid counter value (must be an integer)');
20820 } // We cannot safely handle numbers beyond the safe range for integers
20823 if (value > Number.MAX_SAFE_INTEGER) {
20824 throw new Error('integer value out of safe range');
20827 for (var index = 15; index >= 0; --index) {
20828 this._counter[index] = value % 256;
20829 value = parseInt(value / 256);
20833 Counter.prototype.setBytes = function (bytes) {
20834 bytes = coerceArray(bytes, true);
20836 if (bytes.length != 16) {
20837 throw new Error('invalid counter bytes size (must be 16 bytes)');
20840 this._counter = bytes;
20843 Counter.prototype.increment = function () {
20844 for (var i = 15; i >= 0; i--) {
20845 if (this._counter[i] === 255) {
20846 this._counter[i] = 0;
20848 this._counter[i]++;
20854 * Mode Of Operation - Counter (CTR)
20858 var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
20859 if (!(this instanceof ModeOfOperationCTR)) {
20860 throw Error('AES must be instanitated with `new`');
20863 this.description = "Counter";
20866 if (!(counter instanceof Counter)) {
20867 counter = new Counter(counter);
20870 this._counter = counter;
20871 this._remainingCounter = null;
20872 this._remainingCounterIndex = 16;
20873 this._aes = new AES(key);
20876 ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
20877 var encrypted = coerceArray(plaintext, true);
20879 for (var i = 0; i < encrypted.length; i++) {
20880 if (this._remainingCounterIndex === 16) {
20881 this._remainingCounter = this._aes.encrypt(this._counter._counter);
20882 this._remainingCounterIndex = 0;
20884 this._counter.increment();
20887 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
20891 }; // Decryption is symetric
20894 ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
20896 // See:https://tools.ietf.org/html/rfc2315
20898 function pkcs7pad(data) {
20899 data = coerceArray(data, true);
20900 var padder = 16 - data.length % 16;
20901 var result = createArray(data.length + padder);
20902 copyArray(data, result);
20904 for (var i = data.length; i < result.length; i++) {
20905 result[i] = padder;
20911 function pkcs7strip(data) {
20912 data = coerceArray(data, true);
20914 if (data.length < 16) {
20915 throw new Error('PKCS#7 invalid length');
20918 var padder = data[data.length - 1];
20921 throw new Error('PKCS#7 padding byte out of range');
20924 var length = data.length - padder;
20926 for (var i = 0; i < padder; i++) {
20927 if (data[length + i] !== padder) {
20928 throw new Error('PKCS#7 invalid padding byte');
20932 var result = createArray(length);
20933 copyArray(data, result, 0, 0, length);
20935 } ///////////////////////
20937 // The block cipher
20944 ecb: ModeOfOperationECB,
20945 cbc: ModeOfOperationCBC,
20946 cfb: ModeOfOperationCFB,
20947 ofb: ModeOfOperationOFB,
20948 ctr: ModeOfOperationCTR
20961 coerceArray: coerceArray,
20962 createArray: createArray,
20963 copyArray: copyArray
20968 module.exports = aesjs; // RequireJS/AMD
20969 // http://www.requirejs.org/docs/api.html
20970 // https://github.com/amdjs/amdjs-api/wiki/AMD
20975 // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
20976 // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16));
20977 // This default signing key is built into iD and can be used to mask/unmask sensitive values.
20979 var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
20980 function utilAesEncrypt(text, key) {
20981 key = key || DEFAULT_128;
20982 var textBytes = aesJs.utils.utf8.toBytes(text);
20983 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20984 var encryptedBytes = aesCtr.encrypt(textBytes);
20985 var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
20986 return encryptedHex;
20988 function utilAesDecrypt(encryptedHex, key) {
20989 key = key || DEFAULT_128;
20990 var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
20991 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20992 var decryptedBytes = aesCtr.decrypt(encryptedBytes);
20993 var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
20997 function utilCleanTags(tags) {
21000 for (var k in tags) {
21004 if (v !== undefined) {
21005 out[k] = cleanValue(k, v);
21011 function cleanValue(k, v) {
21012 function keepSpaces(k) {
21013 return /_hours|_times|:conditional$/.test(k);
21017 return /^(description|note|fixme)$/.test(k);
21020 if (skip(k)) return v;
21021 var cleaned = v.split(';').map(function (s) {
21023 }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
21024 // It is only intended to prevent obvious copy-paste errors. (#2323)
21025 // clean website- and email-like tags
21027 if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
21028 cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
21035 // Like selection.property('value', ...), but avoids no-op value sets,
21036 // which can result in layout/repaint thrashing in some situations.
21037 function utilGetSetValue(selection, value) {
21038 function d3_selection_value(value) {
21039 function valueNull() {
21043 function valueConstant() {
21044 if (this.value !== value) {
21045 this.value = value;
21049 function valueFunction() {
21050 var x = value.apply(this, arguments);
21052 if (x === null || x === undefined) {
21054 } else if (this.value !== x) {
21059 return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
21062 if (arguments.length === 1) {
21063 return selection.property('value');
21066 return selection.each(d3_selection_value(value));
21069 function utilKeybinding(namespace) {
21070 var _keybindings = {};
21072 function testBindings(d3_event, isCapturing) {
21073 var didMatch = false;
21074 var bindings = Object.keys(_keybindings).map(function (id) {
21075 return _keybindings[id];
21077 var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
21078 // so we don't strictly match on the shift key, but we prioritize
21079 // shifted keybindings first, and fallback to unshifted only if no match.
21080 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
21081 // priority match shifted keybindings first
21083 for (i = 0; i < bindings.length; i++) {
21084 binding = bindings[i];
21085 if (!binding.event.modifiers.shiftKey) continue; // no shift
21087 if (!!binding.capture !== isCapturing) continue;
21089 if (matches(d3_event, binding, true)) {
21090 binding.callback(d3_event);
21091 didMatch = true; // match a max of one binding per event
21097 if (didMatch) return; // then unshifted keybindings
21099 for (i = 0; i < bindings.length; i++) {
21100 binding = bindings[i];
21101 if (binding.event.modifiers.shiftKey) continue; // shift
21103 if (!!binding.capture !== isCapturing) continue;
21105 if (matches(d3_event, binding, false)) {
21106 binding.callback(d3_event);
21111 function matches(d3_event, binding, testShift) {
21112 var event = d3_event;
21113 var isMatch = false;
21114 var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
21116 if (event.key !== undefined) {
21117 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
21121 if (binding.event.key === undefined) {
21123 } else if (Array.isArray(binding.event.key)) {
21124 if (binding.event.key.map(function (s) {
21125 return s.toLowerCase();
21126 }).indexOf(event.key.toLowerCase()) === -1) isMatch = false;
21128 if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) isMatch = false;
21130 } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
21131 // - browser doesn't support `KeyboardEvent.key`
21132 // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
21135 if (!isMatch && tryKeyCode) {
21136 isMatch = event.keyCode === binding.event.keyCode;
21139 if (!isMatch) return false; // test modifier keys
21141 if (!(event.ctrlKey && event.altKey)) {
21142 // if both are set, assume AltGr and skip it - #4096
21143 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
21144 if (event.altKey !== binding.event.modifiers.altKey) return false;
21147 if (event.metaKey !== binding.event.modifiers.metaKey) return false;
21148 if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
21153 function capture(d3_event) {
21154 testBindings(d3_event, true);
21157 function bubble(d3_event) {
21158 var tagName = select(d3_event.target).node().tagName;
21160 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
21164 testBindings(d3_event, false);
21167 function keybinding(selection) {
21168 selection = selection || select(document);
21169 selection.on('keydown.capture.' + namespace, capture, true);
21170 selection.on('keydown.bubble.' + namespace, bubble, false);
21172 } // was: keybinding.off()
21175 keybinding.unbind = function (selection) {
21177 selection = selection || select(document);
21178 selection.on('keydown.capture.' + namespace, null);
21179 selection.on('keydown.bubble.' + namespace, null);
21183 keybinding.clear = function () {
21186 }; // Remove one or more keycode bindings.
21189 keybinding.off = function (codes, capture) {
21190 var arr = utilArrayUniq([].concat(codes));
21192 for (var i = 0; i < arr.length; i++) {
21193 var id = arr[i] + (capture ? '-capture' : '-bubble');
21194 delete _keybindings[id];
21198 }; // Add one or more keycode bindings.
21201 keybinding.on = function (codes, callback, capture) {
21202 if (typeof callback !== 'function') {
21203 return keybinding.off(codes, capture);
21206 var arr = utilArrayUniq([].concat(codes));
21208 for (var i = 0; i < arr.length; i++) {
21209 var id = arr[i] + (capture ? '-capture' : '-bubble');
21213 callback: callback,
21228 if (_keybindings[id]) {
21229 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
21232 _keybindings[id] = binding;
21233 var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
21235 for (var j = 0; j < matches.length; j++) {
21236 // Normalise matching errors
21237 if (matches[j] === '++') matches[j] = '+';
21239 if (matches[j] in utilKeybinding.modifierCodes) {
21240 var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
21241 binding.event.modifiers[prop] = true;
21243 binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
21245 if (matches[j] in utilKeybinding.keyCodes) {
21246 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
21258 * See https://github.com/keithamus/jwerty
21261 utilKeybinding.modifierCodes = {
21265 // CTRL key, on Mac: ⌃
21268 // ALT key, on Mac: ⌥ (Alt)
21272 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
21279 utilKeybinding.modifierProperties = {
21285 utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
21286 utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
21287 utilKeybinding.keys = {
21288 // Backspace key, on Mac: ⌫ (Backspace)
21290 backspace: 'Backspace',
21291 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21304 'pause-break': 'Pause',
21305 // Caps Lock key, ⇪
21308 'caps-lock': 'CapsLock',
21309 // Escape key, on Mac: ⎋, on Windows: Esc
21310 '⎋': ['Escape', 'Esc'],
21311 escape: ['Escape', 'Esc'],
21312 esc: ['Escape', 'Esc'],
21314 space: [' ', 'Spacebar'],
21315 // Page-Up key, or pgup, on Mac: ↖
21318 'page-up': 'PageUp',
21319 // Page-Down key, or pgdown, on Mac: ↘
21321 pgdown: 'PageDown',
21322 'page-down': 'PageDown',
21323 // END key, on Mac: ⇟
21326 // HOME key, on Mac: ⇞
21329 // Insert key, or ins
21332 // Delete key, on Mac: ⌦ (Delete)
21333 '⌦': ['Delete', 'Del'],
21334 del: ['Delete', 'Del'],
21335 'delete': ['Delete', 'Del'],
21336 // Left Arrow Key, or ←
21337 '←': ['ArrowLeft', 'Left'],
21338 left: ['ArrowLeft', 'Left'],
21339 'arrow-left': ['ArrowLeft', 'Left'],
21340 // Up Arrow Key, or ↑
21341 '↑': ['ArrowUp', 'Up'],
21342 up: ['ArrowUp', 'Up'],
21343 'arrow-up': ['ArrowUp', 'Up'],
21344 // Right Arrow Key, or →
21345 '→': ['ArrowRight', 'Right'],
21346 right: ['ArrowRight', 'Right'],
21347 'arrow-right': ['ArrowRight', 'Right'],
21348 // Up Arrow Key, or ↓
21349 '↓': ['ArrowDown', 'Down'],
21350 down: ['ArrowDown', 'Down'],
21351 'arrow-down': ['ArrowDown', 'Down'],
21352 // odities, stuff for backward compatibility (browsers and code):
21353 // Num-Multiply, or *
21354 '*': ['*', 'Multiply'],
21355 star: ['*', 'Multiply'],
21356 asterisk: ['*', 'Multiply'],
21357 multiply: ['*', 'Multiply'],
21360 'plus': ['+', 'Add'],
21361 // Num-Subtract, or -
21362 '-': ['-', 'Subtract'],
21363 subtract: ['-', 'Subtract'],
21364 'dash': ['-', 'Subtract'],
21371 // Period, or ., or full-stop
21374 // Slash, or /, or forward-slash
21376 'forward-slash': '/',
21377 // Tick, or `, or back-quote
21380 // Open bracket, or [
21381 'open-bracket': '[',
21382 // Back slash, or \
21383 'back-slash': '\\',
21384 // Close backet, or ]
21385 'close-bracket': ']',
21386 // Apostrophe, or Quote, or '
21427 utilKeybinding.keyCodes = {
21428 // Backspace key, on Mac: ⌫ (Backspace)
21431 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21445 // Caps Lock key, ⇪
21449 // Escape key, on Mac: ⎋, on Windows: Esc
21455 // Page-Up key, or pgup, on Mac: ↖
21459 // Page-Down key, or pgdown, on Mac: ↘
21463 // END key, on Mac: ⇟
21466 // HOME key, on Mac: ⇞
21469 // Insert key, or ins
21472 // Delete key, on Mac: ⌦ (Delete)
21476 // Left Arrow Key, or ←
21480 // Up Arrow Key, or ↑
21484 // Right Arrow Key, or →
21488 // Up Arrow Key, or ↓
21492 // odities, printing characters that come out wrong:
21495 // Num-Multiply, or *
21503 // Num-Subtract, or -
21519 // Dash / Underscore key
21521 // Period, or ., or full-stop
21525 // Slash, or /, or forward-slash
21528 'forward-slash': 191,
21529 // Tick, or `, or back-quote
21533 // Open bracket, or [
21535 'open-bracket': 219,
21536 // Back slash, or \
21539 // Close backet, or ]
21541 'close-bracket': 221,
21542 // Apostrophe, or Quote, or '
21551 while (++i$1 < 106) {
21552 utilKeybinding.keyCodes['num-' + n] = i$1;
21560 while (++i$1 < 58) {
21561 utilKeybinding.keyCodes[n] = i$1;
21569 while (++i$1 < 136) {
21570 utilKeybinding.keyCodes['f' + n] = i$1;
21577 while (++i$1 < 91) {
21578 utilKeybinding.keyCodes[String.fromCharCode(i$1).toLowerCase()] = i$1;
21581 function utilObjectOmit(obj, omitKeys) {
21582 return Object.keys(obj).reduce(function (result, key) {
21583 if (omitKeys.indexOf(key) === -1) {
21584 result[key] = obj[key]; // keep
21591 // Copies a variable number of methods from source to target.
21592 function utilRebind(target, source) {
21594 n = arguments.length,
21598 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
21602 } // Method is assumed to be a standard D3 getter-setter:
21603 // If passed with no arguments, gets the value.
21604 // If passed with arguments, sets the value and returns the target.
21606 function d3_rebind(target, source, method) {
21607 return function () {
21608 var value = method.apply(source, arguments);
21609 return value === source ? target : value;
21613 // A per-domain session mutex backed by a cookie and dead man's
21614 // switch. If the session crashes, the mutex will auto-release
21615 // after 5 seconds.
21616 // This accepts a string and returns an object that complies with utilSessionMutexType
21617 function utilSessionMutex(name) {
21622 var expires = new Date();
21623 expires.setSeconds(expires.getSeconds() + 5);
21624 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
21627 mutex.lock = function () {
21628 if (intervalID) return true;
21629 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21630 if (cookie) return false;
21632 intervalID = window.setInterval(renew, 4000);
21636 mutex.unlock = function () {
21637 if (!intervalID) return;
21638 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
21639 clearInterval(intervalID);
21643 mutex.locked = function () {
21644 return !!intervalID;
21650 function utilTiler() {
21651 var _size = [256, 256];
21653 var _tileSize = 256;
21654 var _zoomExtent = [0, 20];
21655 var _translate = [_size[0] / 2, _size[1] / 2];
21657 var _skipNullIsland = false;
21659 function clamp(num, min, max) {
21660 return Math.max(min, Math.min(num, max));
21663 function nearNullIsland(tile) {
21669 var center = Math.pow(2, z - 1);
21670 var width = Math.pow(2, z - 6);
21671 var min = center - width / 2;
21672 var max = center + width / 2 - 1;
21673 return x >= min && x <= max && y >= min && y <= max;
21680 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
21681 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
21683 var tileMax = Math.pow(2, z0) - 1;
21684 var log2ts = Math.log(_tileSize) * Math.LOG2E;
21685 var k = Math.pow(2, z - z0 + log2ts);
21686 var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
21687 var cols = range(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
21688 var rows = range(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
21691 for (var i = 0; i < rows.length; i++) {
21694 for (var j = 0; j < cols.length; j++) {
21697 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
21698 tiles.unshift([x, y, z0]); // tiles in view at beginning
21700 tiles.push([x, y, z0]); // tiles in margin at the end
21705 tiles.translate = origin;
21710 * getTiles() returns an array of tiles that cover the map view
21714 tiler.getTiles = function (projection) {
21715 var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
21716 this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
21717 var tiles = tiler();
21718 var ts = tiles.scale;
21719 return tiles.map(function (tile) {
21720 if (_skipNullIsland && nearNullIsland(tile)) {
21724 var x = tile[0] * ts - origin[0];
21725 var y = tile[1] * ts - origin[1];
21727 id: tile.toString(),
21729 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
21731 }).filter(Boolean);
21734 * getGeoJSON() returns a FeatureCollection for debugging tiles
21738 tiler.getGeoJSON = function (projection) {
21739 var features = tiler.getTiles(projection).map(function (tile) {
21748 coordinates: [tile.extent.polygon()]
21753 type: 'FeatureCollection',
21758 tiler.tileSize = function (val) {
21759 if (!arguments.length) return _tileSize;
21764 tiler.zoomExtent = function (val) {
21765 if (!arguments.length) return _zoomExtent;
21770 tiler.size = function (val) {
21771 if (!arguments.length) return _size;
21776 tiler.scale = function (val) {
21777 if (!arguments.length) return _scale;
21782 tiler.translate = function (val) {
21783 if (!arguments.length) return _translate;
21786 }; // number to extend the rows/columns beyond those covering the viewport
21789 tiler.margin = function (val) {
21790 if (!arguments.length) return _margin;
21795 tiler.skipNullIsland = function (val) {
21796 if (!arguments.length) return _skipNullIsland;
21797 _skipNullIsland = val;
21804 function utilTriggerEvent(target, type) {
21805 target.each(function () {
21806 var evt = document.createEvent('HTMLEvents');
21807 evt.initEvent(type, true, true);
21808 this.dispatchEvent(evt);
21812 var _mainLocalizer = coreLocalizer(); // singleton
21815 var _t = _mainLocalizer.t;
21816 // coreLocalizer manages language and locale parameters including translated strings
21819 function coreLocalizer() {
21820 var localizer = {};
21821 var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
21822 // * `rtl` - right-to-left or left-to-right text direction
21823 // * `pct` - the percent of strings translated; 1 = 100%, full coverage
21826 // en: { rtl: false, pct: {…} },
21827 // de: { rtl: false, pct: {…} },
21831 var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
21833 // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21834 // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21838 var _localeStrings = {}; // the current locale
21840 var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
21842 var _localeCodes = ['en-US', 'en'];
21843 var _languageCode = 'en';
21844 var _textDirection = 'ltr';
21845 var _usesMetric = false;
21846 var _languageNames = {};
21847 var _scriptNames = {}; // getters for the current locale parameters
21849 localizer.localeCode = function () {
21850 return _localeCode;
21853 localizer.localeCodes = function () {
21854 return _localeCodes;
21857 localizer.languageCode = function () {
21858 return _languageCode;
21861 localizer.textDirection = function () {
21862 return _textDirection;
21865 localizer.usesMetric = function () {
21866 return _usesMetric;
21869 localizer.languageNames = function () {
21870 return _languageNames;
21873 localizer.scriptNames = function () {
21874 return _scriptNames;
21875 }; // The client app may want to manually set the locale, regardless of the
21876 // settings provided by the browser
21879 var _preferredLocaleCodes = [];
21881 localizer.preferredLocaleCodes = function (codes) {
21882 if (!arguments.length) return _preferredLocaleCodes;
21884 if (typeof codes === 'string') {
21885 // be generous and accept delimited strings as input
21886 _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
21888 _preferredLocaleCodes = codes;
21896 localizer.ensureLoaded = function () {
21897 if (_loadPromise) return _loadPromise;
21898 return _loadPromise = Promise.all([// load the list of languages
21899 _mainFileFetcher.get('languages'), // load the list of supported locales
21900 _mainFileFetcher.get('locales')]).then(function (results) {
21901 _dataLanguages = results[0];
21902 _dataLocales = results[1];
21903 }).then(function () {
21904 var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
21905 concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
21908 _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
21910 _localeCode = _localeCodes[0]; // Will always return the index for `en` if nothing else
21912 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
21913 return _dataLocales[locale].pct === 1;
21914 }); // We only need to load locales up until we find one with full coverage
21917 var loadStringsPromises = _localeCodes.slice(0, fullCoverageIndex + 1).map(function (code) {
21918 return localizer.loadLocale(code);
21921 return Promise.all(loadStringsPromises);
21922 }).then(function () {
21923 updateForCurrentLocale();
21924 })["catch"](function (err) {
21925 return console.error(err);
21926 }); // eslint-disable-line
21927 }; // Returns the locales from `requestedLocales` supported by iD that we should use
21930 function localesToUseFrom(requestedLocales) {
21931 var supportedLocales = _dataLocales;
21934 for (var i in requestedLocales) {
21935 var locale = requestedLocales[i];
21936 if (supportedLocales[locale]) toUse.push(locale);
21938 if (locale.includes('-')) {
21939 // Full locale ('es-ES'), add fallback to the base ('es')
21940 var langPart = locale.split('-')[0];
21941 if (supportedLocales[langPart]) toUse.push(langPart);
21943 } // remove duplicates
21946 return utilArrayUniq(toUse);
21949 function updateForCurrentLocale() {
21950 if (!_localeCode) return;
21951 _languageCode = _localeCode.split('-')[0];
21952 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
21953 var hash = utilStringQs(window.location.hash);
21955 if (hash.rtl === 'true') {
21956 _textDirection = 'rtl';
21957 } else if (hash.rtl === 'false') {
21958 _textDirection = 'ltr';
21960 _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
21963 var locale = _localeCode;
21964 if (locale.toLowerCase() === 'en-us') locale = 'en';
21965 _languageNames = _localeStrings[locale].languageNames;
21966 _scriptNames = _localeStrings[locale].scriptNames;
21967 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
21970 // Returns a Promise to load the strings for the requested locale
21973 localizer.loadLocale = function (requested) {
21974 if (!_dataLocales) {
21975 return Promise.reject('loadLocale called before init');
21978 var locale = requested; // US English is the default
21980 if (locale.toLowerCase() === 'en-us') locale = 'en';
21982 if (!_dataLocales[locale]) {
21983 return Promise.reject("Unsupported locale: ".concat(requested));
21986 if (_localeStrings[locale]) {
21988 return Promise.resolve(locale);
21991 var fileMap = _mainFileFetcher.fileMap();
21992 var key = "locale_".concat(locale);
21993 fileMap[key] = "locales/".concat(locale, ".json");
21994 return _mainFileFetcher.get(key).then(function (d) {
21995 _localeStrings[locale] = d[locale];
22000 localizer.pluralRule = function (number) {
22001 return pluralRule(number, _localeCode);
22002 }; // Returns the plural rule for the given `number` with the given `localeCode`.
22003 // One of: `zero`, `one`, `two`, `few`, `many`, `other`
22006 function pluralRule(number, localeCode) {
22007 // modern browsers have this functionality built-in
22008 var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
22011 return rules.select(number);
22012 } // fallback to basic one/other, as in English
22015 if (number === 1) return 'one';
22019 * Try to find that string in `locale` or the current `_localeCode` matching
22020 * the given `stringId`. If no string can be found in the requested locale,
22021 * we'll recurse down all the `_localeCodes` until one is found.
22023 * @param {string} stringId string identifier
22024 * @param {object?} replacements token replacements and default string
22025 * @param {string?} locale locale to use (defaults to currentLocale)
22026 * @return {string?} localized string
22030 localizer.tInfo = function (stringId, replacements, locale) {
22031 locale = locale || _localeCode;
22032 var path = stringId.split('.').map(function (s) {
22033 return s.replace(/<TX_DOT>/g, '.');
22035 var stringsKey = locale; // US English is the default
22037 if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
22038 var result = _localeStrings[stringsKey];
22040 while (result !== undefined && path.length) {
22041 result = result[path.pop()];
22044 if (result !== undefined) {
22045 if (replacements) {
22046 if (_typeof(result) === 'object' && Object.keys(result).length) {
22047 // If plural forms are provided, dig one level deeper based on the
22048 // first numeric token replacement provided.
22049 var number = Object.values(replacements).find(function (value) {
22050 return typeof value === 'number';
22053 if (number !== undefined) {
22054 var rule = pluralRule(number, locale);
22056 if (result[rule]) {
22057 result = result[rule];
22059 // We're pretty sure this should be a plural but no string
22060 // could be found for the given rule. Just pick the first
22061 // string and hope it makes sense.
22062 result = Object.values(result)[0];
22067 if (typeof result === 'string') {
22068 for (var key in replacements) {
22069 var value = replacements[key];
22071 if (typeof value === 'number' && value.toLocaleString) {
22072 // format numbers for the locale
22073 value = value.toLocaleString(locale, {
22076 minimumFractionDigits: 0
22080 var token = "{".concat(key, "}");
22081 var regex = new RegExp(token, 'g');
22082 result = result.replace(regex, value);
22087 if (typeof result === 'string') {
22088 // found a localized string!
22094 } // no localized string found...
22095 // attempt to fallback to a lower-priority language
22098 var index = _localeCodes.indexOf(locale);
22100 if (index >= 0 && index < _localeCodes.length - 1) {
22101 // eventually this will be 'en' or another locale with 100% coverage
22102 var fallback = _localeCodes[index + 1];
22103 return localizer.tInfo(stringId, replacements, fallback);
22106 if (replacements && 'default' in replacements) {
22107 // Fallback to a default value if one is specified in `replacements`
22109 text: replacements["default"],
22114 var missing = "Missing ".concat(locale, " translation: ").concat(stringId);
22115 if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
22121 }; // Returns only the localized text, discarding the locale info
22124 localizer.t = function (stringId, replacements, locale) {
22125 return localizer.tInfo(stringId, replacements, locale).text;
22126 }; // Returns the localized text wrapped in an HTML element encoding the locale info
22129 localizer.t.html = function (stringId, replacements, locale) {
22130 var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
22132 return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
22135 localizer.htmlForLocalizedText = function (text, localeCode) {
22136 return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
22139 localizer.languageName = function (code, options) {
22140 if (_languageNames[code]) {
22141 // name in locale language
22143 return _languageNames[code];
22144 } // sometimes we only want the local name
22147 if (options && options.localOnly) return null;
22148 var langInfo = _dataLanguages[code];
22151 if (langInfo.nativeName) {
22152 // name in native language
22153 // e.g. "Deutsch (de)"
22154 return localizer.t('translate.language_and_code', {
22155 language: langInfo.nativeName,
22158 } else if (langInfo.base && langInfo.script) {
22159 var base = langInfo.base; // the code of the language this is based on
22161 if (_languageNames[base]) {
22162 // base language name in locale language
22163 var scriptCode = langInfo.script;
22164 var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
22166 return localizer.t('translate.language_and_code', {
22167 language: _languageNames[base],
22170 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
22171 // e.g. "српски (sr-Cyrl)"
22172 return localizer.t('translate.language_and_code', {
22173 language: _dataLanguages[base].nativeName,
22180 return code; // if not found, use the code
22186 // `presetCollection` is a wrapper around an `Array` of presets `collection`,
22187 // and decorated with some extra methods for searching and matching geometry
22190 function presetCollection(collection) {
22191 var MAXRESULTS = 50;
22194 _this.collection = collection;
22196 _this.item = function (id) {
22197 if (_memo[id]) return _memo[id];
22199 var found = _this.collection.find(function (d) {
22200 return d.id === id;
22203 if (found) _memo[id] = found;
22207 _this.index = function (id) {
22208 return _this.collection.findIndex(function (d) {
22209 return d.id === id;
22213 _this.matchGeometry = function (geometry) {
22214 return presetCollection(_this.collection.filter(function (d) {
22215 return d.matchGeometry(geometry);
22219 _this.matchAllGeometry = function (geometries) {
22220 return presetCollection(_this.collection.filter(function (d) {
22221 return d && d.matchAllGeometry(geometries);
22225 _this.matchAnyGeometry = function (geometries) {
22226 return presetCollection(_this.collection.filter(function (d) {
22227 return geometries.some(function (geom) {
22228 return d.matchGeometry(geom);
22233 _this.fallback = function (geometry) {
22235 if (id === 'vertex') id = 'point';
22236 return _this.item(id);
22239 _this.search = function (value, geometry, countryCode) {
22240 if (!value) return _this;
22241 value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
22243 function leading(a) {
22244 var index = a.indexOf(value);
22245 return index === 0 || a[index - 1] === ' ';
22246 } // match at name beginning only
22249 function leadingStrict(a) {
22250 var index = a.indexOf(value);
22251 return index === 0;
22254 function sortNames(a, b) {
22255 var aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
22256 var bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase(); // priority if search string matches preset name exactly - #4325
22258 if (value === aCompare) return -1;
22259 if (value === bCompare) return 1; // priority for higher matchScore
22261 var i = b.originalScore - a.originalScore;
22262 if (i !== 0) return i; // priority if search string appears earlier in preset name
22264 i = aCompare.indexOf(value) - bCompare.indexOf(value);
22265 if (i !== 0) return i; // priority for shorter preset names
22267 return aCompare.length - bCompare.length;
22270 var pool = _this.collection;
22273 pool = pool.filter(function (a) {
22274 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false;
22275 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false;
22280 var searchable = pool.filter(function (a) {
22281 return a.searchable !== false && a.suggestion !== true;
22283 var suggestions = pool.filter(function (a) {
22284 return a.suggestion === true;
22285 }); // matches value to preset.name
22287 var leading_name = searchable.filter(function (a) {
22288 return leading(a.name().toLowerCase());
22289 }).sort(sortNames); // matches value to preset suggestion name (original name is unhyphenated)
22291 var leading_suggestions = suggestions.filter(function (a) {
22292 return leadingStrict(a.originalName.toLowerCase());
22293 }).sort(sortNames); // matches value to preset.terms values
22295 var leading_terms = searchable.filter(function (a) {
22296 return (a.terms() || []).some(leading);
22297 }); // matches value to preset.tags values
22299 var leading_tag_values = searchable.filter(function (a) {
22300 return Object.values(a.tags || {}).filter(function (val) {
22301 return val !== '*';
22303 }); // finds close matches to value in preset.name
22305 var similar_name = searchable.map(function (a) {
22308 dist: utilEditDistance(value, a.name())
22310 }).filter(function (a) {
22311 return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
22312 }).sort(function (a, b) {
22313 return a.dist - b.dist;
22314 }).map(function (a) {
22316 }); // finds close matches to value to preset suggestion name (original name is unhyphenated)
22318 var similar_suggestions = suggestions.map(function (a) {
22321 dist: utilEditDistance(value, a.originalName.toLowerCase())
22323 }).filter(function (a) {
22324 return a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1;
22325 }).sort(function (a, b) {
22326 return a.dist - b.dist;
22327 }).map(function (a) {
22329 }); // finds close matches to value in preset.terms
22331 var similar_terms = searchable.filter(function (a) {
22332 return (a.terms() || []).some(function (b) {
22333 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
22336 var results = leading_name.concat(leading_suggestions, leading_terms, leading_tag_values, similar_name, similar_suggestions, similar_terms).slice(0, MAXRESULTS - 1);
22339 if (typeof geometry === 'string') {
22340 results.push(_this.fallback(geometry));
22342 geometry.forEach(function (geom) {
22343 return results.push(_this.fallback(geom));
22348 return presetCollection(utilArrayUniq(results));
22354 // `presetCategory` builds a `presetCollection` of member presets,
22355 // decorated with some extra methods for searching and matching geometry
22358 function presetCategory(categoryID, category, all) {
22359 var _this = Object.assign({}, category); // shallow copy
22362 _this.id = categoryID;
22363 _this.members = presetCollection(category.members.map(function (presetID) {
22364 return all.item(presetID);
22365 }).filter(Boolean));
22366 _this.geometry = _this.members.collection.reduce(function (acc, preset) {
22367 for (var i in preset.geometry) {
22368 var geometry = preset.geometry[i];
22370 if (acc.indexOf(geometry) === -1) {
22371 acc.push(geometry);
22378 _this.matchGeometry = function (geom) {
22379 return _this.geometry.indexOf(geom) >= 0;
22382 _this.matchAllGeometry = function (geometries) {
22383 return _this.members.collection.some(function (preset) {
22384 return preset.matchAllGeometry(geometries);
22388 _this.matchScore = function () {
22392 _this.name = function () {
22393 return _t("presets.categories.".concat(categoryID, ".name"), {
22394 'default': categoryID
22398 _this.nameLabel = function () {
22399 return _t.html("presets.categories.".concat(categoryID, ".name"), {
22400 'default': categoryID
22404 _this.terms = function () {
22411 // `presetField` decorates a given `field` Object
22412 // with some extra methods for searching and matching geometry
22415 function presetField(fieldID, field) {
22416 var _this = Object.assign({}, field); // shallow copy
22419 _this.id = fieldID; // for use in classes, element ids, css selectors
22421 _this.safeid = utilSafeClassName(fieldID);
22423 _this.matchGeometry = function (geom) {
22424 return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
22427 _this.matchAllGeometry = function (geometries) {
22428 return !_this.geometry || geometries.every(function (geom) {
22429 return _this.geometry.indexOf(geom) !== -1;
22433 _this.t = function (scope, options) {
22434 return _t("presets.fields.".concat(fieldID, ".").concat(scope), options);
22437 _this.t.html = function (scope, options) {
22438 return _t.html("presets.fields.".concat(fieldID, ".").concat(scope), options);
22441 _this.title = function () {
22442 return _this.overrideLabel || _this.t('label', {
22447 _this.label = function () {
22448 return _this.overrideLabel || _this.t.html('label', {
22453 var _placeholder = _this.placeholder;
22455 _this.placeholder = function () {
22456 return _this.t('placeholder', {
22457 'default': _placeholder
22461 _this.originalTerms = (_this.terms || []).join();
22463 _this.terms = function () {
22464 return _this.t('terms', {
22465 'default': _this.originalTerms
22466 }).toLowerCase().trim().split(/\s*,+\s*/);
22469 _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
22473 // `Array.prototype.lastIndexOf` method
22474 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
22475 _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
22476 lastIndexOf: arrayLastIndexOf
22479 // `presetPreset` decorates a given `preset` Object
22480 // with some extra methods for searching and matching geometry
22483 function presetPreset(presetID, preset, addable, allFields, allPresets) {
22484 allFields = allFields || {};
22485 allPresets = allPresets || {};
22487 var _this = Object.assign({}, preset); // shallow copy
22490 var _addable = addable || false;
22492 var _resolvedFields; // cache
22495 var _resolvedMoreFields; // cache
22498 _this.id = presetID;
22499 _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
22501 _this.originalTerms = (_this.terms || []).join();
22502 _this.originalName = _this.name || '';
22503 _this.originalScore = _this.matchScore || 1;
22504 _this.originalReference = _this.reference || {};
22505 _this.originalFields = _this.fields || [];
22506 _this.originalMoreFields = _this.moreFields || [];
22508 _this.fields = function () {
22509 return _resolvedFields || (_resolvedFields = resolve('fields'));
22512 _this.moreFields = function () {
22513 return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
22516 _this.resetFields = function () {
22517 return _resolvedFields = _resolvedMoreFields = null;
22520 _this.tags = _this.tags || {};
22521 _this.addTags = _this.addTags || _this.tags;
22522 _this.removeTags = _this.removeTags || _this.addTags;
22523 _this.geometry = _this.geometry || [];
22525 _this.matchGeometry = function (geom) {
22526 return _this.geometry.indexOf(geom) >= 0;
22529 _this.matchAllGeometry = function (geoms) {
22530 return geoms.every(_this.matchGeometry);
22533 _this.matchScore = function (entityTags) {
22534 var tags = _this.tags;
22536 var score = 0; // match on tags
22538 for (var k in tags) {
22541 if (entityTags[k] === tags[k]) {
22542 score += _this.originalScore;
22543 } else if (tags[k] === '*' && k in entityTags) {
22544 score += _this.originalScore / 2;
22548 } // boost score for additional matches in addTags - #6802
22551 var addTags = _this.addTags;
22553 for (var _k in addTags) {
22554 if (!seen[_k] && entityTags[_k] === addTags[_k]) {
22555 score += _this.originalScore;
22562 _this.t = function (scope, options) {
22563 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22564 return _t(textID, options);
22567 _this.t.html = function (scope, options) {
22568 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22569 return _t.html(textID, options);
22572 _this.name = function () {
22573 return _this.t('name', {
22574 'default': _this.originalName
22578 _this.nameLabel = function () {
22579 return _this.t.html('name', {
22580 'default': _this.originalName
22584 _this.subtitle = function () {
22585 if (_this.suggestion) {
22586 var path = presetID.split('/');
22587 path.pop(); // remove brand name
22589 return _t('presets.presets.' + path.join('/') + '.name');
22595 _this.subtitleLabel = function () {
22596 if (_this.suggestion) {
22597 var path = presetID.split('/');
22598 path.pop(); // remove brand name
22600 return _t.html('presets.presets.' + path.join('/') + '.name');
22606 _this.terms = function () {
22607 return _this.t('terms', {
22608 'default': _this.originalTerms
22609 }).toLowerCase().trim().split(/\s*,+\s*/);
22612 _this.isFallback = function () {
22613 var tagCount = Object.keys(_this.tags).length;
22614 return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
22617 _this.addable = function (val) {
22618 if (!arguments.length) return _addable;
22623 _this.reference = function () {
22624 // Lookup documentation on Wikidata...
22625 var qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
22631 } // Lookup documentation on OSM Wikibase...
22634 var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
22635 var value = _this.originalReference.value || _this.tags[key];
22637 if (value === '*') {
22649 _this.unsetTags = function (tags, geometry, skipFieldDefaults) {
22650 tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
22652 if (geometry && !skipFieldDefaults) {
22653 _this.fields().forEach(function (field) {
22654 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
22655 delete tags[field.key];
22664 _this.setTags = function (tags, geometry, skipFieldDefaults) {
22665 var addTags = _this.addTags;
22666 tags = Object.assign({}, tags); // shallow copy
22668 for (var k in addTags) {
22669 if (addTags[k] === '*') {
22672 tags[k] = addTags[k];
22674 } // Add area=yes if necessary.
22675 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
22676 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
22677 // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
22680 if (!addTags.hasOwnProperty('area')) {
22683 if (geometry === 'area') {
22684 var needsAreaTag = true;
22686 if (_this.geometry.indexOf('line') === -1) {
22687 for (var _k2 in addTags) {
22688 if (_k2 in osmAreaKeys) {
22689 needsAreaTag = false;
22695 if (needsAreaTag) {
22701 if (geometry && !skipFieldDefaults) {
22702 _this.fields().forEach(function (field) {
22703 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
22704 tags[field.key] = field["default"];
22710 }; // For a preset without fields, use the fields of the parent preset.
22711 // Replace {preset} placeholders with the fields of the specified presets.
22714 function resolve(which) {
22715 var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
22717 fieldIDs.forEach(function (fieldID) {
22718 var match = fieldID.match(/\{(.*)\}/);
22720 if (match !== null) {
22721 // a presetID wrapped in braces {}
22722 resolved = resolved.concat(inheritFields(match[1], which));
22723 } else if (allFields[fieldID]) {
22724 // a normal fieldID
22725 resolved.push(allFields[fieldID]);
22727 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
22729 }); // no fields resolved, so use the parent's if possible
22731 if (!resolved.length) {
22732 var endIndex = _this.id.lastIndexOf('/');
22734 var parentID = endIndex && _this.id.substring(0, endIndex);
22737 resolved = inheritFields(parentID, which);
22741 return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
22743 function inheritFields(presetID, which) {
22744 var parent = allPresets[presetID];
22745 if (!parent) return [];
22747 if (which === 'fields') {
22748 return parent.fields().filter(shouldInherit);
22749 } else if (which === 'moreFields') {
22750 return parent.moreFields();
22754 } // Skip `fields` for the keys which define the preset.
22755 // These are usually `typeCombo` fields like `shop=*`
22758 function shouldInherit(f) {
22759 if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
22760 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
22768 var _mainPresetIndex = presetIndex(); // singleton
22769 // `presetIndex` wraps a `presetCollection`
22770 // with methods for loading new data and returning defaults
22773 function presetIndex() {
22774 var dispatch$1 = dispatch('favoritePreset', 'recentsChange');
22775 var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
22777 var POINT = presetPreset('point', {
22780 geometry: ['point', 'vertex'],
22783 var LINE = presetPreset('line', {
22786 geometry: ['line'],
22789 var AREA = presetPreset('area', {
22794 geometry: ['area'],
22797 var RELATION = presetPreset('relation', {
22800 geometry: ['relation'],
22804 var _this = presetCollection([POINT, LINE, AREA, RELATION]);
22813 point: presetCollection([POINT]),
22814 vertex: presetCollection([POINT]),
22815 line: presetCollection([LINE]),
22816 area: presetCollection([AREA]),
22817 relation: presetCollection([RELATION])
22820 var _categories = {};
22821 var _universal = [];
22822 var _addablePresetIDs = null; // Set of preset IDs that the user can add
22826 var _favorites; // Index of presets by (geometry, tag key).
22829 var _geometryIndex = {
22839 _this.ensureLoaded = function () {
22840 if (_loadPromise) return _loadPromise;
22841 return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
22843 categories: vals[0],
22849 osmSetAreaKeys(_this.areaKeys());
22850 osmSetPointTags(_this.pointTags());
22851 osmSetVertexTags(_this.vertexTags());
22855 _this.merge = function (d) {
22858 Object.keys(d.fields).forEach(function (fieldID) {
22859 var f = d.fields[fieldID];
22863 _fields[fieldID] = presetField(fieldID, f);
22866 delete _fields[fieldID];
22873 Object.keys(d.presets).forEach(function (presetID) {
22874 var p = d.presets[presetID];
22878 var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
22880 _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
22882 // remove (but not if it's a fallback)
22883 var existing = _presets[presetID];
22885 if (existing && !existing.isFallback()) {
22886 delete _presets[presetID];
22890 } // Need to rebuild _this.collection before loading categories
22893 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Categories
22895 if (d.categories) {
22896 Object.keys(d.categories).forEach(function (categoryID) {
22897 var c = d.categories[categoryID];
22901 _categories[categoryID] = presetCategory(categoryID, c, _this);
22904 delete _categories[categoryID];
22907 } // Rebuild _this.collection after loading categories
22910 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
22913 Object.keys(d.defaults).forEach(function (geometry) {
22914 var def = d.defaults[geometry];
22916 if (Array.isArray(def)) {
22918 _defaults[geometry] = presetCollection(def.map(function (id) {
22919 return _presets[id] || _categories[id];
22920 }).filter(Boolean));
22923 delete _defaults[geometry];
22926 } // Rebuild universal fields array
22929 _universal = Object.values(_fields).filter(function (field) {
22930 return field.universal;
22931 }); // Reset all the preset fields - they'll need to be resolved again
22933 Object.values(_presets).forEach(function (preset) {
22934 return preset.resetFields();
22935 }); // Rebuild geometry index
22945 _this.collection.forEach(function (preset) {
22946 (preset.geometry || []).forEach(function (geometry) {
22947 var g = _geometryIndex[geometry];
22949 for (var key in preset.tags) {
22950 (g[key] = g[key] || []).push(preset);
22958 _this.match = function (entity, resolver) {
22959 return resolver["transient"](entity, 'presetMatch', function () {
22960 var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
22962 if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
22963 geometry = 'point';
22966 return _this.matchTags(entity.tags, geometry);
22970 _this.matchTags = function (tags, geometry) {
22971 var geometryMatches = _geometryIndex[geometry];
22976 for (var k in tags) {
22977 // If any part of an address is present, allow fallback to "Address" preset - #4353
22978 if (/^addr:/.test(k) && geometryMatches['addr:*']) {
22979 address = geometryMatches['addr:*'][0];
22982 var keyMatches = geometryMatches[k];
22983 if (!keyMatches) continue;
22985 for (var i = 0; i < keyMatches.length; i++) {
22986 var score = keyMatches[i].matchScore(tags);
22988 if (score > best) {
22990 match = keyMatches[i];
22995 if (address && (!match || match.isFallback())) {
22999 return match || _this.fallback(geometry);
23002 _this.allowsVertex = function (entity, resolver) {
23003 if (entity.type !== 'node') return false;
23004 if (Object.keys(entity.tags).length === 0) return true;
23005 return resolver["transient"](entity, 'vertexMatch', function () {
23006 // address lines allow vertices to act as standalone points
23007 if (entity.isOnAddressLine(resolver)) return true;
23008 var geometries = osmNodeGeometriesForTags(entity.tags);
23009 if (geometries.vertex) return true;
23010 if (geometries.point) return false; // allow vertices for unspecified points
23014 }; // Because of the open nature of tagging, iD will never have a complete
23015 // list of tags used in OSM, so we want it to have logic like "assume
23016 // that a closed way with an amenity tag is an area, unless the amenity
23017 // is one of these specific types". This function computes a structure
23018 // that allows testing of such conditions, based on the presets designated
23019 // as as supporting (or not supporting) the area geometry.
23021 // The returned object L is a keeplist/discardlist of tags. A closed way
23022 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
23023 // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
23024 // and the subkeys form the discardlist.
23027 _this.areaKeys = function () {
23028 // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
23029 var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
23030 var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
23032 var presets = _this.collection.filter(function (p) {
23033 return !p.suggestion && !p.replacement;
23037 presets.forEach(function (p) {
23038 var keys = p.tags && Object.keys(p.tags);
23039 var key = keys && keys.length && keys[0]; // pick the first tag
23042 if (ignore.indexOf(key) !== -1) return;
23044 if (p.geometry.indexOf('area') !== -1) {
23045 // probably an area..
23046 areaKeys[key] = areaKeys[key] || {};
23050 presets.forEach(function (p) {
23053 for (key in p.addTags) {
23054 // examine all addTags to get a better sense of what can be tagged on lines - #6800
23055 var value = p.addTags[key];
23057 if (key in areaKeys && // probably an area...
23058 p.geometry.indexOf('line') !== -1 && // but sometimes a line
23060 areaKeys[key][value] = true;
23067 _this.pointTags = function () {
23068 return _this.collection.reduce(function (pointTags, d) {
23069 // ignore name-suggestion-index, deprecated, and generic presets
23070 if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
23072 var keys = d.tags && Object.keys(d.tags);
23073 var key = keys && keys.length && keys[0]; // pick the first tag
23075 if (!key) return pointTags; // if this can be a point
23077 if (d.geometry.indexOf('point') !== -1) {
23078 pointTags[key] = pointTags[key] || {};
23079 pointTags[key][d.tags[key]] = true;
23086 _this.vertexTags = function () {
23087 return _this.collection.reduce(function (vertexTags, d) {
23088 // ignore name-suggestion-index, deprecated, and generic presets
23089 if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
23091 var keys = d.tags && Object.keys(d.tags);
23092 var key = keys && keys.length && keys[0]; // pick the first tag
23094 if (!key) return vertexTags; // if this can be a vertex
23096 if (d.geometry.indexOf('vertex') !== -1) {
23097 vertexTags[key] = vertexTags[key] || {};
23098 vertexTags[key][d.tags[key]] = true;
23105 _this.field = function (id) {
23106 return _fields[id];
23109 _this.universal = function () {
23113 _this.defaults = function (geometry, n, startWithRecents) {
23116 if (startWithRecents) {
23117 recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
23122 if (_addablePresetIDs) {
23123 defaults = Array.from(_addablePresetIDs).map(function (id) {
23124 var preset = _this.item(id);
23126 if (preset && preset.matchGeometry(geometry)) return preset;
23128 }).filter(Boolean);
23130 defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
23133 return presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
23134 }; // pass a Set of addable preset ids
23137 _this.addablePresetIDs = function (val) {
23138 if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
23140 if (Array.isArray(val)) val = new Set(val);
23141 _addablePresetIDs = val;
23143 if (_addablePresetIDs) {
23144 // reset all presets
23145 _this.collection.forEach(function (p) {
23146 // categories aren't addable
23147 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
23150 _this.collection.forEach(function (p) {
23151 if (p.addable) p.addable(true);
23158 _this.recent = function () {
23159 return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
23164 function RibbonItem(preset, source) {
23166 item.preset = preset;
23167 item.source = source;
23169 item.isFavorite = function () {
23170 return item.source === 'favorite';
23173 item.isRecent = function () {
23174 return item.source === 'recent';
23177 item.matches = function (preset) {
23178 return item.preset.id === preset.id;
23181 item.minified = function () {
23183 pID: item.preset.id
23190 function ribbonItemForMinified(d, source) {
23192 var preset = _this.item(d.pID);
23194 if (!preset) return null;
23195 return RibbonItem(preset, source);
23201 _this.getGenericRibbonItems = function () {
23202 return ['point', 'line', 'area'].map(function (id) {
23203 return RibbonItem(_this.item(id), 'generic');
23207 _this.getAddable = function () {
23208 if (!_addablePresetIDs) return [];
23209 return _addablePresetIDs.map(function (id) {
23210 var preset = _this.item(id);
23212 if (preset) return RibbonItem(preset, 'addable');
23214 }).filter(Boolean);
23217 function setRecents(items) {
23219 var minifiedItems = items.map(function (d) {
23220 return d.minified();
23222 corePreferences('preset_recents', JSON.stringify(minifiedItems));
23223 dispatch$1.call('recentsChange');
23226 _this.getRecents = function () {
23228 // fetch from local storage
23229 _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
23230 var item = ribbonItemForMinified(d, 'recent');
23231 if (item && item.preset.addable()) acc.push(item);
23239 _this.addRecent = function (preset, besidePreset, after) {
23240 var recents = _this.getRecents();
23242 var beforeItem = _this.recentMatching(besidePreset);
23244 var toIndex = recents.indexOf(beforeItem);
23245 if (after) toIndex += 1;
23246 var newItem = RibbonItem(preset, 'recent');
23247 recents.splice(toIndex, 0, newItem);
23248 setRecents(recents);
23251 _this.removeRecent = function (preset) {
23252 var item = _this.recentMatching(preset);
23255 var items = _this.getRecents();
23257 items.splice(items.indexOf(item), 1);
23262 _this.recentMatching = function (preset) {
23263 var items = _this.getRecents();
23265 for (var i in items) {
23266 if (items[i].matches(preset)) {
23274 _this.moveItem = function (items, fromIndex, toIndex) {
23275 if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
23276 items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
23280 _this.moveRecent = function (item, beforeItem) {
23281 var recents = _this.getRecents();
23283 var fromIndex = recents.indexOf(item);
23284 var toIndex = recents.indexOf(beforeItem);
23286 var items = _this.moveItem(recents, fromIndex, toIndex);
23288 if (items) setRecents(items);
23291 _this.setMostRecent = function (preset) {
23292 if (preset.searchable === false) return;
23294 var items = _this.getRecents();
23296 var item = _this.recentMatching(preset);
23299 items.splice(items.indexOf(item), 1);
23301 item = RibbonItem(preset, 'recent');
23302 } // remove the last recent (first in, first out)
23305 while (items.length >= MAXRECENTS) {
23310 items.unshift(item);
23314 function setFavorites(items) {
23315 _favorites = items;
23316 var minifiedItems = items.map(function (d) {
23317 return d.minified();
23319 corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
23321 dispatch$1.call('favoritePreset');
23324 _this.addFavorite = function (preset, besidePreset, after) {
23325 var favorites = _this.getFavorites();
23327 var beforeItem = _this.favoriteMatching(besidePreset);
23329 var toIndex = favorites.indexOf(beforeItem);
23330 if (after) toIndex += 1;
23331 var newItem = RibbonItem(preset, 'favorite');
23332 favorites.splice(toIndex, 0, newItem);
23333 setFavorites(favorites);
23336 _this.toggleFavorite = function (preset) {
23337 var favs = _this.getFavorites();
23339 var favorite = _this.favoriteMatching(preset);
23342 favs.splice(favs.indexOf(favorite), 1);
23344 // only allow 10 favorites
23345 if (favs.length === 10) {
23346 // remove the last favorite (last in, first out)
23351 favs.push(RibbonItem(preset, 'favorite'));
23354 setFavorites(favs);
23357 _this.removeFavorite = function (preset) {
23358 var item = _this.favoriteMatching(preset);
23361 var items = _this.getFavorites();
23363 items.splice(items.indexOf(item), 1);
23364 setFavorites(items);
23368 _this.getFavorites = function () {
23370 // fetch from local storage
23371 var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
23373 if (!rawFavorites) {
23375 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
23378 _favorites = rawFavorites.reduce(function (output, d) {
23379 var item = ribbonItemForMinified(d, 'favorite');
23380 if (item && item.preset.addable()) output.push(item);
23388 _this.favoriteMatching = function (preset) {
23389 var favs = _this.getFavorites();
23391 for (var index in favs) {
23392 if (favs[index].matches(preset)) {
23393 return favs[index];
23400 return utilRebind(_this, dispatch$1, 'on');
23403 function utilTagText(entity) {
23404 var obj = entity && entity.tags || {};
23405 return Object.keys(obj).map(function (k) {
23406 return k + '=' + obj[k];
23409 function utilTotalExtent(array, graph) {
23410 var extent = geoExtent();
23413 for (var i = 0; i < array.length; i++) {
23415 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
23418 extent._extend(entity.extent(graph));
23424 function utilTagDiff(oldTags, newTags) {
23426 var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
23427 keys.forEach(function (k) {
23428 var oldVal = oldTags[k];
23429 var newVal = newTags[k];
23431 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
23437 display: '- ' + k + '=' + oldVal
23441 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
23447 display: '+ ' + k + '=' + newVal
23453 function utilEntitySelector(ids) {
23454 return ids.length ? '.' + ids.join(',.') : 'nothing';
23455 } // returns an selector to select entity ids for:
23456 // - entityIDs passed in
23457 // - shallow descendant entityIDs for any of those entities that are relations
23459 function utilEntityOrMemberSelector(ids, graph) {
23460 var seen = new Set(ids);
23461 ids.forEach(collectShallowDescendants);
23462 return utilEntitySelector(Array.from(seen));
23464 function collectShallowDescendants(id) {
23465 var entity = graph.hasEntity(id);
23466 if (!entity || entity.type !== 'relation') return;
23467 entity.members.map(function (member) {
23469 }).forEach(function (id) {
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 utilEntityOrDeepMemberSelector(ids, graph) {
23478 return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
23479 } // returns an selector to select entity ids for:
23480 // - entityIDs passed in
23481 // - deep descendant entityIDs for any of those entities that are relations
23483 function utilEntityAndDeepMemberIDs(ids, graph) {
23484 var seen = new Set();
23485 ids.forEach(collectDeepDescendants);
23486 return Array.from(seen);
23488 function collectDeepDescendants(id) {
23489 if (seen.has(id)) return;
23491 var entity = graph.hasEntity(id);
23492 if (!entity || entity.type !== 'relation') return;
23493 entity.members.map(function (member) {
23495 }).forEach(collectDeepDescendants); // recurse
23497 } // returns an selector to select entity ids for:
23498 // - deep descendant entityIDs for any of those entities that are relations
23500 function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
23501 var idsSet = new Set(ids);
23502 var seen = new Set();
23503 var returners = new Set();
23504 ids.forEach(collectDeepDescendants);
23505 return utilEntitySelector(Array.from(returners));
23507 function collectDeepDescendants(id) {
23508 if (seen.has(id)) return;
23511 if (!idsSet.has(id)) {
23515 var entity = graph.hasEntity(id);
23516 if (!entity || entity.type !== 'relation') return;
23517 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
23518 entity.members.map(function (member) {
23520 }).forEach(collectDeepDescendants); // recurse
23522 } // Adds or removes highlight styling for the specified entities
23524 function utilHighlightEntities(ids, highlighted, context) {
23525 context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
23526 } // returns an Array that is the union of:
23527 // - nodes for any nodeIDs passed in
23528 // - child nodes of any wayIDs passed in
23529 // - descendant member and child nodes of relationIDs passed in
23531 function utilGetAllNodes(ids, graph) {
23532 var seen = new Set();
23533 var nodes = new Set();
23534 ids.forEach(collectNodes);
23535 return Array.from(nodes);
23537 function collectNodes(id) {
23538 if (seen.has(id)) return;
23540 var entity = graph.hasEntity(id);
23541 if (!entity) return;
23543 if (entity.type === 'node') {
23545 } else if (entity.type === 'way') {
23546 entity.nodes.forEach(collectNodes);
23548 entity.members.map(function (member) {
23550 }).forEach(collectNodes); // recurse
23554 function utilDisplayName(entity) {
23555 var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
23556 var name = entity.tags[localizedNameKey] || entity.tags.name || '';
23557 var network = entity.tags.cycle_network || entity.tags.network;
23559 if (!name && entity.tags.ref) {
23560 name = entity.tags.ref;
23563 name = network + ' ' + name;
23569 function utilDisplayNameForPath(entity) {
23570 var name = utilDisplayName(entity);
23571 var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
23573 if (!isFirefox && name && rtlRegex.test(name)) {
23574 name = fixRTLTextForSvg(name);
23579 function utilDisplayType(id) {
23581 n: _t('inspector.node'),
23582 w: _t('inspector.way'),
23583 r: _t('inspector.relation')
23586 function utilDisplayLabel(entity, graphOrGeometry) {
23587 var displayName = utilDisplayName(entity);
23590 // use the display name if there is one
23591 return displayName;
23594 var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
23596 if (preset && preset.name()) {
23597 // use the preset name if there is a match
23598 return preset.name();
23599 } // fallback to the display type (node/way/relation)
23602 return utilDisplayType(entity.id);
23604 function utilEntityRoot(entityType) {
23610 } // Returns a single object containing the tags of all the given entities.
23613 // highway: 'service',
23614 // service: 'parking_aisle'
23618 // highway: 'service',
23619 // service: 'driveway',
23624 // highway: 'service',
23625 // service: [ 'driveway', 'parking_aisle' ],
23626 // width: [ '3', undefined ]
23629 function utilCombinedTags(entityIDs, graph) {
23631 var tagCounts = {};
23632 var allKeys = new Set();
23633 var entities = entityIDs.map(function (entityID) {
23634 return graph.hasEntity(entityID);
23635 }).filter(Boolean); // gather the aggregate keys
23637 entities.forEach(function (entity) {
23638 var keys = Object.keys(entity.tags).filter(Boolean);
23639 keys.forEach(function (key) {
23643 entities.forEach(function (entity) {
23644 allKeys.forEach(function (key) {
23645 var value = entity.tags[key]; // purposely allow `undefined`
23647 if (!tags.hasOwnProperty(key)) {
23648 // first value, set as raw
23651 if (!Array.isArray(tags[key])) {
23652 if (tags[key] !== value) {
23653 // first alternate value, replace single value with array
23654 tags[key] = [tags[key], value];
23658 if (tags[key].indexOf(value) === -1) {
23659 // subsequent alternate value, add to array
23660 tags[key].push(value);
23665 var tagHash = key + '=' + value;
23666 if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
23667 tagCounts[tagHash] += 1;
23671 for (var key in tags) {
23672 if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
23674 tags[key] = tags[key].sort(function (val1, val2) {
23675 var key = key; // capture
23677 var count2 = tagCounts[key + '=' + val2];
23678 var count1 = tagCounts[key + '=' + val1];
23680 if (count2 !== count1) {
23681 return count2 - count1;
23684 if (val2 && val1) {
23685 return val1.localeCompare(val2);
23688 return val1 ? 1 : -1;
23694 function utilStringQs(str) {
23695 var i = 0; // advance past any leading '?' or '#' characters
23697 while (i < str.length && (str[i] === '?' || str[i] === '#')) {
23701 str = str.slice(i);
23702 return str.split('&').reduce(function (obj, pair) {
23703 var parts = pair.split('=');
23705 if (parts.length === 2) {
23706 obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
23712 function utilQsString(obj, noencode) {
23713 // encode everything except special characters used in certain hash parameters:
23714 // "/" in map states, ":", ",", {" and "}" in background
23715 function softEncode(s) {
23716 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
23719 return Object.keys(obj).sort().map(function (key) {
23720 return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
23723 function utilPrefixDOMProperty(property) {
23724 var prefixes = ['webkit', 'ms', 'moz', 'o'];
23726 var n = prefixes.length;
23727 var s = document.body;
23728 if (property in s) return property;
23729 property = property.substr(0, 1).toUpperCase() + property.substr(1);
23732 if (prefixes[i] + property in s) {
23733 return prefixes[i] + property;
23739 function utilPrefixCSSProperty(property) {
23740 var prefixes = ['webkit', 'ms', 'Moz', 'O'];
23742 var n = prefixes.length;
23743 var s = document.body.style;
23745 if (property.toLowerCase() in s) {
23746 return property.toLowerCase();
23750 if (prefixes[i] + property in s) {
23751 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
23757 var transformProperty;
23758 function utilSetTransform(el, x, y, scale) {
23759 var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
23760 var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
23761 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
23762 } // Calculates Levenshtein distance between two strings
23763 // see: https://en.wikipedia.org/wiki/Levenshtein_distance
23764 // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
23766 function utilEditDistance(a, b) {
23767 a = remove$1(a.toLowerCase());
23768 b = remove$1(b.toLowerCase());
23769 if (a.length === 0) return b.length;
23770 if (b.length === 0) return a.length;
23774 for (i = 0; i <= b.length; i++) {
23778 for (j = 0; j <= a.length; j++) {
23782 for (i = 1; i <= b.length; i++) {
23783 for (j = 1; j <= a.length; j++) {
23784 if (b.charAt(i - 1) === a.charAt(j - 1)) {
23785 matrix[i][j] = matrix[i - 1][j - 1];
23787 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
23788 Math.min(matrix[i][j - 1] + 1, // insertion
23789 matrix[i - 1][j] + 1)); // deletion
23794 return matrix[b.length][a.length];
23795 } // a d3.mouse-alike which
23796 // 1. Only works on HTML elements, not SVG
23797 // 2. Does not cause style recalculation
23799 function utilFastMouse(container) {
23800 var rect = container.getBoundingClientRect();
23801 var rectLeft = rect.left;
23802 var rectTop = rect.top;
23803 var clientLeft = +container.clientLeft;
23804 var clientTop = +container.clientTop;
23805 return function (e) {
23806 return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
23809 function utilAsyncMap(inputs, func, callback) {
23810 var remaining = inputs.length;
23813 inputs.forEach(function (d, i) {
23814 func(d, function done(err, data) {
23818 if (!remaining) callback(errors, results);
23821 } // wraps an index to an interval [0..length-1]
23823 function utilWrap(index, length) {
23825 index += Math.ceil(-index / length) * length;
23828 return index % length;
23831 * a replacement for functor
23833 * @param {*} value any value
23834 * @returns {Function} a function that returns that value or the value if it's a function
23837 function utilFunctor(value) {
23838 if (typeof value === 'function') return value;
23839 return function () {
23843 function utilNoAuto(selection) {
23844 var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
23845 return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
23846 .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
23847 } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
23848 // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
23850 function utilHashcode(str) {
23853 if (str.length === 0) {
23857 for (var i = 0; i < str.length; i++) {
23858 var _char = str.charCodeAt(i);
23860 hash = (hash << 5) - hash + _char;
23861 hash = hash & hash; // Convert to 32bit integer
23865 } // Returns version of `str` with all runs of special characters replaced by `_`;
23866 // suitable for HTML ids, classes, selectors, etc.
23868 function utilSafeClassName(str) {
23869 return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
23870 } // Returns string based on `val` that is highly unlikely to collide with an id
23871 // used previously or that's present elsewhere in the document. Useful for preventing
23872 // browser-provided autofills or when embedding iD on pages with unknown elements.
23874 function utilUniqueDomId(val) {
23875 return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
23876 } // Returns the length of `str` in unicode characters. This can be less than
23877 // `String.length()` since a single unicode character can be composed of multiple
23878 // JavaScript UTF-16 code units.
23880 function utilUnicodeCharsCount(str) {
23881 // Native ES2015 implementations of `Array.from` split strings into unicode characters
23882 return Array.from(str).length;
23883 } // Returns a new string representing `str` cut from its start to `limit` length
23884 // in unicode characters. Note that this runs the risk of splitting graphemes.
23886 function utilUnicodeCharsTruncated(str, limit) {
23887 return Array.from(str).slice(0, limit).join('');
23890 function osmEntity(attrs) {
23891 // For prototypal inheritance.
23892 if (this instanceof osmEntity) return; // Create the appropriate subtype.
23894 if (attrs && attrs.type) {
23895 return osmEntity[attrs.type].apply(this, arguments);
23896 } else if (attrs && attrs.id) {
23897 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
23898 } // Initialize a generic Entity (used only in tests).
23901 return new osmEntity().initialize(arguments);
23904 osmEntity.id = function (type) {
23905 return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
23908 osmEntity.id.next = {
23915 osmEntity.id.fromOSM = function (type, id) {
23916 return type[0] + id;
23919 osmEntity.id.toOSM = function (id) {
23920 return id.slice(1);
23923 osmEntity.id.type = function (id) {
23930 }; // A function suitable for use as the second argument to d3.selection#data().
23933 osmEntity.key = function (entity) {
23934 return entity.id + 'v' + (entity.v || 0);
23937 var _deprecatedTagValuesByKey;
23939 osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
23940 if (!_deprecatedTagValuesByKey) {
23941 _deprecatedTagValuesByKey = {};
23942 dataDeprecated.forEach(function (d) {
23943 var oldKeys = Object.keys(d.old);
23945 if (oldKeys.length === 1) {
23946 var oldKey = oldKeys[0];
23947 var oldValue = d.old[oldKey];
23949 if (oldValue !== '*') {
23950 if (!_deprecatedTagValuesByKey[oldKey]) {
23951 _deprecatedTagValuesByKey[oldKey] = [oldValue];
23953 _deprecatedTagValuesByKey[oldKey].push(oldValue);
23960 return _deprecatedTagValuesByKey;
23963 osmEntity.prototype = {
23965 initialize: function initialize(sources) {
23966 for (var i = 0; i < sources.length; ++i) {
23967 var source = sources[i];
23969 for (var prop in source) {
23970 if (Object.prototype.hasOwnProperty.call(source, prop)) {
23971 if (source[prop] === undefined) {
23974 this[prop] = source[prop];
23980 if (!this.id && this.type) {
23981 this.id = osmEntity.id(this.type);
23984 if (!this.hasOwnProperty('visible')) {
23985 this.visible = true;
23990 copy: function copy(resolver, copies) {
23991 if (copies[this.id]) return copies[this.id];
23992 var copy = osmEntity(this, {
23997 copies[this.id] = copy;
24000 osmId: function osmId() {
24001 return osmEntity.id.toOSM(this.id);
24003 isNew: function isNew() {
24004 return this.osmId() < 0;
24006 update: function update(attrs) {
24007 return osmEntity(this, attrs, {
24008 v: 1 + (this.v || 0)
24011 mergeTags: function mergeTags(tags) {
24012 var merged = Object.assign({}, this.tags); // shallow copy
24014 var changed = false;
24016 for (var k in tags) {
24017 var t1 = merged[k];
24023 } else if (t1 !== t2) {
24025 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
24030 return changed ? this.update({
24034 intersects: function intersects(extent, resolver) {
24035 return this.extent(resolver).intersects(extent);
24037 hasNonGeometryTags: function hasNonGeometryTags() {
24038 return Object.keys(this.tags).some(function (k) {
24039 return k !== 'area';
24042 hasParentRelations: function hasParentRelations(resolver) {
24043 return resolver.parentRelations(this).length > 0;
24045 hasInterestingTags: function hasInterestingTags() {
24046 return Object.keys(this.tags).some(osmIsInterestingTag);
24048 hasWikidata: function hasWikidata() {
24049 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
24051 isHighwayIntersection: function isHighwayIntersection() {
24054 isDegenerate: function isDegenerate() {
24057 deprecatedTags: function deprecatedTags(dataDeprecated) {
24058 var tags = this.tags; // if there are no tags, none can be deprecated
24060 if (Object.keys(tags).length === 0) return [];
24061 var deprecated = [];
24062 dataDeprecated.forEach(function (d) {
24063 var oldKeys = Object.keys(d.old);
24066 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
24067 if (!tags[replaceKey] || d.old[replaceKey]) return false;
24068 var replaceValue = d.replace[replaceKey];
24069 if (replaceValue === '*') return false;
24070 if (replaceValue === tags[replaceKey]) return false;
24072 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
24074 if (hasExistingValues) return;
24077 var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
24078 if (!tags[oldKey]) return false;
24079 if (d.old[oldKey] === '*') return true;
24080 if (d.old[oldKey] === tags[oldKey]) return true;
24081 var vals = tags[oldKey].split(';').filter(Boolean);
24083 if (vals.length === 0) {
24085 } else if (vals.length > 1) {
24086 return vals.indexOf(d.old[oldKey]) !== -1;
24088 if (tags[oldKey] === d.old[oldKey]) {
24089 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
24090 var replaceKeys = Object.keys(d.replace);
24091 return !replaceKeys.every(function (replaceKey) {
24092 return tags[replaceKey] === d.replace[replaceKey];
24103 if (matchesDeprecatedTags) {
24104 deprecated.push(d);
24111 function osmLanes(entity) {
24112 if (entity.type !== 'way') return null;
24113 if (!entity.tags.highway) return null;
24114 var tags = entity.tags;
24115 var isOneWay = entity.isOneWay();
24116 var laneCount = getLaneCount(tags, isOneWay);
24117 var maxspeed = parseMaxspeed(tags);
24118 var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
24119 var forward = laneDirections.forward;
24120 var backward = laneDirections.backward;
24121 var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
24123 var turnLanes = {};
24124 turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
24125 turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
24126 turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
24127 var maxspeedLanes = {};
24128 maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
24129 maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
24130 maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
24132 psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
24133 psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
24134 psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
24136 busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
24137 busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
24138 busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
24139 var taxiLanes = {};
24140 taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
24141 taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
24142 taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
24144 hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
24145 hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
24146 hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
24148 hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
24149 hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
24150 hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
24151 var bicyclewayLanes = {};
24152 bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
24153 bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
24154 bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
24159 }; // map forward/backward/unspecified of each lane type to lanesObj
24161 mapToLanesObj(lanesObj, turnLanes, 'turnLane');
24162 mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
24163 mapToLanesObj(lanesObj, psvLanes, 'psv');
24164 mapToLanesObj(lanesObj, busLanes, 'bus');
24165 mapToLanesObj(lanesObj, taxiLanes, 'taxi');
24166 mapToLanesObj(lanesObj, hovLanes, 'hov');
24167 mapToLanesObj(lanesObj, hgvLanes, 'hgv');
24168 mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
24174 backward: backward,
24175 bothways: bothways,
24176 turnLanes: turnLanes,
24177 maxspeed: maxspeed,
24178 maxspeedLanes: maxspeedLanes,
24179 psvLanes: psvLanes,
24180 busLanes: busLanes,
24181 taxiLanes: taxiLanes,
24182 hovLanes: hovLanes,
24183 hgvLanes: hgvLanes,
24184 bicyclewayLanes: bicyclewayLanes
24190 function getLaneCount(tags, isOneWay) {
24194 count = parseInt(tags.lanes, 10);
24201 switch (tags.highway) {
24204 count = isOneWay ? 2 : 4;
24208 count = isOneWay ? 1 : 2;
24215 function parseMaxspeed(tags) {
24216 var maxspeed = tags.maxspeed;
24217 if (!maxspeed) return;
24218 var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
24219 if (!maxspeedRegex.test(maxspeed)) return;
24220 return parseInt(maxspeed, 10);
24223 function parseLaneDirections(tags, isOneWay, laneCount) {
24224 var forward = parseInt(tags['lanes:forward'], 10);
24225 var backward = parseInt(tags['lanes:backward'], 10);
24226 var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
24228 if (parseInt(tags.oneway, 10) === -1) {
24231 backward = laneCount;
24232 } else if (isOneWay) {
24233 forward = laneCount;
24236 } else if (isNaN(forward) && isNaN(backward)) {
24237 backward = Math.floor((laneCount - bothways) / 2);
24238 forward = laneCount - bothways - backward;
24239 } else if (isNaN(forward)) {
24240 if (backward > laneCount - bothways) {
24241 backward = laneCount - bothways;
24244 forward = laneCount - bothways - backward;
24245 } else if (isNaN(backward)) {
24246 if (forward > laneCount - bothways) {
24247 forward = laneCount - bothways;
24250 backward = laneCount - bothways - forward;
24255 backward: backward,
24260 function parseTurnLanes(tag) {
24262 var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
24263 return tag.split('|').map(function (s) {
24264 if (s === '') s = 'none';
24265 return s.split(';').map(function (d) {
24266 return validValues.indexOf(d) === -1 ? 'unknown' : d;
24271 function parseMaxspeedLanes(tag, maxspeed) {
24273 return tag.split('|').map(function (s) {
24274 if (s === 'none') return s;
24275 var m = parseInt(s, 10);
24276 if (s === '' || m === maxspeed) return null;
24277 return isNaN(m) ? 'unknown' : m;
24281 function parseMiscLanes(tag) {
24283 var validValues = ['yes', 'no', 'designated'];
24284 return tag.split('|').map(function (s) {
24285 if (s === '') s = 'no';
24286 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24290 function parseBicycleWay(tag) {
24292 var validValues = ['yes', 'no', 'designated', 'lane'];
24293 return tag.split('|').map(function (s) {
24294 if (s === '') s = 'no';
24295 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24299 function mapToLanesObj(lanesObj, data, key) {
24300 if (data.forward) data.forward.forEach(function (l, i) {
24301 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
24302 lanesObj.forward[i][key] = l;
24304 if (data.backward) data.backward.forEach(function (l, i) {
24305 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
24306 lanesObj.backward[i][key] = l;
24308 if (data.unspecified) data.unspecified.forEach(function (l, i) {
24309 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
24310 lanesObj.unspecified[i][key] = l;
24314 function osmWay() {
24315 if (!(this instanceof osmWay)) {
24316 return new osmWay().initialize(arguments);
24317 } else if (arguments.length) {
24318 this.initialize(arguments);
24321 osmEntity.way = osmWay;
24322 osmWay.prototype = Object.create(osmEntity.prototype);
24323 Object.assign(osmWay.prototype, {
24326 copy: function copy(resolver, copies) {
24327 if (copies[this.id]) return copies[this.id];
24328 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
24329 var nodes = this.nodes.map(function (id) {
24330 return resolver.entity(id).copy(resolver, copies).id;
24332 copy = copy.update({
24335 copies[this.id] = copy;
24338 extent: function extent(resolver) {
24339 return resolver["transient"](this, 'extent', function () {
24340 var extent = geoExtent();
24342 for (var i = 0; i < this.nodes.length; i++) {
24343 var node = resolver.hasEntity(this.nodes[i]);
24346 extent._extend(node.extent());
24353 first: function first() {
24354 return this.nodes[0];
24356 last: function last() {
24357 return this.nodes[this.nodes.length - 1];
24359 contains: function contains(node) {
24360 return this.nodes.indexOf(node) >= 0;
24362 affix: function affix(node) {
24363 if (this.nodes[0] === node) return 'prefix';
24364 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
24366 layer: function layer() {
24367 // explicit layer tag, clamp between -10, 10..
24368 if (isFinite(this.tags.layer)) {
24369 return Math.max(-10, Math.min(+this.tags.layer, 10));
24370 } // implied layer tag..
24373 if (this.tags.covered === 'yes') return -1;
24374 if (this.tags.location === 'overground') return 1;
24375 if (this.tags.location === 'underground') return -1;
24376 if (this.tags.location === 'underwater') return -10;
24377 if (this.tags.power === 'line') return 10;
24378 if (this.tags.power === 'minor_line') return 10;
24379 if (this.tags.aerialway) return 10;
24380 if (this.tags.bridge) return 1;
24381 if (this.tags.cutting) return -1;
24382 if (this.tags.tunnel) return -1;
24383 if (this.tags.waterway) return -1;
24384 if (this.tags.man_made === 'pipeline') return -10;
24385 if (this.tags.boundary) return -10;
24388 // the approximate width of the line based on its tags except its `width` tag
24389 impliedLineWidthMeters: function impliedLineWidthMeters() {
24390 var averageWidths = {
24392 // width is for single lane
24419 // width includes ties and rail bed, not just track gauge
24442 for (var key in averageWidths) {
24443 if (this.tags[key] && averageWidths[key][this.tags[key]]) {
24444 var width = averageWidths[key][this.tags[key]];
24446 if (key === 'highway') {
24447 var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
24448 if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
24449 return width * laneCount;
24458 isOneWay: function isOneWay() {
24459 // explicit oneway tag..
24464 'reversible': true,
24465 'alternating': true,
24470 if (values[this.tags.oneway] !== undefined) {
24471 return values[this.tags.oneway];
24472 } // implied oneway tag..
24475 for (var key in this.tags) {
24476 if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) return true;
24481 // Some identifier for tag that implies that this way is "sided",
24482 // i.e. the right side is the 'inside' (e.g. the right side of a
24483 // natural=cliff is lower).
24484 sidednessIdentifier: function sidednessIdentifier() {
24485 for (var key in this.tags) {
24486 var value = this.tags[key];
24488 if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
24489 if (osmRightSideIsInsideTags[key][value] === true) {
24492 // if the map's value is something other than a
24493 // literal true, we should use it so we can
24494 // special case some keys (e.g. natural=coastline
24495 // is handled differently to other naturals).
24496 return osmRightSideIsInsideTags[key][value];
24503 isSided: function isSided() {
24504 if (this.tags.two_sided === 'yes') {
24508 return this.sidednessIdentifier() !== null;
24510 lanes: function lanes() {
24511 return osmLanes(this);
24513 isClosed: function isClosed() {
24514 return this.nodes.length > 1 && this.first() === this.last();
24516 isConvex: function isConvex(resolver) {
24517 if (!this.isClosed() || this.isDegenerate()) return null;
24518 var nodes = utilArrayUniq(resolver.childNodes(this));
24519 var coords = nodes.map(function (n) {
24525 for (var i = 0; i < coords.length; i++) {
24526 var o = coords[(i + 1) % coords.length];
24528 var b = coords[(i + 2) % coords.length];
24529 var res = geoVecCross(a, b, o);
24530 curr = res > 0 ? 1 : res < 0 ? -1 : 0;
24534 } else if (prev && curr !== prev) {
24543 // returns an object with the tag that implies this is an area, if any
24544 tagSuggestingArea: function tagSuggestingArea() {
24545 return osmTagSuggestingArea(this.tags);
24547 isArea: function isArea() {
24548 if (this.tags.area === 'yes') return true;
24549 if (!this.isClosed() || this.tags.area === 'no') return false;
24550 return this.tagSuggestingArea() !== null;
24552 isDegenerate: function isDegenerate() {
24553 return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
24555 areAdjacent: function areAdjacent(n1, n2) {
24556 for (var i = 0; i < this.nodes.length; i++) {
24557 if (this.nodes[i] === n1) {
24558 if (this.nodes[i - 1] === n2) return true;
24559 if (this.nodes[i + 1] === n2) return true;
24565 geometry: function geometry(graph) {
24566 return graph["transient"](this, 'geometry', function () {
24567 return this.isArea() ? 'area' : 'line';
24570 // returns an array of objects representing the segments between the nodes in this way
24571 segments: function segments(graph) {
24572 function segmentExtent(graph) {
24573 var n1 = graph.hasEntity(this.nodes[0]);
24574 var n2 = graph.hasEntity(this.nodes[1]);
24575 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])]]);
24578 return graph["transient"](this, 'segments', function () {
24581 for (var i = 0; i < this.nodes.length - 1; i++) {
24583 id: this.id + '-' + i,
24586 nodes: [this.nodes[i], this.nodes[i + 1]],
24587 extent: segmentExtent
24594 // If this way is not closed, append the beginning node to the end of the nodelist to close it.
24595 close: function close() {
24596 if (this.isClosed() || !this.nodes.length) return this;
24597 var nodes = this.nodes.slice();
24598 nodes = nodes.filter(noRepeatNodes);
24599 nodes.push(nodes[0]);
24600 return this.update({
24604 // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
24605 unclose: function unclose() {
24606 if (!this.isClosed()) return this;
24607 var nodes = this.nodes.slice();
24608 var connector = this.first();
24609 var i = nodes.length - 1; // remove trailing connectors..
24611 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24612 nodes.splice(i, 1);
24613 i = nodes.length - 1;
24616 nodes = nodes.filter(noRepeatNodes);
24617 return this.update({
24621 // Adds a node (id) in front of the node which is currently at position index.
24622 // If index is undefined, the node will be added to the end of the way for linear ways,
24623 // or just before the final connecting node for circular ways.
24624 // Consecutive duplicates are eliminated including existing ones.
24625 // Circularity is always preserved when adding a node.
24626 addNode: function addNode(id, index) {
24627 var nodes = this.nodes.slice();
24628 var isClosed = this.isClosed();
24629 var max = isClosed ? nodes.length - 1 : nodes.length;
24631 if (index === undefined) {
24635 if (index < 0 || index > max) {
24636 throw new RangeError('index ' + index + ' out of range 0..' + max);
24637 } // If this is a closed way, remove all connector nodes except the first one
24638 // (there may be duplicates) and adjust index if necessary..
24642 var connector = this.first(); // leading connectors..
24646 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24647 nodes.splice(i, 1);
24648 if (index > i) index--;
24649 } // trailing connectors..
24652 i = nodes.length - 1;
24654 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24655 nodes.splice(i, 1);
24656 if (index > i) index--;
24657 i = nodes.length - 1;
24661 nodes.splice(index, 0, id);
24662 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24664 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24665 nodes.push(nodes[0]);
24668 return this.update({
24672 // Replaces the node which is currently at position index with the given node (id).
24673 // Consecutive duplicates are eliminated including existing ones.
24674 // Circularity is preserved when updating a node.
24675 updateNode: function updateNode(id, index) {
24676 var nodes = this.nodes.slice();
24677 var isClosed = this.isClosed();
24678 var max = nodes.length - 1;
24680 if (index === undefined || index < 0 || index > max) {
24681 throw new RangeError('index ' + index + ' out of range 0..' + max);
24682 } // If this is a closed way, remove all connector nodes except the first one
24683 // (there may be duplicates) and adjust index if necessary..
24687 var connector = this.first(); // leading connectors..
24691 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24692 nodes.splice(i, 1);
24693 if (index > i) index--;
24694 } // trailing connectors..
24697 i = nodes.length - 1;
24699 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24700 nodes.splice(i, 1);
24701 if (index === i) index = 0; // update leading connector instead
24703 i = nodes.length - 1;
24707 nodes.splice(index, 1, id);
24708 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24710 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24711 nodes.push(nodes[0]);
24714 return this.update({
24718 // Replaces each occurrence of node id needle with replacement.
24719 // Consecutive duplicates are eliminated including existing ones.
24720 // Circularity is preserved.
24721 replaceNode: function replaceNode(needleID, replacementID) {
24722 var nodes = this.nodes.slice();
24723 var isClosed = this.isClosed();
24725 for (var i = 0; i < nodes.length; i++) {
24726 if (nodes[i] === needleID) {
24727 nodes[i] = replacementID;
24731 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24733 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24734 nodes.push(nodes[0]);
24737 return this.update({
24741 // Removes each occurrence of node id.
24742 // Consecutive duplicates are eliminated including existing ones.
24743 // Circularity is preserved.
24744 removeNode: function removeNode(id) {
24745 var nodes = this.nodes.slice();
24746 var isClosed = this.isClosed();
24747 nodes = nodes.filter(function (node) {
24748 return node !== id;
24749 }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24751 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24752 nodes.push(nodes[0]);
24755 return this.update({
24759 asJXON: function asJXON(changeset_id) {
24762 '@id': this.osmId(),
24763 '@version': this.version || 0,
24764 nd: this.nodes.map(function (id) {
24767 ref: osmEntity.id.toOSM(id)
24771 tag: Object.keys(this.tags).map(function (k) {
24782 if (changeset_id) {
24783 r.way['@changeset'] = changeset_id;
24788 asGeoJSON: function asGeoJSON(resolver) {
24789 return resolver["transient"](this, 'GeoJSON', function () {
24790 var coordinates = resolver.childNodes(this).map(function (n) {
24794 if (this.isArea() && this.isClosed()) {
24797 coordinates: [coordinates]
24801 type: 'LineString',
24802 coordinates: coordinates
24807 area: function area(resolver) {
24808 return resolver["transient"](this, 'area', function () {
24809 var nodes = resolver.childNodes(this);
24812 coordinates: [nodes.map(function (n) {
24817 if (!this.isClosed() && nodes.length) {
24818 json.coordinates[0].push(nodes[0].loc);
24821 var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
24822 // that OpenStreetMap polygons are not hemisphere-spanning.
24824 if (area > 2 * Math.PI) {
24825 json.coordinates[0] = json.coordinates[0].reverse();
24826 area = d3_geoArea(json);
24829 return isNaN(area) ? 0 : area;
24832 }); // Filter function to eliminate consecutive duplicates.
24834 function noRepeatNodes(node, i, arr) {
24835 return i === 0 || node !== arr[i - 1];
24839 // 1. Relation tagged with `type=multipolygon` and no interesting tags.
24840 // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
24841 // 3. No members without a role.
24843 // Old multipolygons are no longer recommended but are still rendered as areas by iD.
24845 function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
24846 if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
24852 for (var memberIndex in entity.members) {
24853 var member = entity.members[memberIndex];
24855 if (!member.role || member.role === 'outer') {
24856 if (outerMember) return false;
24857 if (member.type !== 'way') return false;
24858 if (!graph.hasEntity(member.id)) return false;
24859 outerMember = graph.entity(member.id);
24861 if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
24867 return outerMember;
24868 } // For fixing up rendering of multipolygons with tags on the outer member.
24869 // https://github.com/openstreetmap/iD/issues/613
24871 function osmIsOldMultipolygonOuterMember(entity, graph) {
24872 if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) return false;
24873 var parents = graph.parentRelations(entity);
24874 if (parents.length !== 1) return false;
24875 var parent = parents[0];
24876 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24877 var members = parent.members,
24880 for (var i = 0; i < members.length; i++) {
24881 member = members[i];
24882 if (member.id === entity.id && member.role && member.role !== 'outer') return false; // Not outer member
24884 if (member.id !== entity.id && (!member.role || member.role === 'outer')) return false; // Not a simple multipolygon
24889 function osmOldMultipolygonOuterMember(entity, graph) {
24890 if (entity.type !== 'way') return false;
24891 var parents = graph.parentRelations(entity);
24892 if (parents.length !== 1) return false;
24893 var parent = parents[0];
24894 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24895 var members = parent.members,
24899 for (var i = 0; i < members.length; i++) {
24900 member = members[i];
24902 if (!member.role || member.role === 'outer') {
24903 if (outerMember) return false; // Not a simple multipolygon
24905 outerMember = member;
24909 if (!outerMember) return false;
24910 var outerEntity = graph.hasEntity(outerMember.id);
24911 if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) return false;
24912 return outerEntity;
24913 } // Join `toJoin` array into sequences of connecting ways.
24914 // Segments which share identical start/end nodes will, as much as possible,
24915 // be connected with each other.
24917 // The return value is a nested array. Each constituent array contains elements
24918 // of `toJoin` which have been determined to connect.
24920 // Each consitituent array also has a `nodes` property whose value is an
24921 // ordered array of member nodes, with appropriate order reversal and
24922 // start/end coordinate de-duplication.
24924 // Members of `toJoin` must have, at minimum, `type` and `id` properties.
24925 // Thus either an array of `osmWay`s or a relation member array may be used.
24927 // If an member is an `osmWay`, its tags and childnodes may be reversed via
24928 // `actionReverse` in the output.
24930 // The returned sequences array also has an `actions` array property, containing
24931 // any reversal actions that should be applied to the graph, should the calling
24932 // code attempt to actually join the given ways.
24934 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
24935 // false) and non-way members are ignored.
24938 function osmJoinWays(toJoin, graph) {
24939 function resolve(member) {
24940 return graph.childNodes(graph.entity(member.id));
24943 function reverse(item) {
24944 var action = actionReverse(item.id, {
24945 reverseOneway: true
24947 sequences.actions.push(action);
24948 return item instanceof osmWay ? action(graph).entity(item.id) : item;
24949 } // make a copy containing only the items to join
24952 toJoin = toJoin.filter(function (member) {
24953 return member.type === 'way' && graph.hasEntity(member.id);
24954 }); // Are the things we are joining relation members or `osmWays`?
24955 // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
24958 var joinAsMembers = true;
24960 for (i = 0; i < toJoin.length; i++) {
24961 if (toJoin[i] instanceof osmWay) {
24962 joinAsMembers = false;
24967 var sequences = [];
24968 sequences.actions = [];
24970 while (toJoin.length) {
24971 // start a new sequence
24972 var item = toJoin.shift();
24973 var currWays = [item];
24974 var currNodes = resolve(item).slice(); // add to it
24976 while (toJoin.length) {
24977 var start = currNodes[0];
24978 var end = currNodes[currNodes.length - 1];
24980 var nodes = null; // Find the next way/member to join.
24982 for (i = 0; i < toJoin.length; i++) {
24984 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
24985 // Strongly prefer to generate a forward path that preserves the order
24986 // of the members array. For multipolygons and most relations, member
24987 // order does not matter - but for routes, it does. (see #4589)
24988 // If we started this sequence backwards (i.e. next member way attaches to
24989 // the start node and not the end node), reverse the initial way before continuing.
24991 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
24992 currWays[0] = reverse(currWays[0]);
24993 currNodes.reverse();
24994 start = currNodes[0];
24995 end = currNodes[currNodes.length - 1];
24998 if (nodes[0] === end) {
24999 fn = currNodes.push; // join to end
25001 nodes = nodes.slice(1);
25003 } else if (nodes[nodes.length - 1] === end) {
25004 fn = currNodes.push; // join to end
25006 nodes = nodes.slice(0, -1).reverse();
25007 item = reverse(item);
25009 } else if (nodes[nodes.length - 1] === start) {
25010 fn = currNodes.unshift; // join to beginning
25012 nodes = nodes.slice(0, -1);
25014 } else if (nodes[0] === start) {
25015 fn = currNodes.unshift; // join to beginning
25017 nodes = nodes.slice(1).reverse();
25018 item = reverse(item);
25026 // couldn't find a joinable way/member
25030 fn.apply(currWays, [item]);
25031 fn.apply(currNodes, nodes);
25032 toJoin.splice(i, 1);
25035 currWays.nodes = currNodes;
25036 sequences.push(currWays);
25042 function actionAddMember(relationId, member, memberIndex, insertPair) {
25043 return function action(graph) {
25044 var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
25046 var isPTv2 = /stop|platform/.test(member.role);
25048 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
25049 // Try to perform sensible inserts based on how the ways join together
25050 graph = addWayMember(relation, graph);
25052 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25053 // Stops and Platforms for PTv2 should be ordered first.
25054 // hack: We do not currently have the ability to place them in the exactly correct order.
25055 if (isPTv2 && isNaN(memberIndex)) {
25059 graph = graph.replace(relation.addMember(member, memberIndex));
25063 }; // Add a way member into the relation "wherever it makes sense".
25064 // In this situation we were not supplied a memberIndex.
25066 function addWayMember(relation, graph) {
25067 var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
25069 var PTv2members = [];
25072 for (i = 0; i < relation.members.length; i++) {
25073 var m = relation.members[i];
25075 if (/stop|platform/.test(m.role)) {
25076 PTv2members.push(m);
25082 relation = relation.update({
25087 // We're adding a member that must stay paired with an existing member.
25088 // (This feature is used by `actionSplit`)
25090 // This is tricky because the members may exist multiple times in the
25091 // member list, and with different A-B/B-A ordering and different roles.
25092 // (e.g. a bus route that loops out and back - #4589).
25094 // Replace the existing member with a temporary way,
25095 // so that `osmJoinWays` can treat the pair like a single way.
25098 nodes: insertPair.nodes
25100 graph = graph.replace(tempWay);
25106 var tempRelation = relation.replaceMember({
25107 id: insertPair.originalID
25108 }, tempMember, true);
25109 groups = utilArrayGroupBy(tempRelation.members, 'type');
25110 groups.way = groups.way || [];
25112 // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
25113 groups = utilArrayGroupBy(relation.members, 'type');
25114 groups.way = groups.way || [];
25115 groups.way.push(member);
25118 members = withIndex(groups.way);
25119 var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
25120 // But will contain only the completed (downloaded) members
25122 for (i = 0; i < joined.length; i++) {
25123 var segment = joined[i];
25124 var nodes = segment.nodes.slice();
25125 var startIndex = segment[0].index; // j = array index in `members` where this segment starts
25127 for (j = 0; j < members.length; j++) {
25128 if (members[j].index === startIndex) {
25131 } // k = each member in segment
25134 for (k = 0; k < segment.length; k++) {
25136 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
25138 if (tempWay && item.id === tempWay.id) {
25139 if (nodes[0].id === insertPair.nodes[0]) {
25141 id: insertPair.originalID,
25145 id: insertPair.insertedID,
25151 id: insertPair.insertedID,
25155 id: insertPair.originalID,
25160 } // reorder `members` if necessary
25164 if (j + k >= members.length || item.index !== members[j + k].index) {
25165 moveMember(members, item.index, j + k);
25169 nodes.splice(0, way.nodes.length - 1);
25174 graph = graph.remove(tempWay);
25175 } // Final pass: skip dead items, split pairs, remove index properties
25178 var wayMembers = [];
25180 for (i = 0; i < members.length; i++) {
25182 if (item.index === -1) continue;
25185 wayMembers.push(item.pair[0]);
25186 wayMembers.push(item.pair[1]);
25188 wayMembers.push(utilObjectOmit(item, ['index']));
25190 } // Put stops and platforms first, then nodes, ways, relations
25191 // This is recommended for Public Transport v2 routes:
25192 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25195 var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
25196 return graph.replace(relation.update({
25197 members: newMembers
25198 })); // `moveMember()` changes the `members` array in place by splicing
25199 // the item with `.index = findIndex` to where it belongs,
25200 // and marking the old position as "dead" with `.index = -1`
25204 // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
25208 // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
25209 // members 0 1 2 3 x 5 4 6 7 8 9 moved
25213 // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
25214 // members 0 1 2 3 x 5 4 7 6 x 8 9 moved
25218 // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
25221 function moveMember(arr, findIndex, toIndex) {
25224 for (i = 0; i < arr.length; i++) {
25225 if (arr[i].index === findIndex) {
25230 var item = Object.assign({}, arr[i]); // shallow copy
25232 arr[i].index = -1; // mark as dead
25234 item.index = toIndex;
25235 arr.splice(toIndex, 0, item);
25236 } // This is the same as `Relation.indexedMembers`,
25237 // Except we don't want to index all the members, only the ways
25240 function withIndex(arr) {
25241 var result = new Array(arr.length);
25243 for (var i = 0; i < arr.length; i++) {
25244 result[i] = Object.assign({}, arr[i]); // shallow copy
25246 result[i].index = i;
25254 function actionAddMidpoint(midpoint, node) {
25255 return function (graph) {
25256 graph = graph.replace(node.move(midpoint.loc));
25257 var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
25258 parents.forEach(function (way) {
25259 for (var i = 0; i < way.nodes.length - 1; i++) {
25260 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
25261 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
25262 // turning them into self-intersections.
25272 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
25273 function actionAddVertex(wayId, nodeId, index) {
25274 return function (graph) {
25275 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
25279 function actionChangeMember(relationId, member, memberIndex) {
25280 return function (graph) {
25281 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
25285 function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
25286 return function action(graph) {
25287 var entity = graph.entity(entityID);
25288 var geometry = entity.geometry(graph);
25289 var tags = entity.tags;
25290 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry);
25291 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
25292 return graph.replace(entity.update({
25298 function actionChangeTags(entityId, tags) {
25299 return function (graph) {
25300 var entity = graph.entity(entityId);
25301 return graph.replace(entity.update({
25307 function osmNode() {
25308 if (!(this instanceof osmNode)) {
25309 return new osmNode().initialize(arguments);
25310 } else if (arguments.length) {
25311 this.initialize(arguments);
25314 osmEntity.node = osmNode;
25315 osmNode.prototype = Object.create(osmEntity.prototype);
25316 Object.assign(osmNode.prototype, {
25319 extent: function extent() {
25320 return new geoExtent(this.loc);
25322 geometry: function geometry(graph) {
25323 return graph["transient"](this, 'geometry', function () {
25324 return graph.isPoi(this) ? 'point' : 'vertex';
25327 move: function move(loc) {
25328 return this.update({
25332 isDegenerate: function isDegenerate() {
25333 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);
25335 // Inspect tags and geometry to determine which direction(s) this node/vertex points
25336 directions: function directions(resolver, projection) {
25338 var i; // which tag to use?
25340 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
25341 // all-way stop tag on a highway intersection
25344 // generic direction tag
25345 val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
25347 var re = /:direction$/i;
25348 var keys = Object.keys(this.tags);
25350 for (i = 0; i < keys.length; i++) {
25351 if (re.test(keys[i])) {
25352 val = this.tags[keys[i]].toLowerCase();
25358 if (val === '') return [];
25362 northnortheast: 22,
25370 eastsoutheast: 112,
25374 southsoutheast: 157,
25378 southsouthwest: 202,
25382 westsouthwest: 247,
25386 westnorthwest: 292,
25390 northnorthwest: 337,
25393 var values = val.split(';');
25395 values.forEach(function (v) {
25396 // swap cardinal for numeric directions
25397 if (cardinal[v] !== undefined) {
25399 } // numeric direction - just add to results
25402 if (v !== '' && !isNaN(+v)) {
25405 } // string direction - inspect parent ways
25408 var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
25409 var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
25410 if (!lookForward && !lookBackward) return;
25412 resolver.parentWays(this).forEach(function (parent) {
25413 var nodes = parent.nodes;
25415 for (i = 0; i < nodes.length; i++) {
25416 if (nodes[i] === this.id) {
25417 // match current entity
25418 if (lookForward && i > 0) {
25419 nodeIds[nodes[i - 1]] = true; // look back to prev node
25422 if (lookBackward && i < nodes.length - 1) {
25423 nodeIds[nodes[i + 1]] = true; // look ahead to next node
25428 Object.keys(nodeIds).forEach(function (nodeId) {
25429 // +90 because geoAngle returns angle from X axis, not Y (north)
25430 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
25433 return utilArrayUniq(results);
25435 isEndpoint: function isEndpoint(resolver) {
25436 return resolver["transient"](this, 'isEndpoint', function () {
25438 return resolver.parentWays(this).filter(function (parent) {
25439 return !parent.isClosed() && !!parent.affix(id);
25443 isConnected: function isConnected(resolver) {
25444 return resolver["transient"](this, 'isConnected', function () {
25445 var parents = resolver.parentWays(this);
25447 if (parents.length > 1) {
25448 // vertex is connected to multiple parent ways
25449 for (var i in parents) {
25450 if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
25452 } else if (parents.length === 1) {
25453 var way = parents[0];
25454 var nodes = way.nodes.slice();
25456 if (way.isClosed()) {
25458 } // ignore connecting node if closed
25459 // return true if vertex appears multiple times (way is self intersecting)
25462 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
25468 parentIntersectionWays: function parentIntersectionWays(resolver) {
25469 return resolver["transient"](this, 'parentIntersectionWays', function () {
25470 return resolver.parentWays(this).filter(function (parent) {
25471 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
25475 isIntersection: function isIntersection(resolver) {
25476 return this.parentIntersectionWays(resolver).length > 1;
25478 isHighwayIntersection: function isHighwayIntersection(resolver) {
25479 return resolver["transient"](this, 'isHighwayIntersection', function () {
25480 return resolver.parentWays(this).filter(function (parent) {
25481 return parent.tags.highway && parent.geometry(resolver) === 'line';
25485 isOnAddressLine: function isOnAddressLine(resolver) {
25486 return resolver["transient"](this, 'isOnAddressLine', function () {
25487 return resolver.parentWays(this).filter(function (parent) {
25488 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
25492 asJXON: function asJXON(changeset_id) {
25495 '@id': this.osmId(),
25496 '@lon': this.loc[0],
25497 '@lat': this.loc[1],
25498 '@version': this.version || 0,
25499 tag: Object.keys(this.tags).map(function (k) {
25509 if (changeset_id) r.node['@changeset'] = changeset_id;
25512 asGeoJSON: function asGeoJSON() {
25515 coordinates: this.loc
25520 function actionCircularize(wayId, projection, maxAngle) {
25521 maxAngle = (maxAngle || 20) * Math.PI / 180;
25523 var action = function action(graph, t) {
25524 if (t === null || !isFinite(t)) t = 1;
25525 t = Math.min(Math.max(+t, 0), 1);
25526 var way = graph.entity(wayId);
25527 var origNodes = {};
25528 graph.childNodes(way).forEach(function (node) {
25529 if (!origNodes[node.id]) origNodes[node.id] = node;
25532 if (!way.isConvex(graph)) {
25533 graph = action.makeConvex(graph);
25536 var nodes = utilArrayUniq(graph.childNodes(way));
25537 var keyNodes = nodes.filter(function (n) {
25538 return graph.parentWays(n).length !== 1;
25540 var points = nodes.map(function (n) {
25541 return projection(n.loc);
25543 var keyPoints = keyNodes.map(function (n) {
25544 return projection(n.loc);
25546 var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
25547 var radius = d3_median(points, function (p) {
25548 return geoVecLength(centroid, p);
25550 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25551 var ids, i, j, k; // we need at least two key nodes for the algorithm to work
25553 if (!keyNodes.length) {
25554 keyNodes = [nodes[0]];
25555 keyPoints = [points[0]];
25558 if (keyNodes.length === 1) {
25559 var index = nodes.indexOf(keyNodes[0]);
25560 var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
25561 keyNodes.push(nodes[oppositeIndex]);
25562 keyPoints.push(points[oppositeIndex]);
25563 } // key points and nodes are those connected to the ways,
25564 // they are projected onto the circle, in between nodes are moved
25565 // to constant intervals between key nodes, extra in between nodes are
25566 // added if necessary.
25569 for (i = 0; i < keyPoints.length; i++) {
25570 var nextKeyNodeIndex = (i + 1) % keyNodes.length;
25571 var startNode = keyNodes[i];
25572 var endNode = keyNodes[nextKeyNodeIndex];
25573 var startNodeIndex = nodes.indexOf(startNode);
25574 var endNodeIndex = nodes.indexOf(endNode);
25575 var numberNewPoints = -1;
25576 var indexRange = endNodeIndex - startNodeIndex;
25577 var nearNodes = {};
25578 var inBetweenNodes = [];
25579 var startAngle, endAngle, totalAngle, eachAngle;
25580 var angle, loc, node, origNode;
25582 if (indexRange < 0) {
25583 indexRange += nodes.length;
25584 } // position this key node
25587 var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
25588 keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
25589 loc = projection.invert(keyPoints[i]);
25590 node = keyNodes[i];
25591 origNode = origNodes[node.id];
25592 node = node.move(geoVecInterp(origNode.loc, loc, t));
25593 graph = graph.replace(node); // figure out the between delta angle we want to match to
25595 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
25596 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
25597 totalAngle = endAngle - startAngle; // detects looping around -pi/pi
25599 if (totalAngle * sign > 0) {
25600 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
25605 eachAngle = totalAngle / (indexRange + numberNewPoints);
25606 } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
25609 for (j = 1; j < indexRange; j++) {
25610 angle = startAngle + j * eachAngle;
25611 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
25612 node = nodes[(j + startNodeIndex) % nodes.length];
25613 origNode = origNodes[node.id];
25614 nearNodes[node.id] = angle;
25615 node = node.move(geoVecInterp(origNode.loc, loc, t));
25616 graph = graph.replace(node);
25617 } // add new in between nodes if necessary
25620 for (j = 0; j < numberNewPoints; j++) {
25621 angle = startAngle + (indexRange + j) * eachAngle;
25622 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
25624 var min = Infinity;
25626 for (var nodeId in nearNodes) {
25627 var nearAngle = nearNodes[nodeId];
25628 var dist = Math.abs(nearAngle - angle);
25632 origNode = origNodes[nodeId];
25637 loc: geoVecInterp(origNode.loc, loc, t)
25639 graph = graph.replace(node);
25640 nodes.splice(endNodeIndex + j, 0, node);
25641 inBetweenNodes.push(node.id);
25642 } // Check for other ways that share these keyNodes..
25643 // If keyNodes are adjacent in both ways,
25644 // we can add inBetweenNodes to that shared way too..
25647 if (indexRange === 1 && inBetweenNodes.length) {
25648 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
25649 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
25650 var wayDirection1 = endIndex1 - startIndex1;
25652 if (wayDirection1 < -1) {
25656 var parentWays = graph.parentWays(keyNodes[i]);
25658 for (j = 0; j < parentWays.length; j++) {
25659 var sharedWay = parentWays[j];
25660 if (sharedWay === way) continue;
25662 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
25663 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
25664 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
25665 var wayDirection2 = endIndex2 - startIndex2;
25666 var insertAt = endIndex2;
25668 if (wayDirection2 < -1) {
25672 if (wayDirection1 !== wayDirection2) {
25673 inBetweenNodes.reverse();
25674 insertAt = startIndex2;
25677 for (k = 0; k < inBetweenNodes.length; k++) {
25678 sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
25681 graph = graph.replace(sharedWay);
25685 } // update the way to have all the new nodes
25688 ids = nodes.map(function (n) {
25695 graph = graph.replace(way);
25699 action.makeConvex = function (graph) {
25700 var way = graph.entity(wayId);
25701 var nodes = utilArrayUniq(graph.childNodes(way));
25702 var points = nodes.map(function (n) {
25703 return projection(n.loc);
25705 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25706 var hull = d3_polygonHull(points);
25707 var i, j; // D3 convex hulls go counterclockwise..
25714 for (i = 0; i < hull.length - 1; i++) {
25715 var startIndex = points.indexOf(hull[i]);
25716 var endIndex = points.indexOf(hull[i + 1]);
25717 var indexRange = endIndex - startIndex;
25719 if (indexRange < 0) {
25720 indexRange += nodes.length;
25721 } // move interior nodes to the surface of the convex hull..
25724 for (j = 1; j < indexRange; j++) {
25725 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
25726 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
25727 graph = graph.replace(node);
25734 action.disabled = function (graph) {
25735 if (!graph.entity(wayId).isClosed()) {
25736 return 'not_closed';
25737 } //disable when already circular
25740 var way = graph.entity(wayId);
25741 var nodes = utilArrayUniq(graph.childNodes(way));
25742 var points = nodes.map(function (n) {
25743 return projection(n.loc);
25745 var hull = d3_polygonHull(points);
25746 var epsilonAngle = Math.PI / 180;
25748 if (hull.length !== points.length || hull.length < 3) {
25752 var centroid = d3_polygonCentroid(points);
25753 var radius = geoVecLengthSquare(centroid, points[0]);
25754 var i, actualPoint; // compare distances between centroid and points
25756 for (i = 0; i < hull.length; i++) {
25757 actualPoint = hull[i];
25758 var actualDist = geoVecLengthSquare(actualPoint, centroid);
25759 var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
25761 if (diff > 0.05 * radius) {
25764 } //check if central angles are smaller than maxAngle
25767 for (i = 0; i < hull.length; i++) {
25768 actualPoint = hull[i];
25769 var nextPoint = hull[(i + 1) % hull.length];
25770 var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
25771 var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
25772 var angle = endAngle - startAngle;
25778 if (angle > Math.PI) {
25779 angle = 2 * Math.PI - angle;
25782 if (angle > maxAngle + epsilonAngle) {
25787 return 'already_circular';
25790 action.transitionable = true;
25794 function actionDeleteWay(wayID) {
25795 function canDeleteNode(node, graph) {
25796 // don't delete nodes still attached to ways or relations
25797 if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
25798 var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
25800 if (geometries.point) return false; // delete if this node only be a vertex
25802 if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
25803 // so only delete if there are no interesting tags
25805 return !node.hasInterestingTags();
25808 var action = function action(graph) {
25809 var way = graph.entity(wayID);
25810 graph.parentRelations(way).forEach(function (parent) {
25811 parent = parent.removeMembersWithID(wayID);
25812 graph = graph.replace(parent);
25814 if (parent.isDegenerate()) {
25815 graph = actionDeleteRelation(parent.id)(graph);
25818 new Set(way.nodes).forEach(function (nodeID) {
25819 graph = graph.replace(way.removeNode(nodeID));
25820 var node = graph.entity(nodeID);
25822 if (canDeleteNode(node, graph)) {
25823 graph = graph.remove(node);
25826 return graph.remove(way);
25832 function actionDeleteMultiple(ids) {
25834 way: actionDeleteWay,
25835 node: actionDeleteNode,
25836 relation: actionDeleteRelation
25839 var action = function action(graph) {
25840 ids.forEach(function (id) {
25841 if (graph.hasEntity(id)) {
25842 // It may have been deleted already.
25843 graph = actions[graph.entity(id).type](id)(graph);
25852 function actionDeleteRelation(relationID, allowUntaggedMembers) {
25853 function canDeleteEntity(entity, graph) {
25854 return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
25857 var action = function action(graph) {
25858 var relation = graph.entity(relationID);
25859 graph.parentRelations(relation).forEach(function (parent) {
25860 parent = parent.removeMembersWithID(relationID);
25861 graph = graph.replace(parent);
25863 if (parent.isDegenerate()) {
25864 graph = actionDeleteRelation(parent.id)(graph);
25867 var memberIDs = utilArrayUniq(relation.members.map(function (m) {
25870 memberIDs.forEach(function (memberID) {
25871 graph = graph.replace(relation.removeMembersWithID(memberID));
25872 var entity = graph.entity(memberID);
25874 if (canDeleteEntity(entity, graph)) {
25875 graph = actionDeleteMultiple([memberID])(graph);
25878 return graph.remove(relation);
25884 function actionDeleteNode(nodeId) {
25885 var action = function action(graph) {
25886 var node = graph.entity(nodeId);
25887 graph.parentWays(node).forEach(function (parent) {
25888 parent = parent.removeNode(nodeId);
25889 graph = graph.replace(parent);
25891 if (parent.isDegenerate()) {
25892 graph = actionDeleteWay(parent.id)(graph);
25895 graph.parentRelations(node).forEach(function (parent) {
25896 parent = parent.removeMembersWithID(nodeId);
25897 graph = graph.replace(parent);
25899 if (parent.isDegenerate()) {
25900 graph = actionDeleteRelation(parent.id)(graph);
25903 return graph.remove(node);
25910 // First choose a node to be the survivor, with preference given
25911 // to an existing (not new) node.
25913 // Tags and relation memberships of of non-surviving nodes are merged
25914 // to the survivor.
25916 // This is the inverse of `iD.actionDisconnect`.
25919 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
25920 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
25923 function actionConnect(nodeIDs) {
25924 var action = function action(graph) {
25928 var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
25930 for (i = 0; i < nodeIDs.length; i++) {
25931 survivor = graph.entity(nodeIDs[i]);
25932 if (survivor.version) break; // found one
25933 } // Replace all non-surviving nodes with the survivor and merge tags.
25936 for (i = 0; i < nodeIDs.length; i++) {
25937 node = graph.entity(nodeIDs[i]);
25938 if (node.id === survivor.id) continue;
25939 parents = graph.parentWays(node);
25941 for (j = 0; j < parents.length; j++) {
25942 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
25945 parents = graph.parentRelations(node);
25947 for (j = 0; j < parents.length; j++) {
25948 graph = graph.replace(parents[j].replaceMember(node, survivor));
25951 survivor = survivor.mergeTags(node.tags);
25952 graph = actionDeleteNode(node.id)(graph);
25955 graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
25957 parents = graph.parentWays(survivor);
25959 for (i = 0; i < parents.length; i++) {
25960 if (parents[i].isDegenerate()) {
25961 graph = actionDeleteWay(parents[i].id)(graph);
25968 action.disabled = function (graph) {
25970 var restrictionIDs = [];
25973 var relations, relation, role;
25974 var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
25976 for (i = 0; i < nodeIDs.length; i++) {
25977 survivor = graph.entity(nodeIDs[i]);
25978 if (survivor.version) break; // found one
25979 } // 1. disable if the nodes being connected have conflicting relation roles
25982 for (i = 0; i < nodeIDs.length; i++) {
25983 node = graph.entity(nodeIDs[i]);
25984 relations = graph.parentRelations(node);
25986 for (j = 0; j < relations.length; j++) {
25987 relation = relations[j];
25988 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
25990 if (relation.hasFromViaTo()) {
25991 restrictionIDs.push(relation.id);
25994 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
25997 seen[relation.id] = role;
26000 } // gather restrictions for parent ways
26003 for (i = 0; i < nodeIDs.length; i++) {
26004 node = graph.entity(nodeIDs[i]);
26005 var parents = graph.parentWays(node);
26007 for (j = 0; j < parents.length; j++) {
26008 var parent = parents[j];
26009 relations = graph.parentRelations(parent);
26011 for (k = 0; k < relations.length; k++) {
26012 relation = relations[k];
26014 if (relation.hasFromViaTo()) {
26015 restrictionIDs.push(relation.id);
26019 } // test restrictions
26022 restrictionIDs = utilArrayUniq(restrictionIDs);
26024 for (i = 0; i < restrictionIDs.length; i++) {
26025 relation = graph.entity(restrictionIDs[i]);
26026 if (!relation.isComplete(graph)) continue;
26027 var memberWays = relation.members.filter(function (m) {
26028 return m.type === 'way';
26029 }).map(function (m) {
26030 return graph.entity(m.id);
26032 memberWays = utilArrayUniq(memberWays);
26033 var f = relation.memberByRole('from');
26034 var t = relation.memberByRole('to');
26035 var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
26036 // (a key node is a node at the junction of ways)
26046 for (j = 0; j < relation.members.length; j++) {
26047 collectNodes(relation.members[j], nodes);
26050 nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
26051 nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
26052 var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
26053 nodes.from = nodes.from.filter(filter);
26054 nodes.via = nodes.via.filter(filter);
26055 nodes.to = nodes.to.filter(filter);
26056 var connectFrom = false;
26057 var connectVia = false;
26058 var connectTo = false;
26059 var connectKeyFrom = false;
26060 var connectKeyTo = false;
26062 for (j = 0; j < nodeIDs.length; j++) {
26063 var n = nodeIDs[j];
26065 if (nodes.from.indexOf(n) !== -1) {
26066 connectFrom = true;
26069 if (nodes.via.indexOf(n) !== -1) {
26073 if (nodes.to.indexOf(n) !== -1) {
26077 if (nodes.keyfrom.indexOf(n) !== -1) {
26078 connectKeyFrom = true;
26081 if (nodes.keyto.indexOf(n) !== -1) {
26082 connectKeyTo = true;
26086 if (connectFrom && connectTo && !isUturn) {
26087 return 'restriction';
26090 if (connectFrom && connectVia) {
26091 return 'restriction';
26094 if (connectTo && connectVia) {
26095 return 'restriction';
26096 } // connecting to a key node -
26097 // if both nodes are on a member way (i.e. part of the turn restriction),
26098 // the connecting node must be adjacent to the key node.
26101 if (connectKeyFrom || connectKeyTo) {
26102 if (nodeIDs.length !== 2) {
26103 return 'restriction';
26109 for (j = 0; j < memberWays.length; j++) {
26110 way = memberWays[j];
26112 if (way.contains(nodeIDs[0])) {
26116 if (way.contains(nodeIDs[1])) {
26122 // both nodes are part of the restriction
26125 for (j = 0; j < memberWays.length; j++) {
26126 way = memberWays[j];
26128 if (way.areAdjacent(n0, n1)) {
26135 return 'restriction';
26138 } // 2b. disable if nodes being connected will destroy a member way in a restriction
26139 // (to test, make a copy and try actually connecting the nodes)
26142 for (j = 0; j < memberWays.length; j++) {
26143 way = memberWays[j].update({}); // make copy
26145 for (k = 0; k < nodeIDs.length; k++) {
26146 if (nodeIDs[k] === survivor.id) continue;
26148 if (way.areAdjacent(nodeIDs[k], survivor.id)) {
26149 way = way.removeNode(nodeIDs[k]);
26151 way = way.replaceNode(nodeIDs[k], survivor.id);
26155 if (way.isDegenerate()) {
26156 return 'restriction';
26161 return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
26163 function hasDuplicates(n, i, arr) {
26164 return arr.indexOf(n) !== arr.lastIndexOf(n);
26167 function keyNodeFilter(froms, tos) {
26168 return function (n) {
26169 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
26173 function collectNodes(member, collection) {
26174 var entity = graph.hasEntity(member.id);
26175 if (!entity) return;
26176 var role = member.role || '';
26178 if (!collection[role]) {
26179 collection[role] = [];
26182 if (member.type === 'node') {
26183 collection[role].push(member.id);
26185 if (role === 'via') {
26186 collection.keyfrom.push(member.id);
26187 collection.keyto.push(member.id);
26189 } else if (member.type === 'way') {
26190 collection[role].push.apply(collection[role], entity.nodes);
26192 if (role === 'from' || role === 'via') {
26193 collection.keyfrom.push(entity.first());
26194 collection.keyfrom.push(entity.last());
26197 if (role === 'to' || role === 'via') {
26198 collection.keyto.push(entity.first());
26199 collection.keyto.push(entity.last());
26208 function actionCopyEntities(ids, fromGraph) {
26211 var action = function action(graph) {
26212 ids.forEach(function (id) {
26213 fromGraph.entity(id).copy(fromGraph, _copies);
26216 for (var id in _copies) {
26217 graph = graph.replace(_copies[id]);
26223 action.copies = function () {
26230 function actionDeleteMember(relationId, memberIndex) {
26231 return function (graph) {
26232 var relation = graph.entity(relationId).removeMember(memberIndex);
26233 graph = graph.replace(relation);
26234 if (relation.isDegenerate()) graph = actionDeleteRelation(relation.id)(graph);
26239 function actionDiscardTags(difference, discardTags) {
26240 discardTags = discardTags || {};
26241 return function (graph) {
26242 difference.modified().forEach(checkTags);
26243 difference.created().forEach(checkTags);
26246 function checkTags(entity) {
26247 var keys = Object.keys(entity.tags);
26248 var didDiscard = false;
26251 for (var i = 0; i < keys.length; i++) {
26254 if (discardTags[k] || !entity.tags[k]) {
26257 tags[k] = entity.tags[k];
26262 graph = graph.replace(entity.update({
26271 // Optionally, disconnect only the given ways.
26273 // For testing convenience, accepts an ID to assign to the (first) new node.
26274 // Normally, this will be undefined and the way will automatically
26275 // be assigned a new ID.
26277 // This is the inverse of `iD.actionConnect`.
26280 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
26281 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
26284 function actionDisconnect(nodeId, newNodeId) {
26287 var action = function action(graph) {
26288 var node = graph.entity(nodeId);
26289 var connections = action.connections(graph);
26290 connections.forEach(function (connection) {
26291 var way = graph.entity(connection.wayID);
26292 var newNode = osmNode({
26297 graph = graph.replace(newNode);
26299 if (connection.index === 0 && way.isArea()) {
26300 // replace shared node with shared node..
26301 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
26302 } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
26303 // replace closing node with new new node..
26304 graph = graph.replace(way.unclose().addNode(newNode.id));
26306 // replace shared node with multiple new nodes..
26307 graph = graph.replace(way.updateNode(newNode.id, connection.index));
26313 action.connections = function (graph) {
26314 var candidates = [];
26315 var keeping = false;
26316 var parentWays = graph.parentWays(graph.entity(nodeId));
26319 for (var i = 0; i < parentWays.length; i++) {
26320 way = parentWays[i];
26322 if (wayIds && wayIds.indexOf(way.id) === -1) {
26327 if (way.isArea() && way.nodes[0] === nodeId) {
26333 for (var j = 0; j < way.nodes.length; j++) {
26334 waynode = way.nodes[j];
26336 if (waynode === nodeId) {
26337 if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
26350 return keeping ? candidates : candidates.slice(1);
26353 action.disabled = function (graph) {
26354 var connections = action.connections(graph);
26355 if (connections.length === 0) return 'not_connected';
26356 var parentWays = graph.parentWays(graph.entity(nodeId));
26357 var seenRelationIds = {};
26358 var sharedRelation;
26359 parentWays.forEach(function (way) {
26360 var relations = graph.parentRelations(way);
26361 relations.forEach(function (relation) {
26362 if (relation.id in seenRelationIds) {
26364 if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
26365 sharedRelation = relation;
26368 sharedRelation = relation;
26371 seenRelationIds[relation.id] = way.id;
26375 if (sharedRelation) return 'relation';
26378 action.limitWays = function (val) {
26379 if (!arguments.length) return wayIds;
26387 var geojsonRewind = rewind;
26389 function rewind(gj, outer) {
26390 var type = gj && gj.type,
26393 if (type === 'FeatureCollection') {
26394 for (i = 0; i < gj.features.length; i++) {
26395 rewind(gj.features[i], outer);
26397 } else if (type === 'GeometryCollection') {
26398 for (i = 0; i < gj.geometries.length; i++) {
26399 rewind(gj.geometries[i], outer);
26401 } else if (type === 'Feature') {
26402 rewind(gj.geometry, outer);
26403 } else if (type === 'Polygon') {
26404 rewindRings(gj.coordinates, outer);
26405 } else if (type === 'MultiPolygon') {
26406 for (i = 0; i < gj.coordinates.length; i++) {
26407 rewindRings(gj.coordinates[i], outer);
26414 function rewindRings(rings, outer) {
26415 if (rings.length === 0) return;
26416 rewindRing(rings[0], outer);
26418 for (var i = 1; i < rings.length; i++) {
26419 rewindRing(rings[i], !outer);
26423 function rewindRing(ring, dir) {
26426 for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
26427 area += (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
26430 if (area >= 0 !== !!dir) ring.reverse();
26433 function actionExtract(entityID) {
26434 var extractedNodeID;
26436 var action = function action(graph) {
26437 var entity = graph.entity(entityID);
26439 if (entity.type === 'node') {
26440 return extractFromNode(entity, graph);
26443 return extractFromWayOrRelation(entity, graph);
26446 function extractFromNode(node, graph) {
26447 extractedNodeID = node.id; // Create a new node to replace the one we will detach
26449 var replacement = osmNode({
26452 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
26454 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
26455 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
26456 }, graph); // Process any relations too
26458 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
26459 return accGraph.replace(parentRel.replaceMember(node, replacement));
26463 function extractFromWayOrRelation(entity, graph) {
26464 var fromGeometry = entity.geometry(graph);
26465 var keysToCopyAndRetain = ['source', 'wheelchair'];
26466 var keysToRetain = ['area'];
26467 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
26469 var extractedLoc = d3_geoCentroid(geojsonRewind(Object.assign({}, entity.asGeoJSON(graph)), true));
26471 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
26472 extractedLoc = entity.extent(graph).center();
26475 var indoorAreaValues = {
26482 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
26483 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
26484 var entityTags = Object.assign({}, entity.tags); // shallow copy
26486 var pointTags = {};
26488 for (var key in entityTags) {
26489 if (entity.type === 'relation' && key === 'type') {
26493 if (keysToRetain.indexOf(key) !== -1) {
26498 // don't transfer building-related tags
26499 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
26500 } // leave `indoor` tag on the area
26503 if (isIndoorArea && key === 'indoor') {
26505 } // copy the tag from the entity to the point
26508 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
26510 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
26512 } else if (isIndoorArea && key === 'level') {
26513 // leave `level` on both features
26515 } // remove the tag from the entity
26518 delete entityTags[key];
26521 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
26522 // ensure that areas keep area geometry
26523 entityTags.area = 'yes';
26526 var replacement = osmNode({
26530 graph = graph.replace(replacement);
26531 extractedNodeID = replacement.id;
26532 return graph.replace(entity.update({
26537 action.getExtractedNodeID = function () {
26538 return extractedNodeID;
26545 // This is the inverse of `iD.actionSplit`.
26548 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
26549 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
26552 function actionJoin(ids) {
26553 function groupEntitiesByGeometry(graph) {
26554 var entities = ids.map(function (id) {
26555 return graph.entity(id);
26557 return Object.assign({
26559 }, utilArrayGroupBy(entities, function (entity) {
26560 return entity.geometry(graph);
26564 var action = function action(graph) {
26565 var ways = ids.map(graph.entity, graph);
26566 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
26567 // sort them first so they establish the overall order - #6033
26569 ways.sort(function (a, b) {
26570 var aSided = a.isSided();
26571 var bSided = b.isSided();
26572 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
26573 }); // Prefer to keep an existing way.
26575 for (var i = 0; i < ways.length; i++) {
26576 if (!ways[i].isNew()) {
26577 survivorID = ways[i].id;
26582 var sequences = osmJoinWays(ways, graph);
26583 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
26584 // `joined.actions` property will contain any actions we need to apply.
26586 graph = sequences.actions.reduce(function (g, action) {
26589 var survivor = graph.entity(survivorID);
26590 survivor = survivor.update({
26591 nodes: joined.nodes.map(function (n) {
26595 graph = graph.replace(survivor);
26596 joined.forEach(function (way) {
26597 if (way.id === survivorID) return;
26598 graph.parentRelations(way).forEach(function (parent) {
26599 graph = graph.replace(parent.replaceMember(way, survivor));
26601 survivor = survivor.mergeTags(way.tags);
26602 graph = graph.replace(survivor);
26603 graph = actionDeleteWay(way.id)(graph);
26604 }); // Finds if the join created a single-member multipolygon,
26605 // and if so turns it into a basic area instead
26607 function checkForSimpleMultipolygon() {
26608 if (!survivor.isClosed()) return;
26609 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
26610 // find multipolygons where the survivor is the only member
26611 return multipolygon.members.length === 1;
26612 }); // skip if this is the single member of multiple multipolygons
26614 if (multipolygons.length !== 1) return;
26615 var multipolygon = multipolygons[0];
26617 for (var key in survivor.tags) {
26618 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
26619 multipolygon.tags[key] !== survivor.tags[key]) return;
26622 survivor = survivor.mergeTags(multipolygon.tags);
26623 graph = graph.replace(survivor);
26624 graph = actionDeleteRelation(multipolygon.id, true
26625 /* allow untagged members */
26627 var tags = Object.assign({}, survivor.tags);
26629 if (survivor.geometry(graph) !== 'area') {
26630 // ensure the feature persists as an area
26634 delete tags.type; // remove type=multipolygon
26636 survivor = survivor.update({
26639 graph = graph.replace(survivor);
26642 checkForSimpleMultipolygon();
26644 }; // Returns the number of nodes the resultant way is expected to have
26647 action.resultingWayNodesLength = function (graph) {
26648 return ids.reduce(function (count, id) {
26649 return count + graph.entity(id).nodes.length;
26650 }, 0) - ids.length - 1;
26653 action.disabled = function (graph) {
26654 var geometries = groupEntitiesByGeometry(graph);
26656 if (ids.length < 2 || ids.length !== geometries.line.length) {
26657 return 'not_eligible';
26660 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
26662 if (joined.length > 1) {
26663 return 'not_adjacent';
26664 } // Loop through all combinations of path-pairs
26665 // to check potential intersections between all pairs
26668 for (var i = 0; i < ids.length - 1; i++) {
26669 for (var j = i + 1; j < ids.length; j++) {
26670 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
26673 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
26676 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
26677 // each other/the line, as opposed to crossing it
26679 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
26680 return n.loc.toString();
26681 }), intersections.map(function (n) {
26682 return n.toString();
26685 if (common.length !== intersections.length) {
26686 return 'paths_intersect';
26691 var nodeIds = joined[0].nodes.map(function (n) {
26696 var conflicting = false;
26697 joined[0].forEach(function (way) {
26698 var parents = graph.parentRelations(way);
26699 parents.forEach(function (parent) {
26700 if (parent.isRestriction() && parent.members.some(function (m) {
26701 return nodeIds.indexOf(m.id) >= 0;
26707 for (var k in way.tags) {
26708 if (!(k in tags)) {
26709 tags[k] = way.tags[k];
26710 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
26711 conflicting = true;
26717 return 'restriction';
26721 return 'conflicting_tags';
26728 function actionMerge(ids) {
26729 function groupEntitiesByGeometry(graph) {
26730 var entities = ids.map(function (id) {
26731 return graph.entity(id);
26733 return Object.assign({
26738 }, utilArrayGroupBy(entities, function (entity) {
26739 return entity.geometry(graph);
26743 var action = function action(graph) {
26744 var geometries = groupEntitiesByGeometry(graph);
26745 var target = geometries.area[0] || geometries.line[0];
26746 var points = geometries.point;
26747 points.forEach(function (point) {
26748 target = target.mergeTags(point.tags);
26749 graph = graph.replace(target);
26750 graph.parentRelations(point).forEach(function (parent) {
26751 graph = graph.replace(parent.replaceMember(point, target));
26753 var nodes = utilArrayUniq(graph.childNodes(target));
26754 var removeNode = point;
26756 for (var i = 0; i < nodes.length; i++) {
26757 var node = nodes[i];
26759 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
26761 } // Found an uninteresting child node on the target way.
26762 // Move orig point into its place to preserve point's history. #3683
26765 graph = graph.replace(point.update({
26769 target = target.replaceNode(node.id, point.id);
26770 graph = graph.replace(target);
26775 graph = graph.remove(removeNode);
26778 if (target.tags.area === 'yes') {
26779 var tags = Object.assign({}, target.tags); // shallow copy
26783 if (osmTagSuggestingArea(tags)) {
26784 // remove the `area` tag if area geometry is now implied - #3851
26785 target = target.update({
26788 graph = graph.replace(target);
26795 action.disabled = function (graph) {
26796 var geometries = groupEntitiesByGeometry(graph);
26798 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
26799 return 'not_eligible';
26807 // 1. move all the nodes to a common location
26808 // 2. `actionConnect` them
26810 function actionMergeNodes(nodeIDs, loc) {
26811 // If there is a single "interesting" node, use that as the location.
26812 // Otherwise return the average location of all the nodes.
26813 function chooseLoc(graph) {
26814 if (!nodeIDs.length) return null;
26816 var interestingCount = 0;
26817 var interestingLoc;
26819 for (var i = 0; i < nodeIDs.length; i++) {
26820 var node = graph.entity(nodeIDs[i]);
26822 if (node.hasInterestingTags()) {
26823 interestingLoc = ++interestingCount === 1 ? node.loc : null;
26826 sum = geoVecAdd(sum, node.loc);
26829 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
26832 var action = function action(graph) {
26833 if (nodeIDs.length < 2) return graph;
26837 toLoc = chooseLoc(graph);
26840 for (var i = 0; i < nodeIDs.length; i++) {
26841 var node = graph.entity(nodeIDs[i]);
26843 if (node.loc !== toLoc) {
26844 graph = graph.replace(node.move(toLoc));
26848 return actionConnect(nodeIDs)(graph);
26851 action.disabled = function (graph) {
26852 if (nodeIDs.length < 2) return 'not_eligible';
26854 for (var i = 0; i < nodeIDs.length; i++) {
26855 var entity = graph.entity(nodeIDs[i]);
26856 if (entity.type !== 'node') return 'not_eligible';
26859 return actionConnect(nodeIDs).disabled(graph);
26865 function osmChangeset() {
26866 if (!(this instanceof osmChangeset)) {
26867 return new osmChangeset().initialize(arguments);
26868 } else if (arguments.length) {
26869 this.initialize(arguments);
26872 osmEntity.changeset = osmChangeset;
26873 osmChangeset.prototype = Object.create(osmEntity.prototype);
26874 Object.assign(osmChangeset.prototype, {
26876 extent: function extent() {
26877 return new geoExtent();
26879 geometry: function geometry() {
26880 return 'changeset';
26882 asJXON: function asJXON() {
26886 tag: Object.keys(this.tags).map(function (k) {
26898 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26899 // XML. Returns a string.
26900 osmChangeJXON: function osmChangeJXON(changes) {
26901 var changeset_id = this.id;
26903 function nest(x, order) {
26906 for (var i = 0; i < x.length; i++) {
26907 var tagName = Object.keys(x[i])[0];
26908 if (!groups[tagName]) groups[tagName] = [];
26909 groups[tagName].push(x[i][tagName]);
26913 order.forEach(function (o) {
26914 if (groups[o]) ordered[o] = groups[o];
26917 } // sort relations in a changeset by dependencies
26920 function sort(changes) {
26921 // find a referenced relation in the current changeset
26922 function resolve(item) {
26923 return relations.find(function (relation) {
26924 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
26926 } // a new item is an item that has not been already processed
26929 function isNew(item) {
26930 return !sorted[item['@id']] && !processing.find(function (proc) {
26931 return proc['@id'] === item['@id'];
26935 var processing = [];
26937 var relations = changes.relation;
26938 if (!relations) return changes;
26940 for (var i = 0; i < relations.length; i++) {
26941 var relation = relations[i]; // skip relation if already sorted
26943 if (!sorted[relation['@id']]) {
26944 processing.push(relation);
26947 while (processing.length > 0) {
26948 var next = processing[0],
26949 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
26951 if (deps.length === 0) {
26952 sorted[next['@id']] = next;
26953 processing.shift();
26955 processing = deps.concat(processing);
26960 changes.relation = Object.values(sorted);
26964 function rep(entity) {
26965 return entity.asJXON(changeset_id);
26971 '@generator': 'iD',
26972 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
26973 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26974 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
26980 asGeoJSON: function asGeoJSON() {
26985 function osmNote() {
26986 if (!(this instanceof osmNote)) {
26987 return new osmNote().initialize(arguments);
26988 } else if (arguments.length) {
26989 this.initialize(arguments);
26993 osmNote.id = function () {
26994 return osmNote.id.next--;
26997 osmNote.id.next = -1;
26998 Object.assign(osmNote.prototype, {
27000 initialize: function initialize(sources) {
27001 for (var i = 0; i < sources.length; ++i) {
27002 var source = sources[i];
27004 for (var prop in source) {
27005 if (Object.prototype.hasOwnProperty.call(source, prop)) {
27006 if (source[prop] === undefined) {
27009 this[prop] = source[prop];
27016 this.id = osmNote.id().toString();
27021 extent: function extent() {
27022 return new geoExtent(this.loc);
27024 update: function update(attrs) {
27025 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
27027 isNew: function isNew() {
27028 return this.id < 0;
27030 move: function move(loc) {
27031 return this.update({
27037 function osmRelation() {
27038 if (!(this instanceof osmRelation)) {
27039 return new osmRelation().initialize(arguments);
27040 } else if (arguments.length) {
27041 this.initialize(arguments);
27044 osmEntity.relation = osmRelation;
27045 osmRelation.prototype = Object.create(osmEntity.prototype);
27047 osmRelation.creationOrder = function (a, b) {
27048 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
27049 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
27050 if (aId < 0 || bId < 0) return aId - bId;
27054 Object.assign(osmRelation.prototype, {
27057 copy: function copy(resolver, copies) {
27058 if (copies[this.id]) return copies[this.id];
27059 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
27060 var members = this.members.map(function (member) {
27061 return Object.assign({}, member, {
27062 id: resolver.entity(member.id).copy(resolver, copies).id
27065 copy = copy.update({
27068 copies[this.id] = copy;
27071 extent: function extent(resolver, memo) {
27072 return resolver["transient"](this, 'extent', function () {
27073 if (memo && memo[this.id]) return geoExtent();
27075 memo[this.id] = true;
27076 var extent = geoExtent();
27078 for (var i = 0; i < this.members.length; i++) {
27079 var member = resolver.hasEntity(this.members[i].id);
27082 extent._extend(member.extent(resolver, memo));
27089 geometry: function geometry(graph) {
27090 return graph["transient"](this, 'geometry', function () {
27091 return this.isMultipolygon() ? 'area' : 'relation';
27094 isDegenerate: function isDegenerate() {
27095 return this.members.length === 0;
27097 // Return an array of members, each extended with an 'index' property whose value
27098 // is the member index.
27099 indexedMembers: function indexedMembers() {
27100 var result = new Array(this.members.length);
27102 for (var i = 0; i < this.members.length; i++) {
27103 result[i] = Object.assign({}, this.members[i], {
27110 // Return the first member with the given role. A copy of the member object
27111 // is returned, extended with an 'index' property whose value is the member index.
27112 memberByRole: function memberByRole(role) {
27113 for (var i = 0; i < this.members.length; i++) {
27114 if (this.members[i].role === role) {
27115 return Object.assign({}, this.members[i], {
27121 // Same as memberByRole, but returns all members with the given role
27122 membersByRole: function membersByRole(role) {
27125 for (var i = 0; i < this.members.length; i++) {
27126 if (this.members[i].role === role) {
27127 result.push(Object.assign({}, this.members[i], {
27135 // Return the first member with the given id. A copy of the member object
27136 // is returned, extended with an 'index' property whose value is the member index.
27137 memberById: function memberById(id) {
27138 for (var i = 0; i < this.members.length; i++) {
27139 if (this.members[i].id === id) {
27140 return Object.assign({}, this.members[i], {
27146 // Return the first member with the given id and role. A copy of the member object
27147 // is returned, extended with an 'index' property whose value is the member index.
27148 memberByIdAndRole: function memberByIdAndRole(id, role) {
27149 for (var i = 0; i < this.members.length; i++) {
27150 if (this.members[i].id === id && this.members[i].role === role) {
27151 return Object.assign({}, this.members[i], {
27157 addMember: function addMember(member, index) {
27158 var members = this.members.slice();
27159 members.splice(index === undefined ? members.length : index, 0, member);
27160 return this.update({
27164 updateMember: function updateMember(member, index) {
27165 var members = this.members.slice();
27166 members.splice(index, 1, Object.assign({}, members[index], member));
27167 return this.update({
27171 removeMember: function removeMember(index) {
27172 var members = this.members.slice();
27173 members.splice(index, 1);
27174 return this.update({
27178 removeMembersWithID: function removeMembersWithID(id) {
27179 var members = this.members.filter(function (m) {
27180 return m.id !== id;
27182 return this.update({
27186 moveMember: function moveMember(fromIndex, toIndex) {
27187 var members = this.members.slice();
27188 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
27189 return this.update({
27193 // Wherever a member appears with id `needle.id`, replace it with a member
27194 // with id `replacement.id`, type `replacement.type`, and the original role,
27195 // By default, adding a duplicate member (by id and role) is prevented.
27196 // Return an updated relation.
27197 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
27198 if (!this.memberById(needle.id)) return this;
27201 for (var i = 0; i < this.members.length; i++) {
27202 var member = this.members[i];
27204 if (member.id !== needle.id) {
27205 members.push(member);
27206 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
27208 id: replacement.id,
27209 type: replacement.type,
27215 return this.update({
27219 asJXON: function asJXON(changeset_id) {
27222 '@id': this.osmId(),
27223 '@version': this.version || 0,
27224 member: this.members.map(function (member) {
27229 ref: osmEntity.id.toOSM(member.id)
27233 tag: Object.keys(this.tags).map(function (k) {
27244 if (changeset_id) {
27245 r.relation['@changeset'] = changeset_id;
27250 asGeoJSON: function asGeoJSON(resolver) {
27251 return resolver["transient"](this, 'GeoJSON', function () {
27252 if (this.isMultipolygon()) {
27254 type: 'MultiPolygon',
27255 coordinates: this.multipolygon(resolver)
27259 type: 'FeatureCollection',
27260 properties: this.tags,
27261 features: this.members.map(function (member) {
27262 return Object.assign({
27264 }, resolver.entity(member.id).asGeoJSON(resolver));
27270 area: function area(resolver) {
27271 return resolver["transient"](this, 'area', function () {
27272 return d3_geoArea(this.asGeoJSON(resolver));
27275 isMultipolygon: function isMultipolygon() {
27276 return this.tags.type === 'multipolygon';
27278 isComplete: function isComplete(resolver) {
27279 for (var i = 0; i < this.members.length; i++) {
27280 if (!resolver.hasEntity(this.members[i].id)) {
27287 hasFromViaTo: function hasFromViaTo() {
27288 return this.members.some(function (m) {
27289 return m.role === 'from';
27290 }) && this.members.some(function (m) {
27291 return m.role === 'via';
27292 }) && this.members.some(function (m) {
27293 return m.role === 'to';
27296 isRestriction: function isRestriction() {
27297 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27299 isValidRestriction: function isValidRestriction() {
27300 if (!this.isRestriction()) return false;
27301 var froms = this.members.filter(function (m) {
27302 return m.role === 'from';
27304 var vias = this.members.filter(function (m) {
27305 return m.role === 'via';
27307 var tos = this.members.filter(function (m) {
27308 return m.role === 'to';
27310 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
27311 if (froms.some(function (m) {
27312 return m.type !== 'way';
27314 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
27315 if (tos.some(function (m) {
27316 return m.type !== 'way';
27318 if (vias.length === 0) return false;
27319 if (vias.length > 1 && vias.some(function (m) {
27320 return m.type !== 'way';
27324 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27325 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27327 // This corresponds to the structure needed for rendering a multipolygon path using a
27328 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27330 // In the case of invalid geometries, this function will still return a result which
27331 // includes the nodes of all way members, but some Nds may be unclosed and some inner
27332 // rings not matched with the intended outer ring.
27334 multipolygon: function multipolygon(resolver) {
27335 var outers = this.members.filter(function (m) {
27336 return 'outer' === (m.role || 'outer');
27338 var inners = this.members.filter(function (m) {
27339 return 'inner' === m.role;
27341 outers = osmJoinWays(outers, resolver);
27342 inners = osmJoinWays(inners, resolver);
27344 var sequenceToLineString = function sequenceToLineString(sequence) {
27345 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
27346 // close unclosed parts to ensure correct area rendering - #2945
27347 sequence.nodes.push(sequence.nodes[0]);
27350 return sequence.nodes.map(function (node) {
27355 outers = outers.map(sequenceToLineString);
27356 inners = inners.map(sequenceToLineString);
27357 var result = outers.map(function (o) {
27358 // Heuristic for detecting counterclockwise winding order. Assumes
27359 // that OpenStreetMap polygons are not hemisphere-spanning.
27360 return [d3_geoArea({
27363 }) > 2 * Math.PI ? o.reverse() : o];
27366 function findOuter(inner) {
27369 for (o = 0; o < outers.length; o++) {
27371 if (geoPolygonContainsPolygon(outer, inner)) return o;
27374 for (o = 0; o < outers.length; o++) {
27376 if (geoPolygonIntersectsPolygon(outer, inner, false)) return o;
27380 for (var i = 0; i < inners.length; i++) {
27381 var inner = inners[i];
27385 coordinates: [inner]
27386 }) < 2 * Math.PI) {
27387 inner = inner.reverse();
27390 var o = findOuter(inners[i]);
27392 if (o !== undefined) {
27393 result[o].push(inners[i]);
27395 result.push([inners[i]]); // Invalid geometry
27403 var QAItem = /*#__PURE__*/function () {
27404 function QAItem(loc, service, itemType, id, props) {
27405 _classCallCheck(this, QAItem);
27407 // Store required properties
27409 this.service = service.title;
27410 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
27412 this.id = id ? id : "".concat(QAItem.id());
27413 this.update(props); // Some QA services have marker icons to differentiate issues
27415 if (service && typeof service.getIcon === 'function') {
27416 this.icon = service.getIcon(itemType);
27420 _createClass(QAItem, [{
27422 value: function update(props) {
27425 // You can't override this initial information
27426 var loc = this.loc,
27427 service = this.service,
27428 itemType = this.itemType,
27430 Object.keys(props).forEach(function (prop) {
27431 return _this[prop] = props[prop];
27434 this.service = service;
27435 this.itemType = itemType;
27438 } // Generic handling for newly created QAItems
27442 value: function id() {
27443 return this.nextId--;
27449 QAItem.nextId = -1;
27452 // Optionally, split only the given ways, if multiple ways share
27455 // This is the inverse of `iD.actionJoin`.
27457 // For testing convenience, accepts an ID to assign to the new way.
27458 // Normally, this will be undefined and the way will automatically
27459 // be assigned a new ID.
27462 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
27465 function actionSplit(nodeIds, newWayIds) {
27466 // accept single ID for backwards-compatiblity
27467 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
27469 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
27472 var _keepHistoryOn = 'longest'; // 'longest', 'first'
27473 // The IDs of the ways actually created by running this action
27475 var _createdWayIDs = [];
27477 function dist(graph, nA, nB) {
27478 var locA = graph.entity(nA).loc;
27479 var locB = graph.entity(nB).loc;
27480 var epsilon = 1e-6;
27481 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
27482 } // If the way is closed, we need to search for a partner node
27483 // to split the way at.
27485 // The following looks for a node that is both far away from
27486 // the initial node in terms of way segment length and nearby
27487 // in terms of beeline-distance. This assures that areas get
27488 // split on the most "natural" points (independent of the number
27490 // For example: bone-shaped areas get split across their waist
27491 // line, circles across the diameter.
27494 function splitArea(nodes, idxA, graph) {
27495 var lengths = new Array(nodes.length);
27501 function wrap(index) {
27502 return utilWrap(index, nodes.length);
27503 } // calculate lengths
27508 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
27509 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
27510 lengths[i] = length;
27515 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
27516 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
27518 if (length < lengths[i]) {
27519 lengths[i] = length;
27521 } // determine best opposite node to split
27524 for (i = 0; i < nodes.length; i++) {
27525 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
27536 function totalLengthBetweenNodes(graph, nodes) {
27537 var totalLength = 0;
27539 for (var i = 0; i < nodes.length - 1; i++) {
27540 totalLength += dist(graph, nodes[i], nodes[i + 1]);
27543 return totalLength;
27546 function split(graph, nodeId, wayA, newWayId) {
27547 var wayB = osmWay({
27550 }); // `wayB` is the NEW way
27552 var origNodes = wayA.nodes.slice();
27555 var isArea = wayA.isArea();
27556 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
27558 if (wayA.isClosed()) {
27559 var nodes = wayA.nodes.slice(0, -1);
27560 var idxA = nodes.indexOf(nodeId);
27561 var idxB = splitArea(nodes, idxA, graph);
27564 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
27565 nodesB = nodes.slice(idxB, idxA + 1);
27567 nodesA = nodes.slice(idxA, idxB + 1);
27568 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
27571 var idx = wayA.nodes.indexOf(nodeId, 1);
27572 nodesA = wayA.nodes.slice(0, idx + 1);
27573 nodesB = wayA.nodes.slice(idx);
27576 var lengthA = totalLengthBetweenNodes(graph, nodesA);
27577 var lengthB = totalLengthBetweenNodes(graph, nodesB);
27579 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
27580 // keep the history on the longer way, regardless of the node count
27581 wayA = wayA.update({
27584 wayB = wayB.update({
27587 var temp = lengthA;
27591 wayA = wayA.update({
27594 wayB = wayB.update({
27599 if (wayA.tags.step_count) {
27600 // divide up the the step count proportionally between the two ways
27601 var stepCount = parseFloat(wayA.tags.step_count);
27603 if (stepCount && // ensure a number
27604 isFinite(stepCount) && // ensure positive
27605 stepCount > 0 && // ensure integer
27606 Math.round(stepCount) === stepCount) {
27607 var tagsA = Object.assign({}, wayA.tags);
27608 var tagsB = Object.assign({}, wayB.tags);
27609 var ratioA = lengthA / (lengthA + lengthB);
27610 var countA = Math.round(stepCount * ratioA);
27611 tagsA.step_count = countA.toString();
27612 tagsB.step_count = (stepCount - countA).toString();
27613 wayA = wayA.update({
27616 wayB = wayB.update({
27622 graph = graph.replace(wayA);
27623 graph = graph.replace(wayB);
27624 graph.parentRelations(wayA).forEach(function (relation) {
27625 var member; // Turn restrictions - make sure:
27626 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
27627 // (whichever one is connected to the VIA node/ways)
27628 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
27630 if (relation.hasFromViaTo()) {
27631 var f = relation.memberByRole('from');
27632 var v = relation.membersByRole('via');
27633 var t = relation.memberByRole('to');
27634 var i; // 1. split a FROM/TO
27636 if (f.id === wayA.id || t.id === wayA.id) {
27639 if (v.length === 1 && v[0].type === 'node') {
27641 keepB = wayB.contains(v[0].id);
27643 // check via way(s)
27644 for (i = 0; i < v.length; i++) {
27645 if (v[i].type === 'way') {
27646 var wayVia = graph.hasEntity(v[i].id);
27648 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
27657 relation = relation.replaceMember(wayA, wayB);
27658 graph = graph.replace(relation);
27659 } // 2. split a VIA
27662 for (i = 0; i < v.length; i++) {
27663 if (v[i].type === 'way' && v[i].id === wayA.id) {
27669 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
27673 } // All other relations (Routes, Multipolygons, etc):
27674 // 1. Both `wayA` and `wayB` remain in the relation
27675 // 2. But must be inserted as a pair (see `actionAddMember` for details)
27678 if (relation === isOuter) {
27679 graph = graph.replace(relation.mergeTags(wayA.tags));
27680 graph = graph.replace(wayA.update({
27683 graph = graph.replace(wayB.update({
27691 role: relation.memberById(wayA.id).role
27694 originalID: wayA.id,
27695 insertedID: wayB.id,
27698 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
27702 if (!isOuter && isArea) {
27703 var multipolygon = osmRelation({
27704 tags: Object.assign({}, wayA.tags, {
27705 type: 'multipolygon'
27717 graph = graph.replace(multipolygon);
27718 graph = graph.replace(wayA.update({
27721 graph = graph.replace(wayB.update({
27726 _createdWayIDs.push(wayB.id);
27731 var action = function action(graph) {
27732 _createdWayIDs = [];
27733 var newWayIndex = 0;
27735 for (var i = 0; i < nodeIds.length; i++) {
27736 var nodeId = nodeIds[i];
27737 var candidates = action.waysForNode(nodeId, graph);
27739 for (var j = 0; j < candidates.length; j++) {
27740 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
27748 action.getCreatedWayIDs = function () {
27749 return _createdWayIDs;
27752 action.waysForNode = function (nodeId, graph) {
27753 var node = graph.entity(nodeId);
27754 var splittableParents = graph.parentWays(node).filter(isSplittable);
27757 // If the ways to split aren't specified, only split the lines.
27758 // If there are no lines to split, split the areas.
27759 var hasLine = splittableParents.some(function (parent) {
27760 return parent.geometry(graph) === 'line';
27764 return splittableParents.filter(function (parent) {
27765 return parent.geometry(graph) === 'line';
27770 return splittableParents;
27772 function isSplittable(parent) {
27773 // If the ways to split are specified, ignore everything else.
27774 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
27776 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
27778 for (var i = 1; i < parent.nodes.length - 1; i++) {
27779 if (parent.nodes[i] === nodeId) return true;
27786 action.ways = function (graph) {
27787 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
27788 return action.waysForNode(nodeId, graph);
27792 action.disabled = function (graph) {
27793 for (var i = 0; i < nodeIds.length; i++) {
27794 var nodeId = nodeIds[i];
27795 var candidates = action.waysForNode(nodeId, graph);
27797 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
27798 return 'not_eligible';
27803 action.limitWays = function (val) {
27804 if (!arguments.length) return _wayIDs;
27809 action.keepHistoryOn = function (val) {
27810 if (!arguments.length) return _keepHistoryOn;
27811 _keepHistoryOn = val;
27818 function coreGraph(other, mutable) {
27819 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
27821 if (other instanceof coreGraph) {
27822 var base = other.base();
27823 this.entities = Object.assign(Object.create(base.entities), other.entities);
27824 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
27825 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
27827 this.entities = Object.create({});
27828 this._parentWays = Object.create({});
27829 this._parentRels = Object.create({});
27830 this.rebase(other || [], [this]);
27833 this.transients = {};
27834 this._childNodes = {};
27835 this.frozen = !mutable;
27837 coreGraph.prototype = {
27838 hasEntity: function hasEntity(id) {
27839 return this.entities[id];
27841 entity: function entity(id) {
27842 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
27845 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
27849 throw new Error('entity ' + id + ' not found');
27854 geometry: function geometry(id) {
27855 return this.entity(id).geometry(this);
27857 "transient": function transient(entity, key, fn) {
27858 var id = entity.id;
27859 var transients = this.transients[id] || (this.transients[id] = {});
27861 if (transients[key] !== undefined) {
27862 return transients[key];
27865 transients[key] = fn.call(entity);
27866 return transients[key];
27868 parentWays: function parentWays(entity) {
27869 var parents = this._parentWays[entity.id];
27873 parents.forEach(function (id) {
27874 result.push(this.entity(id));
27880 isPoi: function isPoi(entity) {
27881 var parents = this._parentWays[entity.id];
27882 return !parents || parents.size === 0;
27884 isShared: function isShared(entity) {
27885 var parents = this._parentWays[entity.id];
27886 return parents && parents.size > 1;
27888 parentRelations: function parentRelations(entity) {
27889 var parents = this._parentRels[entity.id];
27893 parents.forEach(function (id) {
27894 result.push(this.entity(id));
27900 parentMultipolygons: function parentMultipolygons(entity) {
27901 return this.parentRelations(entity).filter(function (relation) {
27902 return relation.isMultipolygon();
27905 childNodes: function childNodes(entity) {
27906 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27907 if (!entity.nodes) return [];
27910 for (var i = 0; i < entity.nodes.length; i++) {
27911 nodes[i] = this.entity(entity.nodes[i]);
27913 this._childNodes[entity.id] = nodes;
27914 return this._childNodes[entity.id];
27916 base: function base() {
27918 'entities': Object.getPrototypeOf(this.entities),
27919 'parentWays': Object.getPrototypeOf(this._parentWays),
27920 'parentRels': Object.getPrototypeOf(this._parentRels)
27923 // Unlike other graph methods, rebase mutates in place. This is because it
27924 // is used only during the history operation that merges newly downloaded
27925 // data into each state. To external consumers, it should appear as if the
27926 // graph always contained the newly downloaded data.
27927 rebase: function rebase(entities, stack, force) {
27928 var base = this.base();
27931 for (i = 0; i < entities.length; i++) {
27932 var entity = entities[i];
27933 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
27935 base.entities[entity.id] = entity;
27937 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
27940 if (entity.type === 'way') {
27941 for (j = 0; j < entity.nodes.length; j++) {
27942 id = entity.nodes[j];
27944 for (k = 1; k < stack.length; k++) {
27945 var ents = stack[k].entities;
27947 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27955 for (i = 0; i < stack.length; i++) {
27956 stack[i]._updateRebased();
27959 _updateRebased: function _updateRebased() {
27960 var base = this.base();
27961 Object.keys(this._parentWays).forEach(function (child) {
27962 if (base.parentWays[child]) {
27963 base.parentWays[child].forEach(function (id) {
27964 if (!this.entities.hasOwnProperty(id)) {
27965 this._parentWays[child].add(id);
27970 Object.keys(this._parentRels).forEach(function (child) {
27971 if (base.parentRels[child]) {
27972 base.parentRels[child].forEach(function (id) {
27973 if (!this.entities.hasOwnProperty(id)) {
27974 this._parentRels[child].add(id);
27979 this.transients = {}; // this._childNodes is not updated, under the assumption that
27980 // ways are always downloaded with their child nodes.
27982 // Updates calculated properties (parentWays, parentRels) for the specified change
27983 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
27984 parentWays = parentWays || this._parentWays;
27985 parentRels = parentRels || this._parentRels;
27986 var type = entity && entity.type || oldentity && oldentity.type;
27987 var removed, added, i;
27989 if (type === 'way') {
27990 // Update parentWays
27991 if (oldentity && entity) {
27992 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
27993 added = utilArrayDifference(entity.nodes, oldentity.nodes);
27994 } else if (oldentity) {
27995 removed = oldentity.nodes;
27997 } else if (entity) {
27999 added = entity.nodes;
28002 for (i = 0; i < removed.length; i++) {
28003 // make a copy of prototype property, store as own property, and update..
28004 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
28005 parentWays[removed[i]]["delete"](oldentity.id);
28008 for (i = 0; i < added.length; i++) {
28009 // make a copy of prototype property, store as own property, and update..
28010 parentWays[added[i]] = new Set(parentWays[added[i]]);
28011 parentWays[added[i]].add(entity.id);
28013 } else if (type === 'relation') {
28014 // Update parentRels
28015 // diff only on the IDs since the same entity can be a member multiple times with different roles
28016 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
28019 var entityMemberIDs = entity ? entity.members.map(function (m) {
28023 if (oldentity && entity) {
28024 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
28025 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
28026 } else if (oldentity) {
28027 removed = oldentityMemberIDs;
28029 } else if (entity) {
28031 added = entityMemberIDs;
28034 for (i = 0; i < removed.length; i++) {
28035 // make a copy of prototype property, store as own property, and update..
28036 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
28037 parentRels[removed[i]]["delete"](oldentity.id);
28040 for (i = 0; i < added.length; i++) {
28041 // make a copy of prototype property, store as own property, and update..
28042 parentRels[added[i]] = new Set(parentRels[added[i]]);
28043 parentRels[added[i]].add(entity.id);
28047 replace: function replace(entity) {
28048 if (this.entities[entity.id] === entity) return this;
28049 return this.update(function () {
28050 this._updateCalculated(this.entities[entity.id], entity);
28052 this.entities[entity.id] = entity;
28055 remove: function remove(entity) {
28056 return this.update(function () {
28057 this._updateCalculated(entity, undefined);
28059 this.entities[entity.id] = undefined;
28062 revert: function revert(id) {
28063 var baseEntity = this.base().entities[id];
28064 var headEntity = this.entities[id];
28065 if (headEntity === baseEntity) return this;
28066 return this.update(function () {
28067 this._updateCalculated(headEntity, baseEntity);
28069 delete this.entities[id];
28072 update: function update() {
28073 var graph = this.frozen ? coreGraph(this, true) : this;
28075 for (var i = 0; i < arguments.length; i++) {
28076 arguments[i].call(graph, graph);
28079 if (this.frozen) graph.frozen = true;
28082 // Obliterates any existing entities
28083 load: function load(entities) {
28084 var base = this.base();
28085 this.entities = Object.create(base.entities);
28087 for (var i in entities) {
28088 this.entities[i] = entities[i];
28090 this._updateCalculated(base.entities[i], this.entities[i]);
28097 function osmTurn(turn) {
28098 if (!(this instanceof osmTurn)) {
28099 return new osmTurn(turn);
28102 Object.assign(this, turn);
28104 function osmIntersection(graph, startVertexId, maxDistance) {
28105 maxDistance = maxDistance || 30; // in meters
28107 var vgraph = coreGraph(); // virtual graph
28111 function memberOfRestriction(entity) {
28112 return graph.parentRelations(entity).some(function (r) {
28113 return r.isRestriction();
28117 function isRoad(way) {
28118 if (way.isArea() || way.isDegenerate()) return false;
28121 'motorway_link': true,
28123 'trunk_link': true,
28125 'primary_link': true,
28127 'secondary_link': true,
28129 'tertiary_link': true,
28130 'residential': true,
28131 'unclassified': true,
28132 'living_street': true,
28137 return roads[way.tags.highway];
28140 var startNode = graph.entity(startVertexId);
28141 var checkVertices = [startNode];
28144 var vertexIds = [];
28152 var parent; // `actions` will store whatever actions must be performed to satisfy
28153 // preconditions for adding a turn restriction to this intersection.
28154 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
28155 // - Reverse oneways so that they are drawn in the forward direction
28156 // - Split ways on key vertices
28158 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
28159 // for more key vertices and ways to include in the intersection..
28161 while (checkVertices.length) {
28162 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
28164 checkWays = graph.parentWays(vertex);
28165 var hasWays = false;
28167 for (i = 0; i < checkWays.length; i++) {
28168 way = checkWays[i];
28169 if (!isRoad(way) && !memberOfRestriction(way)) continue;
28170 ways.push(way); // it's a road, or it's already in a turn restriction
28172 hasWays = true; // check the way's children for more key vertices
28174 nodes = utilArrayUniq(graph.childNodes(way));
28176 for (j = 0; j < nodes.length; j++) {
28178 if (node === vertex) continue; // same thing
28180 if (vertices.indexOf(node) !== -1) continue; // seen it already
28182 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
28183 // a key vertex will have parents that are also roads
28185 var hasParents = false;
28186 parents = graph.parentWays(node);
28188 for (k = 0; k < parents.length; k++) {
28189 parent = parents[k];
28190 if (parent === way) continue; // same thing
28192 if (ways.indexOf(parent) !== -1) continue; // seen it already
28194 if (!isRoad(parent)) continue; // not a road
28201 checkVertices.push(node);
28207 vertices.push(vertex);
28211 vertices = utilArrayUniq(vertices);
28212 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
28213 // Everything done after this step should act on the virtual graph
28214 // Any actions that must be performed later to the main graph go in `actions` array
28216 ways.forEach(function (way) {
28217 graph.childNodes(way).forEach(function (node) {
28218 vgraph = vgraph.replace(node);
28220 vgraph = vgraph.replace(way);
28221 graph.parentRelations(way).forEach(function (relation) {
28222 if (relation.isRestriction()) {
28223 if (relation.isValidRestriction(graph)) {
28224 vgraph = vgraph.replace(relation);
28225 } else if (relation.isComplete(graph)) {
28226 actions.push(actionDeleteRelation(relation.id));
28230 }); // STEP 3: Force all oneways to be drawn in the forward direction
28232 ways.forEach(function (w) {
28233 var way = vgraph.entity(w.id);
28235 if (way.tags.oneway === '-1') {
28236 var action = actionReverse(way.id, {
28237 reverseOneway: true
28239 actions.push(action);
28240 vgraph = action(vgraph);
28242 }); // STEP 4: Split ways on key vertices
28244 var origCount = osmEntity.id.next.way;
28245 vertices.forEach(function (v) {
28246 // This is an odd way to do it, but we need to find all the ways that
28247 // will be split here, then split them one at a time to ensure that these
28248 // actions can be replayed on the main graph exactly in the same order.
28249 // (It is unintuitive, but the order of ways returned from graph.parentWays()
28250 // is arbitrary, depending on how the main graph and vgraph were built)
28251 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
28253 if (!splitAll.disabled(vgraph)) {
28254 splitAll.ways(vgraph).forEach(function (way) {
28255 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
28256 actions.push(splitOne);
28257 vgraph = splitOne(vgraph);
28260 }); // In here is where we should also split the intersection at nearby junction.
28261 // for https://github.com/mapbox/iD-internal/issues/31
28262 // nearbyVertices.forEach(function(v) {
28264 // Reasons why we reset the way id count here:
28265 // 1. Continuity with way ids created by the splits so that we can replay
28266 // these actions later if the user decides to create a turn restriction
28267 // 2. Avoids churning way ids just by hovering over a vertex
28268 // and displaying the turn restriction editor
28270 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
28272 vertexIds = vertices.map(function (v) {
28277 vertexIds.forEach(function (id) {
28278 var vertex = vgraph.entity(id);
28279 var parents = vgraph.parentWays(vertex);
28280 vertices.push(vertex);
28281 ways = ways.concat(parents);
28283 vertices = utilArrayUniq(vertices);
28284 ways = utilArrayUniq(ways);
28285 vertexIds = vertices.map(function (v) {
28288 wayIds = ways.map(function (w) {
28290 }); // STEP 6: Update the ways with some metadata that will be useful for
28291 // walking the intersection graph later and rendering turn arrows.
28293 function withMetadata(way, vertexIds) {
28294 var __oneWay = way.isOneWay(); // which affixes are key vertices?
28297 var __first = vertexIds.indexOf(way.first()) !== -1;
28299 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
28302 var __via = __first && __last;
28304 var __from = __first && !__oneWay || __last;
28306 var __to = __first || __last && !__oneWay;
28308 return way.update({
28319 wayIds.forEach(function (id) {
28320 var way = withMetadata(vgraph.entity(id), vertexIds);
28321 vgraph = vgraph.replace(way);
28323 }); // STEP 7: Simplify - This is an iterative process where we:
28324 // 1. Find trivial vertices with only 2 parents
28325 // 2. trim off the leaf way from those vertices and remove from vgraph
28328 var removeWayIds = [];
28329 var removeVertexIds = [];
28333 checkVertices = vertexIds.slice();
28335 for (i = 0; i < checkVertices.length; i++) {
28336 var vertexId = checkVertices[i];
28337 vertex = vgraph.hasEntity(vertexId);
28340 if (vertexIds.indexOf(vertexId) !== -1) {
28341 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28344 removeVertexIds.push(vertexId);
28348 parents = vgraph.parentWays(vertex);
28350 if (parents.length < 3) {
28351 if (vertexIds.indexOf(vertexId) !== -1) {
28352 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28356 if (parents.length === 2) {
28357 // vertex with 2 parents is trivial
28358 var a = parents[0];
28359 var b = parents[1];
28360 var aIsLeaf = a && !a.__via;
28361 var bIsLeaf = b && !b.__via;
28362 var leaf, survivor;
28364 if (aIsLeaf && !bIsLeaf) {
28367 } else if (!aIsLeaf && bIsLeaf) {
28372 if (leaf && survivor) {
28373 survivor = withMetadata(survivor, vertexIds); // update survivor way
28375 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
28377 removeWayIds.push(leaf.id);
28382 parents = vgraph.parentWays(vertex);
28384 if (parents.length < 2) {
28385 // vertex is no longer a key vertex
28386 if (vertexIds.indexOf(vertexId) !== -1) {
28387 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28390 removeVertexIds.push(vertexId);
28394 if (parents.length < 1) {
28395 // vertex is no longer attached to anything
28396 vgraph = vgraph.remove(vertex);
28399 } while (keepGoing);
28401 vertices = vertices.filter(function (vertex) {
28402 return removeVertexIds.indexOf(vertex.id) === -1;
28403 }).map(function (vertex) {
28404 return vgraph.entity(vertex.id);
28406 ways = ways.filter(function (way) {
28407 return removeWayIds.indexOf(way.id) === -1;
28408 }).map(function (way) {
28409 return vgraph.entity(way.id);
28410 }); // OK! Here is our intersection..
28412 var intersection = {
28415 vertices: vertices,
28417 }; // Get all the valid turns through this intersection given a starting way id.
28418 // This operates on the virtual graph for everything.
28420 // Basically, walk through all possible paths from starting way,
28421 // honoring the existing turn restrictions as we go (watch out for loops!)
28423 // For each path found, generate and return a `osmTurn` datastructure.
28426 intersection.turns = function (fromWayId, maxViaWay) {
28427 if (!fromWayId) return [];
28428 if (!maxViaWay) maxViaWay = 0;
28429 var vgraph = intersection.graph;
28430 var keyVertexIds = intersection.vertices.map(function (v) {
28433 var start = vgraph.entity(fromWayId);
28434 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
28435 // maxViaWay=1 from-*-via-*-to (1 via max)
28436 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
28438 var maxPathLength = maxViaWay * 2 + 3;
28441 return turns; // traverse the intersection graph and find all the valid paths
28443 function step(entity, currPath, currRestrictions, matchedRestriction) {
28444 currPath = (currPath || []).slice(); // shallow copy
28446 if (currPath.length >= maxPathLength) return;
28447 currPath.push(entity.id);
28448 currRestrictions = (currRestrictions || []).slice(); // shallow copy
28452 if (entity.type === 'node') {
28453 var parents = vgraph.parentWays(entity);
28454 var nextWays = []; // which ways can we step into?
28456 for (i = 0; i < parents.length; i++) {
28457 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
28459 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
28461 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
28463 var restrict = null;
28465 for (j = 0; j < currRestrictions.length; j++) {
28466 var restriction = currRestrictions[j];
28467 var f = restriction.memberByRole('from');
28468 var v = restriction.membersByRole('via');
28469 var t = restriction.memberByRole('to');
28470 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
28472 var matchesFrom = f.id === fromWayId;
28473 var matchesViaTo = false;
28474 var isAlongOnlyPath = false;
28476 if (t.id === way.id) {
28478 if (v.length === 1 && v[0].type === 'node') {
28480 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
28482 // match all VIA ways
28485 for (k = 2; k < currPath.length; k += 2) {
28486 // k = 2 skips FROM
28487 pathVias.push(currPath[k]); // (path goes way-node-way...)
28490 var restrictionVias = [];
28492 for (k = 0; k < v.length; k++) {
28493 if (v[k].type === 'way') {
28494 restrictionVias.push(v[k].id);
28498 var diff = utilArrayDifference(pathVias, restrictionVias);
28499 matchesViaTo = !diff.length;
28501 } else if (isOnly) {
28502 for (k = 0; k < v.length; k++) {
28503 // way doesn't match TO, but is one of the via ways along the path of an "only"
28504 if (v[k].type === 'way' && v[k].id === way.id) {
28505 isAlongOnlyPath = true;
28511 if (matchesViaTo) {
28514 id: restriction.id,
28515 direct: matchesFrom,
28522 id: restriction.id,
28523 direct: matchesFrom,
28530 // indirect - caused by a different nearby restriction
28531 if (isAlongOnlyPath) {
28533 id: restriction.id,
28539 } else if (isOnly) {
28541 id: restriction.id,
28548 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
28551 if (restrict && restrict.direct) break;
28560 nextWays.forEach(function (nextWay) {
28561 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
28564 // entity.type === 'way'
28565 if (currPath.length >= 3) {
28566 // this is a "complete" path..
28567 var turnPath = currPath.slice(); // shallow copy
28568 // an indirect restriction - only include the partial path (starting at FROM)
28570 if (matchedRestriction && matchedRestriction.direct === false) {
28571 for (i = 0; i < turnPath.length; i++) {
28572 if (turnPath[i] === matchedRestriction.from) {
28573 turnPath = turnPath.slice(i);
28579 var turn = pathToTurn(turnPath);
28582 if (matchedRestriction) {
28583 turn.restrictionID = matchedRestriction.id;
28584 turn.no = matchedRestriction.no;
28585 turn.only = matchedRestriction.only;
28586 turn.direct = matchedRestriction.direct;
28589 turns.push(osmTurn(turn));
28592 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
28595 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
28596 // which nodes can we step into?
28598 var n1 = vgraph.entity(entity.first());
28599 var n2 = vgraph.entity(entity.last());
28600 var dist = geoSphericalDistance(n1.loc, n2.loc);
28601 var nextNodes = [];
28603 if (currPath.length > 1) {
28604 if (dist > maxDistance) return; // the next node is too far
28606 if (!entity.__via) return; // this way is a leaf / can't be a via
28609 if (!entity.__oneWay && // bidirectional..
28610 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
28611 currPath.indexOf(n1.id) === -1) {
28612 // haven't seen it yet..
28613 nextNodes.push(n1); // can advance to first node
28616 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
28617 currPath.indexOf(n2.id) === -1) {
28618 // haven't seen it yet..
28619 nextNodes.push(n2); // can advance to last node
28622 nextNodes.forEach(function (nextNode) {
28623 // gather restrictions FROM this way
28624 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
28625 if (!r.isRestriction()) return false;
28626 var f = r.memberByRole('from');
28627 if (!f || f.id !== entity.id) return false;
28628 var isOnly = /^only_/.test(r.tags.restriction);
28629 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
28631 var isOnlyVia = false;
28632 var v = r.membersByRole('via');
28634 if (v.length === 1 && v[0].type === 'node') {
28636 isOnlyVia = v[0].id === nextNode.id;
28639 for (var i = 0; i < v.length; i++) {
28640 if (v[i].type !== 'way') continue;
28641 var viaWay = vgraph.entity(v[i].id);
28643 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
28652 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
28655 } // assumes path is alternating way-node-way of odd length
28658 function pathToTurn(path) {
28659 if (path.length < 3) return;
28660 var fromWayId, fromNodeId, fromVertexId;
28661 var toWayId, toNodeId, toVertexId;
28662 var viaWayIds, viaNodeId, isUturn;
28663 fromWayId = path[0];
28664 toWayId = path[path.length - 1];
28666 if (path.length === 3 && fromWayId === toWayId) {
28668 var way = vgraph.entity(fromWayId);
28669 if (way.__oneWay) return null;
28671 viaNodeId = fromVertexId = toVertexId = path[1];
28672 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
28675 fromVertexId = path[1];
28676 fromNodeId = adjacentNode(fromWayId, fromVertexId);
28677 toVertexId = path[path.length - 2];
28678 toNodeId = adjacentNode(toWayId, toVertexId);
28680 if (path.length === 3) {
28681 viaNodeId = path[1];
28683 viaWayIds = path.filter(function (entityId) {
28684 return entityId[0] === 'w';
28686 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
28691 key: path.join('_'),
28696 vertex: fromVertexId
28710 function adjacentNode(wayId, affixId) {
28711 var nodes = vgraph.entity(wayId).nodes;
28712 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
28717 return intersection;
28719 function osmInferRestriction(graph, turn, projection) {
28720 var fromWay = graph.entity(turn.from.way);
28721 var fromNode = graph.entity(turn.from.node);
28722 var fromVertex = graph.entity(turn.from.vertex);
28723 var toWay = graph.entity(turn.to.way);
28724 var toNode = graph.entity(turn.to.node);
28725 var toVertex = graph.entity(turn.to.vertex);
28726 var fromOneWay = fromWay.tags.oneway === 'yes';
28727 var toOneWay = toWay.tags.oneway === 'yes';
28728 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
28730 while (angle < 0) {
28734 if (fromNode === toNode) return 'no_u_turn';
28735 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
28737 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)
28739 if (angle < 158) return 'no_right_turn';
28740 if (angle > 202) return 'no_left_turn';
28741 return 'no_straight_on';
28744 function actionMergePolygon(ids, newRelationId) {
28745 function groupEntities(graph) {
28746 var entities = ids.map(function (id) {
28747 return graph.entity(id);
28749 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
28750 if (entity.type === 'way' && entity.isClosed()) {
28751 return 'closedWay';
28752 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
28753 return 'multipolygon';
28758 return Object.assign({
28762 }, geometryGroups);
28765 var action = function action(graph) {
28766 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
28768 // Each element is itself an array of objects with an id property, and has a
28769 // locs property which is an array of the locations forming the polygon.
28771 var polygons = entities.multipolygon.reduce(function (polygons, m) {
28772 return polygons.concat(osmJoinWays(m.members, graph));
28773 }, []).concat(entities.closedWay.map(function (d) {
28777 member.nodes = graph.childNodes(d);
28779 })); // contained is an array of arrays of boolean values,
28780 // where contained[j][k] is true iff the jth way is
28781 // contained by the kth way.
28783 var contained = polygons.map(function (w, i) {
28784 return polygons.map(function (d, n) {
28785 if (i === n) return null;
28786 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
28788 }), w.nodes.map(function (n) {
28792 }); // Sort all polygons as either outer or inner ways
28797 while (polygons.length) {
28798 extractUncontained(polygons);
28799 polygons = polygons.filter(isContained);
28800 contained = contained.filter(isContained).map(filterContained);
28803 function isContained(d, i) {
28804 return contained[i].some(function (val) {
28809 function filterContained(d) {
28810 return d.filter(isContained);
28813 function extractUncontained(polygons) {
28814 polygons.forEach(function (d, i) {
28815 if (!isContained(d, i)) {
28816 d.forEach(function (member) {
28820 role: outer ? 'outer' : 'inner'
28826 } // Move all tags to one relation
28829 var relation = entities.multipolygon[0] || osmRelation({
28832 type: 'multipolygon'
28835 entities.multipolygon.slice(1).forEach(function (m) {
28836 relation = relation.mergeTags(m.tags);
28837 graph = graph.remove(m);
28839 entities.closedWay.forEach(function (way) {
28840 function isThisOuter(m) {
28841 return m.id === way.id && m.role !== 'inner';
28844 if (members.some(isThisOuter)) {
28845 relation = relation.mergeTags(way.tags);
28846 graph = graph.replace(way.update({
28851 return graph.replace(relation.update({
28853 tags: utilObjectOmit(relation.tags, ['area'])
28857 action.disabled = function (graph) {
28858 var entities = groupEntities(graph);
28860 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
28861 return 'not_eligible';
28864 if (!entities.multipolygon.every(function (r) {
28865 return r.isComplete(graph);
28867 return 'incomplete_relation';
28870 if (!entities.multipolygon.length) {
28871 var sharedMultipolygons = [];
28872 entities.closedWay.forEach(function (way, i) {
28874 sharedMultipolygons = graph.parentMultipolygons(way);
28876 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
28879 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
28880 return relation.members.length === entities.closedWay.length;
28883 if (sharedMultipolygons.length) {
28884 // don't create a new multipolygon if it'd be redundant
28885 return 'not_eligible';
28887 } else if (entities.closedWay.some(function (way) {
28888 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
28890 // don't add a way to a multipolygon again if it's already a member
28891 return 'not_eligible';
28898 var UNSUPPORTED_Y$3 = regexpStickyHelpers.UNSUPPORTED_Y;
28900 // `RegExp.prototype.flags` getter
28901 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
28902 if (descriptors && (/./g.flags != 'g' || UNSUPPORTED_Y$3)) {
28903 objectDefineProperty.f(RegExp.prototype, 'flags', {
28904 configurable: true,
28909 var fastDeepEqual = function equal(a, b) {
28910 if (a === b) return true;
28912 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
28913 if (a.constructor !== b.constructor) return false;
28914 var length, i, keys;
28916 if (Array.isArray(a)) {
28918 if (length != b.length) return false;
28920 for (i = length; i-- !== 0;) {
28921 if (!equal(a[i], b[i])) return false;
28927 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
28928 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
28929 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
28930 keys = Object.keys(a);
28931 length = keys.length;
28932 if (length !== Object.keys(b).length) return false;
28934 for (i = length; i-- !== 0;) {
28935 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
28938 for (i = length; i-- !== 0;) {
28940 if (!equal(a[key], b[key])) return false;
28944 } // true if both NaN, false otherwise
28947 return a !== a && b !== b;
28950 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
28951 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
28952 // http://www.cs.dartmouth.edu/~doug/
28953 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
28955 // Expects two arrays, finds longest common sequence
28957 function LCS(buffer1, buffer2) {
28958 var equivalenceClasses = {};
28960 for (var j = 0; j < buffer2.length; j++) {
28961 var item = buffer2[j];
28963 if (equivalenceClasses[item]) {
28964 equivalenceClasses[item].push(j);
28966 equivalenceClasses[item] = [j];
28975 var candidates = [NULLRESULT];
28977 for (var i = 0; i < buffer1.length; i++) {
28978 var _item = buffer1[i];
28979 var buffer2indices = equivalenceClasses[_item] || [];
28981 var c = candidates[0];
28983 for (var jx = 0; jx < buffer2indices.length; jx++) {
28984 var _j = buffer2indices[jx];
28987 for (s = r; s < candidates.length; s++) {
28988 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
28993 if (s < candidates.length) {
28994 var newCandidate = {
28997 chain: candidates[s]
29000 if (r === candidates.length) {
29001 candidates.push(c);
29009 if (r === candidates.length) {
29010 break; // no point in examining further (j)s
29016 } // At this point, we know the LCS: it's in the reverse of the
29017 // linked-list through .chain of candidates[candidates.length - 1].
29020 return candidates[candidates.length - 1];
29021 } // We apply the LCS to build a 'comm'-style picture of the
29022 // offsets and lengths of mismatched chunks in the input
29023 // buffers. This is used by diff3MergeRegions.
29026 function diffIndices(buffer1, buffer2) {
29027 var lcs = LCS(buffer1, buffer2);
29029 var tail1 = buffer1.length;
29030 var tail2 = buffer2.length;
29032 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
29033 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
29034 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
29035 tail1 = candidate.buffer1index;
29036 tail2 = candidate.buffer2index;
29038 if (mismatchLength1 || mismatchLength2) {
29040 buffer1: [tail1 + 1, mismatchLength1],
29041 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
29042 buffer2: [tail2 + 1, mismatchLength2],
29043 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
29050 } // We apply the LCS to build a JSON representation of a
29051 // independently derived from O, returns a fairly complicated
29052 // internal representation of merge decisions it's taken. The
29053 // interested reader may wish to consult
29055 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
29056 // 'A Formal Investigation of ' In Arvind and Prasad,
29057 // editors, Foundations of Software Technology and Theoretical
29058 // Computer Science (FSTTCS), December 2007.
29060 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
29064 function diff3MergeRegions(a, o, b) {
29065 // "hunks" are array subsets where `a` or `b` are different from `o`
29066 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
29069 function addHunk(h, ab) {
29072 oStart: h.buffer1[0],
29073 oLength: h.buffer1[1],
29074 // length of o to remove
29075 abStart: h.buffer2[0],
29076 abLength: h.buffer2[1] // length of a/b to insert
29077 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
29082 diffIndices(o, a).forEach(function (item) {
29083 return addHunk(item, 'a');
29085 diffIndices(o, b).forEach(function (item) {
29086 return addHunk(item, 'b');
29088 hunks.sort(function (x, y) {
29089 return x.oStart - y.oStart;
29092 var currOffset = 0;
29094 function advanceTo(endOffset) {
29095 if (endOffset > currOffset) {
29099 bufferStart: currOffset,
29100 bufferLength: endOffset - currOffset,
29101 bufferContent: o.slice(currOffset, endOffset)
29103 currOffset = endOffset;
29107 while (hunks.length) {
29108 var hunk = hunks.shift();
29109 var regionStart = hunk.oStart;
29110 var regionEnd = hunk.oStart + hunk.oLength;
29111 var regionHunks = [hunk];
29112 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
29114 while (hunks.length) {
29115 var nextHunk = hunks[0];
29116 var nextHunkStart = nextHunk.oStart;
29117 if (nextHunkStart > regionEnd) break; // no overlap
29119 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
29120 regionHunks.push(hunks.shift());
29123 if (regionHunks.length === 1) {
29124 // Only one hunk touches this region, meaning that there is no conflict here.
29125 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
29126 if (hunk.abLength > 0) {
29127 var buffer = hunk.ab === 'a' ? a : b;
29131 bufferStart: hunk.abStart,
29132 bufferLength: hunk.abLength,
29133 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
29137 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
29138 // Effectively merge all the `a` hunks into one giant hunk, then do the
29139 // same for the `b` hunks; then, correct for skew in the regions of `o`
29140 // that each side changed, and report appropriate spans for the three sides.
29142 a: [a.length, -1, o.length, -1],
29143 b: [b.length, -1, o.length, -1]
29146 while (regionHunks.length) {
29147 hunk = regionHunks.shift();
29148 var oStart = hunk.oStart;
29149 var oEnd = oStart + hunk.oLength;
29150 var abStart = hunk.abStart;
29151 var abEnd = abStart + hunk.abLength;
29152 var _b = bounds[hunk.ab];
29153 _b[0] = Math.min(abStart, _b[0]);
29154 _b[1] = Math.max(abEnd, _b[1]);
29155 _b[2] = Math.min(oStart, _b[2]);
29156 _b[3] = Math.max(oEnd, _b[3]);
29159 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
29160 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
29161 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
29162 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
29166 aLength: aEnd - aStart,
29167 aContent: a.slice(aStart, aEnd),
29168 oStart: regionStart,
29169 oLength: regionEnd - regionStart,
29170 oContent: o.slice(regionStart, regionEnd),
29172 bLength: bEnd - bStart,
29173 bContent: b.slice(bStart, bEnd)
29175 results.push(result);
29178 currOffset = regionEnd;
29181 advanceTo(o.length);
29183 } // Applies the output of diff3MergeRegions to actually
29184 // construct the merged buffer; the returned result alternates
29185 // between 'ok' and 'conflict' blocks.
29186 // A "false conflict" is where `a` and `b` both change the same from `o`
29189 function diff3Merge(a, o, b, options) {
29191 excludeFalseConflicts: true,
29192 stringSeparator: /\s+/
29194 options = Object.assign(defaults, options);
29195 var aString = typeof a === 'string';
29196 var oString = typeof o === 'string';
29197 var bString = typeof b === 'string';
29198 if (aString) a = a.split(options.stringSeparator);
29199 if (oString) o = o.split(options.stringSeparator);
29200 if (bString) b = b.split(options.stringSeparator);
29202 var regions = diff3MergeRegions(a, o, b);
29205 function flushOk() {
29206 if (okBuffer.length) {
29215 function isFalseConflict(a, b) {
29216 if (a.length !== b.length) return false;
29218 for (var i = 0; i < a.length; i++) {
29219 if (a[i] !== b[i]) return false;
29225 regions.forEach(function (region) {
29226 if (region.stable) {
29229 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
29231 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
29234 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
29239 a: region.aContent,
29240 aIndex: region.aStart,
29241 o: region.oContent,
29242 oIndex: region.oStart,
29243 b: region.bContent,
29244 bIndex: region.bStart
29254 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
29255 discardTags = discardTags || {};
29256 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
29258 var _conflicts = [];
29261 return typeof formatUser === 'function' ? formatUser(d) : d;
29264 function mergeLocation(remote, target) {
29265 function pointEqual(a, b) {
29266 var epsilon = 1e-6;
29267 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
29270 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
29274 if (_option === 'force_remote') {
29275 return target.update({
29280 _conflicts.push(_t('merge_remote_changes.conflict.location', {
29281 user: user(remote.user)
29287 function mergeNodes(base, remote, target) {
29288 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
29292 if (_option === 'force_remote') {
29293 return target.update({
29294 nodes: remote.nodes
29298 var ccount = _conflicts.length;
29299 var o = base.nodes || [];
29300 var a = target.nodes || [];
29301 var b = remote.nodes || [];
29303 var hunks = diff3Merge(a, o, b, {
29304 excludeFalseConflicts: true
29307 for (var i = 0; i < hunks.length; i++) {
29308 var hunk = hunks[i];
29311 nodes.push.apply(nodes, hunk.ok);
29313 // for all conflicts, we can assume c.a !== c.b
29314 // because `diff3Merge` called with `true` option to exclude false conflicts..
29315 var c = hunk.conflict;
29317 if (fastDeepEqual(c.o, c.a)) {
29318 // only changed remotely
29319 nodes.push.apply(nodes, c.b);
29320 } else if (fastDeepEqual(c.o, c.b)) {
29321 // only changed locally
29322 nodes.push.apply(nodes, c.a);
29324 // changed both locally and remotely
29325 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
29326 user: user(remote.user)
29334 return _conflicts.length === ccount ? target.update({
29339 function mergeChildren(targetWay, children, updates, graph) {
29340 function isUsed(node, targetWay) {
29341 var hasInterestingParent = graph.parentWays(node).some(function (way) {
29342 return way.id !== targetWay.id;
29344 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
29347 var ccount = _conflicts.length;
29349 for (var i = 0; i < children.length; i++) {
29350 var id = children[i];
29351 var node = graph.hasEntity(id); // remove unused childNodes..
29353 if (targetWay.nodes.indexOf(id) === -1) {
29354 if (node && !isUsed(node, targetWay)) {
29355 updates.removeIds.push(id);
29359 } // restore used childNodes..
29362 var local = localGraph.hasEntity(id);
29363 var remote = remoteGraph.hasEntity(id);
29366 if (_option === 'force_remote' && remote && remote.visible) {
29367 updates.replacements.push(remote);
29368 } else if (_option === 'force_local' && local) {
29369 target = osmEntity(local);
29372 target = target.update({
29373 version: remote.version
29377 updates.replacements.push(target);
29378 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
29379 target = osmEntity(local, {
29380 version: remote.version
29383 if (remote.visible) {
29384 target = mergeLocation(remote, target);
29386 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29387 user: user(remote.user)
29391 if (_conflicts.length !== ccount) break;
29392 updates.replacements.push(target);
29399 function updateChildren(updates, graph) {
29400 for (var i = 0; i < updates.replacements.length; i++) {
29401 graph = graph.replace(updates.replacements[i]);
29404 if (updates.removeIds.length) {
29405 graph = actionDeleteMultiple(updates.removeIds)(graph);
29411 function mergeMembers(remote, target) {
29412 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
29416 if (_option === 'force_remote') {
29417 return target.update({
29418 members: remote.members
29422 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
29423 user: user(remote.user)
29429 function mergeTags(base, remote, target) {
29430 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
29434 if (_option === 'force_remote') {
29435 return target.update({
29440 var ccount = _conflicts.length;
29441 var o = base.tags || {};
29442 var a = target.tags || {};
29443 var b = remote.tags || {};
29444 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
29445 return !discardTags[k];
29447 var tags = Object.assign({}, a); // shallow copy
29449 var changed = false;
29451 for (var i = 0; i < keys.length; i++) {
29454 if (o[k] !== b[k] && a[k] !== b[k]) {
29455 // changed remotely..
29456 if (o[k] !== a[k]) {
29457 // changed locally..
29458 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
29462 user: user(remote.user)
29465 // unchanged locally, accept remote change..
29466 if (b.hasOwnProperty(k)) {
29477 return changed && _conflicts.length === ccount ? target.update({
29480 } // `graph.base()` is the common ancestor of the two graphs.
29481 // `localGraph` contains user's edits up to saving
29482 // `remoteGraph` contains remote edits to modified nodes
29483 // `graph` must be a descendent of `localGraph` and may include
29484 // some conflict resolution actions performed on it.
29486 // --- ... --- `localGraph` -- ... -- `graph`
29488 // `graph.base()` --- ... --- `remoteGraph`
29492 var action = function action(graph) {
29497 var base = graph.base().entities[id];
29498 var local = localGraph.entity(id);
29499 var remote = remoteGraph.entity(id);
29500 var target = osmEntity(local, {
29501 version: remote.version
29502 }); // delete/undelete
29504 if (!remote.visible) {
29505 if (_option === 'force_remote') {
29506 return actionDeleteMultiple([id])(graph);
29507 } else if (_option === 'force_local') {
29508 if (target.type === 'way') {
29509 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
29510 graph = updateChildren(updates, graph);
29513 return graph.replace(target);
29515 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29516 user: user(remote.user)
29519 return graph; // do nothing
29524 if (target.type === 'node') {
29525 target = mergeLocation(remote, target);
29526 } else if (target.type === 'way') {
29527 // pull in any child nodes that may not be present locally..
29528 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
29529 target = mergeNodes(base, remote, target);
29530 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
29531 } else if (target.type === 'relation') {
29532 target = mergeMembers(remote, target);
29535 target = mergeTags(base, remote, target);
29537 if (!_conflicts.length) {
29538 graph = updateChildren(updates, graph).replace(target);
29544 action.withOption = function (opt) {
29549 action.conflicts = function () {
29556 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
29558 function actionMove(moveIDs, tryDelta, projection, cache) {
29559 var _delta = tryDelta;
29561 function setupCache(graph) {
29562 function canMove(nodeID) {
29563 // Allow movement of any node that is in the selectedIDs list..
29564 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
29566 var parents = graph.parentWays(graph.entity(nodeID));
29567 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
29569 var parentsMoving = parents.every(function (way) {
29570 return cache.moving[way.id];
29572 if (!parentsMoving) delete cache.moving[nodeID];
29573 return parentsMoving;
29576 function cacheEntities(ids) {
29577 for (var i = 0; i < ids.length; i++) {
29579 if (cache.moving[id]) continue;
29580 cache.moving[id] = true;
29581 var entity = graph.hasEntity(id);
29582 if (!entity) continue;
29584 if (entity.type === 'node') {
29585 cache.nodes.push(id);
29586 cache.startLoc[id] = entity.loc;
29587 } else if (entity.type === 'way') {
29588 cache.ways.push(id);
29589 cacheEntities(entity.nodes);
29591 cacheEntities(entity.members.map(function (member) {
29598 function cacheIntersections(ids) {
29599 function isEndpoint(way, id) {
29600 return !way.isClosed() && !!way.affix(id);
29603 for (var i = 0; i < ids.length; i++) {
29604 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
29606 var childNodes = graph.childNodes(graph.entity(id));
29608 for (var j = 0; j < childNodes.length; j++) {
29609 var node = childNodes[j];
29610 var parents = graph.parentWays(node);
29611 if (parents.length !== 2) continue;
29612 var moved = graph.entity(id);
29613 var unmoved = null;
29615 for (var k = 0; k < parents.length; k++) {
29616 var way = parents[k];
29618 if (!cache.moving[way.id]) {
29624 if (!unmoved) continue; // exclude ways that are overly connected..
29626 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
29627 if (moved.isArea() || unmoved.isArea()) continue;
29628 cache.intersections.push({
29631 unmovedId: unmoved.id,
29632 movedIsEP: isEndpoint(moved, node.id),
29633 unmovedIsEP: isEndpoint(unmoved, node.id)
29645 cache.intersections = [];
29646 cache.replacedVertex = {};
29647 cache.startLoc = {};
29650 cacheEntities(moveIDs);
29651 cacheIntersections(cache.ways);
29652 cache.nodes = cache.nodes.filter(canMove);
29655 } // Place a vertex where the moved vertex used to be, to preserve way shape..
29664 // * node '*' added to preserve shape
29666 // / b ---- e way `b,e` moved here:
29673 function replaceMovedVertex(nodeId, wayId, graph, delta) {
29674 var way = graph.entity(wayId);
29675 var moved = graph.entity(nodeId);
29676 var movedIndex = way.nodes.indexOf(nodeId);
29677 var len, prevIndex, nextIndex;
29679 if (way.isClosed()) {
29680 len = way.nodes.length - 1;
29681 prevIndex = (movedIndex + len - 1) % len;
29682 nextIndex = (movedIndex + len + 1) % len;
29684 len = way.nodes.length;
29685 prevIndex = movedIndex - 1;
29686 nextIndex = movedIndex + 1;
29689 var prev = graph.hasEntity(way.nodes[prevIndex]);
29690 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
29692 if (!prev || !next) return graph;
29693 var key = wayId + '_' + nodeId;
29694 var orig = cache.replacedVertex[key];
29698 cache.replacedVertex[key] = orig;
29699 cache.startLoc[orig.id] = cache.startLoc[nodeId];
29705 start = projection(cache.startLoc[nodeId]);
29706 end = projection.invert(geoVecAdd(start, delta));
29708 end = cache.startLoc[nodeId];
29711 orig = orig.move(end);
29712 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..
29714 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
29716 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
29717 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
29718 var d1 = geoPathLength(p1);
29719 var d2 = geoPathLength(p2);
29720 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
29722 if (way.isClosed() && insertAt === 0) insertAt = len;
29723 way = way.addNode(orig.id, insertAt);
29724 return graph.replace(orig).replace(way);
29725 } // Remove duplicate vertex that might have been added by
29726 // replaceMovedVertex. This is done after the unzorro checks.
29729 function removeDuplicateVertices(wayId, graph) {
29730 var way = graph.entity(wayId);
29731 var epsilon = 1e-6;
29734 function isInteresting(node, graph) {
29735 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
29738 for (var i = 0; i < way.nodes.length; i++) {
29739 curr = graph.entity(way.nodes[i]);
29741 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
29742 if (!isInteresting(prev, graph)) {
29743 way = way.removeNode(prev.id);
29744 graph = graph.replace(way).remove(prev);
29745 } else if (!isInteresting(curr, graph)) {
29746 way = way.removeNode(curr.id);
29747 graph = graph.replace(way).remove(curr);
29755 } // Reorder nodes around intersections that have moved..
29757 // Start: way1.nodes: b,e (moving)
29758 // a - b - c ----- d way2.nodes: a,b,c,d (static)
29760 // e isEP1: true, isEP2, false
29762 // way1 `b,e` moved here:
29763 // a ----- c = b - d
29767 // reorder nodes way1.nodes: b,e
29768 // a ----- c - b - d way2.nodes: a,c,b,d
29774 function unZorroIntersection(intersection, graph) {
29775 var vertex = graph.entity(intersection.nodeId);
29776 var way1 = graph.entity(intersection.movedId);
29777 var way2 = graph.entity(intersection.unmovedId);
29778 var isEP1 = intersection.movedIsEP;
29779 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
29781 if (isEP1 && isEP2) return graph;
29782 var nodes1 = graph.childNodes(way1).filter(function (n) {
29783 return n !== vertex;
29785 var nodes2 = graph.childNodes(way2).filter(function (n) {
29786 return n !== vertex;
29788 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
29789 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
29790 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
29791 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
29792 var loc; // snap vertex to nearest edge (or some point between them)..
29794 if (!isEP1 && !isEP2) {
29795 var epsilon = 1e-6,
29798 for (var i = 0; i < maxIter; i++) {
29799 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
29800 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
29801 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
29802 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
29804 } else if (!isEP1) {
29810 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
29812 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
29813 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
29814 graph = graph.replace(way1);
29817 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
29818 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
29819 graph = graph.replace(way2);
29825 function cleanupIntersections(graph) {
29826 for (var i = 0; i < cache.intersections.length; i++) {
29827 var obj = cache.intersections[i];
29828 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
29829 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
29830 graph = unZorroIntersection(obj, graph);
29831 graph = removeDuplicateVertices(obj.movedId, graph);
29832 graph = removeDuplicateVertices(obj.unmovedId, graph);
29836 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
29839 function limitDelta(graph) {
29840 function moveNode(loc) {
29841 return geoVecAdd(projection(loc), _delta);
29844 for (var i = 0; i < cache.intersections.length; i++) {
29845 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
29847 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
29849 if (!obj.movedIsEP) continue;
29850 var node = graph.entity(obj.nodeId);
29851 var start = projection(node.loc);
29852 var end = geoVecAdd(start, _delta);
29853 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
29854 var movedPath = movedNodes.map(function (n) {
29855 return moveNode(n.loc);
29857 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
29858 var unmovedPath = unmovedNodes.map(function (n) {
29859 return projection(n.loc);
29861 var hits = geoPathIntersections(movedPath, unmovedPath);
29863 for (var j = 0; i < hits.length; i++) {
29864 if (geoVecEqual(hits[j], end)) continue;
29865 var edge = geoChooseEdge(unmovedNodes, end, projection);
29866 _delta = geoVecSubtract(projection(edge.loc), start);
29871 var action = function action(graph) {
29872 if (_delta[0] === 0 && _delta[1] === 0) return graph;
29875 if (cache.intersections.length) {
29879 for (var i = 0; i < cache.nodes.length; i++) {
29880 var node = graph.entity(cache.nodes[i]);
29881 var start = projection(node.loc);
29882 var end = geoVecAdd(start, _delta);
29883 graph = graph.replace(node.move(projection.invert(end)));
29886 if (cache.intersections.length) {
29887 graph = cleanupIntersections(graph);
29893 action.delta = function () {
29900 function actionMoveMember(relationId, fromIndex, toIndex) {
29901 return function (graph) {
29902 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
29906 function actionMoveNode(nodeID, toLoc) {
29907 var action = function action(graph, t) {
29908 if (t === null || !isFinite(t)) t = 1;
29909 t = Math.min(Math.max(+t, 0), 1);
29910 var node = graph.entity(nodeID);
29911 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
29914 action.transitionable = true;
29918 function actionNoop() {
29919 return function (graph) {
29924 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
29925 var epsilon = ep || 1e-4;
29926 var threshold = degThresh || 13; // degrees within right or straight to alter
29927 // We test normalized dot products so we can compare as cos(angle)
29929 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
29930 var upperThreshold = Math.cos(threshold * Math.PI / 180);
29932 var action = function action(graph, t) {
29933 if (t === null || !isFinite(t)) t = 1;
29934 t = Math.min(Math.max(+t, 0), 1);
29935 var way = graph.entity(wayID);
29936 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
29938 if (way.tags.nonsquare) {
29939 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
29941 delete tags.nonsquare;
29947 graph = graph.replace(way);
29948 var isClosed = way.isClosed();
29949 var nodes = graph.childNodes(way).slice(); // shallow copy
29951 if (isClosed) nodes.pop();
29953 if (vertexID !== undefined) {
29954 nodes = nodeSubset(nodes, vertexID, isClosed);
29955 if (nodes.length !== 3) return graph;
29956 } // note: all geometry functions here use the unclosed node/point/coord list
29959 var nodeCount = {};
29965 var node, point, loc, score, motions, i, j;
29967 for (i = 0; i < nodes.length; i++) {
29969 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
29972 coord: projection(node.loc)
29976 if (points.length === 3) {
29977 // move only one vertex for right triangle
29978 for (i = 0; i < 1000; i++) {
29979 motions = points.map(calcMotion);
29980 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
29981 score = corner.dotp;
29983 if (score < epsilon) {
29988 node = graph.entity(nodes[corner.i].id);
29989 loc = projection.invert(points[corner.i].coord);
29990 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29992 var straights = [];
29993 var simplified = []; // Remove points from nearly straight sections..
29994 // This produces a simplified shape to orthogonalize
29996 for (i = 0; i < points.length; i++) {
30000 if (isClosed || i > 0 && i < points.length - 1) {
30001 var a = points[(i - 1 + points.length) % points.length];
30002 var b = points[(i + 1) % points.length];
30003 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
30006 if (dotp > upperThreshold) {
30007 straights.push(point);
30009 simplified.push(point);
30011 } // Orthogonalize the simplified shape
30014 var bestPoints = clonePoints(simplified);
30015 var originalPoints = clonePoints(simplified);
30018 for (i = 0; i < 1000; i++) {
30019 motions = simplified.map(calcMotion);
30021 for (j = 0; j < motions.length; j++) {
30022 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
30025 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
30027 if (newScore < score) {
30028 bestPoints = clonePoints(simplified);
30032 if (score < epsilon) {
30037 var bestCoords = bestPoints.map(function (p) {
30040 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
30042 for (i = 0; i < bestPoints.length; i++) {
30043 point = bestPoints[i];
30045 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
30046 node = graph.entity(point.id);
30047 loc = projection.invert(point.coord);
30048 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30050 } // move the nodes along straight segments
30053 for (i = 0; i < straights.length; i++) {
30054 point = straights[i];
30055 if (nodeCount[point.id] > 1) continue; // skip self-intersections
30057 node = graph.entity(point.id);
30059 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
30060 // remove uninteresting points..
30061 graph = actionDeleteNode(node.id)(graph);
30063 // move interesting points to the nearest edge..
30064 var choice = geoVecProject(point.coord, bestCoords);
30067 loc = projection.invert(choice.target);
30068 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30076 function clonePoints(array) {
30077 return array.map(function (p) {
30080 coord: [p.coord[0], p.coord[1]]
30085 function calcMotion(point, i, array) {
30086 // don't try to move the endpoints of a non-closed way.
30087 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)
30089 if (nodeCount[array[i].id] > 1) return [0, 0];
30090 var a = array[(i - 1 + array.length) % array.length].coord;
30091 var origin = point.coord;
30092 var b = array[(i + 1) % array.length].coord;
30093 var p = geoVecSubtract(a, origin);
30094 var q = geoVecSubtract(b, origin);
30095 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
30096 p = geoVecNormalize(p);
30097 q = geoVecNormalize(q);
30098 var dotp = p[0] * q[0] + p[1] * q[1];
30099 var val = Math.abs(dotp);
30101 if (val < lowerThreshold) {
30102 // nearly orthogonal
30105 var vec = geoVecNormalize(geoVecAdd(p, q));
30106 return geoVecScale(vec, 0.1 * dotp * scale);
30109 return [0, 0]; // do nothing
30111 }; // if we are only orthogonalizing one vertex,
30112 // get that vertex and the previous and next
30115 function nodeSubset(nodes, vertexID, isClosed) {
30116 var first = isClosed ? 0 : 1;
30117 var last = isClosed ? nodes.length : nodes.length - 1;
30119 for (var i = first; i < last; i++) {
30120 if (nodes[i].id === vertexID) {
30121 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
30128 action.disabled = function (graph) {
30129 var way = graph.entity(wayID);
30130 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
30132 graph = graph.replace(way);
30133 var isClosed = way.isClosed();
30134 var nodes = graph.childNodes(way).slice(); // shallow copy
30136 if (isClosed) nodes.pop();
30137 var allowStraightAngles = false;
30139 if (vertexID !== undefined) {
30140 allowStraightAngles = true;
30141 nodes = nodeSubset(nodes, vertexID, isClosed);
30142 if (nodes.length !== 3) return 'end_vertex';
30145 var coords = nodes.map(function (n) {
30146 return projection(n.loc);
30148 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
30150 if (score === null) {
30151 return 'not_squarish';
30152 } else if (score === 0) {
30153 return 'square_enough';
30159 action.transitionable = true;
30164 // `turn` must be an `osmTurn` object
30165 // see osm/intersection.js, pathToTurn()
30167 // This specifies a restriction of type `restriction` when traveling from
30168 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
30169 // (The action does not check that these entities form a valid intersection.)
30171 // From, to, and via ways should be split before calling this action.
30172 // (old versions of the code would split the ways here, but we no longer do it)
30174 // For testing convenience, accepts a restrictionID to assign to the new
30175 // relation. Normally, this will be undefined and the relation will
30176 // automatically be assigned a new ID.
30179 function actionRestrictTurn(turn, restrictionType, restrictionID) {
30180 return function (graph) {
30181 var fromWay = graph.entity(turn.from.way);
30182 var toWay = graph.entity(turn.to.way);
30183 var viaNode = turn.via.node && graph.entity(turn.via.node);
30184 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
30185 return graph.entity(id);
30200 } else if (viaWays) {
30201 viaWays.forEach(function (viaWay) {
30215 return graph.replace(osmRelation({
30218 type: 'restriction',
30219 restriction: restrictionType
30226 function actionRevert(id) {
30227 var action = function action(graph) {
30228 var entity = graph.hasEntity(id),
30229 base = graph.base().entities[id];
30231 if (entity && !base) {
30232 // entity will be removed..
30233 if (entity.type === 'node') {
30234 graph.parentWays(entity).forEach(function (parent) {
30235 parent = parent.removeNode(id);
30236 graph = graph.replace(parent);
30238 if (parent.isDegenerate()) {
30239 graph = actionDeleteWay(parent.id)(graph);
30244 graph.parentRelations(entity).forEach(function (parent) {
30245 parent = parent.removeMembersWithID(id);
30246 graph = graph.replace(parent);
30248 if (parent.isDegenerate()) {
30249 graph = actionDeleteRelation(parent.id)(graph);
30254 return graph.revert(id);
30260 function actionRotate(rotateIds, pivot, angle, projection) {
30261 var action = function action(graph) {
30262 return graph.update(function (graph) {
30263 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
30264 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
30265 graph = graph.replace(node.move(projection.invert(point)));
30273 function actionScale(ids, pivotLoc, scaleFactor, projection) {
30274 return function (graph) {
30275 return graph.update(function (graph) {
30277 utilGetAllNodes(ids, graph).forEach(function (node) {
30278 point = projection(node.loc);
30279 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
30280 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
30281 graph = graph.replace(node.move(projection.invert(point)));
30287 /* Align nodes along their common axis */
30289 function actionStraightenNodes(nodeIDs, projection) {
30290 function positionAlongWay(a, o, b) {
30291 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30292 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
30295 function getEndpoints(points) {
30296 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30297 // The shape's surrounding rectangle has 2 axes of symmetry.
30298 // Snap points to the long axis
30300 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30301 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30302 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30303 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30304 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30313 var action = function action(graph, t) {
30314 if (t === null || !isFinite(t)) t = 1;
30315 t = Math.min(Math.max(+t, 0), 1);
30316 var nodes = nodeIDs.map(function (id) {
30317 return graph.entity(id);
30319 var points = nodes.map(function (n) {
30320 return projection(n.loc);
30322 var endpoints = getEndpoints(points);
30323 var startPoint = endpoints[0];
30324 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
30326 for (var i = 0; i < points.length; i++) {
30327 var node = nodes[i];
30328 var point = points[i];
30329 var u = positionAlongWay(point, startPoint, endPoint);
30330 var point2 = geoVecInterp(startPoint, endPoint, u);
30331 var loc2 = projection.invert(point2);
30332 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30338 action.disabled = function (graph) {
30339 var nodes = nodeIDs.map(function (id) {
30340 return graph.entity(id);
30342 var points = nodes.map(function (n) {
30343 return projection(n.loc);
30345 var endpoints = getEndpoints(points);
30346 var startPoint = endpoints[0];
30347 var endPoint = endpoints[1];
30348 var maxDistance = 0;
30350 for (var i = 0; i < points.length; i++) {
30351 var point = points[i];
30352 var u = positionAlongWay(point, startPoint, endPoint);
30353 var p = geoVecInterp(startPoint, endPoint, u);
30354 var dist = geoVecLength(p, point);
30356 if (!isNaN(dist) && dist > maxDistance) {
30357 maxDistance = dist;
30361 if (maxDistance < 0.0001) {
30362 return 'straight_enough';
30366 action.transitionable = true;
30371 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
30374 function actionStraightenWay(selectedIDs, projection) {
30375 function positionAlongWay(a, o, b) {
30376 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30377 } // Return all selected ways as a continuous, ordered array of nodes
30380 function allNodes(graph) {
30382 var startNodes = [];
30384 var remainingWays = [];
30385 var selectedWays = selectedIDs.filter(function (w) {
30386 return graph.entity(w).type === 'way';
30388 var selectedNodes = selectedIDs.filter(function (n) {
30389 return graph.entity(n).type === 'node';
30392 for (var i = 0; i < selectedWays.length; i++) {
30393 var way = graph.entity(selectedWays[i]);
30394 nodes = way.nodes.slice(0);
30395 remainingWays.push(nodes);
30396 startNodes.push(nodes[0]);
30397 endNodes.push(nodes[nodes.length - 1]);
30398 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
30399 // and need to be removed so currNode difference calculation below works)
30400 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
30403 startNodes = startNodes.filter(function (n) {
30404 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
30406 endNodes = endNodes.filter(function (n) {
30407 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
30408 }); // Choose the initial endpoint to start from
30410 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
30412 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
30414 var getNextWay = function getNextWay(currNode, remainingWays) {
30415 return remainingWays.filter(function (way) {
30416 return way[0] === currNode || way[way.length - 1] === currNode;
30418 }; // Add nodes to end of nodes array, until all ways are added
30421 while (remainingWays.length) {
30422 nextWay = getNextWay(currNode, remainingWays);
30423 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
30425 if (nextWay[0] !== currNode) {
30429 nodes = nodes.concat(nextWay);
30430 currNode = nodes[nodes.length - 1];
30431 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
30434 if (selectedNodes.length === 2) {
30435 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
30436 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
30437 var sortedStartEnd = [startNodeIdx, endNodeIdx];
30438 sortedStartEnd.sort(function (a, b) {
30441 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
30444 return nodes.map(function (n) {
30445 return graph.entity(n);
30449 function shouldKeepNode(node, graph) {
30450 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
30453 var action = function action(graph, t) {
30454 if (t === null || !isFinite(t)) t = 1;
30455 t = Math.min(Math.max(+t, 0), 1);
30456 var nodes = allNodes(graph);
30457 var points = nodes.map(function (n) {
30458 return projection(n.loc);
30460 var startPoint = points[0];
30461 var endPoint = points[points.length - 1];
30465 for (i = 1; i < points.length - 1; i++) {
30466 var node = nodes[i];
30467 var point = points[i];
30469 if (t < 1 || shouldKeepNode(node, graph)) {
30470 var u = positionAlongWay(point, startPoint, endPoint);
30471 var p = geoVecInterp(startPoint, endPoint, u);
30472 var loc2 = projection.invert(p);
30473 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30476 if (toDelete.indexOf(node) === -1) {
30477 toDelete.push(node);
30482 for (i = 0; i < toDelete.length; i++) {
30483 graph = actionDeleteNode(toDelete[i].id)(graph);
30489 action.disabled = function (graph) {
30490 // check way isn't too bendy
30491 var nodes = allNodes(graph);
30492 var points = nodes.map(function (n) {
30493 return projection(n.loc);
30495 var startPoint = points[0];
30496 var endPoint = points[points.length - 1];
30497 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
30500 if (threshold === 0) {
30501 return 'too_bendy';
30504 var maxDistance = 0;
30506 for (i = 1; i < points.length - 1; i++) {
30507 var point = points[i];
30508 var u = positionAlongWay(point, startPoint, endPoint);
30509 var p = geoVecInterp(startPoint, endPoint, u);
30510 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
30512 if (isNaN(dist) || dist > threshold) {
30513 return 'too_bendy';
30514 } else if (dist > maxDistance) {
30515 maxDistance = dist;
30519 var keepingAllNodes = nodes.every(function (node, i) {
30520 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
30523 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
30525 return 'straight_enough';
30529 action.transitionable = true;
30534 // `turn` must be an `osmTurn` object with a `restrictionID` property.
30535 // see osm/intersection.js, pathToTurn()
30538 function actionUnrestrictTurn(turn) {
30539 return function (graph) {
30540 return actionDeleteRelation(turn.restrictionID)(graph);
30544 /* Reflect the given area around its axis of symmetry */
30546 function actionReflect(reflectIds, projection) {
30547 var _useLongAxis = true;
30549 var action = function action(graph, t) {
30550 if (t === null || !isFinite(t)) t = 1;
30551 t = Math.min(Math.max(+t, 0), 1);
30552 var nodes = utilGetAllNodes(reflectIds, graph);
30553 var points = nodes.map(function (n) {
30554 return projection(n.loc);
30556 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30557 // The shape's surrounding rectangle has 2 axes of symmetry.
30558 // Reflect across the longer axis by default.
30560 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30561 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30562 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30563 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30565 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30567 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
30573 } // reflect c across pq
30574 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
30577 var dx = q[0] - p[0];
30578 var dy = q[1] - p[1];
30579 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
30580 var b = 2 * dx * dy / (dx * dx + dy * dy);
30582 for (var i = 0; i < nodes.length; i++) {
30583 var node = nodes[i];
30584 var c = projection(node.loc);
30585 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]];
30586 var loc2 = projection.invert(c2);
30587 node = node.move(geoVecInterp(node.loc, loc2, t));
30588 graph = graph.replace(node);
30594 action.useLongAxis = function (val) {
30595 if (!arguments.length) return _useLongAxis;
30596 _useLongAxis = val;
30600 action.transitionable = true;
30604 function actionUpgradeTags(entityId, oldTags, replaceTags) {
30605 return function (graph) {
30606 var entity = graph.entity(entityId);
30607 var tags = Object.assign({}, entity.tags); // shallow copy
30612 for (var oldTagKey in oldTags) {
30613 if (!(oldTagKey in tags)) continue; // wildcard match
30615 if (oldTags[oldTagKey] === '*') {
30616 // note the value since we might need to transfer it
30617 transferValue = tags[oldTagKey];
30618 delete tags[oldTagKey]; // exact match
30619 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
30620 delete tags[oldTagKey]; // match is within semicolon-delimited values
30622 var vals = tags[oldTagKey].split(';').filter(Boolean);
30623 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
30625 if (vals.length === 1 || oldIndex === -1) {
30626 delete tags[oldTagKey];
30628 if (replaceTags && replaceTags[oldTagKey]) {
30629 // replacing a value within a semicolon-delimited value, note the index
30630 semiIndex = oldIndex;
30633 vals.splice(oldIndex, 1);
30634 tags[oldTagKey] = vals.join(';');
30640 for (var replaceKey in replaceTags) {
30641 var replaceValue = replaceTags[replaceKey];
30643 if (replaceValue === '*') {
30644 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
30645 // allow any pre-existing value except `no` (troll tag)
30648 // otherwise assume `yes` is okay
30649 tags[replaceKey] = 'yes';
30651 } else if (replaceValue === '$1') {
30652 tags[replaceKey] = transferValue;
30654 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
30655 // don't override preexisting values
30656 var existingVals = tags[replaceKey].split(';').filter(Boolean);
30658 if (existingVals.indexOf(replaceValue) === -1) {
30659 existingVals.splice(semiIndex, 0, replaceValue);
30660 tags[replaceKey] = existingVals.join(';');
30663 tags[replaceKey] = replaceValue;
30669 return graph.replace(entity.update({
30675 function behaviorEdit(context) {
30676 function behavior() {
30677 context.map().minzoom(context.minEditableZoom());
30680 behavior.off = function () {
30681 context.map().minzoom(0);
30688 The hover behavior adds the `.hover` class on pointerover to all elements to which
30689 the identical datum is bound, and removes it on pointerout.
30691 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
30692 representation may consist of several elements scattered throughout the DOM hierarchy.
30693 Only one of these elements can have the :hover pseudo-class, but all of them will
30694 have the .hover class.
30697 function behaviorHover(context) {
30698 var dispatch$1 = dispatch('hover');
30700 var _selection = select(null);
30702 var _newNodeId = null;
30703 var _initialNodeID = null;
30709 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
30711 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
30713 function keydown(d3_event) {
30714 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30715 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
30717 _selection.classed('hover-disabled', true);
30719 dispatch$1.call('hover', this, null);
30723 function keyup(d3_event) {
30724 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30725 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
30727 _selection.classed('hover-disabled', false);
30729 dispatch$1.call('hover', this, _targets);
30733 function behavior(selection) {
30734 _selection = selection;
30737 if (_initialNodeID) {
30738 _newNodeId = _initialNodeID;
30739 _initialNodeID = null;
30744 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
30745 .on(_pointerPrefix + 'down.hover', pointerover);
30747 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
30749 function eventTarget(d3_event) {
30750 var datum = d3_event.target && d3_event.target.__data__;
30751 if (_typeof(datum) !== 'object') return null;
30753 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
30754 return datum.properties.entity;
30760 function pointerover(d3_event) {
30761 // ignore mouse hovers with buttons pressed unless dragging
30762 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
30763 var target = eventTarget(d3_event);
30765 if (target && _targets.indexOf(target) === -1) {
30766 _targets.push(target);
30768 updateHover(d3_event, _targets);
30772 function pointerout(d3_event) {
30773 var target = eventTarget(d3_event);
30775 var index = _targets.indexOf(target);
30777 if (index !== -1) {
30778 _targets.splice(index);
30780 updateHover(d3_event, _targets);
30784 function allowsVertex(d) {
30785 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30788 function modeAllowsHover(target) {
30789 var mode = context.mode();
30791 if (mode.id === 'add-point') {
30792 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
30798 function updateHover(d3_event, targets) {
30799 _selection.selectAll('.hover').classed('hover', false);
30801 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30803 var mode = context.mode();
30805 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
30806 var node = targets.find(function (target) {
30807 return target instanceof osmEntity && target.type === 'node';
30809 _newNodeId = node && node.id;
30812 targets = targets.filter(function (datum) {
30813 if (datum instanceof osmEntity) {
30814 // If drawing a way, don't hover on a node that was just placed. #3974
30815 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
30822 for (var i in targets) {
30823 var datum = targets[i]; // What are we hovering over?
30825 if (datum.__featurehash__) {
30826 // hovering custom data
30827 selector += ', .data' + datum.__featurehash__;
30828 } else if (datum instanceof QAItem) {
30829 selector += ', .' + datum.service + '.itemId-' + datum.id;
30830 } else if (datum instanceof osmNote) {
30831 selector += ', .note-' + datum.id;
30832 } else if (datum instanceof osmEntity) {
30833 selector += ', .' + datum.id;
30835 if (datum.type === 'relation') {
30836 for (var j in datum.members) {
30837 selector += ', .' + datum.members[j].id;
30843 var suppressed = _altDisables && d3_event && d3_event.altKey;
30845 if (selector.trim().length) {
30846 // remove the first comma
30847 selector = selector.slice(1);
30849 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
30852 dispatch$1.call('hover', this, !suppressed && targets);
30856 behavior.off = function (selection) {
30857 selection.selectAll('.hover').classed('hover', false);
30858 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30859 selection.classed('hover-disabled', false);
30860 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
30861 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
30864 behavior.altDisables = function (val) {
30865 if (!arguments.length) return _altDisables;
30866 _altDisables = val;
30870 behavior.ignoreVertex = function (val) {
30871 if (!arguments.length) return _ignoreVertex;
30872 _ignoreVertex = val;
30876 behavior.initialNodeID = function (nodeId) {
30877 _initialNodeID = nodeId;
30881 return utilRebind(behavior, dispatch$1, 'on');
30884 var _disableSpace = false;
30885 var _lastSpace = null;
30886 function behaviorDraw(context) {
30887 var dispatch$1 = dispatch('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
30888 var keybinding = utilKeybinding('draw');
30890 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
30892 var _edit = behaviorEdit(context);
30894 var _closeTolerance = 4;
30895 var _tolerance = 12;
30896 var _mouseLeave = false;
30897 var _lastMouse = null;
30899 var _lastPointerUpEvent;
30901 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
30904 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
30905 // - `mode/drag_node.js` `datum()`
30908 function datum(d3_event) {
30909 var mode = context.mode();
30910 var isNote = mode && mode.id.indexOf('note') !== -1;
30911 if (d3_event.altKey || isNote) return {};
30914 if (d3_event.type === 'keydown') {
30915 element = _lastMouse && _lastMouse.target;
30917 element = d3_event.target;
30918 } // When drawing, snap only to touch targets..
30919 // (this excludes area fills and active drawing elements)
30922 var d = element.__data__;
30923 return d && d.properties && d.properties.target ? d : {};
30926 function pointerdown(d3_event) {
30927 if (_downPointer) return;
30928 var pointerLocGetter = utilFastMouse(this);
30930 id: d3_event.pointerId || 'mouse',
30931 pointerLocGetter: pointerLocGetter,
30932 downTime: +new Date(),
30933 downLoc: pointerLocGetter(d3_event)
30935 dispatch$1.call('down', this, d3_event, datum(d3_event));
30938 function pointerup(d3_event) {
30939 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
30940 var downPointer = _downPointer;
30941 _downPointer = null;
30942 _lastPointerUpEvent = d3_event;
30943 if (downPointer.isCancelled) return;
30944 var t2 = +new Date();
30945 var p2 = downPointer.pointerLocGetter(d3_event);
30946 var dist = geoVecLength(downPointer.downLoc, p2);
30948 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
30949 // Prevent a quick second click
30950 select(window).on('click.draw-block', function () {
30951 d3_event.stopPropagation();
30953 context.map().dblclickZoomEnable(false);
30954 window.setTimeout(function () {
30955 context.map().dblclickZoomEnable(true);
30956 select(window).on('click.draw-block', null);
30958 click(d3_event, p2);
30962 function pointermove(d3_event) {
30963 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
30964 var p2 = _downPointer.pointerLocGetter(d3_event);
30966 var dist = geoVecLength(_downPointer.downLoc, p2);
30968 if (dist >= _closeTolerance) {
30969 _downPointer.isCancelled = true;
30970 dispatch$1.call('downcancel', this);
30974 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
30975 // events immediately after non-mouse pointerup events; detect and ignore them.
30977 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
30978 _lastMouse = d3_event;
30979 dispatch$1.call('move', this, d3_event, datum(d3_event));
30982 function pointercancel(d3_event) {
30983 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
30984 if (!_downPointer.isCancelled) {
30985 dispatch$1.call('downcancel', this);
30988 _downPointer = null;
30992 function mouseenter() {
30993 _mouseLeave = false;
30996 function mouseleave() {
30997 _mouseLeave = true;
31000 function allowsVertex(d) {
31001 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
31003 // - `mode/drag_node.js` `doMove()`
31004 // - `behavior/draw.js` `click()`
31005 // - `behavior/draw_way.js` `move()`
31008 function click(d3_event, loc) {
31009 var d = datum(d3_event);
31010 var target = d && d.properties && d.properties.entity;
31011 var mode = context.mode();
31013 if (target && target.type === 'node' && allowsVertex(target)) {
31015 dispatch$1.call('clickNode', this, target, d);
31017 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
31019 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
31022 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
31023 dispatch$1.call('clickWay', this, choice.loc, edge, d);
31026 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
31027 var locLatLng = context.projection.invert(loc);
31028 dispatch$1.call('click', this, locLatLng, d);
31030 } // treat a spacebar press like a click
31033 function space(d3_event) {
31034 d3_event.preventDefault();
31035 d3_event.stopPropagation();
31036 var currSpace = context.map().mouse();
31038 if (_disableSpace && _lastSpace) {
31039 var dist = geoVecLength(_lastSpace, currSpace);
31041 if (dist > _tolerance) {
31042 _disableSpace = false;
31046 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
31048 _lastSpace = currSpace;
31049 _disableSpace = true;
31050 select(window).on('keyup.space-block', function () {
31051 d3_event.preventDefault();
31052 d3_event.stopPropagation();
31053 _disableSpace = false;
31054 select(window).on('keyup.space-block', null);
31055 }); // get the current mouse position
31057 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
31058 context.projection(context.map().center());
31059 click(d3_event, loc);
31062 function backspace(d3_event) {
31063 d3_event.preventDefault();
31064 dispatch$1.call('undo');
31067 function del(d3_event) {
31068 d3_event.preventDefault();
31069 dispatch$1.call('cancel');
31072 function ret(d3_event) {
31073 d3_event.preventDefault();
31074 dispatch$1.call('finish');
31077 function behavior(selection) {
31078 context.install(_hover);
31079 context.install(_edit);
31080 _downPointer = null;
31081 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
31082 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
31083 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
31084 select(document).call(keybinding);
31088 behavior.off = function (selection) {
31089 context.ui().sidebar.hover.cancel();
31090 context.uninstall(_hover);
31091 context.uninstall(_edit);
31092 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
31093 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
31095 select(document).call(keybinding.unbind);
31098 behavior.hover = function () {
31102 return utilRebind(behavior, dispatch$1, 'on');
31105 function initRange(domain, range) {
31106 switch (arguments.length) {
31111 this.range(domain);
31115 this.range(range).domain(domain);
31122 function constants(x) {
31123 return function () {
31128 function number$1(x) {
31133 function identity$3(x) {
31137 function normalize$1(a, b) {
31138 return (b -= a = +a) ? function (x) {
31139 return (x - a) / b;
31140 } : constants(isNaN(b) ? NaN : 0.5);
31143 function clamper(a, b) {
31145 if (a > b) t = a, a = b, b = t;
31146 return function (x) {
31147 return Math.max(a, Math.min(b, x));
31149 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
31150 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
31153 function bimap(domain, range, interpolate) {
31154 var d0 = domain[0],
31158 if (d1 < d0) d0 = normalize$1(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize$1(d0, d1), r0 = interpolate(r0, r1);
31159 return function (x) {
31164 function polymap(domain, range, interpolate) {
31165 var j = Math.min(domain.length, range.length) - 1,
31168 i = -1; // Reverse descending domains.
31170 if (domain[j] < domain[0]) {
31171 domain = domain.slice().reverse();
31172 range = range.slice().reverse();
31176 d[i] = normalize$1(domain[i], domain[i + 1]);
31177 r[i] = interpolate(range[i], range[i + 1]);
31180 return function (x) {
31181 var i = bisectRight(domain, x, 1, j) - 1;
31182 return r[i](d[i](x));
31186 function copy(source, target) {
31187 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
31189 function transformer$1() {
31192 interpolate$1 = interpolate,
31196 clamp = identity$3,
31201 function rescale() {
31202 var n = Math.min(domain.length, range.length);
31203 if (clamp !== identity$3) clamp = clamper(domain[0], domain[n - 1]);
31204 piecewise = n > 2 ? polymap : bimap;
31205 output = input = null;
31209 function scale(x) {
31210 return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
31213 scale.invert = function (y) {
31214 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
31217 scale.domain = function (_) {
31218 return arguments.length ? (domain = Array.from(_, number$1), rescale()) : domain.slice();
31221 scale.range = function (_) {
31222 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
31225 scale.rangeRound = function (_) {
31226 return range = Array.from(_), interpolate$1 = interpolateRound, rescale();
31229 scale.clamp = function (_) {
31230 return arguments.length ? (clamp = _ ? true : identity$3, rescale()) : clamp !== identity$3;
31233 scale.interpolate = function (_) {
31234 return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
31237 scale.unknown = function (_) {
31238 return arguments.length ? (unknown = _, scale) : unknown;
31241 return function (t, u) {
31242 transform = t, untransform = u;
31246 function continuous() {
31247 return transformer$1()(identity$3, identity$3);
31250 function formatDecimal (x) {
31251 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
31252 } // Computes the decimal coefficient and exponent of the specified number x with
31253 // significant digits p, where x is positive and p is in [1, 21] or undefined.
31254 // For example, formatDecimalParts(1.23) returns ["123", 0].
31256 function formatDecimalParts(x, p) {
31257 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
31260 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
31261 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
31263 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
31266 function exponent (x) {
31267 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
31270 function formatGroup (grouping, thousands) {
31271 return function (value, width) {
31272 var i = value.length,
31278 while (i > 0 && g > 0) {
31279 if (length + g + 1 > width) g = Math.max(1, width - length);
31280 t.push(value.substring(i -= g, i + g));
31281 if ((length += g + 1) > width) break;
31282 g = grouping[j = (j + 1) % grouping.length];
31285 return t.reverse().join(thousands);
31289 function formatNumerals (numerals) {
31290 return function (value) {
31291 return value.replace(/[0-9]/g, function (i) {
31292 return numerals[+i];
31297 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
31298 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
31299 function formatSpecifier(specifier) {
31300 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
31302 return new FormatSpecifier({
31310 precision: match[8] && match[8].slice(1),
31315 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
31317 function FormatSpecifier(specifier) {
31318 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
31319 this.align = specifier.align === undefined ? ">" : specifier.align + "";
31320 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
31321 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
31322 this.zero = !!specifier.zero;
31323 this.width = specifier.width === undefined ? undefined : +specifier.width;
31324 this.comma = !!specifier.comma;
31325 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
31326 this.trim = !!specifier.trim;
31327 this.type = specifier.type === undefined ? "" : specifier.type + "";
31330 FormatSpecifier.prototype.toString = function () {
31331 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;
31334 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
31335 function formatTrim (s) {
31336 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
31343 if (i0 === 0) i0 = i;
31348 if (!+s[i]) break out;
31349 if (i0 > 0) i0 = 0;
31354 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
31357 // `thisNumberValue` abstract operation
31358 // https://tc39.es/ecma262/#sec-thisnumbervalue
31359 var thisNumberValue = function (value) {
31360 if (typeof value != 'number' && classofRaw(value) != 'Number') {
31361 throw TypeError('Incorrect invocation');
31366 // `String.prototype.repeat` method implementation
31367 // https://tc39.es/ecma262/#sec-string.prototype.repeat
31368 var stringRepeat = ''.repeat || function repeat(count) {
31369 var str = String(requireObjectCoercible(this));
31371 var n = toInteger(count);
31372 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
31373 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
31377 var nativeToFixed = 1.0.toFixed;
31378 var floor$6 = Math.floor;
31380 var pow$2 = function (x, n, acc) {
31381 return n === 0 ? acc : n % 2 === 1 ? pow$2(x, n - 1, acc * x) : pow$2(x * x, n / 2, acc);
31384 var log$2 = function (x) {
31387 while (x2 >= 4096) {
31397 var multiply = function (data, n, c) {
31400 while (++index < 6) {
31401 c2 += n * data[index];
31402 data[index] = c2 % 1e7;
31403 c2 = floor$6(c2 / 1e7);
31407 var divide = function (data, n) {
31410 while (--index >= 0) {
31412 data[index] = floor$6(c / n);
31417 var dataToString = function (data) {
31420 while (--index >= 0) {
31421 if (s !== '' || index === 0 || data[index] !== 0) {
31422 var t = String(data[index]);
31423 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
31428 var FORCED$c = nativeToFixed && (
31429 0.00008.toFixed(3) !== '0.000' ||
31430 0.9.toFixed(0) !== '1' ||
31431 1.255.toFixed(2) !== '1.25' ||
31432 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
31433 ) || !fails(function () {
31434 // V8 ~ Android 4.3-
31435 nativeToFixed.call({});
31438 // `Number.prototype.toFixed` method
31439 // https://tc39.es/ecma262/#sec-number.prototype.tofixed
31440 _export({ target: 'Number', proto: true, forced: FORCED$c }, {
31441 toFixed: function toFixed(fractionDigits) {
31442 var number = thisNumberValue(this);
31443 var fractDigits = toInteger(fractionDigits);
31444 var data = [0, 0, 0, 0, 0, 0];
31449 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
31450 // eslint-disable-next-line no-self-compare -- NaN check
31451 if (number != number) return 'NaN';
31452 if (number <= -1e21 || number >= 1e21) return String(number);
31457 if (number > 1e-21) {
31458 e = log$2(number * pow$2(2, 69, 1)) - 69;
31459 z = e < 0 ? number * pow$2(2, -e, 1) : number / pow$2(2, e, 1);
31460 z *= 0x10000000000000;
31463 multiply(data, 0, z);
31466 multiply(data, 1e7, 0);
31469 multiply(data, pow$2(10, j, 1), 0);
31472 divide(data, 1 << 23);
31475 divide(data, 1 << j);
31476 multiply(data, 1, 1);
31478 result = dataToString(data);
31480 multiply(data, 0, z);
31481 multiply(data, 1 << -e, 0);
31482 result = dataToString(data) + stringRepeat.call('0', fractDigits);
31485 if (fractDigits > 0) {
31487 result = sign + (k <= fractDigits
31488 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
31489 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
31491 result = sign + result;
31496 var nativeToPrecision = 1.0.toPrecision;
31498 var FORCED$d = fails(function () {
31500 return nativeToPrecision.call(1, undefined) !== '1';
31501 }) || !fails(function () {
31502 // V8 ~ Android 4.3-
31503 nativeToPrecision.call({});
31506 // `Number.prototype.toPrecision` method
31507 // https://tc39.es/ecma262/#sec-number.prototype.toprecision
31508 _export({ target: 'Number', proto: true, forced: FORCED$d }, {
31509 toPrecision: function toPrecision(precision) {
31510 return precision === undefined
31511 ? nativeToPrecision.call(thisNumberValue(this))
31512 : nativeToPrecision.call(thisNumberValue(this), precision);
31516 var prefixExponent;
31517 function formatPrefixAuto (x, p) {
31518 var d = formatDecimalParts(x, p);
31519 if (!d) return x + "";
31520 var coefficient = d[0],
31522 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
31523 n = coefficient.length;
31524 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!
31527 function formatRounded (x, p) {
31528 var d = formatDecimalParts(x, p);
31529 if (!d) return x + "";
31530 var coefficient = d[0],
31532 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");
31535 var formatTypes = {
31536 "%": function _(x, p) {
31537 return (x * 100).toFixed(p);
31539 "b": function b(x) {
31540 return Math.round(x).toString(2);
31542 "c": function c(x) {
31545 "d": formatDecimal,
31546 "e": function e(x, p) {
31547 return x.toExponential(p);
31549 "f": function f(x, p) {
31550 return x.toFixed(p);
31552 "g": function g(x, p) {
31553 return x.toPrecision(p);
31555 "o": function o(x) {
31556 return Math.round(x).toString(8);
31558 "p": function p(x, _p) {
31559 return formatRounded(x * 100, _p);
31561 "r": formatRounded,
31562 "s": formatPrefixAuto,
31563 "X": function X(x) {
31564 return Math.round(x).toString(16).toUpperCase();
31566 "x": function x(_x) {
31567 return Math.round(_x).toString(16);
31571 function identity$4 (x) {
31575 var map = Array.prototype.map,
31576 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
31577 function formatLocale (locale) {
31578 var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
31579 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
31580 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
31581 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
31582 numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map.call(locale.numerals, String)),
31583 percent = locale.percent === undefined ? "%" : locale.percent + "",
31584 minus = locale.minus === undefined ? "−" : locale.minus + "",
31585 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
31587 function newFormat(specifier) {
31588 specifier = formatSpecifier(specifier);
31589 var fill = specifier.fill,
31590 align = specifier.align,
31591 sign = specifier.sign,
31592 symbol = specifier.symbol,
31593 zero = specifier.zero,
31594 width = specifier.width,
31595 comma = specifier.comma,
31596 precision = specifier.precision,
31597 trim = specifier.trim,
31598 type = specifier.type; // The "n" type is an alias for ",g".
31600 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
31601 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
31603 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
31604 // For SI-prefix, the suffix is lazily computed.
31606 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
31607 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
31608 // Is this an integer type?
31609 // Can this type generate exponential notation?
31611 var formatType = formatTypes[type],
31612 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
31613 // or clamp the specified precision to the supported range.
31614 // For significant precision, it must be in [1, 21].
31615 // For fixed precision, it must be in [0, 20].
31617 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
31619 function format(value) {
31620 var valuePrefix = prefix,
31621 valueSuffix = suffix,
31626 if (type === "c") {
31627 valueSuffix = formatType(value) + valueSuffix;
31630 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
31632 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
31634 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
31636 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
31638 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
31640 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
31641 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
31642 // grouped, and fractional or exponential “suffix” part that is not.
31645 i = -1, n = value.length;
31648 if (c = value.charCodeAt(i), 48 > c || c > 57) {
31649 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
31650 value = value.slice(0, i);
31655 } // If the fill character is not "0", grouping is applied before padding.
31658 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
31660 var length = valuePrefix.length + value.length + valueSuffix.length,
31661 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
31663 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
31667 value = valuePrefix + value + valueSuffix + padding;
31671 value = valuePrefix + padding + value + valueSuffix;
31675 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
31679 value = padding + valuePrefix + value + valueSuffix;
31683 return numerals(value);
31686 format.toString = function () {
31687 return specifier + "";
31693 function formatPrefix(specifier, value) {
31694 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
31695 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
31696 k = Math.pow(10, -e),
31697 prefix = prefixes[8 + e / 3];
31698 return function (value) {
31699 return f(k * value) + prefix;
31705 formatPrefix: formatPrefix
31715 currency: ["$", ""]
31717 function defaultLocale(definition) {
31718 locale = formatLocale(definition);
31719 format = locale.format;
31720 formatPrefix = locale.formatPrefix;
31724 function precisionFixed (step) {
31725 return Math.max(0, -exponent(Math.abs(step)));
31728 function precisionPrefix (step, value) {
31729 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
31732 function precisionRound (step, max) {
31733 step = Math.abs(step), max = Math.abs(max) - step;
31734 return Math.max(0, exponent(max) - exponent(step)) + 1;
31737 function tickFormat(start, stop, count, specifier) {
31738 var step = tickStep(start, stop, count),
31740 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
31742 switch (specifier.type) {
31745 var value = Math.max(Math.abs(start), Math.abs(stop));
31746 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
31747 return formatPrefix(specifier, value);
31756 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
31763 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
31768 return format(specifier);
31771 function linearish(scale) {
31772 var domain = scale.domain;
31774 scale.ticks = function (count) {
31776 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
31779 scale.tickFormat = function (count, specifier) {
31781 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
31784 scale.nice = function (count) {
31785 if (count == null) count = 10;
31788 var i1 = d.length - 1;
31795 if (stop < start) {
31796 step = start, start = stop, stop = step;
31797 step = i0, i0 = i1, i1 = step;
31800 while (maxIter-- > 0) {
31801 step = tickIncrement(start, stop, count);
31803 if (step === prestep) {
31807 } else if (step > 0) {
31808 start = Math.floor(start / step) * step;
31809 stop = Math.ceil(stop / step) * step;
31810 } else if (step < 0) {
31811 start = Math.ceil(start * step) / step;
31812 stop = Math.floor(stop * step) / step;
31825 function linear$2() {
31826 var scale = continuous();
31828 scale.copy = function () {
31829 return copy(scale, linear$2());
31832 initRange.apply(scale, arguments);
31833 return linearish(scale);
31836 var nativeExpm1 = Math.expm1;
31837 var exp$1 = Math.exp;
31839 // `Math.expm1` method implementation
31840 // https://tc39.es/ecma262/#sec-math.expm1
31841 var mathExpm1 = (!nativeExpm1
31843 || nativeExpm1(10) > 22025.465794806719 || nativeExpm1(10) < 22025.4657948067165168
31845 || nativeExpm1(-2e-17) != -2e-17
31846 ) ? function expm1(x) {
31847 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
31850 function quantize() {
31858 function scale(x) {
31859 return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
31862 function rescale() {
31864 domain = new Array(n);
31867 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
31873 scale.domain = function (_) {
31876 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
31879 scale.range = function (_) {
31880 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
31883 scale.invertExtent = function (y) {
31884 var i = range.indexOf(y);
31885 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
31888 scale.unknown = function (_) {
31889 return arguments.length ? (unknown = _, scale) : scale;
31892 scale.thresholds = function () {
31893 return domain.slice();
31896 scale.copy = function () {
31897 return quantize().domain([x0, x1]).range(range).unknown(unknown);
31900 return initRange.apply(linearish(scale), arguments);
31903 // https://github.com/tc39/proposal-string-pad-start-end
31908 var ceil$1 = Math.ceil;
31910 // `String.prototype.{ padStart, padEnd }` methods implementation
31911 var createMethod$6 = function (IS_END) {
31912 return function ($this, maxLength, fillString) {
31913 var S = String(requireObjectCoercible($this));
31914 var stringLength = S.length;
31915 var fillStr = fillString === undefined ? ' ' : String(fillString);
31916 var intMaxLength = toLength(maxLength);
31917 var fillLen, stringFiller;
31918 if (intMaxLength <= stringLength || fillStr == '') return S;
31919 fillLen = intMaxLength - stringLength;
31920 stringFiller = stringRepeat.call(fillStr, ceil$1(fillLen / fillStr.length));
31921 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
31922 return IS_END ? S + stringFiller : stringFiller + S;
31927 // `String.prototype.padStart` method
31928 // https://tc39.es/ecma262/#sec-string.prototype.padstart
31929 start: createMethod$6(false),
31930 // `String.prototype.padEnd` method
31931 // https://tc39.es/ecma262/#sec-string.prototype.padend
31932 end: createMethod$6(true)
31935 var padStart = stringPad.start;
31937 var abs$3 = Math.abs;
31938 var DatePrototype$1 = Date.prototype;
31939 var getTime$1 = DatePrototype$1.getTime;
31940 var nativeDateToISOString = DatePrototype$1.toISOString;
31942 // `Date.prototype.toISOString` method implementation
31943 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
31944 // PhantomJS / old WebKit fails here:
31945 var dateToIsoString = (fails(function () {
31946 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
31947 }) || !fails(function () {
31948 nativeDateToISOString.call(new Date(NaN));
31949 })) ? function toISOString() {
31950 if (!isFinite(getTime$1.call(this))) throw RangeError('Invalid time value');
31952 var year = date.getUTCFullYear();
31953 var milliseconds = date.getUTCMilliseconds();
31954 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
31955 return sign + padStart(abs$3(year), sign ? 6 : 4, 0) +
31956 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
31957 '-' + padStart(date.getUTCDate(), 2, 0) +
31958 'T' + padStart(date.getUTCHours(), 2, 0) +
31959 ':' + padStart(date.getUTCMinutes(), 2, 0) +
31960 ':' + padStart(date.getUTCSeconds(), 2, 0) +
31961 '.' + padStart(milliseconds, 3, 0) +
31963 } : nativeDateToISOString;
31965 // `Date.prototype.toISOString` method
31966 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
31967 // PhantomJS / old WebKit has a broken implementations
31968 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
31969 toISOString: dateToIsoString
31972 function behaviorBreathe() {
31973 var duration = 800;
31975 var selector = '.selected.shadow, .selected .shadow';
31977 var _selected = select(null);
31985 function ratchetyInterpolator(a, b, steps, units) {
31988 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
31989 return function (t) {
31990 return String(sample(t)) + (units || '');
31994 function reset(selection) {
31995 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
31998 function setAnimationParams(transition, fromTo) {
31999 var toFrom = fromTo === 'from' ? 'to' : 'from';
32000 transition.styleTween('stroke-opacity', function (d) {
32001 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
32002 }).styleTween('stroke-width', function (d) {
32003 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
32004 }).styleTween('fill-opacity', function (d) {
32005 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
32006 }).styleTween('r', function (d) {
32007 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
32011 function calcAnimationParams(selection) {
32012 selection.call(reset).each(function (d) {
32013 var s = select(this);
32014 var tag = s.node().tagName;
32020 var width; // determine base opacity and width
32022 if (tag === 'circle') {
32023 opacity = parseFloat(s.style('fill-opacity') || 0.5);
32024 width = parseFloat(s.style('r') || 15.5);
32026 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
32027 width = parseFloat(s.style('stroke-width') || 10);
32028 } // calculate from/to interpolation params..
32032 p.from.opacity = opacity * 0.6;
32033 p.to.opacity = opacity * 1.25;
32034 p.from.width = width * 0.7;
32035 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
32040 function run(surface, fromTo) {
32041 var toFrom = fromTo === 'from' ? 'to' : 'from';
32042 var currSelected = surface.selectAll(selector);
32043 var currClassed = surface.attr('class');
32045 if (_done || currSelected.empty()) {
32046 _selected.call(reset);
32048 _selected = select(null);
32052 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
32053 _selected.call(reset);
32055 _classed = currClassed;
32056 _selected = currSelected.call(calcAnimationParams);
32059 var didCallNextRun = false;
32061 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
32062 // `end` event is called for each selected element, but we want
32063 // it to run only once
32064 if (!didCallNextRun) {
32065 surface.call(run, toFrom);
32066 didCallNextRun = true;
32067 } // if entity was deselected, remove breathe styling
32070 if (!select(this).classed('selected')) {
32071 reset(select(this));
32076 function behavior(surface) {
32078 _timer = timer(function () {
32079 // wait for elements to actually become selected
32080 if (surface.selectAll(selector).empty()) {
32084 surface.call(run, 'from');
32092 behavior.restartIfNeeded = function (surface) {
32093 if (_selected.empty()) {
32094 surface.call(run, 'from');
32102 behavior.off = function () {
32109 _selected.interrupt().call(reset);
32115 /* Creates a keybinding behavior for an operation */
32116 function behaviorOperation(context) {
32119 function keypress(d3_event) {
32120 // prevent operations during low zoom selection
32121 if (!context.map().withinEditableZoom()) return;
32122 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
32123 d3_event.preventDefault();
32125 var disabled = _operation.disabled();
32128 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
32130 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
32131 if (_operation.point) _operation.point(null);
32137 function behavior() {
32138 if (_operation && _operation.available()) {
32139 context.keybinding().on(_operation.keys, keypress);
32145 behavior.off = function () {
32146 context.keybinding().off(_operation.keys);
32149 behavior.which = function (_) {
32150 if (!arguments.length) return _operation;
32158 function operationCircularize(context, selectedIDs) {
32161 var _actions = selectedIDs.map(getAction).filter(Boolean);
32163 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32165 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32169 function getAction(entityID) {
32170 var entity = context.entity(entityID);
32171 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
32174 _extent = entity.extent(context.graph());
32176 _extent = _extent.extend(entity.extent(context.graph()));
32179 return actionCircularize(entityID, context.projection);
32182 var operation = function operation() {
32183 if (!_actions.length) return;
32185 var combinedAction = function combinedAction(graph, t) {
32186 _actions.forEach(function (action) {
32187 if (!action.disabled(graph)) {
32188 graph = action(graph, t);
32195 combinedAction.transitionable = true;
32196 context.perform(combinedAction, operation.annotation());
32197 window.setTimeout(function () {
32198 context.validator().validate();
32199 }, 300); // after any transition
32202 operation.available = function () {
32203 return _actions.length && selectedIDs.length === _actions.length;
32204 }; // don't cache this because the visible extent could change
32207 operation.disabled = function () {
32208 if (!_actions.length) return '';
32210 var actionDisableds = _actions.map(function (action) {
32211 return action.disabled(context.graph());
32212 }).filter(Boolean);
32214 if (actionDisableds.length === _actions.length) {
32215 // none of the features can be circularized
32216 if (new Set(actionDisableds).size > 1) {
32217 return 'multiple_blockers';
32220 return actionDisableds[0];
32221 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
32222 return 'too_large';
32223 } else if (someMissing()) {
32224 return 'not_downloaded';
32225 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32226 return 'connected_to_hidden';
32231 function someMissing() {
32232 if (context.inIntro()) return false;
32233 var osm = context.connection();
32236 var missing = _coords.filter(function (loc) {
32237 return !osm.isDataLoaded(loc);
32240 if (missing.length) {
32241 missing.forEach(function (loc) {
32242 context.loadTileAtLoc(loc);
32252 operation.tooltip = function () {
32253 var disable = operation.disabled();
32254 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
32257 operation.annotation = function () {
32258 return _t('operations.circularize.annotation.feature', {
32263 operation.id = 'circularize';
32264 operation.keys = [_t('operations.circularize.key')];
32265 operation.title = _t('operations.circularize.title');
32266 operation.behavior = behaviorOperation(context).which(operation);
32270 // For example, ⌘Z -> Ctrl+Z
32272 var uiCmd = function uiCmd(code) {
32273 var detected = utilDetect();
32275 if (detected.os === 'mac') {
32279 if (detected.os === 'win') {
32280 if (code === '⌘⇧Z') return 'Ctrl+Y';
32292 for (var i = 0; i < code.length; i++) {
32293 if (code[i] in replacements) {
32294 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
32301 }; // return a display-focused string for a given keyboard code
32303 uiCmd.display = function (code) {
32304 if (code.length !== 1) return code;
32305 var detected = utilDetect();
32306 var mac = detected.os === 'mac';
32307 var replacements = {
32308 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
32309 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
32310 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
32311 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
32312 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
32313 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
32314 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
32315 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
32316 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
32317 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
32318 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
32319 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
32320 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
32322 return replacements[code] || code;
32325 function operationDelete(context, selectedIDs) {
32326 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32327 var action = actionDeleteMultiple(selectedIDs);
32328 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32329 var coords = nodes.map(function (n) {
32332 var extent = utilTotalExtent(selectedIDs, context.graph());
32334 var operation = function operation() {
32335 var nextSelectedID;
32336 var nextSelectedLoc;
32338 if (selectedIDs.length === 1) {
32339 var id = selectedIDs[0];
32340 var entity = context.entity(id);
32341 var geometry = entity.geometry(context.graph());
32342 var parents = context.graph().parentWays(entity);
32343 var parent = parents[0]; // Select the next closest node in the way.
32345 if (geometry === 'vertex') {
32346 var nodes = parent.nodes;
32347 var i = nodes.indexOf(id);
32351 } else if (i === nodes.length - 1) {
32354 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
32355 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
32356 i = a < b ? i - 1 : i + 1;
32359 nextSelectedID = nodes[i];
32360 nextSelectedLoc = context.entity(nextSelectedID).loc;
32364 context.perform(action, operation.annotation());
32365 context.validator().validate();
32367 if (nextSelectedID && nextSelectedLoc) {
32368 if (context.hasEntity(nextSelectedID)) {
32369 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
32371 context.map().centerEase(nextSelectedLoc);
32372 context.enter(modeBrowse(context));
32375 context.enter(modeBrowse(context));
32379 operation.available = function () {
32383 operation.disabled = function () {
32384 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32385 return 'too_large';
32386 } else if (someMissing()) {
32387 return 'not_downloaded';
32388 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32389 return 'connected_to_hidden';
32390 } else if (selectedIDs.some(protectedMember)) {
32391 return 'part_of_relation';
32392 } else if (selectedIDs.some(incompleteRelation)) {
32393 return 'incomplete_relation';
32394 } else if (selectedIDs.some(hasWikidataTag)) {
32395 return 'has_wikidata_tag';
32400 function someMissing() {
32401 if (context.inIntro()) return false;
32402 var osm = context.connection();
32405 var missing = coords.filter(function (loc) {
32406 return !osm.isDataLoaded(loc);
32409 if (missing.length) {
32410 missing.forEach(function (loc) {
32411 context.loadTileAtLoc(loc);
32420 function hasWikidataTag(id) {
32421 var entity = context.entity(id);
32422 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
32425 function incompleteRelation(id) {
32426 var entity = context.entity(id);
32427 return entity.type === 'relation' && !entity.isComplete(context.graph());
32430 function protectedMember(id) {
32431 var entity = context.entity(id);
32432 if (entity.type !== 'way') return false;
32433 var parents = context.graph().parentRelations(entity);
32435 for (var i = 0; i < parents.length; i++) {
32436 var parent = parents[i];
32437 var type = parent.tags.type;
32438 var role = parent.memberById(id).role || 'outer';
32440 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
32449 operation.tooltip = function () {
32450 var disable = operation.disabled();
32451 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
32454 operation.annotation = function () {
32455 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
32456 n: selectedIDs.length
32460 operation.id = 'delete';
32461 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
32462 operation.title = _t('operations.delete.title');
32463 operation.behavior = behaviorOperation(context).which(operation);
32467 function operationOrthogonalize(context, selectedIDs) {
32472 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
32474 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32476 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32480 function chooseAction(entityID) {
32481 var entity = context.entity(entityID);
32482 var geometry = entity.geometry(context.graph());
32485 _extent = entity.extent(context.graph());
32487 _extent = _extent.extend(entity.extent(context.graph()));
32488 } // square a line/area
32491 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
32492 if (_type && _type !== 'feature') return null;
32494 return actionOrthogonalize(entityID, context.projection); // square a single vertex
32495 } else if (geometry === 'vertex') {
32496 if (_type && _type !== 'corner') return null;
32498 var graph = context.graph();
32499 var parents = graph.parentWays(entity);
32501 if (parents.length === 1) {
32502 var way = parents[0];
32504 if (way.nodes.indexOf(entityID) !== -1) {
32505 return actionOrthogonalize(way.id, context.projection, entityID);
32513 var operation = function operation() {
32514 if (!_actions.length) return;
32516 var combinedAction = function combinedAction(graph, t) {
32517 _actions.forEach(function (action) {
32518 if (!action.disabled(graph)) {
32519 graph = action(graph, t);
32526 combinedAction.transitionable = true;
32527 context.perform(combinedAction, operation.annotation());
32528 window.setTimeout(function () {
32529 context.validator().validate();
32530 }, 300); // after any transition
32533 operation.available = function () {
32534 return _actions.length && selectedIDs.length === _actions.length;
32535 }; // don't cache this because the visible extent could change
32538 operation.disabled = function () {
32539 if (!_actions.length) return '';
32541 var actionDisableds = _actions.map(function (action) {
32542 return action.disabled(context.graph());
32543 }).filter(Boolean);
32545 if (actionDisableds.length === _actions.length) {
32546 // none of the features can be squared
32547 if (new Set(actionDisableds).size > 1) {
32548 return 'multiple_blockers';
32551 return actionDisableds[0];
32552 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
32553 return 'too_large';
32554 } else if (someMissing()) {
32555 return 'not_downloaded';
32556 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32557 return 'connected_to_hidden';
32562 function someMissing() {
32563 if (context.inIntro()) return false;
32564 var osm = context.connection();
32567 var missing = _coords.filter(function (loc) {
32568 return !osm.isDataLoaded(loc);
32571 if (missing.length) {
32572 missing.forEach(function (loc) {
32573 context.loadTileAtLoc(loc);
32583 operation.tooltip = function () {
32584 var disable = operation.disabled();
32585 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
32588 operation.annotation = function () {
32589 return _t('operations.orthogonalize.annotation.' + _type, {
32594 operation.id = 'orthogonalize';
32595 operation.keys = [_t('operations.orthogonalize.key')];
32596 operation.title = _t('operations.orthogonalize.title');
32597 operation.behavior = behaviorOperation(context).which(operation);
32601 function operationReflectShort(context, selectedIDs) {
32602 return operationReflect(context, selectedIDs, 'short');
32604 function operationReflectLong(context, selectedIDs) {
32605 return operationReflect(context, selectedIDs, 'long');
32607 function operationReflect(context, selectedIDs, axis) {
32608 axis = axis || 'long';
32609 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32610 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32611 var coords = nodes.map(function (n) {
32614 var extent = utilTotalExtent(selectedIDs, context.graph());
32616 var operation = function operation() {
32617 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
32618 context.perform(action, operation.annotation());
32619 window.setTimeout(function () {
32620 context.validator().validate();
32621 }, 300); // after any transition
32624 operation.available = function () {
32625 return nodes.length >= 3;
32626 }; // don't cache this because the visible extent could change
32629 operation.disabled = function () {
32630 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32631 return 'too_large';
32632 } else if (someMissing()) {
32633 return 'not_downloaded';
32634 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32635 return 'connected_to_hidden';
32636 } else if (selectedIDs.some(incompleteRelation)) {
32637 return 'incomplete_relation';
32642 function someMissing() {
32643 if (context.inIntro()) return false;
32644 var osm = context.connection();
32647 var missing = coords.filter(function (loc) {
32648 return !osm.isDataLoaded(loc);
32651 if (missing.length) {
32652 missing.forEach(function (loc) {
32653 context.loadTileAtLoc(loc);
32662 function incompleteRelation(id) {
32663 var entity = context.entity(id);
32664 return entity.type === 'relation' && !entity.isComplete(context.graph());
32668 operation.tooltip = function () {
32669 var disable = operation.disabled();
32670 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
32673 operation.annotation = function () {
32674 return _t('operations.reflect.annotation.' + axis + '.feature', {
32675 n: selectedIDs.length
32679 operation.id = 'reflect-' + axis;
32680 operation.keys = [_t('operations.reflect.key.' + axis)];
32681 operation.title = _t('operations.reflect.title.' + axis);
32682 operation.behavior = behaviorOperation(context).which(operation);
32686 function operationMove(context, selectedIDs) {
32687 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32688 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32689 var coords = nodes.map(function (n) {
32692 var extent = utilTotalExtent(selectedIDs, context.graph());
32694 var operation = function operation() {
32695 context.enter(modeMove(context, selectedIDs));
32698 operation.available = function () {
32699 return selectedIDs.length > 1 || context.entity(selectedIDs[0]).type !== 'node';
32702 operation.disabled = function () {
32703 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32704 return 'too_large';
32705 } else if (someMissing()) {
32706 return 'not_downloaded';
32707 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32708 return 'connected_to_hidden';
32709 } else if (selectedIDs.some(incompleteRelation)) {
32710 return 'incomplete_relation';
32715 function someMissing() {
32716 if (context.inIntro()) return false;
32717 var osm = context.connection();
32720 var missing = coords.filter(function (loc) {
32721 return !osm.isDataLoaded(loc);
32724 if (missing.length) {
32725 missing.forEach(function (loc) {
32726 context.loadTileAtLoc(loc);
32735 function incompleteRelation(id) {
32736 var entity = context.entity(id);
32737 return entity.type === 'relation' && !entity.isComplete(context.graph());
32741 operation.tooltip = function () {
32742 var disable = operation.disabled();
32743 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
32746 operation.annotation = function () {
32747 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
32748 n: selectedIDs.length
32752 operation.id = 'move';
32753 operation.keys = [_t('operations.move.key')];
32754 operation.title = _t('operations.move.title');
32755 operation.behavior = behaviorOperation(context).which(operation);
32756 operation.mouseOnly = true;
32760 function modeRotate(context, entityIDs) {
32765 var keybinding = utilKeybinding('rotate');
32766 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];
32767 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
32768 n: entityIDs.length
32775 var _prevTransform;
32779 function doRotate() {
32782 if (context.graph() !== _prevGraph) {
32783 fn = context.perform;
32785 fn = context.replace;
32786 } // projection changed, recalculate _pivot
32789 var projection = context.projection;
32790 var currTransform = projection.transform();
32792 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
32793 var nodes = utilGetAllNodes(entityIDs, context.graph());
32794 var points = nodes.map(function (n) {
32795 return projection(n.loc);
32797 _pivot = getPivot(points);
32798 _prevAngle = undefined;
32801 var currMouse = context.map().mouse();
32802 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
32803 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
32804 var delta = currAngle - _prevAngle;
32805 fn(actionRotate(entityIDs, _pivot, delta, projection));
32806 _prevTransform = currTransform;
32807 _prevAngle = currAngle;
32808 _prevGraph = context.graph();
32811 function getPivot(points) {
32814 if (points.length === 1) {
32815 _pivot = points[0];
32816 } else if (points.length === 2) {
32817 _pivot = geoVecInterp(points[0], points[1], 0.5);
32819 var polygonHull = d3_polygonHull(points);
32821 if (polygonHull.length === 2) {
32822 _pivot = geoVecInterp(points[0], points[1], 0.5);
32824 _pivot = d3_polygonCentroid(d3_polygonHull(points));
32831 function finish(d3_event) {
32832 d3_event.stopPropagation();
32833 context.replace(actionNoop(), annotation);
32834 context.enter(modeSelect(context, entityIDs));
32837 function cancel() {
32839 context.enter(modeSelect(context, entityIDs));
32842 function undone() {
32843 context.enter(modeBrowse(context));
32846 mode.enter = function () {
32847 context.features().forceVisible(entityIDs);
32848 behaviors.forEach(context.install);
32849 context.surface().on('mousemove.rotate', doRotate).on('click.rotate', finish);
32850 context.history().on('undone.rotate', undone);
32851 keybinding.on('⎋', cancel).on('↩', finish);
32852 select(document).call(keybinding);
32855 mode.exit = function () {
32856 behaviors.forEach(context.uninstall);
32857 context.surface().on('mousemove.rotate', null).on('click.rotate', null);
32858 context.history().on('undone.rotate', null);
32859 select(document).call(keybinding.unbind);
32860 context.features().forceVisible([]);
32863 mode.selectedIDs = function () {
32864 if (!arguments.length) return entityIDs; // no assign
32872 function operationRotate(context, selectedIDs) {
32873 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32874 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32875 var coords = nodes.map(function (n) {
32878 var extent = utilTotalExtent(selectedIDs, context.graph());
32880 var operation = function operation() {
32881 context.enter(modeRotate(context, selectedIDs));
32884 operation.available = function () {
32885 return nodes.length >= 2;
32888 operation.disabled = function () {
32889 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32890 return 'too_large';
32891 } else if (someMissing()) {
32892 return 'not_downloaded';
32893 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32894 return 'connected_to_hidden';
32895 } else if (selectedIDs.some(incompleteRelation)) {
32896 return 'incomplete_relation';
32901 function someMissing() {
32902 if (context.inIntro()) return false;
32903 var osm = context.connection();
32906 var missing = coords.filter(function (loc) {
32907 return !osm.isDataLoaded(loc);
32910 if (missing.length) {
32911 missing.forEach(function (loc) {
32912 context.loadTileAtLoc(loc);
32921 function incompleteRelation(id) {
32922 var entity = context.entity(id);
32923 return entity.type === 'relation' && !entity.isComplete(context.graph());
32927 operation.tooltip = function () {
32928 var disable = operation.disabled();
32929 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
32932 operation.annotation = function () {
32933 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
32934 n: selectedIDs.length
32938 operation.id = 'rotate';
32939 operation.keys = [_t('operations.rotate.key')];
32940 operation.title = _t('operations.rotate.title');
32941 operation.behavior = behaviorOperation(context).which(operation);
32942 operation.mouseOnly = true;
32946 function modeMove(context, entityIDs, baseGraph) {
32951 var keybinding = utilKeybinding('move');
32952 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];
32953 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
32954 n: entityIDs.length
32963 var _nudgeInterval;
32965 function doMove(nudge) {
32966 nudge = nudge || [0, 0];
32969 if (_prevGraph !== context.graph()) {
32971 _origin = context.map().mouseCoordinates();
32972 fn = context.perform;
32974 fn = context.overwrite;
32977 var currMouse = context.map().mouse();
32978 var origMouse = context.projection(_origin);
32979 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
32980 fn(actionMove(entityIDs, delta, context.projection, _cache));
32981 _prevGraph = context.graph();
32984 function startNudge(nudge) {
32985 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
32986 _nudgeInterval = window.setInterval(function () {
32987 context.map().pan(nudge);
32992 function stopNudge() {
32993 if (_nudgeInterval) {
32994 window.clearInterval(_nudgeInterval);
32995 _nudgeInterval = null;
33001 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
33010 function finish(d3_event) {
33011 d3_event.stopPropagation();
33012 context.replace(actionNoop(), annotation);
33013 context.enter(modeSelect(context, entityIDs));
33017 function cancel() {
33019 while (context.graph() !== baseGraph) {
33023 context.enter(modeBrowse(context));
33026 context.enter(modeSelect(context, entityIDs));
33032 function undone() {
33033 context.enter(modeBrowse(context));
33036 mode.enter = function () {
33037 _origin = context.map().mouseCoordinates();
33040 context.features().forceVisible(entityIDs);
33041 behaviors.forEach(context.install);
33042 context.surface().on('mousemove.move', move).on('click.move', finish);
33043 context.history().on('undone.move', undone);
33044 keybinding.on('⎋', cancel).on('↩', finish);
33045 select(document).call(keybinding);
33048 mode.exit = function () {
33050 behaviors.forEach(function (behavior) {
33051 context.uninstall(behavior);
33053 context.surface().on('mousemove.move', null).on('click.move', null);
33054 context.history().on('undone.move', null);
33055 select(document).call(keybinding.unbind);
33056 context.features().forceVisible([]);
33059 mode.selectedIDs = function () {
33060 if (!arguments.length) return entityIDs; // no assign
33068 function behaviorPaste(context) {
33069 function doPaste(d3_event) {
33070 // prevent paste during low zoom selection
33071 if (!context.map().withinEditableZoom()) return;
33072 d3_event.preventDefault();
33073 var baseGraph = context.graph();
33074 var mouse = context.map().mouse();
33075 var projection = context.projection;
33076 var viewport = geoExtent(projection.clipExtent()).polygon();
33077 if (!geoPointInPolygon(mouse, viewport)) return;
33078 var oldIDs = context.copyIDs();
33079 if (!oldIDs.length) return;
33080 var extent = geoExtent();
33081 var oldGraph = context.copyGraph();
33083 var action = actionCopyEntities(oldIDs, oldGraph);
33084 context.perform(action);
33085 var copies = action.copies();
33086 var originals = new Set();
33087 Object.values(copies).forEach(function (entity) {
33088 originals.add(entity.id);
33091 for (var id in copies) {
33092 var oldEntity = oldGraph.entity(id);
33093 var newEntity = copies[id];
33095 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
33098 var parents = context.graph().parentWays(newEntity);
33099 var parentCopied = parents.some(function (parent) {
33100 return originals.has(parent.id);
33103 if (!parentCopied) {
33104 newIDs.push(newEntity.id);
33106 } // Put pasted objects where mouse pointer is..
33109 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
33110 var delta = geoVecSubtract(mouse, copyPoint);
33111 context.perform(actionMove(newIDs, delta, projection));
33112 context.enter(modeMove(context, newIDs, baseGraph));
33115 function behavior() {
33116 context.keybinding().on(uiCmd('⌘V'), doPaste);
33120 behavior.off = function () {
33121 context.keybinding().off(uiCmd('⌘V'));
33127 // `String.prototype.repeat` method
33128 // https://tc39.es/ecma262/#sec-string.prototype.repeat
33129 _export({ target: 'String', proto: true }, {
33130 repeat: stringRepeat
33134 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
33136 * The `origin` function is expected to return an [x, y] tuple rather than an
33138 * The events are `start`, `move`, and `end`.
33139 (https://github.com/mbostock/d3/issues/563)
33140 * The `start` event is not dispatched until the first cursor movement occurs.
33141 (https://github.com/mbostock/d3/pull/368)
33142 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
33143 than `x`, `y`, `dx`, and `dy` properties.
33144 * The `end` event is not dispatched if no movement occurs.
33145 * An `off` function is available that unbinds the drag's internal event handlers.
33148 function behaviorDrag() {
33149 var dispatch$1 = dispatch('start', 'move', 'end'); // see also behaviorSelect
33151 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
33153 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
33155 var _origin = null;
33156 var _selector = '';
33164 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
33167 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
33169 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
33171 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
33172 var selection$1 = selection();
33173 var select = selection$1.style(d3_event_userSelectProperty);
33174 selection$1.style(d3_event_userSelectProperty, 'none');
33175 return function () {
33176 selection$1.style(d3_event_userSelectProperty, select);
33180 function pointerdown(d3_event) {
33181 if (_pointerId) return;
33182 _pointerId = d3_event.pointerId || 'mouse';
33183 _targetNode = this; // only force reflow once per drag
33185 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
33187 var startOrigin = pointerLocGetter(d3_event);
33188 var started = false;
33189 var selectEnable = d3_event_userSelectSuppress();
33190 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
33193 offset = _origin.call(_targetNode, _targetEntity);
33194 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
33199 d3_event.stopPropagation();
33201 function pointermove(d3_event) {
33202 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33203 var p = pointerLocGetter(d3_event);
33206 var dist = geoVecLength(startOrigin, p);
33207 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
33209 if (dist < tolerance) return;
33211 dispatch$1.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
33212 // a midpoint will convert the target to a node.
33215 d3_event.stopPropagation();
33216 d3_event.preventDefault();
33217 var dx = p[0] - startOrigin[0];
33218 var dy = p[1] - startOrigin[1];
33219 dispatch$1.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
33223 function pointerup(d3_event) {
33224 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33228 dispatch$1.call('end', this, d3_event, _targetEntity);
33229 d3_event.preventDefault();
33232 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33237 function behavior(selection) {
33238 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
33239 var delegate = pointerdown;
33242 delegate = function delegate(d3_event) {
33244 var target = d3_event.target;
33246 for (; target && target !== root; target = target.parentNode) {
33247 var datum = target.__data__;
33248 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
33250 if (_targetEntity && target[matchesSelector](_selector)) {
33251 return pointerdown.call(target, d3_event);
33257 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
33260 behavior.off = function (selection) {
33261 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
33264 behavior.selector = function (_) {
33265 if (!arguments.length) return _selector;
33270 behavior.origin = function (_) {
33271 if (!arguments.length) return _origin;
33276 behavior.cancel = function () {
33277 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33281 behavior.targetNode = function (_) {
33282 if (!arguments.length) return _targetNode;
33287 behavior.targetEntity = function (_) {
33288 if (!arguments.length) return _targetEntity;
33293 behavior.surface = function (_) {
33294 if (!arguments.length) return _surface;
33299 return utilRebind(behavior, dispatch$1, 'on');
33302 function modeDragNode(context) {
33307 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
33308 var edit = behaviorEdit(context);
33310 var _nudgeInterval;
33312 var _restoreSelectedIDs = [];
33313 var _wasMidpoint = false;
33314 var _isCancelled = false;
33322 function startNudge(d3_event, entity, nudge) {
33323 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
33324 _nudgeInterval = window.setInterval(function () {
33325 context.map().pan(nudge);
33326 doMove(d3_event, entity, nudge);
33330 function stopNudge() {
33331 if (_nudgeInterval) {
33332 window.clearInterval(_nudgeInterval);
33333 _nudgeInterval = null;
33337 function moveAnnotation(entity) {
33338 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
33341 function connectAnnotation(nodeEntity, targetEntity) {
33342 var nodeGeometry = nodeEntity.geometry(context.graph());
33343 var targetGeometry = targetEntity.geometry(context.graph());
33345 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
33346 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
33347 var targetParentWayIDs = context.graph().parentWays(targetEntity);
33348 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
33350 if (sharedParentWays.length !== 0) {
33351 // if the nodes are next to each other, they are merged
33352 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
33353 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
33356 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
33360 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
33363 function shouldSnapToNode(target) {
33364 if (!_activeEntity) return false;
33365 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
33368 function origin(entity) {
33369 return context.projection(entity.loc);
33372 function keydown(d3_event) {
33373 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33374 if (context.surface().classed('nope')) {
33375 context.surface().classed('nope-suppressed', true);
33378 context.surface().classed('nope', false).classed('nope-disabled', true);
33382 function keyup(d3_event) {
33383 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33384 if (context.surface().classed('nope-suppressed')) {
33385 context.surface().classed('nope', true);
33388 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
33392 function start(d3_event, entity) {
33393 _wasMidpoint = entity.type === 'midpoint';
33394 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
33395 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
33397 if (_isCancelled) {
33399 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
33402 return drag.cancel();
33405 if (_wasMidpoint) {
33406 var midpoint = entity;
33407 entity = osmNode();
33408 context.perform(actionAddMidpoint(midpoint, entity));
33409 entity = context.entity(entity.id); // get post-action entity
33411 var vertex = context.surface().selectAll('.' + entity.id);
33412 drag.targetNode(vertex.node()).targetEntity(entity);
33414 context.perform(actionNoop());
33417 _activeEntity = entity;
33418 _startLoc = entity.loc;
33419 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
33420 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
33421 context.enter(mode);
33423 // - `behavior/draw.js` `datum()`
33426 function datum(d3_event) {
33427 if (!d3_event || d3_event.altKey) {
33430 // When dragging, snap only to touch targets..
33431 // (this excludes area fills and active drawing elements)
33432 var d = d3_event.target.__data__;
33433 return d && d.properties && d.properties.target ? d : {};
33437 function doMove(d3_event, entity, nudge) {
33438 nudge = nudge || [0, 0];
33439 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
33440 var currMouse = geoVecSubtract(currPoint, nudge);
33441 var loc = context.projection.invert(currMouse);
33444 if (!_nudgeInterval) {
33445 // If not nudging at the edge of the viewport, try to snap..
33447 // - `mode/drag_node.js` `doMove()`
33448 // - `behavior/draw.js` `click()`
33449 // - `behavior/draw_way.js` `move()`
33450 var d = datum(d3_event);
33451 target = d && d.properties && d.properties.entity;
33452 var targetLoc = target && target.loc;
33453 var targetNodes = d && d.properties && d.properties.nodes;
33456 // snap to node/vertex - a point target with `.loc`
33457 if (shouldSnapToNode(target)) {
33460 } else if (targetNodes) {
33461 // snap to way - a line target with `.nodes`
33462 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
33470 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
33472 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
33475 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
33476 } // Check if this drag causes the geometry to break..
33480 isInvalid = hasInvalidGeometry(entity, context.graph());
33483 var nope = context.surface().classed('nope');
33485 if (isInvalid === 'relation' || isInvalid === 'restriction') {
33487 // about to nope - show hint
33488 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
33489 relation: _mainPresetIndex.item('type/restriction').name()
33492 } else if (isInvalid) {
33493 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
33494 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
33497 // about to un-nope, remove hint
33498 context.ui().flash.duration(1).label('')();
33502 var nopeDisabled = context.surface().classed('nope-disabled');
33504 if (nopeDisabled) {
33505 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
33507 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
33511 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
33514 function hasRelationConflict(entity, target, edge, graph) {
33515 var testGraph = graph.update(); // copy
33516 // if snapping to way - add midpoint there and consider that the target..
33519 var midpoint = osmNode();
33520 var action = actionAddMidpoint({
33522 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
33524 testGraph = action(testGraph);
33526 } // can we connect to it?
33529 var ids = [entity.id, target.id];
33530 return actionConnect(ids).disabled(testGraph);
33533 function hasInvalidGeometry(entity, graph) {
33534 var parents = graph.parentWays(entity);
33537 for (i = 0; i < parents.length; i++) {
33538 var parent = parents[i];
33540 var activeIndex = null; // which multipolygon ring contains node being dragged
33541 // test any parent multipolygons for valid geometry
33543 var relations = graph.parentRelations(parent);
33545 for (j = 0; j < relations.length; j++) {
33546 if (!relations[j].isMultipolygon()) continue;
33547 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
33549 for (k = 0; k < rings.length; k++) {
33550 nodes = rings[k].nodes;
33552 if (nodes.find(function (n) {
33553 return n.id === entity.id;
33557 if (geoHasSelfIntersections(nodes, entity.id)) {
33558 return 'multipolygonMember';
33562 rings[k].coords = nodes.map(function (n) {
33565 } // test active ring for intersections with other rings in the multipolygon
33568 for (k = 0; k < rings.length; k++) {
33569 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
33571 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
33572 return 'multipolygonRing';
33575 } // If we still haven't tested this node's parent way for self-intersections.
33576 // (because it's not a member of a multipolygon), test it now.
33579 if (activeIndex === null) {
33580 nodes = parent.nodes.map(function (nodeID) {
33581 return graph.entity(nodeID);
33584 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
33585 return parent.geometry(graph);
33593 function move(d3_event, entity, point) {
33594 if (_isCancelled) return;
33595 d3_event.stopPropagation();
33596 context.surface().classed('nope-disabled', d3_event.altKey);
33597 _lastLoc = context.projection.invert(point);
33598 doMove(d3_event, entity);
33599 var nudge = geoViewportEdge(point, context.map().dimensions());
33602 startNudge(d3_event, entity, nudge);
33608 function end(d3_event, entity) {
33609 if (_isCancelled) return;
33610 var wasPoint = entity.geometry(context.graph()) === 'point';
33611 var d = datum(d3_event);
33612 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
33613 var target = d && d.properties && d.properties.entity; // entity to snap to
33617 context.perform(_actionBounceBack(entity.id, _startLoc));
33618 } else if (target && target.type === 'way') {
33619 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
33620 context.replace(actionAddMidpoint({
33622 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
33623 }, entity), connectAnnotation(entity, target));
33624 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
33625 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
33626 } else if (_wasMidpoint) {
33627 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
33629 context.replace(actionNoop(), moveAnnotation(entity));
33633 context.enter(modeSelect(context, [entity.id]));
33635 var reselection = _restoreSelectedIDs.filter(function (id) {
33636 return context.graph().hasEntity(id);
33639 if (reselection.length) {
33640 context.enter(modeSelect(context, reselection));
33642 context.enter(modeBrowse(context));
33647 function _actionBounceBack(nodeID, toLoc) {
33648 var moveNode = actionMoveNode(nodeID, toLoc);
33650 var action = function action(graph, t) {
33651 // last time through, pop off the bounceback perform.
33652 // it will then overwrite the initial perform with a moveNode that does nothing
33653 if (t === 1) context.pop();
33654 return moveNode(graph, t);
33657 action.transitionable = true;
33661 function cancel() {
33663 context.enter(modeBrowse(context));
33666 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);
33668 mode.enter = function () {
33669 context.install(hover);
33670 context.install(edit);
33671 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
33672 context.history().on('undone.drag-node', cancel);
33675 mode.exit = function () {
33676 context.ui().sidebar.hover.cancel();
33677 context.uninstall(hover);
33678 context.uninstall(edit);
33679 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
33680 context.history().on('undone.drag-node', null);
33681 _activeEntity = null;
33682 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
33686 mode.selectedIDs = function () {
33687 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
33692 mode.activeID = function () {
33693 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
33698 mode.restoreSelectedIDs = function (_) {
33699 if (!arguments.length) return _restoreSelectedIDs;
33700 _restoreSelectedIDs = _;
33704 mode.behavior = drag;
33709 fixRegexpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybeCallNative) {
33711 // `String.prototype.search` method
33712 // https://tc39.es/ecma262/#sec-string.prototype.search
33713 function search(regexp) {
33714 var O = requireObjectCoercible(this);
33715 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
33716 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
33718 // `RegExp.prototype[@@search]` method
33719 // https://tc39.es/ecma262/#sec-regexp.prototype-@@search
33720 function (regexp) {
33721 var res = maybeCallNative(nativeSearch, regexp, this);
33722 if (res.done) return res.value;
33724 var rx = anObject(regexp);
33725 var S = String(this);
33727 var previousLastIndex = rx.lastIndex;
33728 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
33729 var result = regexpExecAbstract(rx, S);
33730 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
33731 return result === null ? -1 : result.index;
33736 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
33737 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
33738 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
33741 // `Promise.prototype.finally` method
33742 // https://tc39.es/ecma262/#sec-promise.prototype.finally
33743 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
33744 'finally': function (onFinally) {
33745 var C = speciesConstructor(this, getBuiltIn('Promise'));
33746 var isFunction = typeof onFinally == 'function';
33748 isFunction ? function (x) {
33749 return promiseResolve(C, onFinally()).then(function () { return x; });
33751 isFunction ? function (e) {
33752 return promiseResolve(C, onFinally()).then(function () { throw e; });
33758 // patch native Promise.prototype for native async functions
33759 if ( typeof nativePromiseConstructor == 'function' && !nativePromiseConstructor.prototype['finally']) {
33760 redefine(nativePromiseConstructor.prototype, 'finally', getBuiltIn('Promise').prototype['finally']);
33763 function quickselect$1(arr, k, left, right, compare) {
33764 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
33767 function quickselectStep(arr, k, left, right, compare) {
33768 while (right > left) {
33769 if (right - left > 600) {
33770 var n = right - left + 1;
33771 var m = k - left + 1;
33772 var z = Math.log(n);
33773 var s = 0.5 * Math.exp(2 * z / 3);
33774 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
33775 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
33776 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
33777 quickselectStep(arr, k, newLeft, newRight, compare);
33783 swap$1(arr, left, k);
33784 if (compare(arr[right], t) > 0) swap$1(arr, left, right);
33791 while (compare(arr[i], t) < 0) {
33795 while (compare(arr[j], t) > 0) {
33800 if (compare(arr[left], t) === 0) swap$1(arr, left, j);else {
33802 swap$1(arr, j, right);
33804 if (j <= k) left = j + 1;
33805 if (k <= j) right = j - 1;
33809 function swap$1(arr, i, j) {
33815 function defaultCompare(a, b) {
33816 return a < b ? -1 : a > b ? 1 : 0;
33819 var RBush = /*#__PURE__*/function () {
33821 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
33823 _classCallCheck(this, RBush);
33825 // max entries in a node is 9 by default; min node fill is 40% for best performance
33826 this._maxEntries = Math.max(4, maxEntries);
33827 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
33831 _createClass(RBush, [{
33833 value: function all() {
33834 return this._all(this.data, []);
33838 value: function search(bbox) {
33839 var node = this.data;
33841 if (!intersects(bbox, node)) return result;
33842 var toBBox = this.toBBox;
33843 var nodesToSearch = [];
33846 for (var i = 0; i < node.children.length; i++) {
33847 var child = node.children[i];
33848 var childBBox = node.leaf ? toBBox(child) : child;
33850 if (intersects(bbox, childBBox)) {
33851 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
33855 node = nodesToSearch.pop();
33862 value: function collides(bbox) {
33863 var node = this.data;
33864 if (!intersects(bbox, node)) return false;
33865 var nodesToSearch = [];
33868 for (var i = 0; i < node.children.length; i++) {
33869 var child = node.children[i];
33870 var childBBox = node.leaf ? this.toBBox(child) : child;
33872 if (intersects(bbox, childBBox)) {
33873 if (node.leaf || contains(bbox, childBBox)) return true;
33874 nodesToSearch.push(child);
33878 node = nodesToSearch.pop();
33885 value: function load(data) {
33886 if (!(data && data.length)) return this;
33888 if (data.length < this._minEntries) {
33889 for (var i = 0; i < data.length; i++) {
33890 this.insert(data[i]);
33894 } // recursively build the tree with the given data from scratch using OMT algorithm
33897 var node = this._build(data.slice(), 0, data.length - 1, 0);
33899 if (!this.data.children.length) {
33900 // save as is if tree is empty
33902 } else if (this.data.height === node.height) {
33903 // split root if trees have the same height
33904 this._splitRoot(this.data, node);
33906 if (this.data.height < node.height) {
33907 // swap trees if inserted one is bigger
33908 var tmpNode = this.data;
33911 } // insert the small tree into the large tree at appropriate level
33914 this._insert(node, this.data.height - node.height - 1, true);
33921 value: function insert(item) {
33922 if (item) this._insert(item, this.data.height - 1);
33927 value: function clear() {
33928 this.data = createNode([]);
33933 value: function remove(item, equalsFn) {
33934 if (!item) return this;
33935 var node = this.data;
33936 var bbox = this.toBBox(item);
33939 var i, parent, goingUp; // depth-first iterative tree traversal
33941 while (node || path.length) {
33945 parent = path[path.length - 1];
33951 // check current node
33952 var index = findItem(item, node.children, equalsFn);
33954 if (index !== -1) {
33955 // item found, remove the item and condense tree upwards
33956 node.children.splice(index, 1);
33959 this._condense(path);
33965 if (!goingUp && !node.leaf && contains(node, bbox)) {
33971 node = node.children[0];
33972 } else if (parent) {
33975 node = parent.children[i];
33977 } else node = null; // nothing found
33985 value: function toBBox(item) {
33989 key: "compareMinX",
33990 value: function compareMinX(a, b) {
33991 return a.minX - b.minX;
33994 key: "compareMinY",
33995 value: function compareMinY(a, b) {
33996 return a.minY - b.minY;
34000 value: function toJSON() {
34005 value: function fromJSON(data) {
34011 value: function _all(node, result) {
34012 var nodesToSearch = [];
34015 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
34016 node = nodesToSearch.pop();
34023 value: function _build(items, left, right, height) {
34024 var N = right - left + 1;
34025 var M = this._maxEntries;
34029 // reached leaf level; return leaf
34030 node = createNode(items.slice(left, right + 1));
34031 calcBBox(node, this.toBBox);
34036 // target height of the bulk-loaded tree
34037 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
34039 M = Math.ceil(N / Math.pow(M, height - 1));
34042 node = createNode([]);
34044 node.height = height; // split the items into M mostly square tiles
34046 var N2 = Math.ceil(N / M);
34047 var N1 = N2 * Math.ceil(Math.sqrt(M));
34048 multiSelect(items, left, right, N1, this.compareMinX);
34050 for (var i = left; i <= right; i += N1) {
34051 var right2 = Math.min(i + N1 - 1, right);
34052 multiSelect(items, i, right2, N2, this.compareMinY);
34054 for (var j = i; j <= right2; j += N2) {
34055 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
34057 node.children.push(this._build(items, j, right3, height - 1));
34061 calcBBox(node, this.toBBox);
34065 key: "_chooseSubtree",
34066 value: function _chooseSubtree(bbox, node, level, path) {
34069 if (node.leaf || path.length - 1 === level) break;
34070 var minArea = Infinity;
34071 var minEnlargement = Infinity;
34072 var targetNode = void 0;
34074 for (var i = 0; i < node.children.length; i++) {
34075 var child = node.children[i];
34076 var area = bboxArea(child);
34077 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
34079 if (enlargement < minEnlargement) {
34080 minEnlargement = enlargement;
34081 minArea = area < minArea ? area : minArea;
34082 targetNode = child;
34083 } else if (enlargement === minEnlargement) {
34084 // otherwise choose one with the smallest area
34085 if (area < minArea) {
34087 targetNode = child;
34092 node = targetNode || node.children[0];
34099 value: function _insert(item, level, isNode) {
34100 var bbox = isNode ? item : this.toBBox(item);
34101 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
34103 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
34106 node.children.push(item);
34107 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
34109 while (level >= 0) {
34110 if (insertPath[level].children.length > this._maxEntries) {
34111 this._split(insertPath, level);
34115 } // adjust bboxes along the insertion path
34118 this._adjustParentBBoxes(bbox, insertPath, level);
34119 } // split overflowed node into two
34123 value: function _split(insertPath, level) {
34124 var node = insertPath[level];
34125 var M = node.children.length;
34126 var m = this._minEntries;
34128 this._chooseSplitAxis(node, m, M);
34130 var splitIndex = this._chooseSplitIndex(node, m, M);
34132 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
34133 newNode.height = node.height;
34134 newNode.leaf = node.leaf;
34135 calcBBox(node, this.toBBox);
34136 calcBBox(newNode, this.toBBox);
34137 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
34141 value: function _splitRoot(node, newNode) {
34143 this.data = createNode([node, newNode]);
34144 this.data.height = node.height + 1;
34145 this.data.leaf = false;
34146 calcBBox(this.data, this.toBBox);
34149 key: "_chooseSplitIndex",
34150 value: function _chooseSplitIndex(node, m, M) {
34152 var minOverlap = Infinity;
34153 var minArea = Infinity;
34155 for (var i = m; i <= M - m; i++) {
34156 var bbox1 = distBBox(node, 0, i, this.toBBox);
34157 var bbox2 = distBBox(node, i, M, this.toBBox);
34158 var overlap = intersectionArea(bbox1, bbox2);
34159 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
34161 if (overlap < minOverlap) {
34162 minOverlap = overlap;
34164 minArea = area < minArea ? area : minArea;
34165 } else if (overlap === minOverlap) {
34166 // otherwise choose distribution with minimum area
34167 if (area < minArea) {
34174 return index || M - m;
34175 } // sorts node children by the best axis for split
34178 key: "_chooseSplitAxis",
34179 value: function _chooseSplitAxis(node, m, M) {
34180 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
34181 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
34183 var xMargin = this._allDistMargin(node, m, M, compareMinX);
34185 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
34186 // otherwise it's already sorted by minY
34189 if (xMargin < yMargin) node.children.sort(compareMinX);
34190 } // total margin of all possible split distributions where each node is at least m full
34193 key: "_allDistMargin",
34194 value: function _allDistMargin(node, m, M, compare) {
34195 node.children.sort(compare);
34196 var toBBox = this.toBBox;
34197 var leftBBox = distBBox(node, 0, m, toBBox);
34198 var rightBBox = distBBox(node, M - m, M, toBBox);
34199 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
34201 for (var i = m; i < M - m; i++) {
34202 var child = node.children[i];
34203 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
34204 margin += bboxMargin(leftBBox);
34207 for (var _i = M - m - 1; _i >= m; _i--) {
34208 var _child = node.children[_i];
34209 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
34210 margin += bboxMargin(rightBBox);
34216 key: "_adjustParentBBoxes",
34217 value: function _adjustParentBBoxes(bbox, path, level) {
34218 // adjust bboxes along the given tree path
34219 for (var i = level; i >= 0; i--) {
34220 extend$1(path[i], bbox);
34225 value: function _condense(path) {
34226 // go through the path, removing empty nodes and updating bboxes
34227 for (var i = path.length - 1, siblings; i >= 0; i--) {
34228 if (path[i].children.length === 0) {
34230 siblings = path[i - 1].children;
34231 siblings.splice(siblings.indexOf(path[i]), 1);
34232 } else this.clear();
34233 } else calcBBox(path[i], this.toBBox);
34241 function findItem(item, items, equalsFn) {
34242 if (!equalsFn) return items.indexOf(item);
34244 for (var i = 0; i < items.length; i++) {
34245 if (equalsFn(item, items[i])) return i;
34249 } // calculate node's bbox from bboxes of its children
34252 function calcBBox(node, toBBox) {
34253 distBBox(node, 0, node.children.length, toBBox, node);
34254 } // min bounding rectangle of node children from k to p-1
34257 function distBBox(node, k, p, toBBox, destNode) {
34258 if (!destNode) destNode = createNode(null);
34259 destNode.minX = Infinity;
34260 destNode.minY = Infinity;
34261 destNode.maxX = -Infinity;
34262 destNode.maxY = -Infinity;
34264 for (var i = k; i < p; i++) {
34265 var child = node.children[i];
34266 extend$1(destNode, node.leaf ? toBBox(child) : child);
34272 function extend$1(a, b) {
34273 a.minX = Math.min(a.minX, b.minX);
34274 a.minY = Math.min(a.minY, b.minY);
34275 a.maxX = Math.max(a.maxX, b.maxX);
34276 a.maxY = Math.max(a.maxY, b.maxY);
34280 function compareNodeMinX(a, b) {
34281 return a.minX - b.minX;
34284 function compareNodeMinY(a, b) {
34285 return a.minY - b.minY;
34288 function bboxArea(a) {
34289 return (a.maxX - a.minX) * (a.maxY - a.minY);
34292 function bboxMargin(a) {
34293 return a.maxX - a.minX + (a.maxY - a.minY);
34296 function enlargedArea(a, b) {
34297 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));
34300 function intersectionArea(a, b) {
34301 var minX = Math.max(a.minX, b.minX);
34302 var minY = Math.max(a.minY, b.minY);
34303 var maxX = Math.min(a.maxX, b.maxX);
34304 var maxY = Math.min(a.maxY, b.maxY);
34305 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
34308 function contains(a, b) {
34309 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
34312 function intersects(a, b) {
34313 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
34316 function createNode(children) {
34318 children: children,
34326 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
34327 // combines selection algorithm with binary divide & conquer approach
34330 function multiSelect(arr, left, right, n, compare) {
34331 var stack = [left, right];
34333 while (stack.length) {
34334 right = stack.pop();
34335 left = stack.pop();
34336 if (right - left <= n) continue;
34337 var mid = left + Math.ceil((right - left) / n / 2) * n;
34338 quickselect$1(arr, mid, left, right, compare);
34339 stack.push(left, mid, mid, right);
34343 var tiler = utilTiler();
34344 var dispatch$1 = dispatch('loaded');
34345 var _tileZoom = 14;
34346 var _krUrlRoot = 'https://www.keepright.at';
34349 localizeStrings: {}
34350 }; // This gets reassigned if reset
34354 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
34355 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];
34357 function abortRequest(controller) {
34359 controller.abort();
34363 function abortUnwantedRequests(cache, tiles) {
34364 Object.keys(cache.inflightTile).forEach(function (k) {
34365 var wanted = tiles.find(function (tile) {
34366 return k === tile.id;
34370 abortRequest(cache.inflightTile[k]);
34371 delete cache.inflightTile[k];
34376 function encodeIssueRtree(d) {
34384 } // Replace or remove QAItem from rtree
34387 function updateRtree(item, replace) {
34388 _cache.rtree.remove(item, function (a, b) {
34389 return a.data.id === b.data.id;
34393 _cache.rtree.insert(item);
34397 function tokenReplacements(d) {
34398 if (!(d instanceof QAItem)) return;
34399 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
34400 var replacements = {};
34401 var issueTemplate = _krData.errorTypes[d.whichType];
34403 if (!issueTemplate) {
34404 /* eslint-disable no-console */
34405 console.log('No Template: ', d.whichType);
34406 console.log(' ', d.description);
34407 /* eslint-enable no-console */
34410 } // some descriptions are just fixed text
34413 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
34415 var errorRegex = new RegExp(issueTemplate.regex, 'i');
34416 var errorMatch = errorRegex.exec(d.description);
34419 /* eslint-disable no-console */
34420 console.log('Unmatched: ', d.whichType);
34421 console.log(' ', d.description);
34422 console.log(' ', errorRegex);
34423 /* eslint-enable no-console */
34428 for (var i = 1; i < errorMatch.length; i++) {
34430 var capture = errorMatch[i];
34431 var idType = void 0;
34432 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
34434 if (idType && capture) {
34435 // link IDs if present in the capture
34436 capture = parseError(capture, idType);
34437 } else if (htmlRegex.test(capture)) {
34438 // escape any html in non-IDs
34439 capture = '\\' + capture + '\\';
34441 var compare = capture.toLowerCase();
34443 if (_krData.localizeStrings[compare]) {
34444 // some replacement strings can be localized
34445 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34449 replacements['var' + i] = capture;
34452 return replacements;
34455 function parseError(capture, idType) {
34456 var compare = capture.toLowerCase();
34458 if (_krData.localizeStrings[compare]) {
34459 // some replacement strings can be localized
34460 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34464 // link a string like "this node"
34466 capture = linkErrorObject(capture);
34470 capture = linkURL(capture);
34472 // link an entity ID
34477 capture = linkEntity(idType + capture);
34479 // some errors have more complex ID lists/variance
34482 capture = parse20(capture);
34486 capture = parse211(capture);
34490 capture = parse231(capture);
34494 capture = parse294(capture);
34498 capture = parse370(capture);
34504 function linkErrorObject(d) {
34505 return "<a class=\"error_object_link\">".concat(d, "</a>");
34508 function linkEntity(d) {
34509 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34512 function linkURL(d) {
34513 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
34514 } // arbitrary node list of form: #ID, #ID, #ID...
34517 function parse211(capture) {
34519 var items = capture.split(', ');
34520 items.forEach(function (item) {
34521 // ID has # at the front
34522 var id = linkEntity('n' + item.slice(1));
34525 return newList.join(', ');
34526 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
34529 function parse231(capture) {
34530 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
34532 var items = capture.split('),');
34533 items.forEach(function (item) {
34534 var match = item.match(/\#(\d+)\((.+)\)?/);
34536 if (match !== null && match.length > 2) {
34537 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
34542 return newList.join(', ');
34543 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
34546 function parse294(capture) {
34548 var items = capture.split(',');
34549 items.forEach(function (item) {
34550 // item of form "from/to node/relation #ID"
34551 item = item.split(' '); // to/from role is more clear in quotes
34553 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
34555 var idType = item[1].slice(0, 1); // ID has # at the front
34557 var id = item[2].slice(1);
34558 id = linkEntity(idType + id);
34559 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
34561 return newList.join(', ');
34562 } // may or may not include the string "(including the name 'name')"
34565 function parse370(capture) {
34566 if (!capture) return '';
34567 var match = capture.match(/\(including the name (\'.+\')\)/);
34569 if (match && match.length) {
34570 return _t('QA.keepRight.errorTypes.370.including_the_name', {
34576 } // arbitrary node list of form: #ID,#ID,#ID...
34579 function parse20(capture) {
34581 var items = capture.split(',');
34582 items.forEach(function (item) {
34583 // ID has # at the front
34584 var id = linkEntity('n' + item.slice(1));
34587 return newList.join(', ');
34591 var serviceKeepRight = {
34592 title: 'keepRight',
34593 init: function init() {
34594 _mainFileFetcher.get('keepRight').then(function (d) {
34595 return _krData = d;
34602 this.event = utilRebind(this, dispatch$1, 'on');
34604 reset: function reset() {
34606 Object.values(_cache.inflightTile).forEach(abortRequest);
34618 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
34619 loadIssues: function loadIssues(projection) {
34625 }; // determine the needed tiles to cover the view
34627 var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
34629 abortUnwantedRequests(_cache, tiles); // issue new requests..
34631 tiles.forEach(function (tile) {
34632 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
34634 var _tile$extent$rectangl = tile.extent.rectangle(),
34635 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34636 left = _tile$extent$rectangl2[0],
34637 top = _tile$extent$rectangl2[1],
34638 right = _tile$extent$rectangl2[2],
34639 bottom = _tile$extent$rectangl2[3];
34641 var params = Object.assign({}, options, {
34647 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
34648 var controller = new AbortController();
34649 _cache.inflightTile[tile.id] = controller;
34651 signal: controller.signal
34652 }).then(function (data) {
34653 delete _cache.inflightTile[tile.id];
34654 _cache.loadedTile[tile.id] = true;
34656 if (!data || !data.features || !data.features.length) {
34657 throw new Error('No Data');
34660 data.features.forEach(function (feature) {
34661 var _feature$properties = feature.properties,
34662 itemType = _feature$properties.error_type,
34663 id = _feature$properties.error_id,
34664 _feature$properties$c = _feature$properties.comment,
34665 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
34666 objectId = _feature$properties.object_id,
34667 objectType = _feature$properties.object_type,
34668 schema = _feature$properties.schema,
34669 title = _feature$properties.title;
34670 var loc = feature.geometry.coordinates,
34671 _feature$properties$d = feature.properties.description,
34672 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
34673 // Error 191 = "highway-highway"
34674 // Error 190 = "intersections without junctions" (parent)
34676 var issueTemplate = _krData.errorTypes[itemType];
34677 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
34679 var whichType = issueTemplate ? itemType : parentIssueType;
34680 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
34681 // This is done to make them easier to linkify and translate.
34683 switch (whichType) {
34685 description = "This feature has a FIXME tag: ".concat(description);
34690 description = description.replace('A turn-', 'This turn-');
34698 description = "This turn-restriction~".concat(description);
34702 description = 'This highway is missing a maxspeed tag';
34708 description = "This feature~".concat(description);
34710 } // move markers slightly so it doesn't obscure the geometry,
34711 // then move markers away from other coincident markers
34714 var coincident = false;
34717 // first time, move marker up. after that, move marker right.
34718 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
34719 loc = geoVecAdd(loc, delta);
34720 var bbox = geoExtent(loc).bbox();
34721 coincident = _cache.rtree.search(bbox).length;
34722 } while (coincident);
34724 var d = new QAItem(loc, _this, itemType, id, {
34726 description: description,
34727 whichType: whichType,
34728 parentIssueType: parentIssueType,
34729 severity: whichTemplate.severity || 'error',
34730 objectId: objectId,
34731 objectType: objectType,
34735 d.replacements = tokenReplacements(d);
34736 _cache.data[id] = d;
34738 _cache.rtree.insert(encodeIssueRtree(d));
34740 dispatch$1.call('loaded');
34741 })["catch"](function () {
34742 delete _cache.inflightTile[tile.id];
34743 _cache.loadedTile[tile.id] = true;
34747 postUpdate: function postUpdate(d, callback) {
34750 if (_cache.inflightPost[d.id]) {
34752 message: 'Error update already inflight',
34763 params.st = d.newStatus;
34766 if (d.newComment !== undefined) {
34767 params.co = d.newComment;
34768 } // NOTE: This throws a CORS err, but it seems successful.
34769 // We don't care too much about the response, so this is fine.
34772 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
34773 var controller = new AbortController();
34774 _cache.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
34775 // (worst case scenario the request truly fails and issue will show up if iD restarts)
34778 signal: controller.signal
34779 })["finally"](function () {
34780 delete _cache.inflightPost[d.id];
34782 if (d.newStatus === 'ignore') {
34783 // ignore permanently (false positive)
34784 _this2.removeItem(d);
34785 } else if (d.newStatus === 'ignore_t') {
34786 // ignore temporarily (error fixed)
34787 _this2.removeItem(d);
34789 _cache.closed["".concat(d.schema, ":").concat(d.id)] = true;
34791 d = _this2.replaceItem(d.update({
34792 comment: d.newComment,
34793 newComment: undefined,
34794 newState: undefined
34798 if (callback) callback(null, d);
34801 // Get all cached QAItems covering the viewport
34802 getItems: function getItems(projection) {
34803 var viewport = projection.clipExtent();
34804 var min = [viewport[0][0], viewport[1][1]];
34805 var max = [viewport[1][0], viewport[0][1]];
34806 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34807 return _cache.rtree.search(bbox).map(function (d) {
34811 // Get a QAItem from cache
34812 // NOTE: Don't change method name until UI v3 is merged
34813 getError: function getError(id) {
34814 return _cache.data[id];
34816 // Replace a single QAItem in the cache
34817 replaceItem: function replaceItem(item) {
34818 if (!(item instanceof QAItem) || !item.id) return;
34819 _cache.data[item.id] = item;
34820 updateRtree(encodeIssueRtree(item), true); // true = replace
34824 // Remove a single QAItem from the cache
34825 removeItem: function removeItem(item) {
34826 if (!(item instanceof QAItem) || !item.id) return;
34827 delete _cache.data[item.id];
34828 updateRtree(encodeIssueRtree(item), false); // false = remove
34830 issueURL: function issueURL(item) {
34831 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
34833 // Get an array of issues closed during this session.
34834 // Used to populate `closed:keepright` changeset tag
34835 getClosedIDs: function getClosedIDs() {
34836 return Object.keys(_cache.closed).sort();
34840 var tiler$1 = utilTiler();
34841 var dispatch$2 = dispatch('loaded');
34842 var _tileZoom$1 = 14;
34843 var _impOsmUrls = {
34844 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
34845 mr: 'https://grab.community.improve-osm.org/missingGeoService',
34846 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
34848 var _impOsmData = {
34850 }; // This gets reassigned if reset
34854 function abortRequest$1(i) {
34855 Object.values(i).forEach(function (controller) {
34857 controller.abort();
34862 function abortUnwantedRequests$1(cache, tiles) {
34863 Object.keys(cache.inflightTile).forEach(function (k) {
34864 var wanted = tiles.find(function (tile) {
34865 return k === tile.id;
34869 abortRequest$1(cache.inflightTile[k]);
34870 delete cache.inflightTile[k];
34875 function encodeIssueRtree$1(d) {
34883 } // Replace or remove QAItem from rtree
34886 function updateRtree$1(item, replace) {
34887 _cache$1.rtree.remove(item, function (a, b) {
34888 return a.data.id === b.data.id;
34892 _cache$1.rtree.insert(item);
34896 function linkErrorObject(d) {
34897 return "<a class=\"error_object_link\">".concat(d, "</a>");
34900 function linkEntity(d) {
34901 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34904 function pointAverage(points) {
34905 if (points.length) {
34906 var sum = points.reduce(function (acc, point) {
34907 return geoVecAdd(acc, [point.lon, point.lat]);
34909 return geoVecScale(sum, 1 / points.length);
34915 function relativeBearing(p1, p2) {
34916 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
34919 angle += 2 * Math.PI;
34920 } // Return degrees
34923 return angle * 180 / Math.PI;
34924 } // Assuming range [0,360)
34927 function cardinalDirection(bearing) {
34928 var dir = 45 * Math.round(bearing / 45);
34940 return _t("QA.improveOSM.directions.".concat(compass[dir]));
34941 } // Errors shouldn't obscure each other
34944 function preventCoincident(loc, bumpUp) {
34945 var coincident = false;
34948 // first time, move marker up. after that, move marker right.
34949 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
34950 loc = geoVecAdd(loc, delta);
34951 var bbox = geoExtent(loc).bbox();
34952 coincident = _cache$1.rtree.search(bbox).length;
34953 } while (coincident);
34958 var serviceImproveOSM = {
34959 title: 'improveOSM',
34960 init: function init() {
34961 _mainFileFetcher.get('qa_data').then(function (d) {
34962 return _impOsmData = d.improveOSM;
34969 this.event = utilRebind(this, dispatch$2, 'on');
34971 reset: function reset() {
34973 Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
34985 loadIssues: function loadIssues(projection) {
34991 zoom: '19' // Use a high zoom so that clusters aren't returned
34993 }; // determine the needed tiles to cover the view
34995 var tiles = tiler$1.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
34997 abortUnwantedRequests$1(_cache$1, tiles); // issue new requests..
34999 tiles.forEach(function (tile) {
35000 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
35002 var _tile$extent$rectangl = tile.extent.rectangle(),
35003 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
35004 east = _tile$extent$rectangl2[0],
35005 north = _tile$extent$rectangl2[1],
35006 west = _tile$extent$rectangl2[2],
35007 south = _tile$extent$rectangl2[3];
35009 var params = Object.assign({}, options, {
35014 }); // 3 separate requests to store for each tile
35017 Object.keys(_impOsmUrls).forEach(function (k) {
35018 // We exclude WATER from missing geometry as it doesn't seem useful
35019 // We use most confident one-way and turn restrictions only, still have false positives
35020 var kParams = Object.assign({}, params, k === 'mr' ? {
35021 type: 'PARKING,ROAD,BOTH,PATH'
35023 confidenceLevel: 'C1'
35025 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
35026 var controller = new AbortController();
35027 requests[k] = controller;
35029 signal: controller.signal
35030 }).then(function (data) {
35031 delete _cache$1.inflightTile[tile.id][k];
35033 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35034 delete _cache$1.inflightTile[tile.id];
35035 _cache$1.loadedTile[tile.id] = true;
35036 } // Road segments at high zoom == oneways
35039 if (data.roadSegments) {
35040 data.roadSegments.forEach(function (feature) {
35041 // Position error at the approximate middle of the segment
35042 var points = feature.points,
35043 wayId = feature.wayId,
35044 fromNodeId = feature.fromNodeId,
35045 toNodeId = feature.toNodeId;
35046 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
35047 var mid = points.length / 2;
35048 var loc; // Even number of points, find midpoint of the middle two
35049 // Odd number of points, use position of very middle point
35051 if (mid % 1 === 0) {
35052 loc = pointAverage([points[mid - 1], points[mid]]);
35054 mid = points[Math.floor(mid)];
35055 loc = [mid.lon, mid.lat];
35056 } // One-ways can land on same segment in opposite direction
35059 loc = preventCoincident(loc, false);
35060 var d = new QAItem(loc, _this, k, itemId, {
35062 // used as a category
35064 // used to post changes
35066 fromNodeId: fromNodeId,
35071 }); // Variables used in the description
35074 percentage: feature.percentOfTrips,
35075 num_trips: feature.numberOfTrips,
35076 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
35077 from_node: linkEntity('n' + feature.fromNodeId),
35078 to_node: linkEntity('n' + feature.toNodeId)
35080 _cache$1.data[d.id] = d;
35082 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35084 } // Tiles at high zoom == missing roads
35088 data.tiles.forEach(function (feature) {
35089 var type = feature.type,
35092 numberOfTrips = feature.numberOfTrips;
35093 var geoType = type.toLowerCase();
35094 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
35095 // Missing geometry could happen to land on another error
35097 var loc = pointAverage(feature.points);
35098 loc = preventCoincident(loc, false);
35099 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
35107 num_trips: numberOfTrips,
35108 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
35109 }; // -1 trips indicates data came from a 3rd party
35111 if (numberOfTrips === -1) {
35112 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
35115 _cache$1.data[d.id] = d;
35117 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35119 } // Entities at high zoom == turn restrictions
35122 if (data.entities) {
35123 data.entities.forEach(function (feature) {
35124 var point = feature.point,
35126 segments = feature.segments,
35127 numberOfPasses = feature.numberOfPasses,
35128 turnType = feature.turnType;
35129 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
35130 // We also want to bump the error up so node is accessible
35132 var loc = preventCoincident([point.lon, point.lat], true); // Elements are presented in a strange way
35134 var ids = id.split(',');
35135 var from_way = ids[0];
35136 var via_node = ids[3];
35137 var to_way = ids[2].split(':')[1];
35138 var d = new QAItem(loc, _this, k, itemId, {
35141 objectId: via_node,
35143 }); // Travel direction along from_way clarifies the turn restriction
35145 var _segments$0$points = _slicedToArray(segments[0].points, 2),
35146 p1 = _segments$0$points[0],
35147 p2 = _segments$0$points[1];
35149 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
35152 num_passed: numberOfPasses,
35153 num_trips: segments[0].numberOfTrips,
35154 turn_restriction: turnType.toLowerCase(),
35155 from_way: linkEntity('w' + from_way),
35156 to_way: linkEntity('w' + to_way),
35157 travel_direction: dir_of_travel,
35158 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
35160 _cache$1.data[d.id] = d;
35162 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35164 dispatch$2.call('loaded');
35167 })["catch"](function () {
35168 delete _cache$1.inflightTile[tile.id][k];
35170 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35171 delete _cache$1.inflightTile[tile.id];
35172 _cache$1.loadedTile[tile.id] = true;
35176 _cache$1.inflightTile[tile.id] = requests;
35179 getComments: function getComments(item) {
35182 // If comments already retrieved no need to do so again
35183 if (item.comments) {
35184 return Promise.resolve(item);
35187 var key = item.issueKey;
35190 if (key === 'ow') {
35191 qParams = item.identifier;
35192 } else if (key === 'mr') {
35193 qParams.tileX = item.identifier.x;
35194 qParams.tileY = item.identifier.y;
35195 } else if (key === 'tr') {
35196 qParams.targetId = item.identifier;
35199 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
35201 var cacheComments = function cacheComments(data) {
35202 // Assign directly for immediate use afterwards
35203 // comments are served newest to oldest
35204 item.comments = data.comments ? data.comments.reverse() : [];
35206 _this2.replaceItem(item);
35209 return d3_json(url).then(cacheComments).then(function () {
35213 postUpdate: function postUpdate(d, callback) {
35214 if (!serviceOsm.authenticated()) {
35215 // Username required in payload
35217 message: 'Not Authenticated',
35222 if (_cache$1.inflightPost[d.id]) {
35224 message: 'Error update already inflight',
35227 } // Payload can only be sent once username is established
35230 serviceOsm.userDetails(sendPayload.bind(this));
35232 function sendPayload(err, user) {
35236 return callback(err, d);
35239 var key = d.issueKey;
35240 var url = "".concat(_impOsmUrls[key], "/comment");
35242 username: user.display_name,
35243 targetIds: [d.identifier]
35247 payload.status = d.newStatus;
35248 payload.text = 'status changed';
35249 } // Comment take place of default text
35252 if (d.newComment) {
35253 payload.text = d.newComment;
35256 var controller = new AbortController();
35257 _cache$1.inflightPost[d.id] = controller;
35260 signal: controller.signal,
35261 body: JSON.stringify(payload)
35263 d3_json(url, options).then(function () {
35264 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
35266 if (!d.newStatus) {
35267 var now = new Date();
35268 var comments = d.comments ? d.comments : [];
35270 username: payload.username,
35271 text: payload.text,
35272 timestamp: now.getTime() / 1000
35275 _this3.replaceItem(d.update({
35276 comments: comments,
35277 newComment: undefined
35280 _this3.removeItem(d);
35282 if (d.newStatus === 'SOLVED') {
35283 // Keep track of the number of issues closed per type to tag the changeset
35284 if (!(d.issueKey in _cache$1.closed)) {
35285 _cache$1.closed[d.issueKey] = 0;
35288 _cache$1.closed[d.issueKey] += 1;
35292 if (callback) callback(null, d);
35293 })["catch"](function (err) {
35294 delete _cache$1.inflightPost[d.id];
35295 if (callback) callback(err.message);
35299 // Get all cached QAItems covering the viewport
35300 getItems: function getItems(projection) {
35301 var viewport = projection.clipExtent();
35302 var min = [viewport[0][0], viewport[1][1]];
35303 var max = [viewport[1][0], viewport[0][1]];
35304 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35305 return _cache$1.rtree.search(bbox).map(function (d) {
35309 // Get a QAItem from cache
35310 // NOTE: Don't change method name until UI v3 is merged
35311 getError: function getError(id) {
35312 return _cache$1.data[id];
35314 // get the name of the icon to display for this item
35315 getIcon: function getIcon(itemType) {
35316 return _impOsmData.icons[itemType];
35318 // Replace a single QAItem in the cache
35319 replaceItem: function replaceItem(issue) {
35320 if (!(issue instanceof QAItem) || !issue.id) return;
35321 _cache$1.data[issue.id] = issue;
35322 updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
35326 // Remove a single QAItem from the cache
35327 removeItem: function removeItem(issue) {
35328 if (!(issue instanceof QAItem) || !issue.id) return;
35329 delete _cache$1.data[issue.id];
35330 updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
35332 // Used to populate `closed:improveosm:*` changeset tags
35333 getClosedCounts: function getClosedCounts() {
35334 return _cache$1.closed;
35340 // B.2.3.2.1 CreateHTML(string, tag, attribute, value)
35341 // https://tc39.es/ecma262/#sec-createhtml
35342 var createHtml = function (string, tag, attribute, value) {
35343 var S = String(requireObjectCoercible(string));
35344 var p1 = '<' + tag;
35345 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
35346 return p1 + '>' + S + '</' + tag + '>';
35349 // check the existence of a method, lowercase
35350 // of a tag and escaping quotes in arguments
35351 var stringHtmlForced = function (METHOD_NAME) {
35352 return fails(function () {
35353 var test = ''[METHOD_NAME]('"');
35354 return test !== test.toLowerCase() || test.split('"').length > 3;
35358 // `String.prototype.link` method
35359 // https://tc39.es/ecma262/#sec-string.prototype.link
35360 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
35361 link: function link(url) {
35362 return createHtml(this, 'a', 'href', url);
35366 var $trimEnd = stringTrim.end;
35369 var FORCED$e = stringTrimForced('trimEnd');
35371 var trimEnd = FORCED$e ? function trimEnd() {
35372 return $trimEnd(this);
35375 // `String.prototype.{ trimEnd, trimRight }` methods
35376 // https://tc39.es/ecma262/#sec-string.prototype.trimend
35377 // https://tc39.es/ecma262/#String.prototype.trimright
35378 _export({ target: 'String', proto: true, forced: FORCED$e }, {
35383 var defaults = createCommonjsModule(function (module) {
35384 function getDefaults() {
35392 langPrefix: 'language-',
35400 smartypants: false,
35407 function changeDefaults(newDefaults) {
35408 module.exports.defaults = newDefaults;
35412 defaults: getDefaults(),
35413 getDefaults: getDefaults,
35414 changeDefaults: changeDefaults
35421 var escapeTest = /[&<>"']/;
35422 var escapeReplace = /[&<>"']/g;
35423 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
35424 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
35425 var escapeReplacements = {
35433 var getEscapeReplacement = function getEscapeReplacement(ch) {
35434 return escapeReplacements[ch];
35437 function escape$1(html, encode) {
35439 if (escapeTest.test(html)) {
35440 return html.replace(escapeReplace, getEscapeReplacement);
35443 if (escapeTestNoEncode.test(html)) {
35444 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
35451 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
35453 function unescape$1(html) {
35454 // explicitly match decimal, hex, and named HTML entities
35455 return html.replace(unescapeTest, function (_, n) {
35456 n = n.toLowerCase();
35457 if (n === 'colon') return ':';
35459 if (n.charAt(0) === '#') {
35460 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
35467 var caret = /(^|[^\[])\^/g;
35469 function edit(regex, opt) {
35470 regex = regex.source || regex;
35473 replace: function replace(name, val) {
35474 val = val.source || val;
35475 val = val.replace(caret, '$1');
35476 regex = regex.replace(name, val);
35479 getRegex: function getRegex() {
35480 return new RegExp(regex, opt);
35486 var nonWordAndColonTest = /[^\w:]/g;
35487 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
35489 function cleanUrl(sanitize, base, href) {
35494 prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase();
35499 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
35504 if (base && !originIndependentUrl.test(href)) {
35505 href = resolveUrl(base, href);
35509 href = encodeURI(href).replace(/%25/g, '%');
35518 var justDomain = /^[^:]+:\/*[^/]*$/;
35519 var protocol = /^([^:]+:)[\s\S]*$/;
35520 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
35522 function resolveUrl(base, href) {
35523 if (!baseUrls[' ' + base]) {
35524 // we can ignore everything in base after the last slash of its path component,
35525 // but we might need to add _that_
35526 // https://tools.ietf.org/html/rfc3986#section-3
35527 if (justDomain.test(base)) {
35528 baseUrls[' ' + base] = base + '/';
35530 baseUrls[' ' + base] = rtrim$1(base, '/', true);
35534 base = baseUrls[' ' + base];
35535 var relativeBase = base.indexOf(':') === -1;
35537 if (href.substring(0, 2) === '//') {
35538 if (relativeBase) {
35542 return base.replace(protocol, '$1') + href;
35543 } else if (href.charAt(0) === '/') {
35544 if (relativeBase) {
35548 return base.replace(domain, '$1') + href;
35550 return base + href;
35555 exec: function noopTest() {}
35558 function merge$1(obj) {
35563 for (; i < arguments.length; i++) {
35564 target = arguments[i];
35566 for (key in target) {
35567 if (Object.prototype.hasOwnProperty.call(target, key)) {
35568 obj[key] = target[key];
35576 function splitCells(tableRow, count) {
35577 // ensure that every cell-delimiting pipe has a space
35578 // before it to distinguish it from an escaped pipe
35579 var row = tableRow.replace(/\|/g, function (match, offset, str) {
35580 var escaped = false,
35583 while (--curr >= 0 && str[curr] === '\\') {
35584 escaped = !escaped;
35588 // odd number of slashes means | is escaped
35589 // so we leave it alone
35592 // add space before unescaped |
35596 cells = row.split(/ \|/);
35599 if (cells.length > count) {
35600 cells.splice(count);
35602 while (cells.length < count) {
35607 for (; i < cells.length; i++) {
35608 // leading or trailing whitespace is ignored per the gfm spec
35609 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
35613 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
35614 // /c*$/ is vulnerable to REDOS.
35615 // invert: Remove suffix of non-c chars instead. Default falsey.
35618 function rtrim$1(str, c, invert) {
35619 var l = str.length;
35623 } // Length of suffix matching the invert condition.
35626 var suffLen = 0; // Step left until we fail to match the invert condition.
35628 while (suffLen < l) {
35629 var currChar = str.charAt(l - suffLen - 1);
35631 if (currChar === c && !invert) {
35633 } else if (currChar !== c && invert) {
35640 return str.substr(0, l - suffLen);
35643 function findClosingBracket(str, b) {
35644 if (str.indexOf(b[1]) === -1) {
35648 var l = str.length;
35652 for (; i < l; i++) {
35653 if (str[i] === '\\') {
35655 } else if (str[i] === b[0]) {
35657 } else if (str[i] === b[1]) {
35669 function checkSanitizeDeprecation(opt) {
35670 if (opt && opt.sanitize && !opt.silent) {
35671 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');
35673 } // copied from https://stackoverflow.com/a/5450113/806777
35676 function repeatString(pattern, count) {
35683 while (count > 1) {
35689 pattern += pattern;
35692 return result + pattern;
35697 unescape: unescape$1,
35699 cleanUrl: cleanUrl,
35700 resolveUrl: resolveUrl,
35701 noopTest: noopTest,
35703 splitCells: splitCells,
35705 findClosingBracket: findClosingBracket,
35706 checkSanitizeDeprecation: checkSanitizeDeprecation,
35707 repeatString: repeatString
35710 var defaults$1 = defaults.defaults;
35711 var rtrim$2 = helpers.rtrim,
35712 splitCells$1 = helpers.splitCells,
35713 _escape = helpers.escape,
35714 findClosingBracket$1 = helpers.findClosingBracket;
35716 function outputLink(cap, link, raw) {
35717 var href = link.href;
35718 var title = link.title ? _escape(link.title) : null;
35719 var text = cap[1].replace(/\\([\[\]])/g, '$1');
35721 if (cap[0].charAt(0) !== '!') {
35735 text: _escape(text)
35740 function indentCodeCompensation(raw, text) {
35741 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
35743 if (matchIndentToCode === null) {
35747 var indentToCode = matchIndentToCode[1];
35748 return text.split('\n').map(function (node) {
35749 var matchIndentInNode = node.match(/^\s+/);
35751 if (matchIndentInNode === null) {
35755 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
35756 indentInNode = _matchIndentInNode[0];
35758 if (indentInNode.length >= indentToCode.length) {
35759 return node.slice(indentToCode.length);
35770 var Tokenizer_1 = /*#__PURE__*/function () {
35771 function Tokenizer(options) {
35772 _classCallCheck(this, Tokenizer);
35774 this.options = options || defaults$1;
35777 _createClass(Tokenizer, [{
35779 value: function space(src) {
35780 var cap = this.rules.block.newline.exec(src);
35783 if (cap[0].length > 1) {
35797 value: function code(src, tokens) {
35798 var cap = this.rules.block.code.exec(src);
35801 var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
35803 if (lastToken && lastToken.type === 'paragraph') {
35806 text: cap[0].trimRight()
35810 var text = cap[0].replace(/^ {1,4}/gm, '');
35814 codeBlockStyle: 'indented',
35815 text: !this.options.pedantic ? rtrim$2(text, '\n') : text
35821 value: function fences(src) {
35822 var cap = this.rules.block.fences.exec(src);
35826 var text = indentCodeCompensation(raw, cap[3] || '');
35830 lang: cap[2] ? cap[2].trim() : cap[2],
35837 value: function heading(src) {
35838 var cap = this.rules.block.heading.exec(src);
35841 var text = cap[2].trim(); // remove trailing #s
35843 if (/#$/.test(text)) {
35844 var trimmed = rtrim$2(text, '#');
35846 if (this.options.pedantic) {
35847 text = trimmed.trim();
35848 } else if (!trimmed || / $/.test(trimmed)) {
35849 // CommonMark requires space before trailing #s
35850 text = trimmed.trim();
35857 depth: cap[1].length,
35864 value: function nptable(src) {
35865 var cap = this.rules.block.nptable.exec(src);
35870 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
35871 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
35872 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
35876 if (item.header.length === item.align.length) {
35877 var l = item.align.length;
35880 for (i = 0; i < l; i++) {
35881 if (/^ *-+: *$/.test(item.align[i])) {
35882 item.align[i] = 'right';
35883 } else if (/^ *:-+: *$/.test(item.align[i])) {
35884 item.align[i] = 'center';
35885 } else if (/^ *:-+ *$/.test(item.align[i])) {
35886 item.align[i] = 'left';
35888 item.align[i] = null;
35892 l = item.cells.length;
35894 for (i = 0; i < l; i++) {
35895 item.cells[i] = splitCells$1(item.cells[i], item.header.length);
35904 value: function hr(src) {
35905 var cap = this.rules.block.hr.exec(src);
35916 value: function blockquote(src) {
35917 var cap = this.rules.block.blockquote.exec(src);
35920 var text = cap[0].replace(/^ *> ?/gm, '');
35922 type: 'blockquote',
35930 value: function list(src) {
35931 var cap = this.rules.block.list.exec(src);
35936 var isordered = bull.length > 1;
35940 ordered: isordered,
35941 start: isordered ? +bull.slice(0, -1) : '',
35944 }; // Get each top-level item.
35946 var itemMatch = cap[0].match(this.rules.block.item);
35956 var l = itemMatch.length;
35957 bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
35959 for (var i = 0; i < l; i++) {
35960 item = itemMatch[i];
35961 raw = item; // Determine whether the next list item belongs here.
35962 // Backpedal if it does not belong in this list.
35965 bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
35967 if (!this.options.pedantic ? bnext[1].length > bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) {
35969 itemMatch.splice(i, 2, itemMatch[i] + '\n' + itemMatch[i + 1]);
35974 if ( // different bullet style
35975 !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
35976 addBack = itemMatch.slice(i + 1).join('\n');
35977 list.raw = list.raw.substring(0, list.raw.length - addBack.length);
35983 } // Remove the list item's bullet
35984 // so it is seen as the next token.
35987 space = item.length;
35988 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
35989 // list item contains. Hacky.
35991 if (~item.indexOf('\n ')) {
35992 space -= item.length;
35993 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
35994 } // Determine whether item is loose or not.
35995 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
35996 // for discount behavior.
35999 loose = next || /\n\n(?!\s*$)/.test(item);
36002 next = item.charAt(item.length - 1) === '\n';
36003 if (!loose) loose = next;
36008 } // Check for task list items
36011 if (this.options.gfm) {
36012 istask = /^\[[ xX]\] /.test(item);
36013 ischecked = undefined;
36016 ischecked = item[1] !== ' ';
36017 item = item.replace(/^\[[ xX]\] +/, '');
36025 checked: ischecked,
36036 value: function html(src) {
36037 var cap = this.rules.block.html.exec(src);
36041 type: this.options.sanitize ? 'paragraph' : 'html',
36043 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
36044 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36050 value: function def(src) {
36051 var cap = this.rules.block.def.exec(src);
36054 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
36055 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
36066 value: function table(src) {
36067 var cap = this.rules.block.table.exec(src);
36072 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
36073 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
36074 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
36077 if (item.header.length === item.align.length) {
36079 var l = item.align.length;
36082 for (i = 0; i < l; i++) {
36083 if (/^ *-+: *$/.test(item.align[i])) {
36084 item.align[i] = 'right';
36085 } else if (/^ *:-+: *$/.test(item.align[i])) {
36086 item.align[i] = 'center';
36087 } else if (/^ *:-+ *$/.test(item.align[i])) {
36088 item.align[i] = 'left';
36090 item.align[i] = null;
36094 l = item.cells.length;
36096 for (i = 0; i < l; i++) {
36097 item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
36106 value: function lheading(src) {
36107 var cap = this.rules.block.lheading.exec(src);
36113 depth: cap[2].charAt(0) === '=' ? 1 : 2,
36120 value: function paragraph(src) {
36121 var cap = this.rules.block.paragraph.exec(src);
36127 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
36133 value: function text(src, tokens) {
36134 var cap = this.rules.block.text.exec(src);
36137 var lastToken = tokens[tokens.length - 1];
36139 if (lastToken && lastToken.type === 'text') {
36155 value: function escape(src) {
36156 var cap = this.rules.inline.escape.exec(src);
36162 text: _escape(cap[1])
36168 value: function tag(src, inLink, inRawBlock) {
36169 var cap = this.rules.inline.tag.exec(src);
36172 if (!inLink && /^<a /i.test(cap[0])) {
36174 } else if (inLink && /^<\/a>/i.test(cap[0])) {
36178 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36180 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36181 inRawBlock = false;
36185 type: this.options.sanitize ? 'text' : 'html',
36188 inRawBlock: inRawBlock,
36189 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36195 value: function link(src) {
36196 var cap = this.rules.inline.link.exec(src);
36199 var trimmedUrl = cap[2].trim();
36201 if (!this.options.pedantic && /^</.test(trimmedUrl)) {
36202 // commonmark requires matching angle brackets
36203 if (!/>$/.test(trimmedUrl)) {
36205 } // ending angle bracket cannot be escaped
36208 var rtrimSlash = rtrim$2(trimmedUrl.slice(0, -1), '\\');
36210 if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
36214 // find closing parenthesis
36215 var lastParenIndex = findClosingBracket$1(cap[2], '()');
36217 if (lastParenIndex > -1) {
36218 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
36219 var linkLen = start + cap[1].length + lastParenIndex;
36220 cap[2] = cap[2].substring(0, lastParenIndex);
36221 cap[0] = cap[0].substring(0, linkLen).trim();
36229 if (this.options.pedantic) {
36230 // split pedantic href and title
36231 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
36238 title = cap[3] ? cap[3].slice(1, -1) : '';
36241 href = href.trim();
36243 if (/^</.test(href)) {
36244 if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
36245 // pedantic allows starting angle bracket without ending angle bracket
36246 href = href.slice(1);
36248 href = href.slice(1, -1);
36252 return outputLink(cap, {
36253 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
36254 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
36260 value: function reflink(src, links) {
36263 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
36264 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
36265 link = links[link.toLowerCase()];
36267 if (!link || !link.href) {
36268 var text = cap[0].charAt(0);
36276 return outputLink(cap, link, cap[0]);
36281 value: function strong(src, maskedSrc) {
36282 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36283 var match = this.rules.inline.strong.start.exec(src);
36285 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36286 maskedSrc = maskedSrc.slice(-1 * src.length);
36287 var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd;
36288 endReg.lastIndex = 0;
36291 while ((match = endReg.exec(maskedSrc)) != null) {
36292 cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3));
36297 raw: src.slice(0, cap[0].length),
36298 text: src.slice(2, cap[0].length - 2)
36306 value: function em(src, maskedSrc) {
36307 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36308 var match = this.rules.inline.em.start.exec(src);
36310 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36311 maskedSrc = maskedSrc.slice(-1 * src.length);
36312 var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd;
36313 endReg.lastIndex = 0;
36316 while ((match = endReg.exec(maskedSrc)) != null) {
36317 cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2));
36322 raw: src.slice(0, cap[0].length),
36323 text: src.slice(1, cap[0].length - 1)
36331 value: function codespan(src) {
36332 var cap = this.rules.inline.code.exec(src);
36335 var text = cap[2].replace(/\n/g, ' ');
36336 var hasNonSpaceChars = /[^ ]/.test(text);
36337 var hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
36339 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
36340 text = text.substring(1, text.length - 1);
36343 text = _escape(text, true);
36353 value: function br(src) {
36354 var cap = this.rules.inline.br.exec(src);
36365 value: function del(src) {
36366 var cap = this.rules.inline.del.exec(src);
36378 value: function autolink(src, mangle) {
36379 var cap = this.rules.inline.autolink.exec(src);
36384 if (cap[2] === '@') {
36385 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
36386 href = 'mailto:' + text;
36388 text = _escape(cap[1]);
36407 value: function url(src, mangle) {
36410 if (cap = this.rules.inline.url.exec(src)) {
36413 if (cap[2] === '@') {
36414 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
36415 href = 'mailto:' + text;
36417 // do extended autolink path validation
36421 prevCapZero = cap[0];
36422 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
36423 } while (prevCapZero !== cap[0]);
36425 text = _escape(cap[0]);
36427 if (cap[1] === 'www.') {
36428 href = 'http://' + text;
36449 value: function inlineText(src, inRawBlock, smartypants) {
36450 var cap = this.rules.inline.text.exec(src);
36456 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
36458 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
36473 var noopTest$1 = helpers.noopTest,
36474 edit$1 = helpers.edit,
36475 merge$2 = helpers.merge;
36477 * Block-Level Grammar
36481 newline: /^(?: *(?:\n|$))+/,
36482 code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
36483 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
36484 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
36485 heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
36486 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
36487 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
36488 html: '^ {0,3}(?:' // optional indentation
36489 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
36490 + '|comment[^\\n]*(\\n+|$)' // (2)
36491 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
36492 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
36493 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
36494 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
36495 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
36496 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
36498 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
36499 nptable: noopTest$1,
36501 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
36502 // regex template, placeholders will be replaced according to different paragraph
36503 // interruption rules of commonmark and the original markdown spec:
36504 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,
36507 block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
36508 block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
36509 block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
36510 block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
36511 block.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
36512 block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
36513 block.listItemStart = edit$1(/^( *)(bull)/).replace('bull', block.bullet).getRegex();
36514 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();
36515 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';
36516 block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
36517 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();
36518 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
36519 .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
36520 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
36522 block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex();
36524 * Normal Block Grammar
36527 block.normal = merge$2({}, block);
36529 * GFM Block Grammar
36532 block.gfm = merge$2({}, block.normal, {
36533 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
36534 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
36535 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
36537 table: '^ *\\|(.+)\\n' // Header
36538 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
36539 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
36542 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
36543 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36545 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
36546 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36549 * Pedantic grammar (original John Gruber's loose markdown specification)
36552 block.pedantic = merge$2({}, block.normal, {
36553 html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
36554 + '|<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(),
36555 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
36556 heading: /^(#{1,6})(.*)(?:\n+|$)/,
36557 fences: noopTest$1,
36558 // fences not supported
36559 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()
36562 * Inline-Level Grammar
36566 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
36567 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
36569 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
36570 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
36571 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
36572 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
36573 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
36575 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
36576 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
36577 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
36578 reflinkSearch: 'reflink|nolink(?!\\()',
36580 start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,
36581 // (1) returns if starts w/ punctuation
36582 middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,
36583 endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36584 // last char can't be punct, or final * must also be followed by punct (or endline)
36585 endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36589 start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,
36590 // (1) returns if starts w/ punctuation
36591 middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,
36592 endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36593 // last char can't be punct, or final * must also be followed by punct (or endline)
36594 endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36597 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
36598 br: /^( {2,}|\\)\n(?!\s*$)/,
36600 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n)))/,
36601 punctuation: /^([\s*punctuation])/
36602 }; // list of punctuation marks from common mark spec
36603 // without * and _ to workaround cases with double emphasis
36605 inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
36606 inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
36608 inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>';
36609 inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*';
36610 inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex();
36611 inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex();
36612 inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36613 inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36614 inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36615 inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex();
36616 inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36617 inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36618 inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36619 inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex();
36620 inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex();
36621 inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
36622 inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
36623 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])?)+(?![-_])/;
36624 inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex();
36625 inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
36626 inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex();
36627 inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
36628 inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
36629 inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
36630 inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex();
36631 inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex();
36632 inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex();
36634 * Normal Inline Grammar
36637 inline.normal = merge$2({}, inline);
36639 * Pedantic Inline Grammar
36642 inline.pedantic = merge$2({}, inline.normal, {
36645 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
36646 endAst: /\*\*(?!\*)/g,
36651 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
36652 endAst: /\*(?!\*)/g,
36655 link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(),
36656 reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex()
36659 * GFM Inline Grammar
36662 inline.gfm = merge$2({}, inline.normal, {
36663 escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
36664 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
36665 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
36666 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
36667 del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
36668 text: /^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
36670 inline.gfm.url = edit$1(inline.gfm.url, 'i').replace('email', inline.gfm._extended_email).getRegex();
36672 * GFM + Line Breaks Inline Grammar
36675 inline.breaks = merge$2({}, inline.gfm, {
36676 br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
36677 text: edit$1(inline.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
36684 var defaults$2 = defaults.defaults;
36685 var block$1 = rules.block,
36686 inline$1 = rules.inline;
36687 var repeatString$1 = helpers.repeatString;
36689 * smartypants text replacement
36692 function smartypants(text) {
36693 return text // em-dashes
36694 .replace(/---/g, "\u2014") // en-dashes
36695 .replace(/--/g, "\u2013") // opening singles
36696 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
36697 .replace(/'/g, "\u2019") // opening doubles
36698 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
36699 .replace(/"/g, "\u201D") // ellipses
36700 .replace(/\.{3}/g, "\u2026");
36703 * mangle email addresses
36707 function mangle(text) {
36711 var l = text.length;
36713 for (i = 0; i < l; i++) {
36714 ch = text.charCodeAt(i);
36716 if (Math.random() > 0.5) {
36717 ch = 'x' + ch.toString(16);
36720 out += '&#' + ch + ';';
36730 var Lexer_1 = /*#__PURE__*/function () {
36731 function Lexer(options) {
36732 _classCallCheck(this, Lexer);
36735 this.tokens.links = Object.create(null);
36736 this.options = options || defaults$2;
36737 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
36738 this.tokenizer = this.options.tokenizer;
36739 this.tokenizer.options = this.options;
36741 block: block$1.normal,
36742 inline: inline$1.normal
36745 if (this.options.pedantic) {
36746 rules.block = block$1.pedantic;
36747 rules.inline = inline$1.pedantic;
36748 } else if (this.options.gfm) {
36749 rules.block = block$1.gfm;
36751 if (this.options.breaks) {
36752 rules.inline = inline$1.breaks;
36754 rules.inline = inline$1.gfm;
36758 this.tokenizer.rules = rules;
36765 _createClass(Lexer, [{
36771 function lex(src) {
36772 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
36773 this.blockTokens(src, this.tokens, true);
36774 this.inline(this.tokens);
36775 return this.tokens;
36782 key: "blockTokens",
36783 value: function blockTokens(src) {
36784 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36785 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
36787 if (this.options.pedantic) {
36788 src = src.replace(/^ +$/gm, '');
36791 var token, i, l, lastToken;
36795 if (token = this.tokenizer.space(src)) {
36796 src = src.substring(token.raw.length);
36799 tokens.push(token);
36806 if (token = this.tokenizer.code(src, tokens)) {
36807 src = src.substring(token.raw.length);
36810 tokens.push(token);
36812 lastToken = tokens[tokens.length - 1];
36813 lastToken.raw += '\n' + token.raw;
36814 lastToken.text += '\n' + token.text;
36821 if (token = this.tokenizer.fences(src)) {
36822 src = src.substring(token.raw.length);
36823 tokens.push(token);
36828 if (token = this.tokenizer.heading(src)) {
36829 src = src.substring(token.raw.length);
36830 tokens.push(token);
36832 } // table no leading pipe (gfm)
36835 if (token = this.tokenizer.nptable(src)) {
36836 src = src.substring(token.raw.length);
36837 tokens.push(token);
36842 if (token = this.tokenizer.hr(src)) {
36843 src = src.substring(token.raw.length);
36844 tokens.push(token);
36849 if (token = this.tokenizer.blockquote(src)) {
36850 src = src.substring(token.raw.length);
36851 token.tokens = this.blockTokens(token.text, [], top);
36852 tokens.push(token);
36857 if (token = this.tokenizer.list(src)) {
36858 src = src.substring(token.raw.length);
36859 l = token.items.length;
36861 for (i = 0; i < l; i++) {
36862 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
36865 tokens.push(token);
36870 if (token = this.tokenizer.html(src)) {
36871 src = src.substring(token.raw.length);
36872 tokens.push(token);
36877 if (top && (token = this.tokenizer.def(src))) {
36878 src = src.substring(token.raw.length);
36880 if (!this.tokens.links[token.tag]) {
36881 this.tokens.links[token.tag] = {
36891 if (token = this.tokenizer.table(src)) {
36892 src = src.substring(token.raw.length);
36893 tokens.push(token);
36898 if (token = this.tokenizer.lheading(src)) {
36899 src = src.substring(token.raw.length);
36900 tokens.push(token);
36902 } // top-level paragraph
36905 if (top && (token = this.tokenizer.paragraph(src))) {
36906 src = src.substring(token.raw.length);
36907 tokens.push(token);
36912 if (token = this.tokenizer.text(src, tokens)) {
36913 src = src.substring(token.raw.length);
36916 tokens.push(token);
36918 lastToken = tokens[tokens.length - 1];
36919 lastToken.raw += '\n' + token.raw;
36920 lastToken.text += '\n' + token.text;
36927 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
36929 if (this.options.silent) {
36930 console.error(errMsg);
36933 throw new Error(errMsg);
36942 value: function inline(tokens) {
36943 var i, j, k, l2, row, token;
36944 var l = tokens.length;
36946 for (i = 0; i < l; i++) {
36949 switch (token.type) {
36955 this.inlineTokens(token.text, token.tokens);
36966 l2 = token.header.length;
36968 for (j = 0; j < l2; j++) {
36969 token.tokens.header[j] = [];
36970 this.inlineTokens(token.header[j], token.tokens.header[j]);
36974 l2 = token.cells.length;
36976 for (j = 0; j < l2; j++) {
36977 row = token.cells[j];
36978 token.tokens.cells[j] = [];
36980 for (k = 0; k < row.length; k++) {
36981 token.tokens.cells[j][k] = [];
36982 this.inlineTokens(row[k], token.tokens.cells[j][k]);
36991 this.inline(token.tokens);
36997 l2 = token.items.length;
36999 for (j = 0; j < l2; j++) {
37000 this.inline(token.items[j].tokens);
37015 key: "inlineTokens",
37016 value: function inlineTokens(src) {
37017 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
37018 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
37019 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
37020 var token; // String with links masked to avoid interference with em and strong
37022 var maskedSrc = src;
37024 var keepPrevChar, prevChar; // Mask out reflinks
37026 if (this.tokens.links) {
37027 var links = Object.keys(this.tokens.links);
37029 if (links.length > 0) {
37030 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
37031 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
37032 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
37036 } // Mask out other blocks
37039 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
37040 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
37044 if (!keepPrevChar) {
37048 keepPrevChar = false; // escape
37050 if (token = this.tokenizer.escape(src)) {
37051 src = src.substring(token.raw.length);
37052 tokens.push(token);
37057 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
37058 src = src.substring(token.raw.length);
37059 inLink = token.inLink;
37060 inRawBlock = token.inRawBlock;
37061 tokens.push(token);
37066 if (token = this.tokenizer.link(src)) {
37067 src = src.substring(token.raw.length);
37069 if (token.type === 'link') {
37070 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37073 tokens.push(token);
37075 } // reflink, nolink
37078 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
37079 src = src.substring(token.raw.length);
37081 if (token.type === 'link') {
37082 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37085 tokens.push(token);
37090 if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) {
37091 src = src.substring(token.raw.length);
37092 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37093 tokens.push(token);
37098 if (token = this.tokenizer.em(src, maskedSrc, prevChar)) {
37099 src = src.substring(token.raw.length);
37100 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37101 tokens.push(token);
37106 if (token = this.tokenizer.codespan(src)) {
37107 src = src.substring(token.raw.length);
37108 tokens.push(token);
37113 if (token = this.tokenizer.br(src)) {
37114 src = src.substring(token.raw.length);
37115 tokens.push(token);
37120 if (token = this.tokenizer.del(src)) {
37121 src = src.substring(token.raw.length);
37122 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37123 tokens.push(token);
37128 if (token = this.tokenizer.autolink(src, mangle)) {
37129 src = src.substring(token.raw.length);
37130 tokens.push(token);
37135 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
37136 src = src.substring(token.raw.length);
37137 tokens.push(token);
37142 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
37143 src = src.substring(token.raw.length);
37144 prevChar = token.raw.slice(-1);
37145 keepPrevChar = true;
37146 tokens.push(token);
37151 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
37153 if (this.options.silent) {
37154 console.error(errMsg);
37157 throw new Error(errMsg);
37166 get: function get() {
37173 * Static Lex Method
37178 value: function lex(src, options) {
37179 var lexer = new Lexer(options);
37180 return lexer.lex(src);
37183 * Static Lex Inline Method
37188 value: function lexInline(src, options) {
37189 var lexer = new Lexer(options);
37190 return lexer.inlineTokens(src);
37197 var defaults$3 = defaults.defaults;
37198 var cleanUrl$1 = helpers.cleanUrl,
37199 escape$2 = helpers.escape;
37204 var Renderer_1 = /*#__PURE__*/function () {
37205 function Renderer(options) {
37206 _classCallCheck(this, Renderer);
37208 this.options = options || defaults$3;
37211 _createClass(Renderer, [{
37213 value: function code(_code, infostring, escaped) {
37214 var lang = (infostring || '').match(/\S*/)[0];
37216 if (this.options.highlight) {
37217 var out = this.options.highlight(_code, lang);
37219 if (out != null && out !== _code) {
37225 _code = _code.replace(/\n$/, '') + '\n';
37228 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37231 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37235 value: function blockquote(quote) {
37236 return '<blockquote>\n' + quote + '</blockquote>\n';
37240 value: function html(_html) {
37245 value: function heading(text, level, raw, slugger) {
37246 if (this.options.headerIds) {
37247 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
37251 return '<h' + level + '>' + text + '</h' + level + '>\n';
37255 value: function hr() {
37256 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
37260 value: function list(body, ordered, start) {
37261 var type = ordered ? 'ol' : 'ul',
37262 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
37263 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
37267 value: function listitem(text) {
37268 return '<li>' + text + '</li>\n';
37272 value: function checkbox(checked) {
37273 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
37277 value: function paragraph(text) {
37278 return '<p>' + text + '</p>\n';
37282 value: function table(header, body) {
37283 if (body) body = '<tbody>' + body + '</tbody>';
37284 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
37288 value: function tablerow(content) {
37289 return '<tr>\n' + content + '</tr>\n';
37293 value: function tablecell(content, flags) {
37294 var type = flags.header ? 'th' : 'td';
37295 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
37296 return tag + content + '</' + type + '>\n';
37297 } // span level renderer
37301 value: function strong(text) {
37302 return '<strong>' + text + '</strong>';
37306 value: function em(text) {
37307 return '<em>' + text + '</em>';
37311 value: function codespan(text) {
37312 return '<code>' + text + '</code>';
37316 value: function br() {
37317 return this.options.xhtml ? '<br/>' : '<br>';
37321 value: function del(text) {
37322 return '<del>' + text + '</del>';
37326 value: function link(href, title, text) {
37327 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37329 if (href === null) {
37333 var out = '<a href="' + escape$2(href) + '"';
37336 out += ' title="' + title + '"';
37339 out += '>' + text + '</a>';
37344 value: function image(href, title, text) {
37345 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37347 if (href === null) {
37351 var out = '<img src="' + href + '" alt="' + text + '"';
37354 out += ' title="' + title + '"';
37357 out += this.options.xhtml ? '/>' : '>';
37362 value: function text(_text) {
37372 * returns only the textual part of the token
37374 var TextRenderer_1 = /*#__PURE__*/function () {
37375 function TextRenderer() {
37376 _classCallCheck(this, TextRenderer);
37379 _createClass(TextRenderer, [{
37381 value: // no need for block level renderers
37382 function strong(text) {
37387 value: function em(text) {
37392 value: function codespan(text) {
37397 value: function del(text) {
37402 value: function html(text) {
37407 value: function text(_text) {
37412 value: function link(href, title, text) {
37417 value: function image(href, title, text) {
37422 value: function br() {
37427 return TextRenderer;
37431 * Slugger generates header id
37433 var Slugger_1 = /*#__PURE__*/function () {
37434 function Slugger() {
37435 _classCallCheck(this, Slugger);
37440 _createClass(Slugger, [{
37442 value: function serialize(value) {
37443 return value.toLowerCase().trim() // remove html tags
37444 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
37445 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
37448 * Finds the next safe (unique) slug to use
37452 key: "getNextSafeSlug",
37453 value: function getNextSafeSlug(originalSlug, isDryRun) {
37454 var slug = originalSlug;
37455 var occurenceAccumulator = 0;
37457 if (this.seen.hasOwnProperty(slug)) {
37458 occurenceAccumulator = this.seen[originalSlug];
37461 occurenceAccumulator++;
37462 slug = originalSlug + '-' + occurenceAccumulator;
37463 } while (this.seen.hasOwnProperty(slug));
37467 this.seen[originalSlug] = occurenceAccumulator;
37468 this.seen[slug] = 0;
37474 * Convert string to unique id
37475 * @param {object} options
37476 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
37481 value: function slug(value) {
37482 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
37483 var slug = this.serialize(value);
37484 return this.getNextSafeSlug(slug, options.dryrun);
37491 var defaults$4 = defaults.defaults;
37492 var unescape$2 = helpers.unescape;
37494 * Parsing & Compiling
37497 var Parser_1 = /*#__PURE__*/function () {
37498 function Parser(options) {
37499 _classCallCheck(this, Parser);
37501 this.options = options || defaults$4;
37502 this.options.renderer = this.options.renderer || new Renderer_1();
37503 this.renderer = this.options.renderer;
37504 this.renderer.options = this.options;
37505 this.textRenderer = new TextRenderer_1();
37506 this.slugger = new Slugger_1();
37509 * Static Parse Method
37513 _createClass(Parser, [{
37519 function parse(tokens) {
37520 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
37540 var l = tokens.length;
37542 for (i = 0; i < l; i++) {
37545 switch (token.type) {
37553 out += this.renderer.hr();
37559 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$2(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
37565 out += this.renderer.code(token.text, token.lang, token.escaped);
37571 header = ''; // header
37574 l2 = token.header.length;
37576 for (j = 0; j < l2; j++) {
37577 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
37579 align: token.align[j]
37583 header += this.renderer.tablerow(cell);
37585 l2 = token.cells.length;
37587 for (j = 0; j < l2; j++) {
37588 row = token.tokens.cells[j];
37592 for (k = 0; k < l3; k++) {
37593 cell += this.renderer.tablecell(this.parseInline(row[k]), {
37595 align: token.align[k]
37599 body += this.renderer.tablerow(cell);
37602 out += this.renderer.table(header, body);
37608 body = this.parse(token.tokens);
37609 out += this.renderer.blockquote(body);
37615 ordered = token.ordered;
37616 start = token.start;
37617 loose = token.loose;
37618 l2 = token.items.length;
37621 for (j = 0; j < l2; j++) {
37622 item = token.items[j];
37623 checked = item.checked;
37628 checkbox = this.renderer.checkbox(checked);
37631 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
37632 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
37634 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
37635 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
37638 item.tokens.unshift({
37644 itemBody += checkbox;
37648 itemBody += this.parse(item.tokens, loose);
37649 body += this.renderer.listitem(itemBody, task, checked);
37652 out += this.renderer.list(body, ordered, start);
37658 // TODO parse inline content if parameter markdown=1
37659 out += this.renderer.html(token.text);
37665 out += this.renderer.paragraph(this.parseInline(token.tokens));
37671 body = token.tokens ? this.parseInline(token.tokens) : token.text;
37673 while (i + 1 < l && tokens[i + 1].type === 'text') {
37674 token = tokens[++i];
37675 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
37678 out += top ? this.renderer.paragraph(body) : body;
37684 var errMsg = 'Token with "' + token.type + '" type was not found.';
37686 if (this.options.silent) {
37687 console.error(errMsg);
37690 throw new Error(errMsg);
37699 * Parse Inline Tokens
37703 key: "parseInline",
37704 value: function parseInline(tokens, renderer) {
37705 renderer = renderer || this.renderer;
37709 var l = tokens.length;
37711 for (i = 0; i < l; i++) {
37714 switch (token.type) {
37717 out += renderer.text(token.text);
37723 out += renderer.html(token.text);
37729 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
37735 out += renderer.image(token.href, token.title, token.text);
37741 out += renderer.strong(this.parseInline(token.tokens, renderer));
37747 out += renderer.em(this.parseInline(token.tokens, renderer));
37753 out += renderer.codespan(token.text);
37759 out += renderer.br();
37765 out += renderer.del(this.parseInline(token.tokens, renderer));
37771 out += renderer.text(token.text);
37777 var errMsg = 'Token with "' + token.type + '" type was not found.';
37779 if (this.options.silent) {
37780 console.error(errMsg);
37783 throw new Error(errMsg);
37793 value: function parse(tokens, options) {
37794 var parser = new Parser(options);
37795 return parser.parse(tokens);
37798 * Static Parse Inline Method
37802 key: "parseInline",
37803 value: function parseInline(tokens, options) {
37804 var parser = new Parser(options);
37805 return parser.parseInline(tokens);
37812 var merge$3 = helpers.merge,
37813 checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation,
37814 escape$3 = helpers.escape;
37815 var getDefaults = defaults.getDefaults,
37816 changeDefaults = defaults.changeDefaults,
37817 defaults$5 = defaults.defaults;
37822 function marked(src, opt, callback) {
37823 // throw error in case of non string input
37824 if (typeof src === 'undefined' || src === null) {
37825 throw new Error('marked(): input parameter is undefined or null');
37828 if (typeof src !== 'string') {
37829 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
37832 if (typeof opt === 'function') {
37837 opt = merge$3({}, marked.defaults, opt || {});
37838 checkSanitizeDeprecation$1(opt);
37841 var highlight = opt.highlight;
37845 tokens = Lexer_1.lex(src, opt);
37847 return callback(e);
37850 var done = function done(err) {
37855 out = Parser_1.parse(tokens, opt);
37861 opt.highlight = highlight;
37862 return err ? callback(err) : callback(null, out);
37865 if (!highlight || highlight.length < 3) {
37869 delete opt.highlight;
37870 if (!tokens.length) return done();
37872 marked.walkTokens(tokens, function (token) {
37873 if (token.type === 'code') {
37875 setTimeout(function () {
37876 highlight(token.text, token.lang, function (err, code) {
37881 if (code != null && code !== token.text) {
37883 token.escaped = true;
37888 if (pending === 0) {
37896 if (pending === 0) {
37904 var _tokens = Lexer_1.lex(src, opt);
37906 if (opt.walkTokens) {
37907 marked.walkTokens(_tokens, opt.walkTokens);
37910 return Parser_1.parse(_tokens, opt);
37912 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
37915 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
37926 marked.options = marked.setOptions = function (opt) {
37927 merge$3(marked.defaults, opt);
37928 changeDefaults(marked.defaults);
37932 marked.getDefaults = getDefaults;
37933 marked.defaults = defaults$5;
37938 marked.use = function (extension) {
37939 var opts = merge$3({}, extension);
37941 if (extension.renderer) {
37943 var renderer = marked.defaults.renderer || new Renderer_1();
37945 var _loop = function _loop(prop) {
37946 var prevRenderer = renderer[prop];
37948 renderer[prop] = function () {
37949 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
37950 args[_key] = arguments[_key];
37953 var ret = extension.renderer[prop].apply(renderer, args);
37955 if (ret === false) {
37956 ret = prevRenderer.apply(renderer, args);
37963 for (var prop in extension.renderer) {
37967 opts.renderer = renderer;
37971 if (extension.tokenizer) {
37973 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
37975 var _loop2 = function _loop2(prop) {
37976 var prevTokenizer = tokenizer[prop];
37978 tokenizer[prop] = function () {
37979 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
37980 args[_key2] = arguments[_key2];
37983 var ret = extension.tokenizer[prop].apply(tokenizer, args);
37985 if (ret === false) {
37986 ret = prevTokenizer.apply(tokenizer, args);
37993 for (var prop in extension.tokenizer) {
37997 opts.tokenizer = tokenizer;
38001 if (extension.walkTokens) {
38002 var walkTokens = marked.defaults.walkTokens;
38004 opts.walkTokens = function (token) {
38005 extension.walkTokens(token);
38013 marked.setOptions(opts);
38016 * Run callback for every token
38020 marked.walkTokens = function (tokens, callback) {
38021 var _iterator = _createForOfIteratorHelper(tokens),
38025 for (_iterator.s(); !(_step = _iterator.n()).done;) {
38026 var token = _step.value;
38029 switch (token.type) {
38032 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
38036 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
38037 var cell = _step2.value;
38038 marked.walkTokens(cell, callback);
38046 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
38050 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
38051 var row = _step3.value;
38053 var _iterator4 = _createForOfIteratorHelper(row),
38057 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
38058 var _cell = _step4.value;
38059 marked.walkTokens(_cell, callback);
38078 marked.walkTokens(token.items, callback);
38084 if (token.tokens) {
38085 marked.walkTokens(token.tokens, callback);
38101 marked.parseInline = function (src, opt) {
38102 // throw error in case of non string input
38103 if (typeof src === 'undefined' || src === null) {
38104 throw new Error('marked.parseInline(): input parameter is undefined or null');
38107 if (typeof src !== 'string') {
38108 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
38111 opt = merge$3({}, marked.defaults, opt || {});
38112 checkSanitizeDeprecation$1(opt);
38115 var tokens = Lexer_1.lexInline(src, opt);
38117 if (opt.walkTokens) {
38118 marked.walkTokens(tokens, opt.walkTokens);
38121 return Parser_1.parseInline(tokens, opt);
38123 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
38126 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
38137 marked.Parser = Parser_1;
38138 marked.parser = Parser_1.parse;
38139 marked.Renderer = Renderer_1;
38140 marked.TextRenderer = TextRenderer_1;
38141 marked.Lexer = Lexer_1;
38142 marked.lexer = Lexer_1.lex;
38143 marked.Tokenizer = Tokenizer_1;
38144 marked.Slugger = Slugger_1;
38145 marked.parse = marked;
38146 var marked_1 = marked;
38148 var tiler$2 = utilTiler();
38149 var dispatch$3 = dispatch('loaded');
38150 var _tileZoom$2 = 14;
38151 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
38152 var _osmoseData = {
38155 }; // This gets reassigned if reset
38159 function abortRequest$2(controller) {
38161 controller.abort();
38165 function abortUnwantedRequests$2(cache, tiles) {
38166 Object.keys(cache.inflightTile).forEach(function (k) {
38167 var wanted = tiles.find(function (tile) {
38168 return k === tile.id;
38172 abortRequest$2(cache.inflightTile[k]);
38173 delete cache.inflightTile[k];
38178 function encodeIssueRtree$2(d) {
38186 } // Replace or remove QAItem from rtree
38189 function updateRtree$2(item, replace) {
38190 _cache$2.rtree.remove(item, function (a, b) {
38191 return a.data.id === b.data.id;
38195 _cache$2.rtree.insert(item);
38197 } // Issues shouldn't obscure each other
38200 function preventCoincident$1(loc) {
38201 var coincident = false;
38204 // first time, move marker up. after that, move marker right.
38205 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
38206 loc = geoVecAdd(loc, delta);
38207 var bbox = geoExtent(loc).bbox();
38208 coincident = _cache$2.rtree.search(bbox).length;
38209 } while (coincident);
38214 var serviceOsmose = {
38216 init: function init() {
38217 _mainFileFetcher.get('qa_data').then(function (d) {
38218 _osmoseData = d.osmose;
38219 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
38220 return s.split('-')[0];
38221 }).reduce(function (unique, item) {
38222 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
38230 this.event = utilRebind(this, dispatch$3, 'on');
38232 reset: function reset() {
38237 Object.values(_cache$2.inflightTile).forEach(abortRequest$2); // Strings and colors are static and should not be re-populated
38239 _strings = _cache$2.strings;
38240 _colors = _cache$2.colors;
38249 rtree: new RBush(),
38254 loadIssues: function loadIssues(projection) {
38258 // Tiles return a maximum # of issues
38259 // So we want to filter our request for only types iD supports
38260 item: _osmoseData.items
38261 }; // determine the needed tiles to cover the view
38263 var tiles = tiler$2.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
38265 abortUnwantedRequests$2(_cache$2, tiles); // issue new requests..
38267 tiles.forEach(function (tile) {
38268 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
38270 var _tile$xyz = _slicedToArray(tile.xyz, 3),
38275 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
38276 var controller = new AbortController();
38277 _cache$2.inflightTile[tile.id] = controller;
38279 signal: controller.signal
38280 }).then(function (data) {
38281 delete _cache$2.inflightTile[tile.id];
38282 _cache$2.loadedTile[tile.id] = true;
38284 if (data.features) {
38285 data.features.forEach(function (issue) {
38286 var _issue$properties = issue.properties,
38287 item = _issue$properties.item,
38288 cl = _issue$properties["class"],
38289 id = _issue$properties.uuid;
38290 /* Osmose issues are uniquely identified by a unique
38291 `item` and `class` combination (both integer values) */
38293 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
38295 if (itemType in _osmoseData.icons) {
38296 var loc = issue.geometry.coordinates; // lon, lat
38298 loc = preventCoincident$1(loc);
38299 var d = new QAItem(loc, _this, itemType, id, {
38301 }); // Setting elems here prevents UI detail requests
38303 if (item === 8300 || item === 8360) {
38307 _cache$2.data[d.id] = d;
38309 _cache$2.rtree.insert(encodeIssueRtree$2(d));
38314 dispatch$3.call('loaded');
38315 })["catch"](function () {
38316 delete _cache$2.inflightTile[tile.id];
38317 _cache$2.loadedTile[tile.id] = true;
38321 loadIssueDetail: function loadIssueDetail(issue) {
38324 // Issue details only need to be fetched once
38325 if (issue.elems !== undefined) {
38326 return Promise.resolve(issue);
38329 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
38331 var cacheDetails = function cacheDetails(data) {
38332 // Associated elements used for highlighting
38333 // Assign directly for immediate use in the callback
38334 issue.elems = data.elems.map(function (e) {
38335 return e.type.substring(0, 1) + e.id;
38336 }); // Some issues have instance specific detail in a subtitle
38338 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
38340 _this2.replaceItem(issue);
38343 return d3_json(url).then(cacheDetails).then(function () {
38347 loadStrings: function loadStrings() {
38348 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
38349 var items = Object.keys(_osmoseData.icons);
38351 if (locale in _cache$2.strings && Object.keys(_cache$2.strings[locale]).length === items.length) {
38352 return Promise.resolve(_cache$2.strings[locale]);
38353 } // May be partially populated already if some requests were successful
38356 if (!(locale in _cache$2.strings)) {
38357 _cache$2.strings[locale] = {};
38358 } // Only need to cache strings for supported issue types
38359 // Using multiple individual item + class requests to reduce fetched data size
38362 var allRequests = items.map(function (itemType) {
38363 // No need to request data we already have
38364 if (itemType in _cache$2.strings[locale]) return null;
38366 var cacheData = function cacheData(data) {
38367 // Bunch of nested single value arrays of objects
38368 var _data$categories = _slicedToArray(data.categories, 1),
38369 _data$categories$ = _data$categories[0],
38370 cat = _data$categories$ === void 0 ? {
38372 } : _data$categories$;
38374 var _cat$items = _slicedToArray(cat.items, 1),
38375 _cat$items$ = _cat$items[0],
38376 item = _cat$items$ === void 0 ? {
38380 var _item$class = _slicedToArray(item["class"], 1),
38381 _item$class$ = _item$class[0],
38382 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
38386 /* eslint-disable no-console */
38387 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
38388 /* eslint-enable no-console */
38391 } // Cache served item colors to automatically style issue markers later
38394 var itemInt = item.item,
38395 color = item.color;
38397 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
38398 _cache$2.colors[itemInt] = color;
38399 } // Value of root key will be null if no string exists
38400 // If string exists, value is an object with key 'auto' for string
38403 var title = cl.title,
38404 detail = cl.detail,
38406 trap = cl.trap; // Osmose titles shouldn't contain markdown
38408 var issueStrings = {};
38409 if (title) issueStrings.title = title.auto;
38410 if (detail) issueStrings.detail = marked_1(detail.auto);
38411 if (trap) issueStrings.trap = marked_1(trap.auto);
38412 if (fix) issueStrings.fix = marked_1(fix.auto);
38413 _cache$2.strings[locale][itemType] = issueStrings;
38416 var _itemType$split = itemType.split('-'),
38417 _itemType$split2 = _slicedToArray(_itemType$split, 2),
38418 item = _itemType$split2[0],
38419 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
38422 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
38423 return d3_json(url).then(cacheData);
38424 }).filter(Boolean);
38425 return Promise.all(allRequests).then(function () {
38426 return _cache$2.strings[locale];
38429 getStrings: function getStrings(itemType) {
38430 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
38431 // No need to fallback to English, Osmose API handles this for us
38432 return locale in _cache$2.strings ? _cache$2.strings[locale][itemType] : {};
38434 getColor: function getColor(itemType) {
38435 return itemType in _cache$2.colors ? _cache$2.colors[itemType] : '#FFFFFF';
38437 postUpdate: function postUpdate(issue, callback) {
38440 if (_cache$2.inflightPost[issue.id]) {
38442 message: 'Issue update already inflight',
38445 } // UI sets the status to either 'done' or 'false'
38448 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
38449 var controller = new AbortController();
38451 var after = function after() {
38452 delete _cache$2.inflightPost[issue.id];
38454 _this3.removeItem(issue);
38456 if (issue.newStatus === 'done') {
38457 // Keep track of the number of issues closed per `item` to tag the changeset
38458 if (!(issue.item in _cache$2.closed)) {
38459 _cache$2.closed[issue.item] = 0;
38462 _cache$2.closed[issue.item] += 1;
38465 if (callback) callback(null, issue);
38468 _cache$2.inflightPost[issue.id] = controller;
38470 signal: controller.signal
38471 }).then(after)["catch"](function (err) {
38472 delete _cache$2.inflightPost[issue.id];
38473 if (callback) callback(err.message);
38476 // Get all cached QAItems covering the viewport
38477 getItems: function getItems(projection) {
38478 var viewport = projection.clipExtent();
38479 var min = [viewport[0][0], viewport[1][1]];
38480 var max = [viewport[1][0], viewport[0][1]];
38481 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38482 return _cache$2.rtree.search(bbox).map(function (d) {
38486 // Get a QAItem from cache
38487 // NOTE: Don't change method name until UI v3 is merged
38488 getError: function getError(id) {
38489 return _cache$2.data[id];
38491 // get the name of the icon to display for this item
38492 getIcon: function getIcon(itemType) {
38493 return _osmoseData.icons[itemType];
38495 // Replace a single QAItem in the cache
38496 replaceItem: function replaceItem(item) {
38497 if (!(item instanceof QAItem) || !item.id) return;
38498 _cache$2.data[item.id] = item;
38499 updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
38503 // Remove a single QAItem from the cache
38504 removeItem: function removeItem(item) {
38505 if (!(item instanceof QAItem) || !item.id) return;
38506 delete _cache$2.data[item.id];
38507 updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
38509 // Used to populate `closed:osmose:*` changeset tags
38510 getClosedCounts: function getClosedCounts() {
38511 return _cache$2.closed;
38513 itemURL: function itemURL(item) {
38514 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
38518 var apibase = 'https://a.mapillary.com/v3/';
38519 var viewercss = 'mapillary-js/mapillary.min.css';
38520 var viewerjs = 'mapillary-js/mapillary.min.js';
38521 var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
38522 var mapFeatureConfig = {
38523 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(',')
38525 var maxResults = 1000;
38527 var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
38528 var dispatch$4 = dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'nodeChanged');
38529 var _mlyFallback = false;
38535 var _mlyActiveImage;
38537 var _mlySelectedImageKey;
38541 var _mlyViewerFilter = ['all'];
38543 var _loadViewerPromise;
38545 var _mlyHighlightedDetection;
38547 var _mlyShowFeatureDetections = false;
38548 var _mlyShowSignDetections = false;
38550 function abortRequest$3(controller) {
38551 controller.abort();
38554 function loadTiles(which, url, projection) {
38555 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
38556 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
38558 var cache = _mlyCache[which];
38559 Object.keys(cache.inflight).forEach(function (k) {
38560 var wanted = tiles.find(function (tile) {
38561 return k.indexOf(tile.id + ',') === 0;
38565 abortRequest$3(cache.inflight[k]);
38566 delete cache.inflight[k];
38569 tiles.forEach(function (tile) {
38570 loadNextTilePage(which, currZoom, url, tile);
38574 function loadNextTilePage(which, currZoom, url, tile) {
38575 var cache = _mlyCache[which];
38576 var rect = tile.extent.rectangle();
38577 var maxPages = maxPageAtZoom(currZoom);
38578 var nextPage = cache.nextPage[tile.id] || 0;
38579 var nextURL = cache.nextURL[tile.id] || url + utilQsString({
38580 per_page: maxResults,
38582 client_id: clientId,
38583 bbox: [rect[0], rect[1], rect[2], rect[3]].join(',')
38585 if (nextPage > maxPages) return;
38586 var id = tile.id + ',' + String(nextPage);
38587 if (cache.loaded[id] || cache.inflight[id]) return;
38588 var controller = new AbortController();
38589 cache.inflight[id] = controller;
38592 signal: controller.signal,
38594 'Content-Type': 'application/json'
38597 fetch(nextURL, options).then(function (response) {
38598 if (!response.ok) {
38599 throw new Error(response.status + ' ' + response.statusText);
38602 var linkHeader = response.headers.get('Link');
38605 var pagination = parsePagination(linkHeader);
38607 if (pagination.next) {
38608 cache.nextURL[tile.id] = pagination.next;
38612 return response.json();
38613 }).then(function (data) {
38614 cache.loaded[id] = true;
38615 delete cache.inflight[id];
38617 if (!data || !data.features || !data.features.length) {
38618 throw new Error('No Data');
38621 var features = data.features.map(function (feature) {
38622 var loc = feature.geometry.coordinates;
38623 var d; // An image (shown as a green dot on the map) is a single street photo with extra
38624 // information such as location, camera angle (CA), camera model, and so on.
38625 // Each image feature is a GeoJSON Point
38627 if (which === 'images') {
38630 key: feature.properties.key,
38631 ca: feature.properties.ca,
38632 captured_at: feature.properties.captured_at,
38633 captured_by: feature.properties.username,
38634 pano: feature.properties.pano
38636 cache.forImageKey[d.key] = d; // cache imageKey -> image
38637 // Mapillary organizes images as sequences. A sequence of images are continuously captured
38638 // by a user at a give time. Sequences are shown on the map as green lines.
38639 // Each sequence feature is a GeoJSON LineString
38640 } else if (which === 'sequences') {
38641 var sequenceKey = feature.properties.key;
38642 cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString
38644 feature.properties.coordinateProperties.image_keys.forEach(function (imageKey) {
38645 cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey
38647 return false; // because no `d` data worth loading into an rbush
38648 // A map feature is a real world object that can be shown on a map. It could be any object
38649 // recognized from images, manually added in images, or added on the map.
38650 // Each map feature is a GeoJSON Point (located where the feature is)
38651 } else if (which === 'map_features' || which === 'points') {
38654 key: feature.properties.key,
38655 value: feature.properties.value,
38656 detections: feature.properties.detections,
38657 direction: feature.properties.direction,
38658 accuracy: feature.properties.accuracy,
38659 first_seen_at: feature.properties.first_seen_at,
38660 last_seen_at: feature.properties.last_seen_at
38671 }).filter(Boolean);
38673 if (cache.rtree && features) {
38674 cache.rtree.load(features);
38677 if (data.features.length === maxResults) {
38678 // more pages to load
38679 cache.nextPage[tile.id] = nextPage + 1;
38680 loadNextTilePage(which, currZoom, url, tile);
38682 cache.nextPage[tile.id] = Infinity; // no more pages to load
38685 if (which === 'images' || which === 'sequences') {
38686 dispatch$4.call('loadedImages');
38687 } else if (which === 'map_features') {
38688 dispatch$4.call('loadedSigns');
38689 } else if (which === 'points') {
38690 dispatch$4.call('loadedMapFeatures');
38692 })["catch"](function () {
38693 cache.loaded[id] = true;
38694 delete cache.inflight[id];
38698 function loadData(which, url) {
38699 var cache = _mlyCache[which];
38703 'Content-Type': 'application/json'
38706 var nextUrl = url + '&client_id=' + clientId;
38707 return fetch(nextUrl, options).then(function (response) {
38708 if (!response.ok) {
38709 throw new Error(response.status + ' ' + response.statusText);
38712 return response.json();
38713 }).then(function (data) {
38714 if (!data || !data.features || !data.features.length) {
38715 throw new Error('No Data');
38718 data.features.forEach(function (feature) {
38721 if (which === 'image_detections') {
38723 key: feature.properties.key,
38724 image_key: feature.properties.image_key,
38725 value: feature.properties.value,
38726 shape: feature.properties.shape
38729 if (!cache.forImageKey[d.image_key]) {
38730 cache.forImageKey[d.image_key] = [];
38733 cache.forImageKey[d.image_key].push(d);
38739 function maxPageAtZoom(z) {
38740 if (z < 15) return 2;
38741 if (z === 15) return 5;
38742 if (z === 16) return 10;
38743 if (z === 17) return 20;
38744 if (z === 18) return 40;
38745 if (z > 18) return 80;
38746 } // extract links to pages of API results
38749 function parsePagination(links) {
38750 return links.split(',').map(function (rel) {
38751 var elements = rel.split(';');
38753 if (elements.length === 2) {
38754 return [/<(.+)>/.exec(elements[0])[1], /rel="(.+)"/.exec(elements[1])[1]];
38758 }).reduce(function (pagination, val) {
38759 pagination[val[1]] = val[0];
38762 } // partition viewport into higher zoom tiles
38765 function partitionViewport(projection) {
38766 var z = geoScaleToZoom(projection.scale());
38767 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
38769 var tiler = utilTiler().zoomExtent([z2, z2]);
38770 return tiler.getTiles(projection).map(function (tile) {
38771 return tile.extent;
38773 } // no more than `limit` results per partition.
38776 function searchLimited(limit, projection, rtree) {
38777 limit = limit || 5;
38778 return partitionViewport(projection).reduce(function (result, extent) {
38779 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
38782 return found.length ? result.concat(found) : result;
38786 var serviceMapillary = {
38787 init: function init() {
38792 this.event = utilRebind(this, dispatch$4, 'on');
38794 reset: function reset() {
38796 Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
38797 Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
38798 Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
38799 Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
38800 Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
38809 rtree: new RBush(),
38812 image_detections: {
38838 rtree: new RBush(),
38843 _mlySelectedImageKey = null;
38844 _mlyActiveImage = null;
38847 images: function images(projection) {
38849 return searchLimited(limit, projection, _mlyCache.images.rtree);
38851 signs: function signs(projection) {
38853 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
38855 mapFeatures: function mapFeatures(projection) {
38857 return searchLimited(limit, projection, _mlyCache.points.rtree);
38859 cachedImage: function cachedImage(imageKey) {
38860 return _mlyCache.images.forImageKey[imageKey];
38862 sequences: function sequences(projection) {
38863 var viewport = projection.clipExtent();
38864 var min = [viewport[0][0], viewport[1][1]];
38865 var max = [viewport[1][0], viewport[0][1]];
38866 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38867 var sequenceKeys = {}; // all sequences for images in viewport
38869 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
38870 var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
38873 sequenceKeys[sequenceKey] = true;
38875 }); // Return lineStrings for the sequences
38878 return Object.keys(sequenceKeys).map(function (sequenceKey) {
38879 return _mlyCache.sequences.lineString[sequenceKey];
38882 signsSupported: function signsSupported() {
38885 loadImages: function loadImages(projection) {
38886 loadTiles('images', apibase + 'images?sort_by=key&', projection);
38887 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
38889 loadSigns: function loadSigns(projection) {
38890 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
38892 loadMapFeatures: function loadMapFeatures(projection) {
38893 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
38895 ensureViewerLoaded: function ensureViewerLoaded(context) {
38896 if (_loadViewerPromise) return _loadViewerPromise; // add mly-wrapper
38898 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
38899 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
38901 _loadViewerPromise = new Promise(function (resolve, reject) {
38902 var loadedCount = 0;
38904 function loaded() {
38905 loadedCount += 1; // wait until both files are loaded
38907 if (loadedCount === 2) resolve();
38910 var head = select('head'); // load mapillary-viewercss
38912 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 () {
38914 }); // load mapillary-viewerjs
38916 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 () {
38919 })["catch"](function () {
38920 _loadViewerPromise = null;
38921 }).then(function () {
38922 that.initViewer(context);
38924 return _loadViewerPromise;
38926 loadSignResources: function loadSignResources(context) {
38927 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
38928 /* don't override colors */
38932 loadObjectResources: function loadObjectResources(context) {
38933 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
38934 /* don't override colors */
38938 resetTags: function resetTags() {
38939 if (_mlyViewer && !_mlyFallback) {
38940 _mlyViewer.getComponent('tag').removeAll(); // remove previous detections
38944 showFeatureDetections: function showFeatureDetections(value) {
38945 _mlyShowFeatureDetections = value;
38947 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38951 showSignDetections: function showSignDetections(value) {
38952 _mlyShowSignDetections = value;
38954 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38958 filterViewer: function filterViewer(context) {
38959 var showsPano = context.photos().showsPanoramic();
38960 var showsFlat = context.photos().showsFlat();
38961 var fromDate = context.photos().fromDate();
38962 var toDate = context.photos().toDate();
38963 var usernames = context.photos().usernames();
38964 var filter = ['all'];
38965 if (!showsPano) filter.push(['==', 'pano', false]);
38966 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
38967 if (usernames && usernames.length) filter.push(['==', 'username', usernames[0]]);
38970 var fromTimestamp = new Date(fromDate).getTime();
38971 filter.push(['>=', 'capturedAt', fromTimestamp]);
38975 var toTimestamp = new Date(toDate).getTime();
38976 filter.push(['>=', 'capturedAt', toTimestamp]);
38980 _mlyViewer.setFilter(filter);
38983 _mlyViewerFilter = filter;
38986 showViewer: function showViewer(context) {
38987 var wrap = context.container().select('.photoviewer').classed('hide', false);
38988 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
38990 if (isHidden && _mlyViewer) {
38991 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
38992 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
38994 _mlyViewer.resize();
38999 hideViewer: function hideViewer(context) {
39000 _mlyActiveImage = null;
39001 _mlySelectedImageKey = null;
39003 if (!_mlyFallback && _mlyViewer) {
39004 _mlyViewer.getComponent('sequence').stop();
39007 var viewer = context.container().select('.photoviewer');
39008 if (!viewer.empty()) viewer.datum(null);
39009 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
39010 this.updateUrlImage(null);
39011 dispatch$4.call('nodeChanged');
39012 return this.setStyles(context, null, true);
39014 parsePagination: parsePagination,
39015 updateUrlImage: function updateUrlImage(imageKey) {
39016 if (!window.mocha) {
39017 var hash = utilStringQs(window.location.hash);
39020 hash.photo = 'mapillary/' + imageKey;
39025 window.location.replace('#' + utilQsString(hash, true));
39028 highlightDetection: function highlightDetection(detection) {
39030 _mlyHighlightedDetection = detection.detection_key;
39035 initViewer: function initViewer(context) {
39037 if (!window.Mapillary) return;
39039 baseImageSize: 320,
39045 }; // Disable components requiring WebGL support
39047 if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
39048 _mlyFallback = true;
39059 navigation: true // fallback
39064 _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
39066 _mlyViewer.on('nodechanged', nodeChanged);
39068 _mlyViewer.on('bearingchanged', bearingChanged);
39070 if (_mlyViewerFilter) {
39071 _mlyViewer.setFilter(_mlyViewerFilter);
39072 } // Register viewer resize handler
39075 context.ui().photoviewer.on('resize.mapillary', function () {
39076 if (_mlyViewer) _mlyViewer.resize();
39077 }); // nodeChanged: called after the viewer has changed images and is ready.
39079 // There is some logic here to batch up clicks into a _mlyClicks array
39080 // because the user might click on a lot of markers quickly and nodechanged
39081 // may be called out of order asynchronously.
39083 // Clicks are added to the array in `selectedImage` and removed here.
39086 function nodeChanged(node) {
39088 var clicks = _mlyClicks;
39089 var index = clicks.indexOf(node.key);
39090 var selectedKey = _mlySelectedImageKey;
39091 that.setActiveImage(node);
39094 // `nodechanged` initiated from clicking on a marker..
39095 clicks.splice(index, 1); // remove the click
39096 // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
39097 // one more time to update the detections and attribution..
39099 if (node.key === selectedKey) {
39100 that.selectImage(context, _mlySelectedImageKey, true);
39103 // `nodechanged` initiated from the Mapillary viewer controls..
39104 var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
39105 context.map().centerEase(loc);
39106 that.selectImage(context, node.key, true);
39109 dispatch$4.call('nodeChanged');
39112 function bearingChanged(e) {
39113 dispatch$4.call('bearingChanged', undefined, e);
39116 // Pass in the image key string as `imageKey`.
39117 // This allows images to be selected from places that dont have access
39118 // to the full image datum (like the street signs layer or the js viewer)
39119 selectImage: function selectImage(context, imageKey, fromViewer) {
39120 _mlySelectedImageKey = imageKey;
39121 this.updateUrlImage(imageKey);
39122 var d = _mlyCache.images.forImageKey[imageKey];
39123 var viewer = context.container().select('.photoviewer');
39124 if (!viewer.empty()) viewer.datum(d);
39125 imageKey = d && d.key || imageKey;
39127 if (!fromViewer && imageKey) {
39128 _mlyClicks.push(imageKey);
39131 this.setStyles(context, null, true);
39133 if (_mlyShowFeatureDetections) {
39134 this.updateDetections(imageKey, apibase + 'image_detections?layers=points&values=' + mapFeatureConfig.values + '&image_keys=' + imageKey);
39137 if (_mlyShowSignDetections) {
39138 this.updateDetections(imageKey, apibase + 'image_detections?layers=trafficsigns&image_keys=' + imageKey);
39141 if (_mlyViewer && imageKey) {
39142 _mlyViewer.moveToKey(imageKey)["catch"](function (e) {
39143 console.error('mly3', e);
39144 }); // eslint-disable-line no-console
39150 getActiveImage: function getActiveImage() {
39151 return _mlyActiveImage;
39153 getSelectedImageKey: function getSelectedImageKey() {
39154 return _mlySelectedImageKey;
39156 getSequenceKeyForImageKey: function getSequenceKeyForImageKey(imageKey) {
39157 return _mlyCache.sequences.forImageKey[imageKey];
39159 setActiveImage: function setActiveImage(node) {
39161 _mlyActiveImage = {
39162 ca: node.originalCA,
39164 loc: [node.originalLatLon.lon, node.originalLatLon.lat],
39168 _mlyActiveImage = null;
39171 // Updates the currently highlighted sequence and selected bubble.
39172 // Reset is only necessary when interacting with the viewport because
39173 // this implicitly changes the currently selected bubble/sequence
39174 setStyles: function setStyles(context, hovered, reset) {
39176 // reset all layers
39177 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false);
39178 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
39181 var hoveredImageKey = hovered && hovered.key;
39182 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
39183 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
39184 var hoveredImageKeys = hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys || [];
39185 var selectedImageKey = _mlySelectedImageKey;
39186 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
39187 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
39188 var selectedImageKeys = selectedLineString && selectedLineString.properties.coordinateProperties.image_keys || []; // highlight sibling viewfields on either the selected or the hovered sequences
39190 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
39191 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
39192 return highlightedImageKeys.indexOf(d.key) !== -1;
39193 }).classed('hovered', function (d) {
39194 return d.key === hoveredImageKey;
39196 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
39197 return d.properties.key === hoveredSequenceKey;
39198 }).classed('currentView', function (d) {
39199 return d.properties.key === selectedSequenceKey;
39200 }); // update viewfields if needed
39202 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
39204 function viewfieldPath() {
39205 var d = this.parentNode.__data__;
39207 if (d.pano && d.key !== selectedImageKey) {
39208 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
39210 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
39216 updateDetections: function updateDetections(imageKey, url) {
39217 if (!_mlyViewer || _mlyFallback) return;
39218 if (!imageKey) return;
39220 if (!_mlyCache.image_detections.forImageKey[imageKey]) {
39221 loadData('image_detections', url).then(function () {
39222 showDetections(_mlyCache.image_detections.forImageKey[imageKey] || []);
39225 showDetections(_mlyCache.image_detections.forImageKey[imageKey]);
39228 function showDetections(detections) {
39229 detections.forEach(function (data) {
39230 var tag = makeTag(data);
39233 var tagComponent = _mlyViewer.getComponent('tag');
39235 tagComponent.add([tag]);
39240 function makeTag(data) {
39241 var valueParts = data.value.split('--');
39242 if (!valueParts.length) return;
39245 var color = 0xffffff;
39247 if (_mlyHighlightedDetection === data.key) {
39249 text = valueParts[1];
39251 if (text === 'flat' || text === 'discrete' || text === 'sign') {
39252 text = valueParts[2];
39255 text = text.replace(/-/g, ' ');
39256 text = text.charAt(0).toUpperCase() + text.slice(1);
39257 _mlyHighlightedDetection = null;
39260 if (data.shape.type === 'Polygon') {
39261 var polygonGeometry = new Mapillary.TagComponent.PolygonGeometry(data.shape.coordinates[0]);
39262 tag = new Mapillary.TagComponent.OutlineTag(data.key, polygonGeometry, {
39270 } else if (data.shape.type === 'Point') {
39271 var pointGeometry = new Mapillary.TagComponent.PointGeometry(data.shape.coordinates[0]);
39272 tag = new Mapillary.TagComponent.SpotTag(data.key, pointGeometry, {
39282 cache: function cache() {
39287 function validationIssue(attrs) {
39288 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
39290 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
39292 this.severity = attrs.severity; // required - 'warning' or 'error'
39294 this.message = attrs.message; // required - function returning localized string
39296 this.reference = attrs.reference; // optional - function(selection) to render reference information
39298 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
39300 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
39302 this.data = attrs.data; // optional - object containing extra data for the fixes
39304 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
39306 this.hash = attrs.hash; // optional - string to further differentiate the issue
39308 this.id = generateID.apply(this); // generated - see below
39310 this.autoFix = null; // generated - if autofix exists, will be set below
39311 // A unique, deterministic string hash.
39312 // Issues with identical id values are considered identical.
39314 function generateID() {
39315 var parts = [this.type];
39318 // subclasses can pass in their own differentiator
39319 parts.push(this.hash);
39322 if (this.subtype) {
39323 parts.push(this.subtype);
39324 } // include the entities this issue is for
39325 // (sort them so the id is deterministic)
39328 if (this.entityIds) {
39329 var entityKeys = this.entityIds.slice().sort();
39330 parts.push.apply(parts, entityKeys);
39333 return parts.join(':');
39336 this.extent = function (resolver) {
39338 return geoExtent(this.loc);
39341 if (this.entityIds && this.entityIds.length) {
39342 return this.entityIds.reduce(function (extent, entityId) {
39343 return extent.extend(resolver.entity(entityId).extent(resolver));
39350 this.fixes = function (context) {
39351 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
39354 if (issue.severity === 'warning') {
39355 // allow ignoring any issue that's not an error
39356 fixes.push(new validationIssueFix({
39357 title: _t.html('issues.fix.ignore_issue.title'),
39358 icon: 'iD-icon-close',
39359 onClick: function onClick() {
39360 context.validator().ignoreIssue(this.issue.id);
39365 fixes.forEach(function (fix) {
39366 // the id doesn't matter as long as it's unique to this issue/fix
39367 fix.id = fix.title; // add a reference to the issue for use in actions
39371 if (fix.autoArgs) {
39372 issue.autoFix = fix;
39378 function validationIssueFix(attrs) {
39379 this.title = attrs.title; // Required
39381 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
39383 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
39385 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
39387 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
39389 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
39391 this.issue = null; // Generated link - added by validationIssue
39394 var buildRuleChecks = function buildRuleChecks() {
39396 equals: function equals(_equals) {
39397 return function (tags) {
39398 return Object.keys(_equals).every(function (k) {
39399 return _equals[k] === tags[k];
39403 notEquals: function notEquals(_notEquals) {
39404 return function (tags) {
39405 return Object.keys(_notEquals).some(function (k) {
39406 return _notEquals[k] !== tags[k];
39410 absence: function absence(_absence) {
39411 return function (tags) {
39412 return Object.keys(tags).indexOf(_absence) === -1;
39415 presence: function presence(_presence) {
39416 return function (tags) {
39417 return Object.keys(tags).indexOf(_presence) > -1;
39420 greaterThan: function greaterThan(_greaterThan) {
39421 var key = Object.keys(_greaterThan)[0];
39422 var value = _greaterThan[key];
39423 return function (tags) {
39424 return tags[key] > value;
39427 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
39428 var key = Object.keys(_greaterThanEqual)[0];
39429 var value = _greaterThanEqual[key];
39430 return function (tags) {
39431 return tags[key] >= value;
39434 lessThan: function lessThan(_lessThan) {
39435 var key = Object.keys(_lessThan)[0];
39436 var value = _lessThan[key];
39437 return function (tags) {
39438 return tags[key] < value;
39441 lessThanEqual: function lessThanEqual(_lessThanEqual) {
39442 var key = Object.keys(_lessThanEqual)[0];
39443 var value = _lessThanEqual[key];
39444 return function (tags) {
39445 return tags[key] <= value;
39448 positiveRegex: function positiveRegex(_positiveRegex) {
39449 var tagKey = Object.keys(_positiveRegex)[0];
39451 var expression = _positiveRegex[tagKey].join('|');
39453 var regex = new RegExp(expression);
39454 return function (tags) {
39455 return regex.test(tags[tagKey]);
39458 negativeRegex: function negativeRegex(_negativeRegex) {
39459 var tagKey = Object.keys(_negativeRegex)[0];
39461 var expression = _negativeRegex[tagKey].join('|');
39463 var regex = new RegExp(expression);
39464 return function (tags) {
39465 return !regex.test(tags[tagKey]);
39471 var buildLineKeys = function buildLineKeys() {
39487 var serviceMapRules = {
39488 init: function init() {
39489 this._ruleChecks = buildRuleChecks();
39490 this._validationRules = [];
39491 this._areaKeys = osmAreaKeys;
39492 this._lineKeys = buildLineKeys();
39494 // list of rules only relevant to tag checks...
39495 filterRuleChecks: function filterRuleChecks(selector) {
39496 var _ruleChecks = this._ruleChecks;
39497 return Object.keys(selector).reduce(function (rules, key) {
39498 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
39499 rules.push(_ruleChecks[key](selector[key]));
39505 // builds tagMap from mapcss-parse selector object...
39506 buildTagMap: function buildTagMap(selector) {
39507 var getRegexValues = function getRegexValues(regexes) {
39508 return regexes.map(function (regex) {
39509 return regex.replace(/\$|\^/g, '');
39513 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
39515 var isRegex = /regex/gi.test(key);
39516 var isEqual = /equals/gi.test(key);
39518 if (isRegex || isEqual) {
39519 Object.keys(selector[key]).forEach(function (selectorKey) {
39520 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
39522 if (expectedTags.hasOwnProperty(selectorKey)) {
39523 values = values.concat(expectedTags[selectorKey]);
39526 expectedTags[selectorKey] = values;
39528 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
39529 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
39530 values = [selector[key][tagKey]];
39532 if (expectedTags.hasOwnProperty(tagKey)) {
39533 values = values.concat(expectedTags[tagKey]);
39536 expectedTags[tagKey] = values;
39539 return expectedTags;
39543 // inspired by osmWay#isArea()
39544 inferGeometry: function inferGeometry(tagMap) {
39545 var _lineKeys = this._lineKeys;
39546 var _areaKeys = this._areaKeys;
39548 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
39549 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
39552 var keyValueImpliesLine = function keyValueImpliesLine(key) {
39553 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
39556 if (tagMap.hasOwnProperty('area')) {
39557 if (tagMap.area.indexOf('yes') > -1) {
39561 if (tagMap.area.indexOf('no') > -1) {
39566 for (var key in tagMap) {
39567 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
39571 if (key in _lineKeys && keyValueImpliesLine(key)) {
39578 // adds from mapcss-parse selector check...
39579 addRule: function addRule(selector) {
39581 // checks relevant to mapcss-selector
39582 checks: this.filterRuleChecks(selector),
39583 // true if all conditions for a tag error are true..
39584 matches: function matches(entity) {
39585 return this.checks.every(function (check) {
39586 return check(entity.tags);
39589 // borrowed from Way#isArea()
39590 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
39591 geometryMatches: function geometryMatches(entity, graph) {
39592 if (entity.type === 'node' || entity.type === 'relation') {
39593 return selector.geometry === entity.type;
39594 } else if (entity.type === 'way') {
39595 return this.inferredGeometry === entity.geometry(graph);
39598 // when geometries match and tag matches are present, return a warning...
39599 findIssues: function findIssues(entity, graph, issues) {
39600 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
39601 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
39602 var _message = selector[severity];
39603 issues.push(new validationIssue({
39605 severity: severity,
39606 message: function message() {
39609 entityIds: [entity.id]
39615 this._validationRules.push(rule);
39617 clearRules: function clearRules() {
39618 this._validationRules = [];
39620 // returns validationRules...
39621 validationRules: function validationRules() {
39622 return this._validationRules;
39624 // returns ruleChecks
39625 ruleChecks: function ruleChecks() {
39626 return this._ruleChecks;
39630 var apibase$1 = 'https://nominatim.openstreetmap.org/';
39631 var _inflight = {};
39633 var _nominatimCache;
39635 var serviceNominatim = {
39636 init: function init() {
39638 _nominatimCache = new RBush();
39640 reset: function reset() {
39641 Object.values(_inflight).forEach(function (controller) {
39642 controller.abort();
39645 _nominatimCache = new RBush();
39647 countryCode: function countryCode(location, callback) {
39648 this.reverse(location, function (err, result) {
39650 return callback(err);
39651 } else if (result.address) {
39652 return callback(null, result.address.country_code);
39654 return callback('Unable to geocode', null);
39658 reverse: function reverse(loc, callback) {
39659 var cached = _nominatimCache.search({
39666 if (cached.length > 0) {
39667 if (callback) callback(null, cached[0].data);
39678 var url = apibase$1 + 'reverse?' + utilQsString(params);
39679 if (_inflight[url]) return;
39680 var controller = new AbortController();
39681 _inflight[url] = controller;
39683 signal: controller.signal
39684 }).then(function (result) {
39685 delete _inflight[url];
39687 if (result && result.error) {
39688 throw new Error(result.error);
39691 var extent = geoExtent(loc).padByMeters(200);
39693 _nominatimCache.insert(Object.assign(extent.bbox(), {
39697 if (callback) callback(null, result);
39698 })["catch"](function (err) {
39699 delete _inflight[url];
39700 if (err.name === 'AbortError') return;
39701 if (callback) callback(err.message);
39704 search: function search(val, callback) {
39705 var searchVal = encodeURIComponent(val);
39706 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
39707 if (_inflight[url]) return;
39708 var controller = new AbortController();
39709 _inflight[url] = controller;
39711 signal: controller.signal
39712 }).then(function (result) {
39713 delete _inflight[url];
39715 if (result && result.error) {
39716 throw new Error(result.error);
39719 if (callback) callback(null, result);
39720 })["catch"](function (err) {
39721 delete _inflight[url];
39722 if (err.name === 'AbortError') return;
39723 if (callback) callback(err.message);
39728 var apibase$2 = 'https://openstreetcam.org';
39729 var maxResults$1 = 1000;
39730 var tileZoom$1 = 14;
39731 var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
39732 var dispatch$5 = dispatch('loadedImages');
39733 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
39737 var _oscSelectedImage;
39739 var _loadViewerPromise$1;
39741 function abortRequest$4(controller) {
39742 controller.abort();
39745 function maxPageAtZoom$1(z) {
39746 if (z < 15) return 2;
39747 if (z === 15) return 5;
39748 if (z === 16) return 10;
39749 if (z === 17) return 20;
39750 if (z === 18) return 40;
39751 if (z > 18) return 80;
39754 function loadTiles$1(which, url, projection) {
39755 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
39756 var tiles = tiler$4.getTiles(projection); // abort inflight requests that are no longer needed
39758 var cache = _oscCache[which];
39759 Object.keys(cache.inflight).forEach(function (k) {
39760 var wanted = tiles.find(function (tile) {
39761 return k.indexOf(tile.id + ',') === 0;
39765 abortRequest$4(cache.inflight[k]);
39766 delete cache.inflight[k];
39769 tiles.forEach(function (tile) {
39770 loadNextTilePage$1(which, currZoom, url, tile);
39774 function loadNextTilePage$1(which, currZoom, url, tile) {
39775 var cache = _oscCache[which];
39776 var bbox = tile.extent.bbox();
39777 var maxPages = maxPageAtZoom$1(currZoom);
39778 var nextPage = cache.nextPage[tile.id] || 1;
39779 var params = utilQsString({
39782 // client_id: clientId,
39783 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
39784 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
39786 if (nextPage > maxPages) return;
39787 var id = tile.id + ',' + String(nextPage);
39788 if (cache.loaded[id] || cache.inflight[id]) return;
39789 var controller = new AbortController();
39790 cache.inflight[id] = controller;
39793 signal: controller.signal,
39796 'Content-Type': 'application/x-www-form-urlencoded'
39799 d3_json(url, options).then(function (data) {
39800 cache.loaded[id] = true;
39801 delete cache.inflight[id];
39803 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
39804 throw new Error('No Data');
39807 var features = data.currentPageItems.map(function (item) {
39808 var loc = [+item.lng, +item.lat];
39811 if (which === 'images') {
39816 captured_at: item.shot_date || item.date_added,
39817 captured_by: item.username,
39818 imagePath: item.lth_name,
39819 sequence_id: item.sequence_id,
39820 sequence_index: +item.sequence_index
39821 }; // cache sequence info
39823 var seq = _oscCache.sequences[d.sequence_id];
39830 _oscCache.sequences[d.sequence_id] = seq;
39833 seq.images[d.sequence_index] = d;
39834 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
39845 cache.rtree.load(features);
39847 if (data.currentPageItems.length === maxResults$1) {
39848 // more pages to load
39849 cache.nextPage[tile.id] = nextPage + 1;
39850 loadNextTilePage$1(which, currZoom, url, tile);
39852 cache.nextPage[tile.id] = Infinity; // no more pages to load
39855 if (which === 'images') {
39856 dispatch$5.call('loadedImages');
39858 })["catch"](function () {
39859 cache.loaded[id] = true;
39860 delete cache.inflight[id];
39862 } // partition viewport into higher zoom tiles
39865 function partitionViewport$1(projection) {
39866 var z = geoScaleToZoom(projection.scale());
39867 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
39869 var tiler = utilTiler().zoomExtent([z2, z2]);
39870 return tiler.getTiles(projection).map(function (tile) {
39871 return tile.extent;
39873 } // no more than `limit` results per partition.
39876 function searchLimited$1(limit, projection, rtree) {
39877 limit = limit || 5;
39878 return partitionViewport$1(projection).reduce(function (result, extent) {
39879 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
39882 return found.length ? result.concat(found) : result;
39886 var serviceOpenstreetcam = {
39887 init: function init() {
39892 this.event = utilRebind(this, dispatch$5, 'on');
39894 reset: function reset() {
39896 Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
39904 rtree: new RBush(),
39909 _oscSelectedImage = null;
39911 images: function images(projection) {
39913 return searchLimited$1(limit, projection, _oscCache.images.rtree);
39915 sequences: function sequences(projection) {
39916 var viewport = projection.clipExtent();
39917 var min = [viewport[0][0], viewport[1][1]];
39918 var max = [viewport[1][0], viewport[0][1]];
39919 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
39920 var sequenceKeys = {}; // all sequences for images in viewport
39922 _oscCache.images.rtree.search(bbox).forEach(function (d) {
39923 sequenceKeys[d.data.sequence_id] = true;
39924 }); // make linestrings from those sequences
39927 var lineStrings = [];
39928 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
39929 var seq = _oscCache.sequences[sequenceKey];
39930 var images = seq && seq.images;
39934 type: 'LineString',
39935 coordinates: images.map(function (d) {
39937 }).filter(Boolean),
39939 captured_at: images[0] ? images[0].captured_at : null,
39940 captured_by: images[0] ? images[0].captured_by : null,
39946 return lineStrings;
39948 cachedImage: function cachedImage(imageKey) {
39949 return _oscCache.images.forImageKey[imageKey];
39951 loadImages: function loadImages(projection) {
39952 var url = apibase$2 + '/1.0/list/nearby-photos/';
39953 loadTiles$1('images', url, projection);
39955 ensureViewerLoaded: function ensureViewerLoaded(context) {
39956 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
39958 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
39960 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
39961 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
39962 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
39963 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
39964 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
39965 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
39966 controlsEnter.append('button').on('click.forward', step(1)).html('►');
39967 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
39969 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
39970 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
39973 function zoomPan(d3_event) {
39974 var t = d3_event.transform;
39975 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
39978 function rotate(deg) {
39979 return function () {
39980 if (!_oscSelectedImage) return;
39981 var sequenceKey = _oscSelectedImage.sequence_id;
39982 var sequence = _oscCache.sequences[sequenceKey];
39983 if (!sequence) return;
39984 var r = sequence.rotation || 0;
39986 if (r > 180) r -= 360;
39987 if (r < -180) r += 360;
39988 sequence.rotation = r;
39989 var wrap = context.container().select('.photoviewer .osc-wrapper');
39990 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39991 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
39995 function step(stepBy) {
39996 return function () {
39997 if (!_oscSelectedImage) return;
39998 var sequenceKey = _oscSelectedImage.sequence_id;
39999 var sequence = _oscCache.sequences[sequenceKey];
40000 if (!sequence) return;
40001 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
40002 var nextImage = sequence.images[nextIndex];
40003 if (!nextImage) return;
40004 context.map().centerEase(nextImage.loc);
40005 that.selectImage(context, nextImage.key);
40007 } // don't need any async loading so resolve immediately
40010 _loadViewerPromise$1 = Promise.resolve();
40011 return _loadViewerPromise$1;
40013 showViewer: function showViewer(context) {
40014 var viewer = context.container().select('.photoviewer').classed('hide', false);
40015 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
40018 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
40019 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
40024 hideViewer: function hideViewer(context) {
40025 _oscSelectedImage = null;
40026 this.updateUrlImage(null);
40027 var viewer = context.container().select('.photoviewer');
40028 if (!viewer.empty()) viewer.datum(null);
40029 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
40030 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
40031 return this.setStyles(context, null, true);
40033 selectImage: function selectImage(context, imageKey) {
40034 var d = this.cachedImage(imageKey);
40035 _oscSelectedImage = d;
40036 this.updateUrlImage(imageKey);
40037 var viewer = context.container().select('.photoviewer');
40038 if (!viewer.empty()) viewer.datum(d);
40039 this.setStyles(context, null, true);
40040 context.container().selectAll('.icon-sign').classed('currentView', false);
40041 if (!d) return this;
40042 var wrap = context.container().select('.photoviewer .osc-wrapper');
40043 var imageWrap = wrap.selectAll('.osc-image-wrap');
40044 var attribution = wrap.selectAll('.photo-attribution').html('');
40045 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
40046 imageWrap.selectAll('.osc-image').remove();
40049 var sequence = _oscCache.sequences[d.sequence_id];
40050 var r = sequence && sequence.rotation || 0;
40051 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$2 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
40053 if (d.captured_by) {
40054 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
40055 attribution.append('span').html('|');
40058 if (d.captured_at) {
40059 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
40060 attribution.append('span').html('|');
40063 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');
40068 function localeDateString(s) {
40069 if (!s) return null;
40075 var d = new Date(s);
40076 if (isNaN(d.getTime())) return null;
40077 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
40080 getSelectedImage: function getSelectedImage() {
40081 return _oscSelectedImage;
40083 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
40084 return d && d.sequence_id;
40086 // Updates the currently highlighted sequence and selected bubble.
40087 // Reset is only necessary when interacting with the viewport because
40088 // this implicitly changes the currently selected bubble/sequence
40089 setStyles: function setStyles(context, hovered, reset) {
40091 // reset all layers
40092 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
40093 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
40096 var hoveredImageKey = hovered && hovered.key;
40097 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
40098 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
40099 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
40102 var viewer = context.container().select('.photoviewer');
40103 var selected = viewer.empty() ? undefined : viewer.datum();
40104 var selectedImageKey = selected && selected.key;
40105 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
40106 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
40107 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
40109 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
40111 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
40112 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
40113 return highlightedImageKeys.indexOf(d.key) !== -1;
40114 }).classed('hovered', function (d) {
40115 return d.key === hoveredImageKey;
40116 }).classed('currentView', function (d) {
40117 return d.key === selectedImageKey;
40119 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
40120 return d.properties.key === hoveredSequenceKey;
40121 }).classed('currentView', function (d) {
40122 return d.properties.key === selectedSequenceKey;
40123 }); // update viewfields if needed
40125 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
40127 function viewfieldPath() {
40128 var d = this.parentNode.__data__;
40130 if (d.pano && d.key !== selectedImageKey) {
40131 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
40133 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
40139 updateUrlImage: function updateUrlImage(imageKey) {
40140 if (!window.mocha) {
40141 var hash = utilStringQs(window.location.hash);
40144 hash.photo = 'openstreetcam/' + imageKey;
40149 window.location.replace('#' + utilQsString(hash, true));
40152 cache: function cache() {
40157 var FORCED$f = fails(function () {
40158 return new Date(NaN).toJSON() !== null
40159 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
40162 // `Date.prototype.toJSON` method
40163 // https://tc39.es/ecma262/#sec-date.prototype.tojson
40164 _export({ target: 'Date', proto: true, forced: FORCED$f }, {
40165 // eslint-disable-next-line no-unused-vars -- required for `.length`
40166 toJSON: function toJSON(key) {
40167 var O = toObject(this);
40168 var pv = toPrimitive(O);
40169 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
40173 // `URL.prototype.toJSON` method
40174 // https://url.spec.whatwg.org/#dom-url-tojson
40175 _export({ target: 'URL', proto: true, enumerable: true }, {
40176 toJSON: function toJSON() {
40177 return URL.prototype.toString.call(this);
40182 * Checks if `value` is the
40183 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
40184 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
40190 * @param {*} value The value to check.
40191 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
40197 * _.isObject([1, 2, 3]);
40200 * _.isObject(_.noop);
40203 * _.isObject(null);
40206 function isObject$1(value) {
40207 var type = _typeof(value);
40209 return value != null && (type == 'object' || type == 'function');
40212 /** Detect free variable `global` from Node.js. */
40213 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
40215 /** Detect free variable `self`. */
40217 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
40218 /** Used as a reference to the global object. */
40220 var root$1 = freeGlobal || freeSelf || Function('return this')();
40223 * Gets the timestamp of the number of milliseconds that have elapsed since
40224 * the Unix epoch (1 January 1970 00:00:00 UTC).
40230 * @returns {number} Returns the timestamp.
40233 * _.defer(function(stamp) {
40234 * console.log(_.now() - stamp);
40236 * // => Logs the number of milliseconds it took for the deferred invocation.
40239 var now$1 = function now() {
40240 return root$1.Date.now();
40243 /** Used to match a single whitespace character. */
40244 var reWhitespace = /\s/;
40246 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
40247 * character of `string`.
40250 * @param {string} string The string to inspect.
40251 * @returns {number} Returns the index of the last non-whitespace character.
40254 function trimmedEndIndex(string) {
40255 var index = string.length;
40257 while (index-- && reWhitespace.test(string.charAt(index))) {}
40262 /** Used to match leading whitespace. */
40264 var reTrimStart = /^\s+/;
40266 * The base implementation of `_.trim`.
40269 * @param {string} string The string to trim.
40270 * @returns {string} Returns the trimmed string.
40273 function baseTrim(string) {
40274 return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
40277 /** Built-in value references. */
40279 var _Symbol = root$1.Symbol;
40281 /** Used for built-in method references. */
40283 var objectProto = Object.prototype;
40284 /** Used to check objects for own properties. */
40286 var hasOwnProperty$1 = objectProto.hasOwnProperty;
40288 * Used to resolve the
40289 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40293 var nativeObjectToString = objectProto.toString;
40294 /** Built-in value references. */
40296 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
40298 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
40301 * @param {*} value The value to query.
40302 * @returns {string} Returns the raw `toStringTag`.
40305 function getRawTag(value) {
40306 var isOwn = hasOwnProperty$1.call(value, symToStringTag),
40307 tag = value[symToStringTag];
40310 value[symToStringTag] = undefined;
40311 var unmasked = true;
40314 var result = nativeObjectToString.call(value);
40318 value[symToStringTag] = tag;
40320 delete value[symToStringTag];
40327 /** Used for built-in method references. */
40328 var objectProto$1 = Object.prototype;
40330 * Used to resolve the
40331 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40335 var nativeObjectToString$1 = objectProto$1.toString;
40337 * Converts `value` to a string using `Object.prototype.toString`.
40340 * @param {*} value The value to convert.
40341 * @returns {string} Returns the converted string.
40344 function objectToString$1(value) {
40345 return nativeObjectToString$1.call(value);
40348 /** `Object#toString` result references. */
40350 var nullTag = '[object Null]',
40351 undefinedTag = '[object Undefined]';
40352 /** Built-in value references. */
40354 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
40356 * The base implementation of `getTag` without fallbacks for buggy environments.
40359 * @param {*} value The value to query.
40360 * @returns {string} Returns the `toStringTag`.
40363 function baseGetTag(value) {
40364 if (value == null) {
40365 return value === undefined ? undefinedTag : nullTag;
40368 return symToStringTag$1 && symToStringTag$1 in Object(value) ? getRawTag(value) : objectToString$1(value);
40372 * Checks if `value` is object-like. A value is object-like if it's not `null`
40373 * and has a `typeof` result of "object".
40379 * @param {*} value The value to check.
40380 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
40383 * _.isObjectLike({});
40386 * _.isObjectLike([1, 2, 3]);
40389 * _.isObjectLike(_.noop);
40392 * _.isObjectLike(null);
40395 function isObjectLike(value) {
40396 return value != null && _typeof(value) == 'object';
40399 /** `Object#toString` result references. */
40401 var symbolTag = '[object Symbol]';
40403 * Checks if `value` is classified as a `Symbol` primitive or object.
40409 * @param {*} value The value to check.
40410 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
40413 * _.isSymbol(Symbol.iterator);
40416 * _.isSymbol('abc');
40420 function isSymbol$1(value) {
40421 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
40424 /** Used as references for various `Number` constants. */
40427 /** Used to detect bad signed hexadecimal string values. */
40429 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
40430 /** Used to detect binary string values. */
40432 var reIsBinary = /^0b[01]+$/i;
40433 /** Used to detect octal string values. */
40435 var reIsOctal = /^0o[0-7]+$/i;
40436 /** Built-in method references without a dependency on `root`. */
40438 var freeParseInt = parseInt;
40440 * Converts `value` to a number.
40446 * @param {*} value The value to process.
40447 * @returns {number} Returns the number.
40453 * _.toNumber(Number.MIN_VALUE);
40456 * _.toNumber(Infinity);
40459 * _.toNumber('3.2');
40463 function toNumber$1(value) {
40464 if (typeof value == 'number') {
40468 if (isSymbol$1(value)) {
40472 if (isObject$1(value)) {
40473 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
40474 value = isObject$1(other) ? other + '' : other;
40477 if (typeof value != 'string') {
40478 return value === 0 ? value : +value;
40481 value = baseTrim(value);
40482 var isBinary = reIsBinary.test(value);
40483 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
40486 /** Error message constants. */
40488 var FUNC_ERROR_TEXT = 'Expected a function';
40489 /* Built-in method references for those with the same name as other `lodash` methods. */
40491 var nativeMax = Math.max,
40492 nativeMin = Math.min;
40494 * Creates a debounced function that delays invoking `func` until after `wait`
40495 * milliseconds have elapsed since the last time the debounced function was
40496 * invoked. The debounced function comes with a `cancel` method to cancel
40497 * delayed `func` invocations and a `flush` method to immediately invoke them.
40498 * Provide `options` to indicate whether `func` should be invoked on the
40499 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
40500 * with the last arguments provided to the debounced function. Subsequent
40501 * calls to the debounced function return the result of the last `func`
40504 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40505 * invoked on the trailing edge of the timeout only if the debounced function
40506 * is invoked more than once during the `wait` timeout.
40508 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40509 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40511 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40512 * for details over the differences between `_.debounce` and `_.throttle`.
40517 * @category Function
40518 * @param {Function} func The function to debounce.
40519 * @param {number} [wait=0] The number of milliseconds to delay.
40520 * @param {Object} [options={}] The options object.
40521 * @param {boolean} [options.leading=false]
40522 * Specify invoking on the leading edge of the timeout.
40523 * @param {number} [options.maxWait]
40524 * The maximum time `func` is allowed to be delayed before it's invoked.
40525 * @param {boolean} [options.trailing=true]
40526 * Specify invoking on the trailing edge of the timeout.
40527 * @returns {Function} Returns the new debounced function.
40530 * // Avoid costly calculations while the window size is in flux.
40531 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
40533 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
40534 * jQuery(element).on('click', _.debounce(sendMail, 300, {
40536 * 'trailing': false
40539 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
40540 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
40541 * var source = new EventSource('/stream');
40542 * jQuery(source).on('message', debounced);
40544 * // Cancel the trailing debounced invocation.
40545 * jQuery(window).on('popstate', debounced.cancel);
40548 function debounce(func, wait, options) {
40555 lastInvokeTime = 0,
40560 if (typeof func != 'function') {
40561 throw new TypeError(FUNC_ERROR_TEXT);
40564 wait = toNumber$1(wait) || 0;
40566 if (isObject$1(options)) {
40567 leading = !!options.leading;
40568 maxing = 'maxWait' in options;
40569 maxWait = maxing ? nativeMax(toNumber$1(options.maxWait) || 0, wait) : maxWait;
40570 trailing = 'trailing' in options ? !!options.trailing : trailing;
40573 function invokeFunc(time) {
40574 var args = lastArgs,
40575 thisArg = lastThis;
40576 lastArgs = lastThis = undefined;
40577 lastInvokeTime = time;
40578 result = func.apply(thisArg, args);
40582 function leadingEdge(time) {
40583 // Reset any `maxWait` timer.
40584 lastInvokeTime = time; // Start the timer for the trailing edge.
40586 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
40588 return leading ? invokeFunc(time) : result;
40591 function remainingWait(time) {
40592 var timeSinceLastCall = time - lastCallTime,
40593 timeSinceLastInvoke = time - lastInvokeTime,
40594 timeWaiting = wait - timeSinceLastCall;
40595 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
40598 function shouldInvoke(time) {
40599 var timeSinceLastCall = time - lastCallTime,
40600 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
40601 // trailing edge, the system time has gone backwards and we're treating
40602 // it as the trailing edge, or we've hit the `maxWait` limit.
40604 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
40607 function timerExpired() {
40608 var time = now$1();
40610 if (shouldInvoke(time)) {
40611 return trailingEdge(time);
40612 } // Restart the timer.
40615 timerId = setTimeout(timerExpired, remainingWait(time));
40618 function trailingEdge(time) {
40619 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
40620 // debounced at least once.
40622 if (trailing && lastArgs) {
40623 return invokeFunc(time);
40626 lastArgs = lastThis = undefined;
40630 function cancel() {
40631 if (timerId !== undefined) {
40632 clearTimeout(timerId);
40635 lastInvokeTime = 0;
40636 lastArgs = lastCallTime = lastThis = timerId = undefined;
40640 return timerId === undefined ? result : trailingEdge(now$1());
40643 function debounced() {
40644 var time = now$1(),
40645 isInvoking = shouldInvoke(time);
40646 lastArgs = arguments;
40648 lastCallTime = time;
40651 if (timerId === undefined) {
40652 return leadingEdge(lastCallTime);
40656 // Handle invocations in a tight loop.
40657 clearTimeout(timerId);
40658 timerId = setTimeout(timerExpired, wait);
40659 return invokeFunc(lastCallTime);
40663 if (timerId === undefined) {
40664 timerId = setTimeout(timerExpired, wait);
40670 debounced.cancel = cancel;
40671 debounced.flush = flush;
40675 /** Error message constants. */
40677 var FUNC_ERROR_TEXT$1 = 'Expected a function';
40679 * Creates a throttled function that only invokes `func` at most once per
40680 * every `wait` milliseconds. The throttled function comes with a `cancel`
40681 * method to cancel delayed `func` invocations and a `flush` method to
40682 * immediately invoke them. Provide `options` to indicate whether `func`
40683 * should be invoked on the leading and/or trailing edge of the `wait`
40684 * timeout. The `func` is invoked with the last arguments provided to the
40685 * throttled function. Subsequent calls to the throttled function return the
40686 * result of the last `func` invocation.
40688 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40689 * invoked on the trailing edge of the timeout only if the throttled function
40690 * is invoked more than once during the `wait` timeout.
40692 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40693 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40695 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40696 * for details over the differences between `_.throttle` and `_.debounce`.
40701 * @category Function
40702 * @param {Function} func The function to throttle.
40703 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
40704 * @param {Object} [options={}] The options object.
40705 * @param {boolean} [options.leading=true]
40706 * Specify invoking on the leading edge of the timeout.
40707 * @param {boolean} [options.trailing=true]
40708 * Specify invoking on the trailing edge of the timeout.
40709 * @returns {Function} Returns the new throttled function.
40712 * // Avoid excessively updating the position while scrolling.
40713 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
40715 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
40716 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
40717 * jQuery(element).on('click', throttled);
40719 * // Cancel the trailing throttled invocation.
40720 * jQuery(window).on('popstate', throttled.cancel);
40723 function throttle(func, wait, options) {
40724 var leading = true,
40727 if (typeof func != 'function') {
40728 throw new TypeError(FUNC_ERROR_TEXT$1);
40731 if (isObject$1(options)) {
40732 leading = 'leading' in options ? !!options.leading : leading;
40733 trailing = 'trailing' in options ? !!options.trailing : trailing;
40736 return debounce(func, wait, {
40737 'leading': leading,
40739 'trailing': trailing
40743 var hashes = createCommonjsModule(function (module, exports) {
40745 * jshashes - https://github.com/h2non/jshashes
40746 * Released under the "New BSD" license
40748 * Algorithms specification:
40750 * MD5 - http://www.ietf.org/rfc/rfc1321.txt
40751 * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
40752 * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40753 * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40754 * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40755 * HMAC - http://www.ietf.org/rfc/rfc2104.txt
40760 function utf8Encode(str) {
40767 if (str && str.length) {
40770 while ((i += 1) < l) {
40771 /* Decode utf-16 surrogate pairs */
40772 x = str.charCodeAt(i);
40773 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
40775 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
40776 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
40779 /* Encode output as utf-8 */
40783 output += String.fromCharCode(x);
40784 } else if (x <= 0x7FF) {
40785 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
40786 } else if (x <= 0xFFFF) {
40787 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40788 } else if (x <= 0x1FFFFF) {
40789 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40797 function utf8Decode(str) {
40805 i = ac = c1 = c2 = c3 = 0;
40807 if (str && str.length) {
40812 c1 = str.charCodeAt(i);
40816 arr[ac] = String.fromCharCode(c1);
40818 } else if (c1 > 191 && c1 < 224) {
40819 c2 = str.charCodeAt(i + 1);
40820 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
40823 c2 = str.charCodeAt(i + 1);
40824 c3 = str.charCodeAt(i + 2);
40825 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
40831 return arr.join('');
40834 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
40835 * to work around bugs in some JS interpreters.
40839 function safe_add(x, y) {
40840 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
40841 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
40842 return msw << 16 | lsw & 0xFFFF;
40845 * Bitwise rotate a 32-bit number to the left.
40849 function bit_rol(num, cnt) {
40850 return num << cnt | num >>> 32 - cnt;
40853 * Convert a raw string to a hex string
40857 function rstr2hex(input, hexcase) {
40858 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
40864 for (; i < l; i += 1) {
40865 x = input.charCodeAt(i);
40866 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
40872 * Convert an array of big-endian words to a string
40876 function binb2rstr(input) {
40878 l = input.length * 32,
40881 for (i = 0; i < l; i += 8) {
40882 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
40888 * Convert an array of little-endian words to a string
40892 function binl2rstr(input) {
40894 l = input.length * 32,
40897 for (i = 0; i < l; i += 8) {
40898 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
40904 * Convert a raw string to an array of little-endian words
40905 * Characters >255 have their high-byte silently ignored.
40909 function rstr2binl(input) {
40911 l = input.length * 8,
40912 output = Array(input.length >> 2),
40913 lo = output.length;
40915 for (i = 0; i < lo; i += 1) {
40919 for (i = 0; i < l; i += 8) {
40920 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
40926 * Convert a raw string to an array of big-endian words
40927 * Characters >255 have their high-byte silently ignored.
40931 function rstr2binb(input) {
40933 l = input.length * 8,
40934 output = Array(input.length >> 2),
40935 lo = output.length;
40937 for (i = 0; i < lo; i += 1) {
40941 for (i = 0; i < l; i += 8) {
40942 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
40948 * Convert a raw string to an arbitrary string encoding
40952 function rstr2any(input, encoding) {
40953 var divisor = encoding.length,
40954 remainders = Array(),
40963 /* Convert to an array of 16-bit big-endian values, forming the dividend */
40965 dividend = Array(Math.ceil(input.length / 2));
40966 ld = dividend.length;
40968 for (i = 0; i < ld; i += 1) {
40969 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
40972 * Repeatedly perform a long division. The binary array forms the dividend,
40973 * the length of the encoding is the divisor. Once computed, the quotient
40974 * forms the dividend for the next step. We stop when the dividend is zerHashes.
40975 * All remainders are stored for later use.
40979 while (dividend.length > 0) {
40980 quotient = Array();
40983 for (i = 0; i < dividend.length; i += 1) {
40984 x = (x << 16) + dividend[i];
40985 q = Math.floor(x / divisor);
40988 if (quotient.length > 0 || q > 0) {
40989 quotient[quotient.length] = q;
40993 remainders[remainders.length] = x;
40994 dividend = quotient;
40996 /* Convert the remainders to the output string */
41001 for (i = remainders.length - 1; i >= 0; i--) {
41002 output += encoding.charAt(remainders[i]);
41004 /* Append leading zero equivalents */
41007 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
41009 for (i = output.length; i < full_length; i += 1) {
41010 output = encoding[0] + output;
41016 * Convert a raw string to a base-64 string
41020 function rstr2b64(input, b64pad) {
41021 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
41023 len = input.length,
41027 b64pad = b64pad || '=';
41029 for (i = 0; i < len; i += 3) {
41030 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
41032 for (j = 0; j < 4; j += 1) {
41033 if (i * 8 + j * 6 > input.length * 8) {
41036 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
41046 * @property {String} version
41056 Base64: function Base64() {
41057 // private properties
41058 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
41060 // URL encoding support @todo
41061 utf8 = true; // by default enable UTF-8 support encoding
41062 // public method for encoding
41064 this.encode = function (input) {
41069 len = input.length;
41071 input = utf8 ? utf8Encode(input) : input;
41073 for (i = 0; i < len; i += 3) {
41074 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
41076 for (j = 0; j < 4; j += 1) {
41077 if (i * 8 + j * 6 > len * 8) {
41080 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
41086 }; // public method for decoding
41089 this.decode = function (input) {
41090 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
41109 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
41113 // unpack four hexets into three octets using index points in b64
41114 h1 = tab.indexOf(input.charAt(i += 1));
41115 h2 = tab.indexOf(input.charAt(i += 1));
41116 h3 = tab.indexOf(input.charAt(i += 1));
41117 h4 = tab.indexOf(input.charAt(i += 1));
41118 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
41119 o1 = bits >> 16 & 0xff;
41120 o2 = bits >> 8 & 0xff;
41125 arr[ac] = String.fromCharCode(o1);
41126 } else if (h4 === 64) {
41127 arr[ac] = String.fromCharCode(o1, o2);
41129 arr[ac] = String.fromCharCode(o1, o2, o3);
41131 } while (i < input.length);
41133 dec = arr.join('');
41134 dec = utf8 ? utf8Decode(dec) : dec;
41136 }; // set custom pad string
41139 this.setPad = function (str) {
41142 }; // set custom tab string characters
41145 this.setTab = function (str) {
41150 this.setUTF8 = function (bool) {
41151 if (typeof bool === 'boolean') {
41160 * CRC-32 calculation
41164 * @param {String} str Input String
41167 CRC32: function CRC32(str) {
41174 str = utf8Encode(str);
41175 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('');
41178 for (i = 0, iTop = str.length; i < iTop; i += 1) {
41179 y = (crc ^ str.charCodeAt(i)) & 0xFF;
41180 x = '0x' + table.substr(y * 9, 8);
41181 crc = crc >>> 8 ^ x;
41182 } // always return a positive number (that's what >>> 0 does)
41185 return (crc ^ -1) >>> 0;
41192 * @param {Object} [config]
41194 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
41195 * Digest Algorithm, as defined in RFC 1321.
41196 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
41197 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41198 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
41200 MD5: function MD5(options) {
41202 * Private config properties. You may need to tweak these to be compatible with
41203 * the server-side, but the defaults work in most cases.
41204 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41206 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41207 // hexadecimal output case format. false - lowercase; true - uppercase
41208 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41209 // base-64 pad character. Defaults to '=' for strict RFC compliance
41210 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41211 // privileged (public) methods
41213 this.hex = function (s) {
41214 return rstr2hex(rstr(s), hexcase);
41217 this.b64 = function (s) {
41218 return rstr2b64(rstr(s), b64pad);
41221 this.any = function (s, e) {
41222 return rstr2any(rstr(s), e);
41225 this.raw = function (s) {
41229 this.hex_hmac = function (k, d) {
41230 return rstr2hex(rstr_hmac(k, d), hexcase);
41233 this.b64_hmac = function (k, d) {
41234 return rstr2b64(rstr_hmac(k, d), b64pad);
41237 this.any_hmac = function (k, d, e) {
41238 return rstr2any(rstr_hmac(k, d), e);
41241 * Perform a simple self-test to see if the VM is working
41242 * @return {String} Hexadecimal hash sample
41246 this.vm_test = function () {
41247 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41250 * Enable/disable uppercase hexadecimal returned string
41252 * @return {Object} this
41256 this.setUpperCase = function (a) {
41257 if (typeof a === 'boolean') {
41264 * Defines a base64 pad string
41265 * @param {String} Pad
41266 * @return {Object} this
41270 this.setPad = function (a) {
41271 b64pad = a || b64pad;
41275 * Defines a base64 pad string
41277 * @return {Object} [this]
41281 this.setUTF8 = function (a) {
41282 if (typeof a === 'boolean') {
41287 }; // private methods
41290 * Calculate the MD5 of a raw string
41295 s = utf8 ? utf8Encode(s) : s;
41296 return binl2rstr(binl(rstr2binl(s), s.length * 8));
41299 * Calculate the HMAC-MD5, of a key and some data (raw strings)
41303 function rstr_hmac(key, data) {
41304 var bkey, ipad, opad, hash, i;
41305 key = utf8 ? utf8Encode(key) : key;
41306 data = utf8 ? utf8Encode(data) : data;
41307 bkey = rstr2binl(key);
41309 if (bkey.length > 16) {
41310 bkey = binl(bkey, key.length * 8);
41313 ipad = Array(16), opad = Array(16);
41315 for (i = 0; i < 16; i += 1) {
41316 ipad[i] = bkey[i] ^ 0x36363636;
41317 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41320 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
41321 return binl2rstr(binl(opad.concat(hash), 512 + 128));
41324 * Calculate the MD5 of an array of little-endian words, and a bit length.
41328 function binl(x, len) {
41338 /* append padding */
41340 x[len >> 5] |= 0x80 << len % 32;
41341 x[(len + 64 >>> 9 << 4) + 14] = len;
41343 for (i = 0; i < x.length; i += 16) {
41348 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
41349 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
41350 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
41351 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
41352 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
41353 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
41354 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
41355 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
41356 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
41357 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
41358 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
41359 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
41360 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
41361 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
41362 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
41363 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
41364 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
41365 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
41366 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
41367 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
41368 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
41369 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
41370 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
41371 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
41372 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
41373 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
41374 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
41375 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
41376 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
41377 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
41378 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
41379 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
41380 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
41381 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
41382 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
41383 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
41384 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
41385 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
41386 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
41387 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
41388 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
41389 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
41390 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
41391 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
41392 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
41393 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
41394 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
41395 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
41396 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
41397 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
41398 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
41399 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
41400 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
41401 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
41402 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
41403 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
41404 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
41405 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
41406 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
41407 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
41408 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
41409 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
41410 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
41411 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
41412 a = safe_add(a, olda);
41413 b = safe_add(b, oldb);
41414 c = safe_add(c, oldc);
41415 d = safe_add(d, oldd);
41418 return Array(a, b, c, d);
41421 * These functions implement the four basic operations the algorithm uses.
41425 function md5_cmn(q, a, b, x, s, t) {
41426 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
41429 function md5_ff(a, b, c, d, x, s, t) {
41430 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
41433 function md5_gg(a, b, c, d, x, s, t) {
41434 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
41437 function md5_hh(a, b, c, d, x, s, t) {
41438 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
41441 function md5_ii(a, b, c, d, x, s, t) {
41442 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
41448 * @class Hashes.SHA1
41449 * @param {Object} [config]
41452 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
41453 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
41454 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41455 * See http://pajhome.org.uk/crypt/md5 for details.
41457 SHA1: function SHA1(options) {
41459 * Private config properties. You may need to tweak these to be compatible with
41460 * the server-side, but the defaults work in most cases.
41461 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41463 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41464 // hexadecimal output case format. false - lowercase; true - uppercase
41465 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41466 // base-64 pad character. Defaults to '=' for strict RFC compliance
41467 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41470 this.hex = function (s) {
41471 return rstr2hex(rstr(s), hexcase);
41474 this.b64 = function (s) {
41475 return rstr2b64(rstr(s), b64pad);
41478 this.any = function (s, e) {
41479 return rstr2any(rstr(s), e);
41482 this.raw = function (s) {
41486 this.hex_hmac = function (k, d) {
41487 return rstr2hex(rstr_hmac(k, d));
41490 this.b64_hmac = function (k, d) {
41491 return rstr2b64(rstr_hmac(k, d), b64pad);
41494 this.any_hmac = function (k, d, e) {
41495 return rstr2any(rstr_hmac(k, d), e);
41498 * Perform a simple self-test to see if the VM is working
41499 * @return {String} Hexadecimal hash sample
41504 this.vm_test = function () {
41505 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41508 * @description Enable/disable uppercase hexadecimal returned string
41510 * @return {Object} this
41515 this.setUpperCase = function (a) {
41516 if (typeof a === 'boolean') {
41523 * @description Defines a base64 pad string
41524 * @param {string} Pad
41525 * @return {Object} this
41530 this.setPad = function (a) {
41531 b64pad = a || b64pad;
41535 * @description Defines a base64 pad string
41537 * @return {Object} this
41542 this.setUTF8 = function (a) {
41543 if (typeof a === 'boolean') {
41548 }; // private methods
41551 * Calculate the SHA-512 of a raw string
41556 s = utf8 ? utf8Encode(s) : s;
41557 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41560 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
41564 function rstr_hmac(key, data) {
41565 var bkey, ipad, opad, i, hash;
41566 key = utf8 ? utf8Encode(key) : key;
41567 data = utf8 ? utf8Encode(data) : data;
41568 bkey = rstr2binb(key);
41570 if (bkey.length > 16) {
41571 bkey = binb(bkey, key.length * 8);
41574 ipad = Array(16), opad = Array(16);
41576 for (i = 0; i < 16; i += 1) {
41577 ipad[i] = bkey[i] ^ 0x36363636;
41578 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41581 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41582 return binb2rstr(binb(opad.concat(hash), 512 + 160));
41585 * Calculate the SHA-1 of an array of big-endian words, and a bit length
41589 function binb(x, len) {
41604 /* append padding */
41606 x[len >> 5] |= 0x80 << 24 - len % 32;
41607 x[(len + 64 >> 9 << 4) + 15] = len;
41609 for (i = 0; i < x.length; i += 16) {
41616 for (j = 0; j < 80; j += 1) {
41620 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
41623 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)));
41626 c = bit_rol(b, 30);
41631 a = safe_add(a, olda);
41632 b = safe_add(b, oldb);
41633 c = safe_add(c, oldc);
41634 d = safe_add(d, oldd);
41635 e = safe_add(e, olde);
41638 return Array(a, b, c, d, e);
41641 * Perform the appropriate triplet combination function for the current
41646 function sha1_ft(t, b, c, d) {
41648 return b & c | ~b & d;
41656 return b & c | b & d | c & d;
41662 * Determine the appropriate additive constant for the current iteration
41666 function sha1_kt(t) {
41667 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
41672 * @class Hashes.SHA256
41675 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
41676 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
41677 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41678 * See http://pajhome.org.uk/crypt/md5 for details.
41679 * Also http://anmar.eu.org/projects/jssha2/
41681 SHA256: function SHA256(options) {
41683 * Private properties configuration variables. You may need to tweak these to be compatible with
41684 * the server-side, but the defaults work in most cases.
41685 * @see this.setUpperCase() method
41686 * @see this.setPad() method
41688 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41689 // hexadecimal output case format. false - lowercase; true - uppercase */
41690 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41692 /* base-64 pad character. Default '=' for strict RFC compliance */
41693 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41695 /* enable/disable utf8 encoding */
41697 /* privileged (public) methods */
41699 this.hex = function (s) {
41700 return rstr2hex(rstr(s, utf8));
41703 this.b64 = function (s) {
41704 return rstr2b64(rstr(s, utf8), b64pad);
41707 this.any = function (s, e) {
41708 return rstr2any(rstr(s, utf8), e);
41711 this.raw = function (s) {
41712 return rstr(s, utf8);
41715 this.hex_hmac = function (k, d) {
41716 return rstr2hex(rstr_hmac(k, d));
41719 this.b64_hmac = function (k, d) {
41720 return rstr2b64(rstr_hmac(k, d), b64pad);
41723 this.any_hmac = function (k, d, e) {
41724 return rstr2any(rstr_hmac(k, d), e);
41727 * Perform a simple self-test to see if the VM is working
41728 * @return {String} Hexadecimal hash sample
41733 this.vm_test = function () {
41734 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41737 * Enable/disable uppercase hexadecimal returned string
41739 * @return {Object} this
41744 this.setUpperCase = function (a) {
41745 if (typeof a === 'boolean') {
41752 * @description Defines a base64 pad string
41753 * @param {string} Pad
41754 * @return {Object} this
41759 this.setPad = function (a) {
41760 b64pad = a || b64pad;
41764 * Defines a base64 pad string
41766 * @return {Object} this
41771 this.setUTF8 = function (a) {
41772 if (typeof a === 'boolean') {
41777 }; // private methods
41780 * Calculate the SHA-512 of a raw string
41784 function rstr(s, utf8) {
41785 s = utf8 ? utf8Encode(s) : s;
41786 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41789 * Calculate the HMAC-sha256 of a key and some data (raw strings)
41793 function rstr_hmac(key, data) {
41794 key = utf8 ? utf8Encode(key) : key;
41795 data = utf8 ? utf8Encode(data) : data;
41798 bkey = rstr2binb(key),
41802 if (bkey.length > 16) {
41803 bkey = binb(bkey, key.length * 8);
41806 for (; i < 16; i += 1) {
41807 ipad[i] = bkey[i] ^ 0x36363636;
41808 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41811 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41812 return binb2rstr(binb(opad.concat(hash), 512 + 256));
41815 * Main sha256 function, with its support functions
41819 function sha256_S(X, n) {
41820 return X >>> n | X << 32 - n;
41823 function sha256_R(X, n) {
41827 function sha256_Ch(x, y, z) {
41828 return x & y ^ ~x & z;
41831 function sha256_Maj(x, y, z) {
41832 return x & y ^ x & z ^ y & z;
41835 function sha256_Sigma0256(x) {
41836 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
41839 function sha256_Sigma1256(x) {
41840 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
41843 function sha256_Gamma0256(x) {
41844 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
41847 function sha256_Gamma1256(x) {
41848 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
41851 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];
41853 function binb(m, l) {
41854 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
41855 var W = new Array(64);
41856 var a, b, c, d, e, f, g, h;
41858 /* append padding */
41860 m[l >> 5] |= 0x80 << 24 - l % 32;
41861 m[(l + 64 >> 9 << 4) + 15] = l;
41863 for (i = 0; i < m.length; i += 16) {
41873 for (j = 0; j < 64; j += 1) {
41877 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
41880 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
41881 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
41885 e = safe_add(d, T1);
41889 a = safe_add(T1, T2);
41892 HASH[0] = safe_add(a, HASH[0]);
41893 HASH[1] = safe_add(b, HASH[1]);
41894 HASH[2] = safe_add(c, HASH[2]);
41895 HASH[3] = safe_add(d, HASH[3]);
41896 HASH[4] = safe_add(e, HASH[4]);
41897 HASH[5] = safe_add(f, HASH[5]);
41898 HASH[6] = safe_add(g, HASH[6]);
41899 HASH[7] = safe_add(h, HASH[7]);
41907 * @class Hashes.SHA512
41910 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
41911 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
41912 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41913 * See http://pajhome.org.uk/crypt/md5 for details.
41915 SHA512: function SHA512(options) {
41917 * Private properties configuration variables. You may need to tweak these to be compatible with
41918 * the server-side, but the defaults work in most cases.
41919 * @see this.setUpperCase() method
41920 * @see this.setPad() method
41922 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41924 /* hexadecimal output case format. false - lowercase; true - uppercase */
41925 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41927 /* base-64 pad character. Default '=' for strict RFC compliance */
41928 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41930 /* enable/disable utf8 encoding */
41932 /* privileged (public) methods */
41934 this.hex = function (s) {
41935 return rstr2hex(rstr(s));
41938 this.b64 = function (s) {
41939 return rstr2b64(rstr(s), b64pad);
41942 this.any = function (s, e) {
41943 return rstr2any(rstr(s), e);
41946 this.raw = function (s) {
41950 this.hex_hmac = function (k, d) {
41951 return rstr2hex(rstr_hmac(k, d));
41954 this.b64_hmac = function (k, d) {
41955 return rstr2b64(rstr_hmac(k, d), b64pad);
41958 this.any_hmac = function (k, d, e) {
41959 return rstr2any(rstr_hmac(k, d), e);
41962 * Perform a simple self-test to see if the VM is working
41963 * @return {String} Hexadecimal hash sample
41968 this.vm_test = function () {
41969 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41972 * @description Enable/disable uppercase hexadecimal returned string
41974 * @return {Object} this
41979 this.setUpperCase = function (a) {
41980 if (typeof a === 'boolean') {
41987 * @description Defines a base64 pad string
41988 * @param {string} Pad
41989 * @return {Object} this
41994 this.setPad = function (a) {
41995 b64pad = a || b64pad;
41999 * @description Defines a base64 pad string
42001 * @return {Object} this
42006 this.setUTF8 = function (a) {
42007 if (typeof a === 'boolean') {
42013 /* private methods */
42016 * Calculate the SHA-512 of a raw string
42021 s = utf8 ? utf8Encode(s) : s;
42022 return binb2rstr(binb(rstr2binb(s), s.length * 8));
42025 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
42029 function rstr_hmac(key, data) {
42030 key = utf8 ? utf8Encode(key) : key;
42031 data = utf8 ? utf8Encode(data) : data;
42034 bkey = rstr2binb(key),
42038 if (bkey.length > 32) {
42039 bkey = binb(bkey, key.length * 8);
42042 for (; i < 32; i += 1) {
42043 ipad[i] = bkey[i] ^ 0x36363636;
42044 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42047 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
42048 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
42051 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
42055 function binb(x, len) {
42060 hash = new Array(16),
42061 //Initial hash values
42062 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)],
42063 T1 = new int64(0, 0),
42064 T2 = new int64(0, 0),
42065 a = new int64(0, 0),
42066 b = new int64(0, 0),
42067 c = new int64(0, 0),
42068 d = new int64(0, 0),
42069 e = new int64(0, 0),
42070 f = new int64(0, 0),
42071 g = new int64(0, 0),
42072 h = new int64(0, 0),
42073 //Temporary variables not specified by the document
42074 s0 = new int64(0, 0),
42075 s1 = new int64(0, 0),
42076 Ch = new int64(0, 0),
42077 Maj = new int64(0, 0),
42078 r1 = new int64(0, 0),
42079 r2 = new int64(0, 0),
42080 r3 = new int64(0, 0);
42082 if (sha512_k === undefined) {
42084 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)];
42087 for (i = 0; i < 80; i += 1) {
42088 W[i] = new int64(0, 0);
42089 } // append padding to the source string. The format is described in the FIPS.
42092 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
42093 x[(len + 128 >> 10 << 5) + 31] = len;
42096 for (i = 0; i < l; i += 32) {
42097 //32 dwords is the block size
42098 int64copy(a, H[0]);
42099 int64copy(b, H[1]);
42100 int64copy(c, H[2]);
42101 int64copy(d, H[3]);
42102 int64copy(e, H[4]);
42103 int64copy(f, H[5]);
42104 int64copy(g, H[6]);
42105 int64copy(h, H[7]);
42107 for (j = 0; j < 16; j += 1) {
42108 W[j].h = x[i + 2 * j];
42109 W[j].l = x[i + 2 * j + 1];
42112 for (j = 16; j < 80; j += 1) {
42114 int64rrot(r1, W[j - 2], 19);
42115 int64revrrot(r2, W[j - 2], 29);
42116 int64shr(r3, W[j - 2], 6);
42117 s1.l = r1.l ^ r2.l ^ r3.l;
42118 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
42120 int64rrot(r1, W[j - 15], 1);
42121 int64rrot(r2, W[j - 15], 8);
42122 int64shr(r3, W[j - 15], 7);
42123 s0.l = r1.l ^ r2.l ^ r3.l;
42124 s0.h = r1.h ^ r2.h ^ r3.h;
42125 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
42128 for (j = 0; j < 80; j += 1) {
42130 Ch.l = e.l & f.l ^ ~e.l & g.l;
42131 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
42133 int64rrot(r1, e, 14);
42134 int64rrot(r2, e, 18);
42135 int64revrrot(r3, e, 9);
42136 s1.l = r1.l ^ r2.l ^ r3.l;
42137 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
42139 int64rrot(r1, a, 28);
42140 int64revrrot(r2, a, 2);
42141 int64revrrot(r3, a, 7);
42142 s0.l = r1.l ^ r2.l ^ r3.l;
42143 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
42145 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
42146 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
42147 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
42148 int64add(T2, s0, Maj);
42152 int64add(e, d, T1);
42156 int64add(a, T1, T2);
42159 int64add(H[0], H[0], a);
42160 int64add(H[1], H[1], b);
42161 int64add(H[2], H[2], c);
42162 int64add(H[3], H[3], d);
42163 int64add(H[4], H[4], e);
42164 int64add(H[5], H[5], f);
42165 int64add(H[6], H[6], g);
42166 int64add(H[7], H[7], h);
42167 } //represent the hash as an array of 32-bit dwords
42170 for (i = 0; i < 8; i += 1) {
42171 hash[2 * i] = H[i].h;
42172 hash[2 * i + 1] = H[i].l;
42176 } //A constructor for 64-bit numbers
42179 function int64(h, l) {
42181 this.l = l; //this.toString = int64toString;
42182 } //Copies src into dst, assuming both are 64-bit numbers
42185 function int64copy(dst, src) {
42188 } //Right-rotates a 64-bit number by shift
42189 //Won't handle cases of shift>=32
42190 //The function revrrot() is for that
42193 function int64rrot(dst, x, shift) {
42194 dst.l = x.l >>> shift | x.h << 32 - shift;
42195 dst.h = x.h >>> shift | x.l << 32 - shift;
42196 } //Reverses the dwords of the source and then rotates right by shift.
42197 //This is equivalent to rotation by 32+shift
42200 function int64revrrot(dst, x, shift) {
42201 dst.l = x.h >>> shift | x.l << 32 - shift;
42202 dst.h = x.l >>> shift | x.h << 32 - shift;
42203 } //Bitwise-shifts right a 64-bit number by shift
42204 //Won't handle shift>=32, but it's never needed in SHA512
42207 function int64shr(dst, x, shift) {
42208 dst.l = x.l >>> shift | x.h << 32 - shift;
42209 dst.h = x.h >>> shift;
42210 } //Adds two 64-bit numbers
42211 //Like the original implementation, does not rely on 32-bit operations
42214 function int64add(dst, x, y) {
42215 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
42216 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
42217 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
42218 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
42219 dst.l = w0 & 0xffff | w1 << 16;
42220 dst.h = w2 & 0xffff | w3 << 16;
42221 } //Same, except with 4 addends. Works faster than adding them one by one.
42224 function int64add4(dst, a, b, c, d) {
42225 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
42226 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
42227 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
42228 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
42229 dst.l = w0 & 0xffff | w1 << 16;
42230 dst.h = w2 & 0xffff | w3 << 16;
42231 } //Same, except with 5 addends
42234 function int64add5(dst, a, b, c, d, e) {
42235 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
42236 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
42237 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
42238 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
42239 dst.l = w0 & 0xffff | w1 << 16;
42240 dst.h = w2 & 0xffff | w3 << 16;
42245 * @class Hashes.RMD160
42247 * @param {Object} [config]
42249 * A JavaScript implementation of the RIPEMD-160 Algorithm
42250 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
42251 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
42252 * See http://pajhome.org.uk/crypt/md5 for details.
42253 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
42255 RMD160: function RMD160(options) {
42257 * Private properties configuration variables. You may need to tweak these to be compatible with
42258 * the server-side, but the defaults work in most cases.
42259 * @see this.setUpperCase() method
42260 * @see this.setPad() method
42262 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
42264 /* hexadecimal output case format. false - lowercase; true - uppercase */
42265 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
42267 /* base-64 pad character. Default '=' for strict RFC compliance */
42268 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
42270 /* enable/disable utf8 encoding */
42271 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],
42272 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],
42273 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],
42274 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];
42275 /* privileged (public) methods */
42277 this.hex = function (s) {
42278 return rstr2hex(rstr(s));
42281 this.b64 = function (s) {
42282 return rstr2b64(rstr(s), b64pad);
42285 this.any = function (s, e) {
42286 return rstr2any(rstr(s), e);
42289 this.raw = function (s) {
42293 this.hex_hmac = function (k, d) {
42294 return rstr2hex(rstr_hmac(k, d));
42297 this.b64_hmac = function (k, d) {
42298 return rstr2b64(rstr_hmac(k, d), b64pad);
42301 this.any_hmac = function (k, d, e) {
42302 return rstr2any(rstr_hmac(k, d), e);
42305 * Perform a simple self-test to see if the VM is working
42306 * @return {String} Hexadecimal hash sample
42311 this.vm_test = function () {
42312 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
42315 * @description Enable/disable uppercase hexadecimal returned string
42317 * @return {Object} this
42322 this.setUpperCase = function (a) {
42323 if (typeof a === 'boolean') {
42330 * @description Defines a base64 pad string
42331 * @param {string} Pad
42332 * @return {Object} this
42337 this.setPad = function (a) {
42338 if (typeof a !== 'undefined') {
42345 * @description Defines a base64 pad string
42347 * @return {Object} this
42352 this.setUTF8 = function (a) {
42353 if (typeof a === 'boolean') {
42359 /* private methods */
42362 * Calculate the rmd160 of a raw string
42367 s = utf8 ? utf8Encode(s) : s;
42368 return binl2rstr(binl(rstr2binl(s), s.length * 8));
42371 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
42375 function rstr_hmac(key, data) {
42376 key = utf8 ? utf8Encode(key) : key;
42377 data = utf8 ? utf8Encode(data) : data;
42380 bkey = rstr2binl(key),
42384 if (bkey.length > 16) {
42385 bkey = binl(bkey, key.length * 8);
42388 for (i = 0; i < 16; i += 1) {
42389 ipad[i] = bkey[i] ^ 0x36363636;
42390 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42393 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
42394 return binl2rstr(binl(opad.concat(hash), 512 + 160));
42397 * Convert an array of little-endian words to a string
42401 function binl2rstr(input) {
42404 l = input.length * 32;
42406 for (i = 0; i < l; i += 8) {
42407 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
42413 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
42417 function binl(x, len) {
42437 /* append padding */
42439 x[len >> 5] |= 0x80 << len % 32;
42440 x[(len + 64 >>> 9 << 4) + 14] = len;
42443 for (i = 0; i < l; i += 16) {
42450 for (j = 0; j <= 79; j += 1) {
42451 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
42452 T = safe_add(T, x[i + rmd160_r1[j]]);
42453 T = safe_add(T, rmd160_K1(j));
42454 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
42457 D1 = bit_rol(C1, 10);
42460 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
42461 T = safe_add(T, x[i + rmd160_r2[j]]);
42462 T = safe_add(T, rmd160_K2(j));
42463 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
42466 D2 = bit_rol(C2, 10);
42471 T = safe_add(h1, safe_add(C1, D2));
42472 h1 = safe_add(h2, safe_add(D1, E2));
42473 h2 = safe_add(h3, safe_add(E1, A2));
42474 h3 = safe_add(h4, safe_add(A1, B2));
42475 h4 = safe_add(h0, safe_add(B1, C2));
42479 return [h0, h1, h2, h3, h4];
42480 } // specific algorithm methods
42483 function rmd160_f(j, x, y, z) {
42484 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';
42487 function rmd160_K1(j) {
42488 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';
42491 function rmd160_K2(j) {
42492 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';
42495 }; // exposes Hashes
42497 (function (window, undefined$1) {
42498 var freeExports = false;
42501 freeExports = exports;
42503 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
42504 window = commonjsGlobal;
42508 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
42509 // define as an anonymous module, so, through path mapping, it can be aliased
42510 undefined$1(function () {
42513 } else if (freeExports) {
42514 // in Node.js or RingoJS v0.8.0+
42515 if ( module && module.exports === freeExports) {
42516 module.exports = Hashes;
42517 } // in Narwhal or RingoJS v0.7.0-
42519 freeExports.Hashes = Hashes;
42522 // in a browser or Rhino
42523 window.Hashes = Hashes;
42530 var immutable = extend$2;
42531 var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
42533 function extend$2() {
42536 for (var i = 0; i < arguments.length; i++) {
42537 var source = arguments[i];
42539 for (var key in source) {
42540 if (hasOwnProperty$2.call(source, key)) {
42541 target[key] = source[key];
42549 var sha1 = new hashes.SHA1();
42552 ohauth.qsString = function (obj) {
42553 return Object.keys(obj).sort().map(function (key) {
42554 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
42558 ohauth.stringQs = function (str) {
42559 return str.split('&').filter(function (pair) {
42560 return pair !== '';
42561 }).reduce(function (obj, pair) {
42562 var parts = pair.split('=');
42563 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
42568 ohauth.rawxhr = function (method, url, data, headers, callback) {
42569 var xhr = new XMLHttpRequest(),
42570 twoHundred = /^20\d$/;
42572 xhr.onreadystatechange = function () {
42573 if (4 === xhr.readyState && 0 !== xhr.status) {
42574 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
42578 xhr.onerror = function (e) {
42579 return callback(e, null);
42582 xhr.open(method, url, true);
42584 for (var h in headers) {
42585 xhr.setRequestHeader(h, headers[h]);
42592 ohauth.xhr = function (method, url, auth, data, options, callback) {
42593 var headers = options && options.header || {
42594 'Content-Type': 'application/x-www-form-urlencoded'
42596 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
42597 return ohauth.rawxhr(method, url, data, headers, callback);
42600 ohauth.nonce = function () {
42601 for (var o = ''; o.length < 6;) {
42602 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
42608 ohauth.authHeader = function (obj) {
42609 return Object.keys(obj).sort().map(function (key) {
42610 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
42614 ohauth.timestamp = function () {
42615 return ~~(+new Date() / 1000);
42618 ohauth.percentEncode = function (s) {
42619 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
42622 ohauth.baseString = function (method, url, params) {
42623 if (params.oauth_signature) delete params.oauth_signature;
42624 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
42627 ohauth.signature = function (oauth_secret, token_secret, baseString) {
42628 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
42631 * Takes an options object for configuration (consumer_key,
42632 * consumer_secret, version, signature_method, token, token_secret)
42633 * and returns a function that generates the Authorization header
42636 * The returned function takes these parameters:
42637 * - method: GET/POST/...
42638 * - uri: full URI with protocol, port, path and query string
42639 * - extra_params: any extra parameters (that are passed in the POST data),
42640 * can be an object or a from-urlencoded string.
42642 * Returned function returns full OAuth header with "OAuth" string in it.
42646 ohauth.headerGenerator = function (options) {
42647 options = options || {};
42648 var consumer_key = options.consumer_key || '',
42649 consumer_secret = options.consumer_secret || '',
42650 signature_method = options.signature_method || 'HMAC-SHA1',
42651 version = options.version || '1.0',
42652 token = options.token || '',
42653 token_secret = options.token_secret || '';
42654 return function (method, uri, extra_params) {
42655 method = method.toUpperCase();
42657 if (typeof extra_params === 'string' && extra_params.length > 0) {
42658 extra_params = ohauth.stringQs(extra_params);
42661 var uri_parts = uri.split('?', 2),
42662 base_uri = uri_parts[0];
42663 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
42664 var oauth_params = {
42665 oauth_consumer_key: consumer_key,
42666 oauth_signature_method: signature_method,
42667 oauth_version: version,
42668 oauth_timestamp: ohauth.timestamp(),
42669 oauth_nonce: ohauth.nonce()
42671 if (token) oauth_params.oauth_token = token;
42672 var all_params = immutable({}, oauth_params, query_params, extra_params),
42673 base_str = ohauth.baseString(method, base_uri, all_params);
42674 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
42675 return 'OAuth ' + ohauth.authHeader(oauth_params);
42679 var ohauth_1 = ohauth;
42681 var resolveUrl$1 = createCommonjsModule(function (module, exports) {
42682 // Copyright 2014 Simon Lydell
42683 // X11 (“MIT”) Licensed. (See LICENSE.)
42684 void function (root, factory) {
42686 module.exports = factory();
42688 }(commonjsGlobal, function () {
42689 function resolveUrl()
42692 var numUrls = arguments.length;
42694 if (numUrls === 0) {
42695 throw new Error("resolveUrl requires at least one argument; got none.");
42698 var base = document.createElement("base");
42699 base.href = arguments[0];
42701 if (numUrls === 1) {
42705 var head = document.getElementsByTagName("head")[0];
42706 head.insertBefore(base, head.firstChild);
42707 var a = document.createElement("a");
42710 for (var index = 1; index < numUrls; index++) {
42711 a.href = arguments[index];
42713 base.href = resolved;
42716 head.removeChild(base);
42724 var assign = make_assign();
42725 var create$1 = make_create();
42726 var trim$3 = make_trim();
42727 var Global = typeof window !== 'undefined' ? window : commonjsGlobal;
42738 isFunction: isFunction,
42739 isObject: isObject$2,
42743 function make_assign() {
42744 if (Object.assign) {
42745 return Object.assign;
42747 return function shimAssign(obj, props1, props2, etc) {
42748 for (var i = 1; i < arguments.length; i++) {
42749 each(Object(arguments[i]), function (val, key) {
42759 function make_create() {
42760 if (Object.create) {
42761 return function create(obj, assignProps1, assignProps2, etc) {
42762 var assignArgsList = slice$2(arguments, 1);
42763 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
42766 var F = function F() {}; // eslint-disable-line no-inner-declarations
42769 return function create(obj, assignProps1, assignProps2, etc) {
42770 var assignArgsList = slice$2(arguments, 1);
42772 return assign.apply(this, [new F()].concat(assignArgsList));
42777 function make_trim() {
42778 if (String.prototype.trim) {
42779 return function trim(str) {
42780 return String.prototype.trim.call(str);
42783 return function trim(str) {
42784 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
42789 function bind$1(obj, fn) {
42790 return function () {
42791 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
42795 function slice$2(arr, index) {
42796 return Array.prototype.slice.call(arr, index || 0);
42799 function each(obj, fn) {
42800 pluck(obj, function (val, key) {
42806 function map$1(obj, fn) {
42807 var res = isList(obj) ? [] : {};
42808 pluck(obj, function (v, k) {
42815 function pluck(obj, fn) {
42817 for (var i = 0; i < obj.length; i++) {
42818 if (fn(obj[i], i)) {
42823 for (var key in obj) {
42824 if (obj.hasOwnProperty(key)) {
42825 if (fn(obj[key], key)) {
42833 function isList(val) {
42834 return val != null && typeof val != 'function' && typeof val.length == 'number';
42837 function isFunction(val) {
42838 return val && {}.toString.call(val) === '[object Function]';
42841 function isObject$2(val) {
42842 return val && {}.toString.call(val) === '[object Object]';
42845 var slice$3 = util.slice;
42846 var pluck$1 = util.pluck;
42847 var each$1 = util.each;
42848 var bind$2 = util.bind;
42849 var create$2 = util.create;
42850 var isList$1 = util.isList;
42851 var isFunction$1 = util.isFunction;
42852 var isObject$3 = util.isObject;
42853 var storeEngine = {
42854 createStore: _createStore
42859 // get returns the value of the given key. If that value
42860 // is undefined, it returns optionalDefaultValue instead.
42861 get: function get(key, optionalDefaultValue) {
42862 var data = this.storage.read(this._namespacePrefix + key);
42863 return this._deserialize(data, optionalDefaultValue);
42865 // set will store the given value at key and returns value.
42866 // Calling set with value === undefined is equivalent to calling remove.
42867 set: function set(key, value) {
42868 if (value === undefined) {
42869 return this.remove(key);
42872 this.storage.write(this._namespacePrefix + key, this._serialize(value));
42875 // remove deletes the key and value stored at the given key.
42876 remove: function remove(key) {
42877 this.storage.remove(this._namespacePrefix + key);
42879 // each will call the given callback once for each key-value pair
42881 each: function each(callback) {
42883 this.storage.each(function (val, namespacedKey) {
42884 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
42887 // clearAll will remove all the stored key-value pairs in this store.
42888 clearAll: function clearAll() {
42889 this.storage.clearAll();
42891 // additional functionality that can't live in plugins
42892 // ---------------------------------------------------
42893 // hasNamespace returns true if this store instance has the given namespace.
42894 hasNamespace: function hasNamespace(namespace) {
42895 return this._namespacePrefix == '__storejs_' + namespace + '_';
42897 // createStore creates a store.js instance with the first
42898 // functioning storage in the list of storage candidates,
42899 // and applies the the given mixins to the instance.
42900 createStore: function createStore() {
42901 return _createStore.apply(this, arguments);
42903 addPlugin: function addPlugin(plugin) {
42904 this._addPlugin(plugin);
42906 namespace: function namespace(_namespace) {
42907 return _createStore(this.storage, this.plugins, _namespace);
42912 var _console = typeof console == 'undefined' ? null : console;
42918 var fn = _console.warn ? _console.warn : _console.log;
42919 fn.apply(_console, arguments);
42922 function _createStore(storages, plugins, namespace) {
42927 if (storages && !isList$1(storages)) {
42928 storages = [storages];
42931 if (plugins && !isList$1(plugins)) {
42932 plugins = [plugins];
42935 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
42936 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
42937 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
42939 if (!legalNamespaces.test(namespace)) {
42940 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
42943 var _privateStoreProps = {
42944 _namespacePrefix: namespacePrefix,
42945 _namespaceRegexp: namespaceRegexp,
42946 _testStorage: function _testStorage(storage) {
42948 var testStr = '__storejs__test__';
42949 storage.write(testStr, testStr);
42950 var ok = storage.read(testStr) === testStr;
42951 storage.remove(testStr);
42957 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
42958 var oldFn = this[propName];
42960 this[propName] = function pluginFn() {
42961 var args = slice$3(arguments, 0);
42962 var self = this; // super_fn calls the old function which was overwritten by
42965 function super_fn() {
42970 each$1(arguments, function (arg, i) {
42973 return oldFn.apply(self, args);
42974 } // Give mixing function access to super_fn by prefixing all mixin function
42975 // arguments with super_fn.
42978 var newFnArgs = [super_fn].concat(args);
42979 return pluginFnProp.apply(self, newFnArgs);
42982 _serialize: function _serialize(obj) {
42983 return JSON.stringify(obj);
42985 _deserialize: function _deserialize(strVal, defaultVal) {
42988 } // It is possible that a raw string value has been previously stored
42989 // in a storage without using store.js, meaning it will be a raw
42990 // string value instead of a JSON serialized string. By defaulting
42991 // to the raw string value in case of a JSON parse error, we allow
42992 // for past stored values to be forwards-compatible with store.js
42998 val = JSON.parse(strVal);
43003 return val !== undefined ? val : defaultVal;
43005 _addStorage: function _addStorage(storage) {
43006 if (this.enabled) {
43010 if (this._testStorage(storage)) {
43011 this.storage = storage;
43012 this.enabled = true;
43015 _addPlugin: function _addPlugin(plugin) {
43016 var self = this; // If the plugin is an array, then add all plugins in the array.
43017 // This allows for a plugin to depend on other plugins.
43019 if (isList$1(plugin)) {
43020 each$1(plugin, function (plugin) {
43021 self._addPlugin(plugin);
43024 } // Keep track of all plugins we've seen so far, so that we
43025 // don't add any of them twice.
43028 var seenPlugin = pluck$1(this.plugins, function (seenPlugin) {
43029 return plugin === seenPlugin;
43036 this.plugins.push(plugin); // Check that the plugin is properly formed
43038 if (!isFunction$1(plugin)) {
43039 throw new Error('Plugins must be function values that return objects');
43042 var pluginProperties = plugin.call(this);
43044 if (!isObject$3(pluginProperties)) {
43045 throw new Error('Plugins must return an object of function properties');
43046 } // Add the plugin function properties to this store instance.
43049 each$1(pluginProperties, function (pluginFnProp, propName) {
43050 if (!isFunction$1(pluginFnProp)) {
43051 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
43054 self._assignPluginFnProp(pluginFnProp, propName);
43057 // Put deprecated properties in the private API, so as to not expose it to accidential
43058 // discovery through inspection of the store object.
43059 // Deprecated: addStorage
43060 addStorage: function addStorage(storage) {
43061 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
43063 this._addStorage(storage);
43066 var store = create$2(_privateStoreProps, storeAPI, {
43070 each$1(store, function (prop, propName) {
43071 if (isFunction$1(prop)) {
43072 store.raw[propName] = bind$2(store, prop);
43075 each$1(storages, function (storage) {
43076 store._addStorage(storage);
43078 each$1(plugins, function (plugin) {
43079 store._addPlugin(plugin);
43084 var Global$1 = util.Global;
43085 var localStorage_1 = {
43086 name: 'localStorage',
43094 function localStorage$1() {
43095 return Global$1.localStorage;
43098 function read(key) {
43099 return localStorage$1().getItem(key);
43102 function write(key, data) {
43103 return localStorage$1().setItem(key, data);
43106 function each$2(fn) {
43107 for (var i = localStorage$1().length - 1; i >= 0; i--) {
43108 var key = localStorage$1().key(i);
43109 fn(read(key), key);
43113 function remove$2(key) {
43114 return localStorage$1().removeItem(key);
43117 function clearAll() {
43118 return localStorage$1().clear();
43121 // versions 6 and 7, where no localStorage, etc
43124 var Global$2 = util.Global;
43125 var oldFFGlobalStorage = {
43126 name: 'oldFF-globalStorage',
43131 clearAll: clearAll$1
43133 var globalStorage = Global$2.globalStorage;
43135 function read$1(key) {
43136 return globalStorage[key];
43139 function write$1(key, data) {
43140 globalStorage[key] = data;
43143 function each$3(fn) {
43144 for (var i = globalStorage.length - 1; i >= 0; i--) {
43145 var key = globalStorage.key(i);
43146 fn(globalStorage[key], key);
43150 function remove$3(key) {
43151 return globalStorage.removeItem(key);
43154 function clearAll$1() {
43155 each$3(function (key, _) {
43156 delete globalStorage[key];
43160 // versions 6 and 7, where no localStorage, sessionStorage, etc
43163 var Global$3 = util.Global;
43164 var oldIEUserDataStorage = {
43165 name: 'oldIE-userDataStorage',
43170 clearAll: clearAll$2
43172 var storageName = 'storejs';
43173 var doc = Global$3.document;
43175 var _withStorageEl = _makeIEStorageElFunction();
43177 var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
43179 function write$2(unfixedKey, data) {
43184 var fixedKey = fixKey(unfixedKey);
43186 _withStorageEl(function (storageEl) {
43187 storageEl.setAttribute(fixedKey, data);
43188 storageEl.save(storageName);
43192 function read$2(unfixedKey) {
43197 var fixedKey = fixKey(unfixedKey);
43200 _withStorageEl(function (storageEl) {
43201 res = storageEl.getAttribute(fixedKey);
43207 function each$4(callback) {
43208 _withStorageEl(function (storageEl) {
43209 var attributes = storageEl.XMLDocument.documentElement.attributes;
43211 for (var i = attributes.length - 1; i >= 0; i--) {
43212 var attr = attributes[i];
43213 callback(storageEl.getAttribute(attr.name), attr.name);
43218 function remove$4(unfixedKey) {
43219 var fixedKey = fixKey(unfixedKey);
43221 _withStorageEl(function (storageEl) {
43222 storageEl.removeAttribute(fixedKey);
43223 storageEl.save(storageName);
43227 function clearAll$2() {
43228 _withStorageEl(function (storageEl) {
43229 var attributes = storageEl.XMLDocument.documentElement.attributes;
43230 storageEl.load(storageName);
43232 for (var i = attributes.length - 1; i >= 0; i--) {
43233 storageEl.removeAttribute(attributes[i].name);
43236 storageEl.save(storageName);
43240 // In IE7, keys cannot start with a digit or contain certain chars.
43241 // See https://github.com/marcuswestin/store.js/issues/40
43242 // See https://github.com/marcuswestin/store.js/issues/83
43245 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
43247 function fixKey(key) {
43248 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
43251 function _makeIEStorageElFunction() {
43252 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
43256 var scriptTag = 'script',
43259 storageEl; // Since #userData storage applies only to specific paths, we need to
43260 // somehow link our data to a specific path. We choose /favicon.ico
43261 // as a pretty safe option, since all browsers already make a request to
43262 // this URL anyway and being a 404 will not hurt us here. We wrap an
43263 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
43264 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
43265 // since the iframe access rules appear to allow direct access and
43266 // manipulation of the document element, even for a 404 page. This
43267 // document can be used instead of the current document (which would
43268 // have been limited to the current path) to perform #userData storage.
43271 /* global ActiveXObject */
43272 storageContainer = new ActiveXObject('htmlfile');
43273 storageContainer.open();
43274 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
43275 storageContainer.close();
43276 storageOwner = storageContainer.w.frames[0].document;
43277 storageEl = storageOwner.createElement('div');
43279 // somehow ActiveXObject instantiation failed (perhaps some special
43280 // security settings or otherwse), fall back to per-path storage
43281 storageEl = doc.createElement('div');
43282 storageOwner = doc.body;
43285 return function (storeFunction) {
43286 var args = [].slice.call(arguments, 0);
43287 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
43288 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
43290 storageOwner.appendChild(storageEl);
43291 storageEl.addBehavior('#default#userData');
43292 storageEl.load(storageName);
43293 storeFunction.apply(this, args);
43294 storageOwner.removeChild(storageEl);
43299 // doesn't work but cookies do. This implementation is adopted from
43300 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
43302 var Global$4 = util.Global;
43303 var trim$4 = util.trim;
43304 var cookieStorage = {
43305 name: 'cookieStorage',
43310 clearAll: clearAll$3
43312 var doc$1 = Global$4.document;
43314 function read$3(key) {
43315 if (!key || !_has(key)) {
43319 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
43320 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"));
43323 function each$5(callback) {
43324 var cookies = doc$1.cookie.split(/; ?/g);
43326 for (var i = cookies.length - 1; i >= 0; i--) {
43327 if (!trim$4(cookies[i])) {
43331 var kvp = cookies[i].split('=');
43332 var key = unescape(kvp[0]);
43333 var val = unescape(kvp[1]);
43334 callback(val, key);
43338 function write$3(key, data) {
43343 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
43346 function remove$5(key) {
43347 if (!key || !_has(key)) {
43351 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
43354 function clearAll$3() {
43355 each$5(function (_, key) {
43360 function _has(key) {
43361 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc$1.cookie);
43364 var Global$5 = util.Global;
43365 var sessionStorage_1 = {
43366 name: 'sessionStorage',
43371 clearAll: clearAll$4
43374 function sessionStorage() {
43375 return Global$5.sessionStorage;
43378 function read$4(key) {
43379 return sessionStorage().getItem(key);
43382 function write$4(key, data) {
43383 return sessionStorage().setItem(key, data);
43386 function each$6(fn) {
43387 for (var i = sessionStorage().length - 1; i >= 0; i--) {
43388 var key = sessionStorage().key(i);
43389 fn(read$4(key), key);
43393 function remove$6(key) {
43394 return sessionStorage().removeItem(key);
43397 function clearAll$4() {
43398 return sessionStorage().clear();
43401 // memoryStorage is a useful last fallback to ensure that the store
43402 // is functions (meaning store.get(), store.set(), etc will all function).
43403 // However, stored values will not persist when the browser navigates to
43404 // a new page or reloads the current page.
43405 var memoryStorage_1 = {
43406 name: 'memoryStorage',
43411 clearAll: clearAll$5
43413 var memoryStorage = {};
43415 function read$5(key) {
43416 return memoryStorage[key];
43419 function write$5(key, data) {
43420 memoryStorage[key] = data;
43423 function each$7(callback) {
43424 for (var key in memoryStorage) {
43425 if (memoryStorage.hasOwnProperty(key)) {
43426 callback(memoryStorage[key], key);
43431 function remove$7(key) {
43432 delete memoryStorage[key];
43435 function clearAll$5(key) {
43436 memoryStorage = {};
43439 var all = [// Listed in order of usage preference
43440 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
43442 /* eslint-disable */
43446 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
43447 // See http://www.JSON.org/js.html
43448 // This code should be minified before deployment.
43449 // See http://javascript.crockford.com/jsmin.html
43450 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
43452 // This file creates a global JSON object containing two methods: stringify
43453 // and parse. This file provides the ES5 JSON capability to ES3 systems.
43454 // If a project might run on IE8 or earlier, then this file should be included.
43455 // This file does nothing on ES5 systems.
43456 // JSON.stringify(value, replacer, space)
43457 // value any JavaScript value, usually an object or array.
43458 // replacer an optional parameter that determines how object
43459 // values are stringified for objects. It can be a
43460 // function or an array of strings.
43461 // space an optional parameter that specifies the indentation
43462 // of nested structures. If it is omitted, the text will
43463 // be packed without extra whitespace. If it is a number,
43464 // it will specify the number of spaces to indent at each
43465 // level. If it is a string (such as "\t" or " "),
43466 // it contains the characters used to indent at each level.
43467 // This method produces a JSON text from a JavaScript value.
43468 // When an object value is found, if the object contains a toJSON
43469 // method, its toJSON method will be called and the result will be
43470 // stringified. A toJSON method does not serialize: it returns the
43471 // value represented by the name/value pair that should be serialized,
43472 // or undefined if nothing should be serialized. The toJSON method
43473 // will be passed the key associated with the value, and this will be
43474 // bound to the value.
43475 // For example, this would serialize Dates as ISO strings.
43476 // Date.prototype.toJSON = function (key) {
43478 // // Format integers to have at least two digits.
43483 // return this.getUTCFullYear() + "-" +
43484 // f(this.getUTCMonth() + 1) + "-" +
43485 // f(this.getUTCDate()) + "T" +
43486 // f(this.getUTCHours()) + ":" +
43487 // f(this.getUTCMinutes()) + ":" +
43488 // f(this.getUTCSeconds()) + "Z";
43490 // You can provide an optional replacer method. It will be passed the
43491 // key and value of each member, with this bound to the containing
43492 // object. The value that is returned from your method will be
43493 // serialized. If your method returns undefined, then the member will
43494 // be excluded from the serialization.
43495 // If the replacer parameter is an array of strings, then it will be
43496 // used to select the members to be serialized. It filters the results
43497 // such that only members with keys listed in the replacer array are
43499 // Values that do not have JSON representations, such as undefined or
43500 // functions, will not be serialized. Such values in objects will be
43501 // dropped; in arrays they will be replaced with null. You can use
43502 // a replacer function to replace those with JSON values.
43503 // JSON.stringify(undefined) returns undefined.
43504 // The optional space parameter produces a stringification of the
43505 // value that is filled with line breaks and indentation to make it
43507 // If the space parameter is a non-empty string, then that string will
43508 // be used for indentation. If the space parameter is a number, then
43509 // the indentation will be that many spaces.
43511 // text = JSON.stringify(["e", {pluribus: "unum"}]);
43512 // // text is '["e",{"pluribus":"unum"}]'
43513 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
43514 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
43515 // text = JSON.stringify([new Date()], function (key, value) {
43516 // return this[key] instanceof Date
43517 // ? "Date(" + this[key] + ")"
43520 // // text is '["Date(---current time---)"]'
43521 // JSON.parse(text, reviver)
43522 // This method parses a JSON text to produce an object or array.
43523 // It can throw a SyntaxError exception.
43524 // The optional reviver parameter is a function that can filter and
43525 // transform the results. It receives each of the keys and values,
43526 // and its return value is used instead of the original value.
43527 // If it returns what it received, then the structure is not modified.
43528 // If it returns undefined then the member is deleted.
43530 // // Parse the text. Values that look like ISO date strings will
43531 // // be converted to Date objects.
43532 // myData = JSON.parse(text, function (key, value) {
43534 // if (typeof value === "string") {
43536 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
43538 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
43544 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
43546 // if (typeof value === "string" &&
43547 // value.slice(0, 5) === "Date(" &&
43548 // value.slice(-1) === ")") {
43549 // d = new Date(value.slice(5, -1));
43556 // This is a reference implementation. You are free to copy, modify, or
43564 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
43565 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
43566 lastIndex, length, parse, prototype, push, replace, slice, stringify,
43567 test, toJSON, toString, valueOf
43569 // Create a JSON object only if one does not already exist. We create the
43570 // methods in a closure to avoid creating global variables.
43571 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
43577 var rx_one = /^[\],:{}\s]*$/;
43578 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
43579 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
43580 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
43581 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43582 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43585 // Format integers to have at least two digits.
43586 return n < 10 ? "0" + n : n;
43589 function this_value() {
43590 return this.valueOf();
43593 if (typeof Date.prototype.toJSON !== "function") {
43594 Date.prototype.toJSON = function () {
43595 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;
43598 Boolean.prototype.toJSON = this_value;
43599 Number.prototype.toJSON = this_value;
43600 String.prototype.toJSON = this_value;
43608 function quote(string) {
43609 // If the string contains no control characters, no quote characters, and no
43610 // backslash characters, then we can safely slap some quotes around it.
43611 // Otherwise we must also replace the offending characters with safe escape
43613 rx_escapable.lastIndex = 0;
43614 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
43616 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43617 }) + "\"" : "\"" + string + "\"";
43620 function str(key, holder) {
43621 // Produce a string from holder[key].
43622 var i; // The loop counter.
43624 var k; // The member key.
43626 var v; // The member value.
43631 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
43633 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
43634 value = value.toJSON(key);
43635 } // If we were called with a replacer function, then call the replacer to
43636 // obtain a replacement value.
43639 if (typeof rep === "function") {
43640 value = rep.call(holder, key, value);
43641 } // What happens next depends on the value's type.
43644 switch (_typeof(value)) {
43646 return quote(value);
43649 // JSON numbers must be finite. Encode non-finite numbers as null.
43650 return isFinite(value) ? String(value) : "null";
43654 // If the value is a boolean or null, convert it to a string. Note:
43655 // typeof null does not produce "null". The case is included here in
43656 // the remote chance that this gets fixed someday.
43657 return String(value);
43658 // If the type is "object", we might be dealing with an object or an array or
43662 // Due to a specification blunder in ECMAScript, typeof null is "object",
43663 // so watch out for that case.
43666 } // Make an array to hold the partial results of stringifying this object value.
43670 partial = []; // Is the value an array?
43672 if (Object.prototype.toString.apply(value) === "[object Array]") {
43673 // The value is an array. Stringify every element. Use null as a placeholder
43674 // for non-JSON values.
43675 length = value.length;
43677 for (i = 0; i < length; i += 1) {
43678 partial[i] = str(i, value) || "null";
43679 } // Join all of the elements together, separated with commas, and wrap them in
43683 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
43686 } // If the replacer is an array, use it to select the members to be stringified.
43689 if (rep && _typeof(rep) === "object") {
43690 length = rep.length;
43692 for (i = 0; i < length; i += 1) {
43693 if (typeof rep[i] === "string") {
43698 partial.push(quote(k) + (gap ? ": " : ":") + v);
43703 // Otherwise, iterate through all of the keys in the object.
43705 if (Object.prototype.hasOwnProperty.call(value, k)) {
43709 partial.push(quote(k) + (gap ? ": " : ":") + v);
43713 } // Join all of the member texts together, separated with commas,
43714 // and wrap them in braces.
43717 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
43721 } // If the JSON object does not yet have a stringify method, give it one.
43724 if (typeof JSON.stringify !== "function") {
43726 // table of character substitutions
43736 JSON.stringify = function (value, replacer, space) {
43737 // The stringify method takes a value and an optional replacer, and an optional
43738 // space parameter, and returns a JSON text. The replacer can be a function
43739 // that can replace values, or an array of strings that will select the keys.
43740 // A default replacer method can be provided. Use of the space parameter can
43741 // produce text that is more easily readable.
43744 indent = ""; // If the space parameter is a number, make an indent string containing that
43747 if (typeof space === "number") {
43748 for (i = 0; i < space; i += 1) {
43750 } // If the space parameter is a string, it will be used as the indent string.
43752 } else if (typeof space === "string") {
43754 } // If there is a replacer, it must be a function or an array.
43755 // Otherwise, throw an error.
43760 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
43761 throw new Error("JSON.stringify");
43762 } // Make a fake root object containing our value under the key of "".
43763 // Return the result of stringifying the value.
43770 } // If the JSON object does not yet have a parse method, give it one.
43773 if (typeof JSON.parse !== "function") {
43774 JSON.parse = function (text, reviver) {
43775 // The parse method takes a text and an optional reviver function, and returns
43776 // a JavaScript value if the text is a valid JSON text.
43779 function walk(holder, key) {
43780 // The walk method is used to recursively walk the resulting structure so
43781 // that modifications can be made.
43784 var value = holder[key];
43786 if (value && _typeof(value) === "object") {
43788 if (Object.prototype.hasOwnProperty.call(value, k)) {
43789 v = walk(value, k);
43791 if (v !== undefined) {
43800 return reviver.call(holder, key, value);
43801 } // Parsing happens in four stages. In the first stage, we replace certain
43802 // Unicode characters with escape sequences. JavaScript handles many characters
43803 // incorrectly, either silently deleting them, or treating them as line endings.
43806 text = String(text);
43807 rx_dangerous.lastIndex = 0;
43809 if (rx_dangerous.test(text)) {
43810 text = text.replace(rx_dangerous, function (a) {
43811 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43813 } // In the second stage, we run the text against regular expressions that look
43814 // for non-JSON patterns. We are especially concerned with "()" and "new"
43815 // because they can cause invocation, and "=" because it can cause mutation.
43816 // But just to be safe, we want to reject all unexpected forms.
43817 // We split the second stage into 4 regexp operations in order to work around
43818 // crippling inefficiencies in IE's and Safari's regexp engines. First we
43819 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
43820 // replace all simple value tokens with "]" characters. Third, we delete all
43821 // open brackets that follow a colon or comma or that begin the text. Finally,
43822 // we look to see that the remaining characters are only whitespace or "]" or
43823 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
43826 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
43827 // In the third stage we use the eval function to compile the text into a
43828 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
43829 // in JavaScript: it can begin a block or an object literal. We wrap the text
43830 // in parens to eliminate the ambiguity.
43831 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
43832 // each name/value pair to a reviver function for possible transformation.
43834 return typeof reviver === "function" ? walk({
43837 } // If the text is not JSON parseable, then a SyntaxError is thrown.
43840 throw new SyntaxError("JSON.parse");
43845 var json2 = json2Plugin;
43847 function json2Plugin() {
43851 var plugins = [json2];
43852 var store_legacy = storeEngine.createStore(all, plugins);
43855 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
43856 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
43857 // does not support custom headers, which this uses everywhere.
43860 var osmAuth = function osmAuth(o) {
43861 var oauth = {}; // authenticated users will also have a request token secret, but it's
43862 // not used in transactions with the server
43864 oauth.authenticated = function () {
43865 return !!(token('oauth_token') && token('oauth_token_secret'));
43868 oauth.logout = function () {
43869 token('oauth_token', '');
43870 token('oauth_token_secret', '');
43871 token('oauth_request_token_secret', '');
43873 }; // TODO: detect lack of click event
43876 oauth.authenticate = function (callback) {
43877 if (oauth.authenticated()) return callback();
43878 oauth.logout(); // ## Getting a request token
43880 var params = timenonce(getAuth(o)),
43881 url = o.url + '/oauth/request_token';
43882 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
43884 if (!o.singlepage) {
43885 // Create a 600x550 popup window in the center of the screen
43888 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
43889 return x.join('=');
43891 popup = window.open('about:blank', 'oauth_window', settings);
43892 oauth.popupWindow = popup;
43895 var error = new Error('Popup was blocked');
43896 error.status = 'popup-blocked';
43899 } // Request a request token. When this is complete, the popup
43900 // window is redirected to OSM's authorization page.
43903 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
43906 function reqTokenDone(err, xhr) {
43908 if (err) return callback(err);
43909 var resp = ohauth_1.stringQs(xhr.response);
43910 token('oauth_request_token_secret', resp.oauth_token_secret);
43911 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
43912 oauth_token: resp.oauth_token,
43913 oauth_callback: resolveUrl$1(o.landing)
43916 if (o.singlepage) {
43917 location.href = authorize_url;
43919 popup.location = authorize_url;
43921 } // Called by a function in a landing page, in the popup window. The
43922 // window closes itself.
43925 window.authComplete = function (token) {
43926 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
43927 get_access_token(oauth_token.oauth_token);
43928 delete window.authComplete;
43929 }; // ## Getting an request token
43931 // At this point we have an `oauth_token`, brought in from a function
43932 // call on a landing page popup.
43935 function get_access_token(oauth_token) {
43936 var url = o.url + '/oauth/access_token',
43937 params = timenonce(getAuth(o)),
43938 request_token_secret = token('oauth_request_token_secret');
43939 params.oauth_token = oauth_token;
43940 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43942 // The final token required for authentication. At this point
43943 // we have a `request token secret`
43945 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43949 function accessTokenDone(err, xhr) {
43951 if (err) return callback(err);
43952 var access_token = ohauth_1.stringQs(xhr.response);
43953 token('oauth_token', access_token.oauth_token);
43954 token('oauth_token_secret', access_token.oauth_token_secret);
43955 callback(null, oauth);
43959 oauth.bringPopupWindowToFront = function () {
43960 var brougtPopupToFront = false;
43963 // This may cause a cross-origin error:
43964 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
43965 if (oauth.popupWindow && !oauth.popupWindow.closed) {
43966 oauth.popupWindow.focus();
43967 brougtPopupToFront = true;
43969 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
43972 return brougtPopupToFront;
43975 oauth.bootstrapToken = function (oauth_token, callback) {
43976 // ## Getting an request token
43977 // At this point we have an `oauth_token`, brought in from a function
43978 // call on a landing page popup.
43979 function get_access_token(oauth_token) {
43980 var url = o.url + '/oauth/access_token',
43981 params = timenonce(getAuth(o)),
43982 request_token_secret = token('oauth_request_token_secret');
43983 params.oauth_token = oauth_token;
43984 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43985 // The final token required for authentication. At this point
43986 // we have a `request token secret`
43988 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43992 function accessTokenDone(err, xhr) {
43994 if (err) return callback(err);
43995 var access_token = ohauth_1.stringQs(xhr.response);
43996 token('oauth_token', access_token.oauth_token);
43997 token('oauth_token_secret', access_token.oauth_token_secret);
43998 callback(null, oauth);
44001 get_access_token(oauth_token);
44004 // A single XMLHttpRequest wrapper that does authenticated calls if the
44005 // user has logged in.
44008 oauth.xhr = function (options, callback) {
44009 if (!oauth.authenticated()) {
44011 return oauth.authenticate(run);
44013 callback('not authenticated', null);
44021 var params = timenonce(getAuth(o)),
44022 oauth_token_secret = token('oauth_token_secret'),
44023 url = options.prefix !== false ? o.url + options.path : options.path,
44024 url_parts = url.replace(/#.*$/, '').split('?', 2),
44025 base_url = url_parts[0],
44026 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
44028 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
44029 params = immutable(params, ohauth_1.stringQs(options.content));
44032 params.oauth_token = token('oauth_token');
44033 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))));
44034 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
44037 function done(err, xhr) {
44038 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
44040 }; // pre-authorize this object, if we can just get a token and token_secret
44044 oauth.preauth = function (c) {
44046 if (c.oauth_token) token('oauth_token', c.oauth_token);
44047 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
44051 oauth.options = function (_) {
44052 if (!arguments.length) return o;
44054 o.url = o.url || 'https://www.openstreetmap.org';
44055 o.landing = o.landing || 'land.html';
44056 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
44057 // by default, no-ops
44059 o.loading = o.loading || function () {};
44061 o.done = o.done || function () {};
44063 return oauth.preauth(o);
44064 }; // 'stamp' an authentication object from `getAuth()`
44065 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
44069 function timenonce(o) {
44070 o.oauth_timestamp = ohauth_1.timestamp();
44071 o.oauth_nonce = ohauth_1.nonce();
44073 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
44074 // can be used with multiple APIs and the keys in `localStorage`
44080 if (store_legacy.enabled) {
44081 token = function token(x, y) {
44082 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
44087 token = function token(x, y) {
44088 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
44090 } // Get an authentication object. If you just add and remove properties
44091 // from a single object, you'll need to use `delete` to make sure that
44092 // it doesn't contain undesired properties for authentication
44095 function getAuth(o) {
44097 oauth_consumer_key: o.oauth_consumer_key,
44098 oauth_signature_method: 'HMAC-SHA1'
44100 } // potentially pre-authorize
44107 var JXON = new function () {
44108 var sValueProp = 'keyValue',
44109 sAttributesProp = 'keyAttributes',
44112 /* you can customize these values */
44115 rIsBool = /^(?:true|false)$/i;
44117 function parseText(sValue) {
44118 if (rIsNull.test(sValue)) {
44122 if (rIsBool.test(sValue)) {
44123 return sValue.toLowerCase() === 'true';
44126 if (isFinite(sValue)) {
44127 return parseFloat(sValue);
44130 if (isFinite(Date.parse(sValue))) {
44131 return new Date(sValue);
44137 function EmptyTree() {}
44139 EmptyTree.prototype.toString = function () {
44143 EmptyTree.prototype.valueOf = function () {
44147 function objectify(vValue) {
44148 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
44151 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
44152 var nLevelStart = aCache.length,
44153 bChildren = oParentNode.hasChildNodes(),
44154 bAttributes = oParentNode.hasAttributes(),
44155 bHighVerb = Boolean(nVerb & 2);
44159 sCollectedTxt = '',
44160 vResult = bHighVerb ? {} :
44161 /* put here the default value for empty nodes: */
44165 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
44166 oNode = oParentNode.childNodes.item(nItem);
44168 if (oNode.nodeType === 4) {
44169 sCollectedTxt += oNode.nodeValue;
44171 /* nodeType is 'CDATASection' (4) */
44172 else if (oNode.nodeType === 3) {
44173 sCollectedTxt += oNode.nodeValue.trim();
44175 /* nodeType is 'Text' (3) */
44176 else if (oNode.nodeType === 1 && !oNode.prefix) {
44177 aCache.push(oNode);
44179 /* nodeType is 'Element' (1) */
44184 var nLevelEnd = aCache.length,
44185 vBuiltVal = parseText(sCollectedTxt);
44187 if (!bHighVerb && (bChildren || bAttributes)) {
44188 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
44191 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
44192 sProp = aCache[nElId].nodeName.toLowerCase();
44193 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
44195 if (vResult.hasOwnProperty(sProp)) {
44196 if (vResult[sProp].constructor !== Array) {
44197 vResult[sProp] = [vResult[sProp]];
44200 vResult[sProp].push(vContent);
44202 vResult[sProp] = vContent;
44208 var nAttrLen = oParentNode.attributes.length,
44209 sAPrefix = bNesteAttr ? '' : sAttrPref,
44210 oAttrParent = bNesteAttr ? {} : vResult;
44212 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
44213 oAttrib = oParentNode.attributes.item(nAttrib);
44214 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
44219 Object.freeze(oAttrParent);
44222 vResult[sAttributesProp] = oAttrParent;
44223 nLength -= nAttrLen - 1;
44227 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
44228 vResult[sValueProp] = vBuiltVal;
44229 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
44230 vResult = vBuiltVal;
44233 if (bFreeze && (bHighVerb || nLength > 0)) {
44234 Object.freeze(vResult);
44237 aCache.length = nLevelStart;
44241 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
44242 var vValue, oChild;
44244 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
44245 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
44246 /* verbosity level is 0 */
44247 } else if (oParentObj.constructor === Date) {
44248 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
44251 for (var sName in oParentObj) {
44252 vValue = oParentObj[sName];
44254 if (isFinite(sName) || vValue instanceof Function) {
44257 /* verbosity level is 0 */
44260 if (sName === sValueProp) {
44261 if (vValue !== null && vValue !== true) {
44262 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
44264 } else if (sName === sAttributesProp) {
44265 /* verbosity level is 3 */
44266 for (var sAttrib in vValue) {
44267 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
44269 } else if (sName.charAt(0) === sAttrPref) {
44270 oParentEl.setAttribute(sName.slice(1), vValue);
44271 } else if (vValue.constructor === Array) {
44272 for (var nItem = 0; nItem < vValue.length; nItem++) {
44273 oChild = oXMLDoc.createElement(sName);
44274 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
44275 oParentEl.appendChild(oChild);
44278 oChild = oXMLDoc.createElement(sName);
44280 if (vValue instanceof Object) {
44281 loadObjTree(oXMLDoc, oChild, vValue);
44282 } else if (vValue !== null && vValue !== true) {
44283 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
44286 oParentEl.appendChild(oChild);
44291 this.build = function (oXMLParent, nVerbosity
44298 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
44299 /* put here the default verbosity level: */
44302 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
44305 this.unbuild = function (oObjTree) {
44306 var oNewDoc = document.implementation.createDocument('', '', null);
44307 loadObjTree(oNewDoc, oNewDoc, oObjTree);
44311 this.stringify = function (oObjTree) {
44312 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
44314 }(); // var myObject = JXON.build(doc);
44315 // we got our javascript object! try: alert(JSON.stringify(myObject));
44316 // var newDoc = JXON.unbuild(myObject);
44317 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
44319 var tiler$5 = utilTiler();
44320 var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
44321 var urlroot = 'https://www.openstreetmap.org';
44322 var oauth = osmAuth({
44324 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
44325 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
44326 loading: authLoading,
44328 }); // hardcode default block of Google Maps
44330 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
44352 var _cachedApiStatus;
44354 var _changeset = {};
44356 var _deferred = new Set();
44358 var _connectionID = 1;
44359 var _tileZoom$3 = 16;
44360 var _noteZoom = 12;
44362 var _rateLimitError;
44364 var _userChangesets;
44368 var _off; // set a default but also load this from the API status
44371 var _maxWayNodes = 2000;
44373 function authLoading() {
44374 dispatch$6.call('authLoading');
44377 function authDone() {
44378 dispatch$6.call('authDone');
44381 function abortRequest$5(controllerOrXHR) {
44382 if (controllerOrXHR) {
44383 controllerOrXHR.abort();
44387 function hasInflightRequests(cache) {
44388 return Object.keys(cache.inflight).length;
44391 function abortUnwantedRequests$3(cache, visibleTiles) {
44392 Object.keys(cache.inflight).forEach(function (k) {
44393 if (cache.toLoad[k]) return;
44394 if (visibleTiles.find(function (tile) {
44395 return k === tile.id;
44397 abortRequest$5(cache.inflight[k]);
44398 delete cache.inflight[k];
44402 function getLoc(attrs) {
44403 var lon = attrs.lon && attrs.lon.value;
44404 var lat = attrs.lat && attrs.lat.value;
44405 return [parseFloat(lon), parseFloat(lat)];
44408 function getNodes(obj) {
44409 var elems = obj.getElementsByTagName('nd');
44410 var nodes = new Array(elems.length);
44412 for (var i = 0, l = elems.length; i < l; i++) {
44413 nodes[i] = 'n' + elems[i].attributes.ref.value;
44419 function getNodesJSON(obj) {
44420 var elems = obj.nodes;
44421 var nodes = new Array(elems.length);
44423 for (var i = 0, l = elems.length; i < l; i++) {
44424 nodes[i] = 'n' + elems[i];
44430 function getTags(obj) {
44431 var elems = obj.getElementsByTagName('tag');
44434 for (var i = 0, l = elems.length; i < l; i++) {
44435 var attrs = elems[i].attributes;
44436 tags[attrs.k.value] = attrs.v.value;
44442 function getMembers(obj) {
44443 var elems = obj.getElementsByTagName('member');
44444 var members = new Array(elems.length);
44446 for (var i = 0, l = elems.length; i < l; i++) {
44447 var attrs = elems[i].attributes;
44449 id: attrs.type.value[0] + attrs.ref.value,
44450 type: attrs.type.value,
44451 role: attrs.role.value
44458 function getMembersJSON(obj) {
44459 var elems = obj.members;
44460 var members = new Array(elems.length);
44462 for (var i = 0, l = elems.length; i < l; i++) {
44463 var attrs = elems[i];
44465 id: attrs.type[0] + attrs.ref,
44474 function getVisible(attrs) {
44475 return !attrs.visible || attrs.visible.value !== 'false';
44478 function parseComments(comments) {
44479 var parsedComments = []; // for each comment
44481 for (var i = 0; i < comments.length; i++) {
44482 var comment = comments[i];
44484 if (comment.nodeName === 'comment') {
44485 var childNodes = comment.childNodes;
44486 var parsedComment = {};
44488 for (var j = 0; j < childNodes.length; j++) {
44489 var node = childNodes[j];
44490 var nodeName = node.nodeName;
44491 if (nodeName === '#text') continue;
44492 parsedComment[nodeName] = node.textContent;
44494 if (nodeName === 'uid') {
44495 var uid = node.textContent;
44497 if (uid && !_userCache.user[uid]) {
44498 _userCache.toLoad[uid] = true;
44503 if (parsedComment) {
44504 parsedComments.push(parsedComment);
44509 return parsedComments;
44512 function encodeNoteRtree(note) {
44522 var jsonparsers = {
44523 node: function nodeData(obj, uid) {
44524 return new osmNode({
44526 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44527 version: obj.version && obj.version.toString(),
44528 changeset: obj.changeset && obj.changeset.toString(),
44529 timestamp: obj.timestamp,
44531 uid: obj.uid && obj.uid.toString(),
44532 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
44536 way: function wayData(obj, uid) {
44537 return new osmWay({
44539 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44540 version: obj.version && obj.version.toString(),
44541 changeset: obj.changeset && obj.changeset.toString(),
44542 timestamp: obj.timestamp,
44544 uid: obj.uid && obj.uid.toString(),
44546 nodes: getNodesJSON(obj)
44549 relation: function relationData(obj, uid) {
44550 return new osmRelation({
44552 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44553 version: obj.version && obj.version.toString(),
44554 changeset: obj.changeset && obj.changeset.toString(),
44555 timestamp: obj.timestamp,
44557 uid: obj.uid && obj.uid.toString(),
44559 members: getMembersJSON(obj)
44564 function parseJSON(payload, callback, options) {
44565 options = Object.assign({
44571 message: 'No JSON',
44576 var json = payload;
44577 if (_typeof(json) !== 'object') json = JSON.parse(payload);
44578 if (!json.elements) return callback({
44579 message: 'No JSON',
44582 var children = json.elements;
44583 var handle = window.requestIdleCallback(function () {
44587 for (var i = 0; i < children.length; i++) {
44588 result = parseChild(children[i]);
44589 if (result) results.push(result);
44592 callback(null, results);
44595 _deferred.add(handle);
44597 function parseChild(child) {
44598 var parser = jsonparsers[child.type];
44599 if (!parser) return null;
44601 uid = osmEntity.id.fromOSM(child.type, child.id);
44603 if (options.skipSeen) {
44604 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44606 _tileCache.seen[uid] = true;
44609 return parser(child, uid);
44614 node: function nodeData(obj, uid) {
44615 var attrs = obj.attributes;
44616 return new osmNode({
44618 visible: getVisible(attrs),
44619 version: attrs.version.value,
44620 changeset: attrs.changeset && attrs.changeset.value,
44621 timestamp: attrs.timestamp && attrs.timestamp.value,
44622 user: attrs.user && attrs.user.value,
44623 uid: attrs.uid && attrs.uid.value,
44624 loc: getLoc(attrs),
44628 way: function wayData(obj, uid) {
44629 var attrs = obj.attributes;
44630 return new osmWay({
44632 visible: getVisible(attrs),
44633 version: attrs.version.value,
44634 changeset: attrs.changeset && attrs.changeset.value,
44635 timestamp: attrs.timestamp && attrs.timestamp.value,
44636 user: attrs.user && attrs.user.value,
44637 uid: attrs.uid && attrs.uid.value,
44638 tags: getTags(obj),
44639 nodes: getNodes(obj)
44642 relation: function relationData(obj, uid) {
44643 var attrs = obj.attributes;
44644 return new osmRelation({
44646 visible: getVisible(attrs),
44647 version: attrs.version.value,
44648 changeset: attrs.changeset && attrs.changeset.value,
44649 timestamp: attrs.timestamp && attrs.timestamp.value,
44650 user: attrs.user && attrs.user.value,
44651 uid: attrs.uid && attrs.uid.value,
44652 tags: getTags(obj),
44653 members: getMembers(obj)
44656 note: function parseNote(obj, uid) {
44657 var attrs = obj.attributes;
44658 var childNodes = obj.childNodes;
44661 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
44663 var coincident = false;
44664 var epsilon = 0.00001;
44668 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
44671 var bbox = geoExtent(props.loc).bbox();
44672 coincident = _noteCache.rtree.search(bbox).length;
44673 } while (coincident); // parse note contents
44676 for (var i = 0; i < childNodes.length; i++) {
44677 var node = childNodes[i];
44678 var nodeName = node.nodeName;
44679 if (nodeName === '#text') continue; // if the element is comments, parse the comments
44681 if (nodeName === 'comments') {
44682 props[nodeName] = parseComments(node.childNodes);
44684 props[nodeName] = node.textContent;
44688 var note = new osmNote(props);
44689 var item = encodeNoteRtree(note);
44690 _noteCache.note[note.id] = note;
44692 _noteCache.rtree.insert(item);
44696 user: function parseUser(obj, uid) {
44697 var attrs = obj.attributes;
44700 display_name: attrs.display_name && attrs.display_name.value,
44701 account_created: attrs.account_created && attrs.account_created.value,
44702 changesets_count: '0',
44705 var img = obj.getElementsByTagName('img');
44707 if (img && img[0] && img[0].getAttribute('href')) {
44708 user.image_url = img[0].getAttribute('href');
44711 var changesets = obj.getElementsByTagName('changesets');
44713 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
44714 user.changesets_count = changesets[0].getAttribute('count');
44717 var blocks = obj.getElementsByTagName('blocks');
44719 if (blocks && blocks[0]) {
44720 var received = blocks[0].getElementsByTagName('received');
44722 if (received && received[0] && received[0].getAttribute('active')) {
44723 user.active_blocks = received[0].getAttribute('active');
44727 _userCache.user[uid] = user;
44728 delete _userCache.toLoad[uid];
44733 function parseXML(xml, callback, options) {
44734 options = Object.assign({
44738 if (!xml || !xml.childNodes) {
44745 var root = xml.childNodes[0];
44746 var children = root.childNodes;
44747 var handle = window.requestIdleCallback(function () {
44751 for (var i = 0; i < children.length; i++) {
44752 result = parseChild(children[i]);
44753 if (result) results.push(result);
44756 callback(null, results);
44759 _deferred.add(handle);
44761 function parseChild(child) {
44762 var parser = parsers[child.nodeName];
44763 if (!parser) return null;
44766 if (child.nodeName === 'user') {
44767 uid = child.attributes.id.value;
44769 if (options.skipSeen && _userCache.user[uid]) {
44770 delete _userCache.toLoad[uid];
44773 } else if (child.nodeName === 'note') {
44774 uid = child.getElementsByTagName('id')[0].textContent;
44776 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
44778 if (options.skipSeen) {
44779 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44781 _tileCache.seen[uid] = true;
44785 return parser(child, uid);
44787 } // replace or remove note from rtree
44790 function updateRtree$3(item, replace) {
44791 _noteCache.rtree.remove(item, function isEql(a, b) {
44792 return a.data.id === b.data.id;
44796 _noteCache.rtree.insert(item);
44800 function wrapcb(thisArg, callback, cid) {
44801 return function (err, result) {
44803 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
44804 if (err.status === 400 || err.status === 401 || err.status === 403) {
44808 return callback.call(thisArg, err);
44809 } else if (thisArg.getConnectionId() !== cid) {
44810 return callback.call(thisArg, {
44811 message: 'Connection Switched',
44815 return callback.call(thisArg, err, result);
44821 init: function init() {
44822 utilRebind(this, dispatch$6, 'on');
44824 reset: function reset() {
44825 Array.from(_deferred).forEach(function (handle) {
44826 window.cancelIdleCallback(handle);
44828 _deferred["delete"](handle);
44831 _userChangesets = undefined;
44832 _userDetails = undefined;
44833 _rateLimitError = undefined;
44834 Object.values(_tileCache.inflight).forEach(abortRequest$5);
44835 Object.values(_noteCache.inflight).forEach(abortRequest$5);
44836 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
44837 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
44858 _cachedApiStatus = undefined;
44862 getConnectionId: function getConnectionId() {
44863 return _connectionID;
44865 changesetURL: function changesetURL(changesetID) {
44866 return urlroot + '/changeset/' + changesetID;
44868 changesetsURL: function changesetsURL(center, zoom) {
44869 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
44870 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
44872 entityURL: function entityURL(entity) {
44873 return urlroot + '/' + entity.type + '/' + entity.osmId();
44875 historyURL: function historyURL(entity) {
44876 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
44878 userURL: function userURL(username) {
44879 return urlroot + '/user/' + username;
44881 noteURL: function noteURL(note) {
44882 return urlroot + '/note/' + note.id;
44884 noteReportURL: function noteReportURL(note) {
44885 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
44887 // Generic method to load data from the OSM API
44888 // Can handle either auth or unauth calls.
44889 loadFromAPI: function loadFromAPI(path, callback, options) {
44890 options = Object.assign({
44894 var cid = _connectionID;
44896 function done(err, payload) {
44897 if (that.getConnectionId() !== cid) {
44898 if (callback) callback({
44899 message: 'Connection Switched',
44905 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
44906 // Logout and retry the request..
44908 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
44910 that.loadFromAPI(path, callback, options); // else, no retry..
44912 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
44913 // Set the rateLimitError flag and trigger a warning..
44914 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
44915 _rateLimitError = err;
44916 dispatch$6.call('change');
44917 that.reloadApiStatus();
44918 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
44919 // If the response's error state doesn't match the status,
44920 // it's likely we lost or gained the connection so reload the status
44921 that.reloadApiStatus();
44926 return callback(err);
44928 if (path.indexOf('.json') !== -1) {
44929 return parseJSON(payload, callback, options);
44931 return parseXML(payload, callback, options);
44938 if (this.authenticated()) {
44944 var url = urlroot + path;
44945 var controller = new AbortController();
44947 signal: controller.signal
44948 }).then(function (data) {
44950 })["catch"](function (err) {
44951 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
44952 // but we can't access the response itself
44953 // https://github.com/d3/d3-fetch/issues/27
44955 var match = err.message.match(/^\d{3}/);
44960 statusText: err.message
44969 // Load a single entity by id (ways and relations use the `/full` call)
44970 // GET /api/0.6/node/#id
44971 // GET /api/0.6/[way|relation]/#id/full
44972 loadEntity: function loadEntity(id, callback) {
44973 var type = osmEntity.id.type(id);
44974 var osmID = osmEntity.id.toOSM(id);
44978 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
44979 if (callback) callback(err, {
44984 // Load a single entity with a specific version
44985 // GET /api/0.6/[node|way|relation]/#id/#version
44986 loadEntityVersion: function loadEntityVersion(id, version, callback) {
44987 var type = osmEntity.id.type(id);
44988 var osmID = osmEntity.id.toOSM(id);
44992 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
44993 if (callback) callback(err, {
44998 // Load multiple entities in chunks
44999 // (note: callback may be called multiple times)
45000 // Unlike `loadEntity`, child nodes and members are not fetched
45001 // GET /api/0.6/[nodes|ways|relations]?#parameters
45002 loadMultiple: function loadMultiple(ids, callback) {
45004 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
45005 Object.keys(groups).forEach(function (k) {
45006 var type = k + 's'; // nodes, ways, relations
45008 var osmIDs = groups[k].map(function (id) {
45009 return osmEntity.id.toOSM(id);
45014 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
45015 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
45016 if (callback) callback(err, {
45023 // Create, upload, and close a changeset
45024 // PUT /api/0.6/changeset/create
45025 // POST /api/0.6/changeset/#id/upload
45026 // PUT /api/0.6/changeset/#id/close
45027 putChangeset: function putChangeset(changeset, changes, callback) {
45028 var cid = _connectionID;
45030 if (_changeset.inflight) {
45032 message: 'Changeset already inflight',
45035 } else if (_changeset.open) {
45036 // reuse existing open changeset..
45037 return createdChangeset.call(this, null, _changeset.open);
45039 // Open a new changeset..
45042 path: '/api/0.6/changeset/create',
45045 'Content-Type': 'text/xml'
45048 content: JXON.stringify(changeset.asJXON())
45050 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
45053 function createdChangeset(err, changesetID) {
45054 _changeset.inflight = null;
45057 return callback(err, changeset);
45060 _changeset.open = changesetID;
45061 changeset = changeset.update({
45063 }); // Upload the changeset..
45067 path: '/api/0.6/changeset/' + changesetID + '/upload',
45070 'Content-Type': 'text/xml'
45073 content: JXON.stringify(changeset.osmChangeJXON(changes))
45075 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
45078 function uploadedChangeset(err) {
45079 _changeset.inflight = null;
45080 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
45081 // Add delay to allow for postgres replication #1646 #2678
45083 window.setTimeout(function () {
45084 callback(null, changeset);
45086 _changeset.open = null; // At this point, we don't really care if the connection was switched..
45087 // Only try to close the changeset if we're still talking to the same server.
45089 if (this.getConnectionId() === cid) {
45090 // Still attempt to close changeset, but ignore response because #2667
45093 path: '/api/0.6/changeset/' + changeset.id + '/close',
45096 'Content-Type': 'text/xml'
45105 // Load multiple users in chunks
45106 // (note: callback may be called multiple times)
45107 // GET /api/0.6/users?users=#id1,#id2,...,#idn
45108 loadUsers: function loadUsers(uids, callback) {
45111 utilArrayUniq(uids).forEach(function (uid) {
45112 if (_userCache.user[uid]) {
45113 delete _userCache.toLoad[uid];
45114 cached.push(_userCache.user[uid]);
45120 if (cached.length || !this.authenticated()) {
45121 callback(undefined, cached);
45122 if (!this.authenticated()) return; // require auth
45125 utilArrayChunk(toLoad, 150).forEach(function (arr) {
45128 path: '/api/0.6/users?users=' + arr.join()
45129 }, wrapcb(this, done, _connectionID));
45132 function done(err, xml) {
45134 return callback(err);
45140 return parseXML(xml, function (err, results) {
45142 return callback(err);
45144 return callback(undefined, results);
45149 // Load a given user by id
45150 // GET /api/0.6/user/#id
45151 loadUser: function loadUser(uid, callback) {
45152 if (_userCache.user[uid] || !this.authenticated()) {
45154 delete _userCache.toLoad[uid];
45155 return callback(undefined, _userCache.user[uid]);
45160 path: '/api/0.6/user/' + uid
45161 }, wrapcb(this, done, _connectionID));
45163 function done(err, xml) {
45165 return callback(err);
45171 return parseXML(xml, function (err, results) {
45173 return callback(err);
45175 return callback(undefined, results[0]);
45180 // Load the details of the logged-in user
45181 // GET /api/0.6/user/details
45182 userDetails: function userDetails(callback) {
45183 if (_userDetails) {
45185 return callback(undefined, _userDetails);
45190 path: '/api/0.6/user/details'
45191 }, wrapcb(this, done, _connectionID));
45193 function done(err, xml) {
45195 return callback(err);
45201 return parseXML(xml, function (err, results) {
45203 return callback(err);
45205 _userDetails = results[0];
45206 return callback(undefined, _userDetails);
45211 // Load previous changesets for the logged in user
45212 // GET /api/0.6/changesets?user=#id
45213 userChangesets: function userChangesets(callback) {
45214 if (_userChangesets) {
45216 return callback(undefined, _userChangesets);
45219 this.userDetails(wrapcb(this, gotDetails, _connectionID));
45221 function gotDetails(err, user) {
45223 return callback(err);
45228 path: '/api/0.6/changesets?user=' + user.id
45229 }, wrapcb(this, done, _connectionID));
45232 function done(err, xml) {
45234 return callback(err);
45237 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
45239 tags: getTags(changeset)
45241 }).filter(function (changeset) {
45242 var comment = changeset.tags.comment;
45243 return comment && comment !== '';
45245 return callback(undefined, _userChangesets);
45248 // Fetch the status of the OSM API
45249 // GET /api/capabilities
45250 status: function status(callback) {
45251 var url = urlroot + '/api/capabilities';
45252 var errback = wrapcb(this, done, _connectionID);
45253 d3_xml(url).then(function (data) {
45254 errback(null, data);
45255 })["catch"](function (err) {
45256 errback(err.message);
45259 function done(err, xml) {
45261 // the status is null if no response could be retrieved
45262 return callback(err, null);
45263 } // update blocklists
45266 var elements = xml.getElementsByTagName('blacklist');
45269 for (var i = 0; i < elements.length; i++) {
45270 var regexString = elements[i].getAttribute('regex'); // needs unencode?
45274 var regex = new RegExp(regexString);
45275 regexes.push(regex);
45282 if (regexes.length) {
45283 _imageryBlocklists = regexes;
45286 if (_rateLimitError) {
45287 return callback(_rateLimitError, 'rateLimited');
45289 var waynodes = xml.getElementsByTagName('waynodes');
45290 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
45291 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
45292 var apiStatus = xml.getElementsByTagName('status');
45293 var val = apiStatus[0].getAttribute('api');
45294 return callback(undefined, val);
45298 // Calls `status` and dispatches an `apiStatusChange` event if the returned
45299 // status differs from the cached status.
45300 reloadApiStatus: function reloadApiStatus() {
45301 // throttle to avoid unnecessary API calls
45302 if (!this.throttledReloadApiStatus) {
45304 this.throttledReloadApiStatus = throttle(function () {
45305 that.status(function (err, status) {
45306 if (status !== _cachedApiStatus) {
45307 _cachedApiStatus = status;
45308 dispatch$6.call('apiStatusChange', that, err, status);
45314 this.throttledReloadApiStatus();
45316 // Returns the maximum number of nodes a single way can have
45317 maxWayNodes: function maxWayNodes() {
45318 return _maxWayNodes;
45320 // Load data (entities) from the API in tiles
45321 // GET /api/0.6/map?bbox=
45322 loadTiles: function loadTiles(projection, callback) {
45323 if (_off) return; // determine the needed tiles to cover the view
45325 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
45327 var hadRequests = hasInflightRequests(_tileCache);
45328 abortUnwantedRequests$3(_tileCache, tiles);
45330 if (hadRequests && !hasInflightRequests(_tileCache)) {
45331 dispatch$6.call('loaded'); // stop the spinner
45332 } // issue new requests..
45335 tiles.forEach(function (tile) {
45336 this.loadTile(tile, callback);
45339 // Load a single data tile
45340 // GET /api/0.6/map?bbox=
45341 loadTile: function loadTile(tile, callback) {
45343 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45345 if (!hasInflightRequests(_tileCache)) {
45346 dispatch$6.call('loading'); // start the spinner
45349 var path = '/api/0.6/map.json?bbox=';
45353 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
45355 function tileCallback(err, parsed) {
45356 delete _tileCache.inflight[tile.id];
45359 delete _tileCache.toLoad[tile.id];
45360 _tileCache.loaded[tile.id] = true;
45361 var bbox = tile.extent.bbox();
45364 _tileCache.rtree.insert(bbox);
45368 callback(err, Object.assign({
45373 if (!hasInflightRequests(_tileCache)) {
45374 dispatch$6.call('loaded'); // stop the spinner
45378 isDataLoaded: function isDataLoaded(loc) {
45385 return _tileCache.rtree.collides(bbox);
45387 // load the tile that covers the given `loc`
45388 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
45389 // Back off if the toLoad queue is filling up.. re #6417
45390 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
45391 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
45392 if (Object.keys(_tileCache.toLoad).length > 50) return;
45393 var k = geoZoomToScale(_tileZoom$3 + 1);
45394 var offset = geoRawMercator().scale(k)(loc);
45395 var projection = geoRawMercator().transform({
45400 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
45401 tiles.forEach(function (tile) {
45402 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45403 _tileCache.toLoad[tile.id] = true;
45404 this.loadTile(tile, callback);
45407 // Load notes from the API in tiles
45408 // GET /api/0.6/notes?bbox=
45409 loadNotes: function loadNotes(projection, noteOptions) {
45410 noteOptions = Object.assign({
45416 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
45418 var throttleLoadUsers = throttle(function () {
45419 var uids = Object.keys(_userCache.toLoad);
45420 if (!uids.length) return;
45421 that.loadUsers(uids, function () {}); // eagerly load user details
45422 }, 750); // determine the needed tiles to cover the view
45425 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
45427 abortUnwantedRequests$3(_noteCache, tiles); // issue new requests..
45429 tiles.forEach(function (tile) {
45430 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
45434 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
45435 delete _noteCache.inflight[tile.id];
45438 _noteCache.loaded[tile.id] = true;
45441 throttleLoadUsers();
45442 dispatch$6.call('loadedNotes');
45447 // POST /api/0.6/notes?params
45448 postNoteCreate: function postNoteCreate(note, callback) {
45449 if (!this.authenticated()) {
45451 message: 'Not Authenticated',
45456 if (_noteCache.inflightPost[note.id]) {
45458 message: 'Note update already inflight',
45463 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
45465 var comment = note.newComment;
45467 if (note.newCategory && note.newCategory !== 'None') {
45468 comment += ' #' + note.newCategory;
45471 var path = '/api/0.6/notes?' + utilQsString({
45476 _noteCache.inflightPost[note.id] = oauth.xhr({
45479 }, wrapcb(this, done, _connectionID));
45481 function done(err, xml) {
45482 delete _noteCache.inflightPost[note.id];
45485 return callback(err);
45486 } // we get the updated note back, remove from caches and reparse..
45489 this.removeNote(note);
45493 return parseXML(xml, function (err, results) {
45495 return callback(err);
45497 return callback(undefined, results[0]);
45503 // POST /api/0.6/notes/#id/comment?text=comment
45504 // POST /api/0.6/notes/#id/close?text=comment
45505 // POST /api/0.6/notes/#id/reopen?text=comment
45506 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
45507 if (!this.authenticated()) {
45509 message: 'Not Authenticated',
45514 if (_noteCache.inflightPost[note.id]) {
45516 message: 'Note update already inflight',
45523 if (note.status !== 'closed' && newStatus === 'closed') {
45525 } else if (note.status !== 'open' && newStatus === 'open') {
45528 action = 'comment';
45529 if (!note.newComment) return; // when commenting, comment required
45532 var path = '/api/0.6/notes/' + note.id + '/' + action;
45534 if (note.newComment) {
45535 path += '?' + utilQsString({
45536 text: note.newComment
45540 _noteCache.inflightPost[note.id] = oauth.xhr({
45543 }, wrapcb(this, done, _connectionID));
45545 function done(err, xml) {
45546 delete _noteCache.inflightPost[note.id];
45549 return callback(err);
45550 } // we get the updated note back, remove from caches and reparse..
45553 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
45555 if (action === 'close') {
45556 _noteCache.closed[note.id] = true;
45557 } else if (action === 'reopen') {
45558 delete _noteCache.closed[note.id];
45564 return parseXML(xml, function (err, results) {
45566 return callback(err);
45568 return callback(undefined, results[0]);
45573 "switch": function _switch(options) {
45574 urlroot = options.urlroot;
45575 oauth.options(Object.assign({
45577 loading: authLoading,
45581 this.userChangesets(function () {}); // eagerly load user details/changesets
45583 dispatch$6.call('change');
45586 toggle: function toggle(val) {
45590 isChangesetInflight: function isChangesetInflight() {
45591 return !!_changeset.inflight;
45593 // get/set cached data
45594 // This is used to save/restore the state when entering/exiting the walkthrough
45595 // Also used for testing purposes.
45596 caches: function caches(obj) {
45597 function cloneCache(source) {
45599 Object.keys(source).forEach(function (k) {
45600 if (k === 'rtree') {
45601 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
45602 } else if (k === 'note') {
45604 Object.keys(source.note).forEach(function (id) {
45605 target.note[id] = osmNote(source.note[id]); // copy notes
45608 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
45614 if (!arguments.length) {
45616 tile: cloneCache(_tileCache),
45617 note: cloneCache(_noteCache),
45618 user: cloneCache(_userCache)
45620 } // access caches directly for testing (e.g., loading notes rtree)
45623 if (obj === 'get') {
45632 _tileCache = obj.tile;
45633 _tileCache.inflight = {};
45637 _noteCache = obj.note;
45638 _noteCache.inflight = {};
45639 _noteCache.inflightPost = {};
45643 _userCache = obj.user;
45648 logout: function logout() {
45649 _userChangesets = undefined;
45650 _userDetails = undefined;
45652 dispatch$6.call('change');
45655 authenticated: function authenticated() {
45656 return oauth.authenticated();
45658 authenticate: function authenticate(callback) {
45660 var cid = _connectionID;
45661 _userChangesets = undefined;
45662 _userDetails = undefined;
45664 function done(err, res) {
45666 if (callback) callback(err);
45670 if (that.getConnectionId() !== cid) {
45671 if (callback) callback({
45672 message: 'Connection Switched',
45678 _rateLimitError = undefined;
45679 dispatch$6.call('change');
45680 if (callback) callback(err, res);
45681 that.userChangesets(function () {}); // eagerly load user details/changesets
45684 return oauth.authenticate(done);
45686 imageryBlocklists: function imageryBlocklists() {
45687 return _imageryBlocklists;
45689 tileZoom: function tileZoom(val) {
45690 if (!arguments.length) return _tileZoom$3;
45694 // get all cached notes covering the viewport
45695 notes: function notes(projection) {
45696 var viewport = projection.clipExtent();
45697 var min = [viewport[0][0], viewport[1][1]];
45698 var max = [viewport[1][0], viewport[0][1]];
45699 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
45700 return _noteCache.rtree.search(bbox).map(function (d) {
45704 // get a single note from the cache
45705 getNote: function getNote(id) {
45706 return _noteCache.note[id];
45708 // remove a single note from the cache
45709 removeNote: function removeNote(note) {
45710 if (!(note instanceof osmNote) || !note.id) return;
45711 delete _noteCache.note[note.id];
45712 updateRtree$3(encodeNoteRtree(note), false); // false = remove
45714 // replace a single note in the cache
45715 replaceNote: function replaceNote(note) {
45716 if (!(note instanceof osmNote) || !note.id) return;
45717 _noteCache.note[note.id] = note;
45718 updateRtree$3(encodeNoteRtree(note), true); // true = replace
45722 // Get an array of note IDs closed during this session.
45723 // Used to populate `closed:note` changeset tag
45724 getClosedIDs: function getClosedIDs() {
45725 return Object.keys(_noteCache.closed).sort();
45729 var _apibase = 'https://wiki.openstreetmap.org/w/api.php';
45730 var _inflight$1 = {};
45731 var _wikibaseCache = {};
45736 var debouncedRequest = debounce(request, 500, {
45740 function request(url, callback) {
45741 if (_inflight$1[url]) return;
45742 var controller = new AbortController();
45743 _inflight$1[url] = controller;
45745 signal: controller.signal
45746 }).then(function (result) {
45747 delete _inflight$1[url];
45748 if (callback) callback(null, result);
45749 })["catch"](function (err) {
45750 delete _inflight$1[url];
45751 if (err.name === 'AbortError') return;
45752 if (callback) callback(err.message);
45756 var serviceOsmWikibase = {
45757 init: function init() {
45759 _wikibaseCache = {};
45762 reset: function reset() {
45763 Object.values(_inflight$1).forEach(function (controller) {
45764 controller.abort();
45770 * Get the best value for the property, or undefined if not found
45771 * @param entity object from wikibase
45772 * @param property string e.g. 'P4' for image
45773 * @param langCode string e.g. 'fr' for French
45775 claimToValue: function claimToValue(entity, property, langCode) {
45776 if (!entity.claims[property]) return undefined;
45777 var locale = _localeIDs[langCode];
45778 var preferredPick, localePick;
45779 entity.claims[property].forEach(function (stmt) {
45780 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
45781 // Or if not found, use the first value with the "preferred" rank
45782 if (!preferredPick && stmt.rank === 'preferred') {
45783 preferredPick = stmt;
45786 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
45790 var result = localePick || preferredPick;
45793 var datavalue = result.mainsnak.datavalue;
45794 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
45801 * Convert monolingual property into a key-value object (language -> value)
45802 * @param entity object from wikibase
45803 * @param property string e.g. 'P31' for monolingual wiki page title
45805 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
45806 if (!entity || !entity.claims[property]) return undefined;
45807 return entity.claims[property].reduce(function (acc, obj) {
45808 var value = obj.mainsnak.datavalue.value;
45809 acc[value.language] = value.text;
45813 toSitelink: function toSitelink(key, value) {
45814 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
45815 return result.replace(/_/g, ' ').trim();
45818 // Pass params object of the form:
45821 // value: 'string',
45822 // langCode: 'string'
45825 getEntity: function getEntity(params, callback) {
45826 var doRequest = params.debounce ? debouncedRequest : request;
45830 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
45831 var keySitelink = params.key ? this.toSitelink(params.key) : false;
45832 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
45833 var localeSitelink;
45835 if (params.langCodes) {
45836 params.langCodes.forEach(function (langCode) {
45837 if (_localeIDs[langCode] === undefined) {
45838 // If this is the first time we are asking about this locale,
45839 // fetch corresponding entity (if it exists), and cache it.
45840 // If there is no such entry, cache `false` value to avoid re-requesting it.
45841 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
45842 titles.push(localeSitelink);
45847 if (rtypeSitelink) {
45848 if (_wikibaseCache[rtypeSitelink]) {
45849 result.rtype = _wikibaseCache[rtypeSitelink];
45851 titles.push(rtypeSitelink);
45856 if (_wikibaseCache[keySitelink]) {
45857 result.key = _wikibaseCache[keySitelink];
45859 titles.push(keySitelink);
45864 if (_wikibaseCache[tagSitelink]) {
45865 result.tag = _wikibaseCache[tagSitelink];
45867 titles.push(tagSitelink);
45871 if (!titles.length) {
45872 // Nothing to do, we already had everything in the cache
45873 return callback(null, result);
45874 } // Requesting just the user language code
45875 // If backend recognizes the code, it will perform proper fallbacks,
45876 // and the result will contain the requested code. If not, all values are returned:
45877 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
45878 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
45882 action: 'wbgetentities',
45884 titles: titles.join('|'),
45885 languages: params.langCodes.join('|'),
45886 languagefallback: 1,
45888 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
45889 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
45890 // formatversion: 2,
45893 var url = _apibase + '?' + utilQsString(obj);
45894 doRequest(url, function (err, d) {
45897 } else if (!d.success || d.error) {
45898 callback(d.error.messages.map(function (v) {
45899 return v.html['*'];
45902 var localeID = false;
45903 Object.values(d.entities).forEach(function (res) {
45904 if (res.missing !== '') {
45905 var title = res.sitelinks.wiki.title;
45907 if (title === rtypeSitelink) {
45908 _wikibaseCache[rtypeSitelink] = res;
45909 result.rtype = res;
45910 } else if (title === keySitelink) {
45911 _wikibaseCache[keySitelink] = res;
45913 } else if (title === tagSitelink) {
45914 _wikibaseCache[tagSitelink] = res;
45916 } else if (title === localeSitelink) {
45919 console.log('Unexpected title ' + title); // eslint-disable-line no-console
45924 if (localeSitelink) {
45925 // If locale ID is not found, store false to prevent repeated queries
45926 that.addLocale(params.langCodes[0], localeID);
45929 callback(null, result);
45934 // Pass params object of the form:
45936 // key: 'string', // required
45937 // value: 'string' // optional
45940 // Get an result object used to display tag documentation
45942 // title: 'string',
45943 // description: 'string',
45944 // editURL: 'string',
45945 // imageURL: 'string',
45946 // wiki: { title: 'string', text: 'string', url: 'string' }
45949 getDocs: function getDocs(params, callback) {
45951 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
45952 return code.toLowerCase();
45954 params.langCodes = langCodes;
45955 this.getEntity(params, function (err, data) {
45961 var entity = data.rtype || data.tag || data.key;
45964 callback('No entity');
45971 for (i in langCodes) {
45972 var _code = langCodes[i];
45974 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
45975 description = entity.descriptions[_code];
45980 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
45983 title: entity.title,
45984 description: description ? description.value : '',
45985 descriptionLocaleCode: description ? description.language : '',
45986 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
45989 if (entity.claims) {
45991 var image = that.claimToValue(entity, 'P4', langCodes[0]);
45994 imageroot = 'https://commons.wikimedia.org/w/index.php';
45996 image = that.claimToValue(entity, 'P28', langCodes[0]);
45999 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
46003 if (imageroot && image) {
46004 result.imageURL = imageroot + '?' + utilQsString({
46005 title: 'Special:Redirect/file/' + image,
46009 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
46010 // If neither tag nor key data item contain a wiki page in the needed language nor English,
46011 // get the first found wiki page from either the tag or the key item.
46014 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
46015 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
46016 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
46017 var wikis = [rtypeWiki, tagWiki, keyWiki];
46020 var wiki = wikis[i];
46022 for (var j in langCodes) {
46023 var code = langCodes[j];
46024 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
46025 var info = getWikiInfo(wiki, code, referenceId);
46028 result.wiki = info;
46033 if (result.wiki) break;
46036 callback(null, result); // Helper method to get wiki info if a given language exists
46038 function getWikiInfo(wiki, langCode, tKey) {
46039 if (wiki && wiki[langCode]) {
46041 title: wiki[langCode],
46043 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
46049 addLocale: function addLocale(langCode, qid) {
46050 // Makes it easier to unit test
46051 _localeIDs[langCode] = qid;
46053 apibase: function apibase(val) {
46054 if (!arguments.length) return _apibase;
46060 var jsonpCache = {};
46061 window.jsonpCache = jsonpCache;
46062 function jsonpRequest(url, callback) {
46064 abort: function abort() {}
46067 if (window.JSONP_FIX) {
46068 if (window.JSONP_DELAY === 0) {
46069 callback(window.JSONP_FIX);
46071 var t = window.setTimeout(function () {
46072 callback(window.JSONP_FIX);
46073 }, window.JSONP_DELAY || 0);
46075 request.abort = function () {
46076 window.clearTimeout(t);
46084 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
46089 c += chars.charAt(Math.floor(Math.random() * 52));
46095 function create(url) {
46096 var e = url.match(/callback=(\w+)/);
46097 var c = e ? e[1] : rand();
46099 jsonpCache[c] = function (data) {
46100 if (jsonpCache[c]) {
46107 function finalize() {
46108 delete jsonpCache[c];
46112 request.abort = finalize;
46113 return 'jsonpCache.' + c;
46116 var cb = create(url);
46117 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
46121 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
46122 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
46123 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
46124 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
46125 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
46126 var maxResults$2 = 2000;
46127 var tileZoom$2 = 16.5;
46128 var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
46129 var dispatch$7 = dispatch('loadedImages', 'viewerChanged');
46130 var minHfov = 10; // zoom in degrees: 20, 10, 5
46132 var maxHfov = 90; // zoom out degrees
46134 var defaultHfov = 45;
46135 var _hires = false;
46136 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
46138 var _currScene = 0;
46142 var _pannellumViewer;
46144 var _sceneOptions = {
46145 showFullscreenCtrl: false,
46156 var _loadViewerPromise$2;
46162 function abortRequest$6(i) {
46166 * localeTimeStamp().
46170 function localeTimestamp(s) {
46171 if (!s) return null;
46177 var d = new Date(s);
46178 if (isNaN(d.getTime())) return null;
46179 return d.toLocaleString(_mainLocalizer.localeCode(), options);
46182 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
46186 function loadTiles$2(which, url, projection, margin) {
46187 var tiles = tiler$6.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
46189 var cache = _ssCache[which];
46190 Object.keys(cache.inflight).forEach(function (k) {
46191 var wanted = tiles.find(function (tile) {
46192 return k.indexOf(tile.id + ',') === 0;
46196 abortRequest$6(cache.inflight[k]);
46197 delete cache.inflight[k];
46200 tiles.forEach(function (tile) {
46201 return loadNextTilePage$2(which, url, tile);
46205 * loadNextTilePage() load data for the next tile page in line.
46209 function loadNextTilePage$2(which, url, tile) {
46210 var cache = _ssCache[which];
46211 var nextPage = cache.nextPage[tile.id] || 0;
46212 var id = tile.id + ',' + String(nextPage);
46213 if (cache.loaded[id] || cache.inflight[id]) return;
46214 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
46215 cache.loaded[id] = true;
46216 delete cache.inflight[id];
46217 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
46220 var features = bubbles.map(function (bubble) {
46221 if (cache.points[bubble.id]) return null; // skip duplicates
46223 var loc = [bubble.lo, bubble.la];
46228 captured_at: bubble.cd,
46229 captured_by: 'microsoft',
46230 // nbn: bubble.nbn,
46231 // pbn: bubble.pbn,
46241 cache.points[bubble.id] = d; // a sequence starts here
46243 if (bubble.pr === undefined) {
46244 cache.leaders.push(bubble.id);
46254 }).filter(Boolean);
46255 cache.rtree.load(features);
46256 connectSequences();
46258 if (which === 'bubbles') {
46259 dispatch$7.call('loadedImages');
46262 } // call this sometimes to connect the bubbles into sequences
46265 function connectSequences() {
46266 var cache = _ssCache.bubbles;
46267 var keepLeaders = [];
46269 for (var i = 0; i < cache.leaders.length; i++) {
46270 var bubble = cache.points[cache.leaders[i]];
46271 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
46277 var complete = false;
46280 sequence.bubbles.push(bubble);
46281 seen[bubble.key] = true;
46283 if (bubble.ne === undefined) {
46286 bubble = cache.points[bubble.ne]; // advance to next
46288 } while (bubble && !seen[bubble.key] && !complete);
46291 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
46293 for (var j = 0; j < sequence.bubbles.length; j++) {
46294 sequence.bubbles[j].sequenceKey = sequence.key;
46295 } // create a GeoJSON LineString
46298 sequence.geojson = {
46299 type: 'LineString',
46301 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
46302 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
46305 coordinates: sequence.bubbles.map(function (d) {
46310 keepLeaders.push(cache.leaders[i]);
46312 } // couldn't complete these, save for later
46315 cache.leaders = keepLeaders;
46318 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
46322 function getBubbles(url, tile, callback) {
46323 var rect = tile.extent.rectangle();
46324 var urlForRequest = url + utilQsString({
46330 appkey: bubbleAppKey,
46331 jsCallback: '{callback}'
46333 return jsonpRequest(urlForRequest, function (data) {
46334 if (!data || data.error) {
46340 } // partition viewport into higher zoom tiles
46343 function partitionViewport$2(projection) {
46344 var z = geoScaleToZoom(projection.scale());
46345 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
46347 var tiler = utilTiler().zoomExtent([z2, z2]);
46348 return tiler.getTiles(projection).map(function (tile) {
46349 return tile.extent;
46351 } // no more than `limit` results per partition.
46354 function searchLimited$2(limit, projection, rtree) {
46355 limit = limit || 5;
46356 return partitionViewport$2(projection).reduce(function (result, extent) {
46357 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
46360 return found.length ? result.concat(found) : result;
46368 function loadImage(imgInfo) {
46369 return new Promise(function (resolve) {
46370 var img = new Image();
46372 img.onload = function () {
46373 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
46374 var ctx = canvas.getContext('2d');
46375 ctx.drawImage(img, imgInfo.x, imgInfo.y);
46382 img.onerror = function () {
46389 img.setAttribute('crossorigin', '');
46390 img.src = imgInfo.url;
46398 function loadCanvas(imageGroup) {
46399 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
46400 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
46409 var face = data[0].imgInfo.face;
46410 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
46412 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
46421 function loadFaces(faceGroup) {
46422 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
46424 status: 'loadFaces done'
46429 function setupCanvas(selection, reset) {
46431 selection.selectAll('#ideditor-stitcher-canvases').remove();
46432 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
46433 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
46436 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) {
46437 return 'ideditor-' + d;
46438 }).attr('width', _resolution).attr('height', _resolution);
46441 function qkToXY(qk) {
46446 for (var i = qk.length; i > 0; i--) {
46447 var key = qk[i - 1];
46448 x += +(key === '1' || key === '3') * scale;
46449 y += +(key === '2' || key === '3') * scale;
46456 function getQuadKeys() {
46457 var dim = _resolution / 256;
46461 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'];
46462 } else if (dim === 8) {
46463 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'];
46464 } else if (dim === 4) {
46465 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
46468 quadKeys = ['0', '1', '2', '3'];
46474 var serviceStreetside = {
46476 * init() initialize streetside.
46478 init: function init() {
46483 this.event = utilRebind(this, dispatch$7, 'on');
46487 * reset() reset the cache.
46489 reset: function reset() {
46491 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
46499 rtree: new RBush(),
46510 bubbles: function bubbles(projection) {
46512 return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
46514 cachedImage: function cachedImage(imageKey) {
46515 return _ssCache.bubbles.points[imageKey];
46517 sequences: function sequences(projection) {
46518 var viewport = projection.clipExtent();
46519 var min = [viewport[0][0], viewport[1][1]];
46520 var max = [viewport[1][0], viewport[0][1]];
46521 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46523 var results = []; // all sequences for bubbles in viewport
46525 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
46526 var key = d.data.sequenceKey;
46528 if (key && !seen[key]) {
46530 results.push(_ssCache.sequences[key].geojson);
46540 loadBubbles: function loadBubbles(projection, margin) {
46541 // by default: request 2 nearby tiles so we can connect sequences.
46542 if (margin === undefined) margin = 2;
46543 loadTiles$2('bubbles', bubbleApi, projection, margin);
46545 viewer: function viewer() {
46546 return _pannellumViewer;
46548 initViewer: function initViewer() {
46549 if (!window.pannellum) return;
46550 if (_pannellumViewer) return;
46553 var sceneID = _currScene.toString();
46557 firstScene: sceneID
46561 options.scenes[sceneID] = _sceneOptions;
46562 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
46564 ensureViewerLoaded: function ensureViewerLoaded(context) {
46565 if (_loadViewerPromise$2) return _loadViewerPromise$2; // create ms-wrapper, a photo wrapper class
46567 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
46568 // (used by all to house each custom photo viewer)
46570 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
46572 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
46574 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
46575 select(window).on(pointerPrefix + 'move.streetside', function () {
46576 dispatch$7.call('viewerChanged');
46578 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
46579 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
46581 var t = timer(function (elapsed) {
46582 dispatch$7.call('viewerChanged');
46584 if (elapsed > 2000) {
46588 }).append('div').attr('class', 'photo-attribution fillD');
46589 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
46590 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
46591 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
46593 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
46595 context.ui().photoviewer.on('resize.streetside', function () {
46596 if (_pannellumViewer) {
46597 _pannellumViewer.resize();
46600 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
46601 var loadedCount = 0;
46603 function loaded() {
46604 loadedCount += 1; // wait until both files are loaded
46606 if (loadedCount === 2) resolve();
46609 var head = select('head'); // load streetside pannellum viewer css
46611 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 () {
46613 }); // load streetside pannellum viewer js
46615 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 () {
46618 })["catch"](function () {
46619 _loadViewerPromise$2 = null;
46621 return _loadViewerPromise$2;
46623 function step(stepBy) {
46624 return function () {
46625 var viewer = context.container().select('.photoviewer');
46626 var selected = viewer.empty() ? undefined : viewer.datum();
46627 if (!selected) return;
46628 var nextID = stepBy === 1 ? selected.ne : selected.pr;
46630 var yaw = _pannellumViewer.getYaw();
46632 var ca = selected.ca + yaw;
46633 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
46636 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
46637 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46638 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46639 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
46640 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
46642 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
46643 poly = geoRotate(poly, -angle, origin);
46644 var extent = poly.reduce(function (extent, point) {
46645 return extent.extend(geoExtent(point));
46646 }, geoExtent()); // find nearest other bubble in the search polygon
46648 var minDist = Infinity;
46650 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
46651 if (d.data.key === selected.key) return;
46652 if (!geoPointInPolygon(d.data.loc, poly)) return;
46653 var dist = geoVecLength(d.data.loc, selected.loc);
46654 var theta = selected.ca - d.data.ca;
46655 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
46657 if (minTheta > 20) {
46658 dist += 5; // penalize distance if camera angles don't match
46661 if (dist < minDist) {
46662 nextID = d.data.key;
46667 var nextBubble = nextID && that.cachedImage(nextID);
46668 if (!nextBubble) return;
46669 context.map().centerEase(nextBubble.loc);
46670 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
46674 yaw: function yaw(_yaw) {
46675 if (typeof _yaw !== 'number') return _yaw;
46676 _sceneOptions.yaw = _yaw;
46683 showViewer: function showViewer(context) {
46684 var wrap = context.container().select('.photoviewer').classed('hide', false);
46685 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
46688 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
46689 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
46698 hideViewer: function hideViewer(context) {
46699 var viewer = context.container().select('.photoviewer');
46700 if (!viewer.empty()) viewer.datum(null);
46701 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
46702 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
46703 this.updateUrlImage(null);
46704 return this.setStyles(context, null, true);
46710 selectImage: function selectImage(context, key) {
46712 var d = this.cachedImage(key);
46713 var viewer = context.container().select('.photoviewer');
46714 if (!viewer.empty()) viewer.datum(d);
46715 this.setStyles(context, null, true);
46716 var wrap = context.container().select('.photoviewer .ms-wrapper');
46717 var attribution = wrap.selectAll('.photo-attribution').html('');
46718 wrap.selectAll('.pnlm-load-box') // display "loading.."
46719 .style('display', 'block');
46720 if (!d) return this;
46721 this.updateUrlImage(key);
46722 _sceneOptions.northOffset = d.ca;
46723 var line1 = attribution.append('div').attr('class', 'attribution-row');
46724 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
46726 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
46727 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
46728 d3_event.stopPropagation();
46730 _resolution = _hires ? 1024 : 512;
46731 wrap.call(setupCanvas, true);
46733 yaw: _pannellumViewer.getYaw(),
46734 pitch: _pannellumViewer.getPitch(),
46735 hfov: _pannellumViewer.getHfov()
46737 _sceneOptions = Object.assign(_sceneOptions, viewstate);
46738 that.selectImage(context, d.key).showViewer(context);
46740 label.append('span').html(_t.html('streetside.hires'));
46741 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
46743 if (d.captured_by) {
46744 var yyyy = new Date().getFullYear();
46745 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
46746 captureInfo.append('span').html('|');
46749 if (d.captured_at) {
46750 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
46751 } // Add image links
46754 var line2 = attribution.append('div').attr('class', 'attribution-row');
46755 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'));
46756 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'));
46757 var bubbleIdQuadKey = d.key.toString(4);
46758 var paddingNeeded = 16 - bubbleIdQuadKey.length;
46760 for (var i = 0; i < paddingNeeded; i++) {
46761 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
46764 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
46765 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
46767 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
46769 var quadKeys = getQuadKeys();
46770 var faces = faceKeys.map(function (faceKey) {
46771 return quadKeys.map(function (quadKey) {
46772 var xy = qkToXY(quadKey);
46775 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
46781 loadFaces(faces).then(function () {
46782 if (!_pannellumViewer) {
46785 // make a new scene
46788 var sceneID = _currScene.toString();
46790 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
46793 if (_currScene > 2) {
46794 sceneID = (_currScene - 1).toString();
46796 _pannellumViewer.removeScene(sceneID);
46802 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
46803 return d && d.sequenceKey;
46805 // Updates the currently highlighted sequence and selected bubble.
46806 // Reset is only necessary when interacting with the viewport because
46807 // this implicitly changes the currently selected bubble/sequence
46808 setStyles: function setStyles(context, hovered, reset) {
46810 // reset all layers
46811 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
46812 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
46815 var hoveredBubbleKey = hovered && hovered.key;
46816 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
46817 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
46818 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
46821 var viewer = context.container().select('.photoviewer');
46822 var selected = viewer.empty() ? undefined : viewer.datum();
46823 var selectedBubbleKey = selected && selected.key;
46824 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
46825 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
46826 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
46828 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
46830 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
46831 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
46832 return highlightedBubbleKeys.indexOf(d.key) !== -1;
46833 }).classed('hovered', function (d) {
46834 return d.key === hoveredBubbleKey;
46835 }).classed('currentView', function (d) {
46836 return d.key === selectedBubbleKey;
46838 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
46839 return d.properties.key === hoveredSequenceKey;
46840 }).classed('currentView', function (d) {
46841 return d.properties.key === selectedSequenceKey;
46842 }); // update viewfields if needed
46844 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
46846 function viewfieldPath() {
46847 var d = this.parentNode.__data__;
46849 if (d.pano && d.key !== selectedBubbleKey) {
46850 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
46852 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
46858 updateUrlImage: function updateUrlImage(imageKey) {
46859 if (!window.mocha) {
46860 var hash = utilStringQs(window.location.hash);
46863 hash.photo = 'streetside/' + imageKey;
46868 window.location.replace('#' + utilQsString(hash, true));
46875 cache: function cache() {
46880 var _apibase$1 = 'https://taginfo.openstreetmap.org/api/4/';
46881 var _inflight$2 = {};
46882 var _popularKeys = {};
46883 var _taginfoCache = {};
46885 point: 'count_nodes',
46886 vertex: 'count_nodes',
46887 area: 'count_ways',
46890 var tag_sort_members = {
46891 point: 'count_node_members',
46892 vertex: 'count_node_members',
46893 area: 'count_way_members',
46894 line: 'count_way_members',
46895 relation: 'count_relation_members'
46897 var tag_filters = {
46903 var tag_members_fractions = {
46904 point: 'count_node_members_fraction',
46905 vertex: 'count_node_members_fraction',
46906 area: 'count_way_members_fraction',
46907 line: 'count_way_members_fraction',
46908 relation: 'count_relation_members_fraction'
46911 function sets(params, n, o) {
46912 if (params.geometry && o[params.geometry]) {
46913 params[n] = o[params.geometry];
46919 function setFilter(params) {
46920 return sets(params, 'filter', tag_filters);
46923 function setSort(params) {
46924 return sets(params, 'sortname', tag_sorts);
46927 function setSortMembers(params) {
46928 return sets(params, 'sortname', tag_sort_members);
46931 function clean(params) {
46932 return utilObjectOmit(params, ['geometry', 'debounce']);
46935 function filterKeys(type) {
46936 var count_type = type ? 'count_' + type : 'count_all';
46937 return function (d) {
46938 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
46942 function filterMultikeys(prefix) {
46943 return function (d) {
46944 // d.key begins with prefix, and d.key contains no additional ':'s
46945 var re = new RegExp('^' + prefix + '(.*)$');
46946 var matches = d.key.match(re) || [];
46947 return matches.length === 2 && matches[1].indexOf(':') === -1;
46951 function filterValues(allowUpperCase) {
46952 return function (d) {
46953 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
46955 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
46957 return parseFloat(d.fraction) > 0.0;
46961 function filterRoles(geometry) {
46962 return function (d) {
46963 if (d.role === '') return false; // exclude empty role
46965 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
46967 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
46971 function valKey(d) {
46978 function valKeyDescription(d) {
46981 title: d.description || d.value
46985 obj.count = d.count;
46991 function roleKey(d) {
46996 } // sort keys with ':' lower than keys without ':'
46999 function sortKeys(a, b) {
47000 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
47003 var debouncedRequest$1 = debounce(request$1, 300, {
47007 function request$1(url, params, exactMatch, callback, loaded) {
47008 if (_inflight$2[url]) return;
47009 if (checkCache(url, params, exactMatch, callback)) return;
47010 var controller = new AbortController();
47011 _inflight$2[url] = controller;
47013 signal: controller.signal
47014 }).then(function (result) {
47015 delete _inflight$2[url];
47016 if (loaded) loaded(null, result);
47017 })["catch"](function (err) {
47018 delete _inflight$2[url];
47019 if (err.name === 'AbortError') return;
47020 if (loaded) loaded(err.message);
47024 function checkCache(url, params, exactMatch, callback) {
47025 var rp = params.rp || 25;
47026 var testQuery = params.query || '';
47030 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
47032 if (hit && (url === testUrl || hit.length < rp)) {
47033 callback(null, hit);
47035 } // don't try to shorten the query
47038 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
47039 // that has returned fewer than max results (rp)
47041 testQuery = testQuery.slice(0, -1);
47042 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
47043 } while (testQuery.length >= 0);
47048 var serviceTaginfo = {
47049 init: function init() {
47051 _taginfoCache = {};
47053 // manually exclude some keys – #5377, #7485
47059 sorting_name: true,
47063 'bridge:name': true
47064 }; // Fetch popular keys. We'll exclude these from `values`
47065 // lookups because they stress taginfo, and they aren't likely
47066 // to yield meaningful autocomplete results.. see #3955
47070 sortname: 'values_all',
47074 lang: _mainLocalizer.languageCode()
47076 this.keys(params, function (err, data) {
47078 data.forEach(function (d) {
47079 if (d.value === 'opening_hours') return; // exception
47081 _popularKeys[d.value] = true;
47085 reset: function reset() {
47086 Object.values(_inflight$2).forEach(function (controller) {
47087 controller.abort();
47091 keys: function keys(params, callback) {
47092 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47093 params = clean(setSort(params));
47094 params = Object.assign({
47096 sortname: 'count_all',
47099 lang: _mainLocalizer.languageCode()
47101 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47102 doRequest(url, params, false, callback, function (err, d) {
47106 var f = filterKeys(params.filter);
47107 var result = d.data.filter(f).sort(sortKeys).map(valKey);
47108 _taginfoCache[url] = result;
47109 callback(null, result);
47113 multikeys: function multikeys(params, callback) {
47114 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47115 params = clean(setSort(params));
47116 params = Object.assign({
47118 sortname: 'count_all',
47121 lang: _mainLocalizer.languageCode()
47123 var prefix = params.query;
47124 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47125 doRequest(url, params, true, callback, function (err, d) {
47129 var f = filterMultikeys(prefix);
47130 var result = d.data.filter(f).map(valKey);
47131 _taginfoCache[url] = result;
47132 callback(null, result);
47136 values: function values(params, callback) {
47137 // Exclude popular keys from values lookups.. see #3955
47138 var key = params.key;
47140 if (key && _popularKeys[key]) {
47141 callback(null, []);
47145 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47146 params = clean(setSort(setFilter(params)));
47147 params = Object.assign({
47149 sortname: 'count_all',
47152 lang: _mainLocalizer.languageCode()
47154 var url = _apibase$1 + 'key/values?' + utilQsString(params);
47155 doRequest(url, params, false, callback, function (err, d) {
47159 // In most cases we prefer taginfo value results with lowercase letters.
47160 // A few OSM keys expect values to contain uppercase values (see #3377).
47161 // This is not an exhaustive list (e.g. `name` also has uppercase values)
47162 // but these are the fields where taginfo value lookup is most useful.
47163 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
47164 var allowUpperCase = re.test(params.key);
47165 var f = filterValues(allowUpperCase);
47166 var result = d.data.filter(f).map(valKeyDescription);
47167 _taginfoCache[url] = result;
47168 callback(null, result);
47172 roles: function roles(params, callback) {
47173 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47174 var geometry = params.geometry;
47175 params = clean(setSortMembers(params));
47176 params = Object.assign({
47178 sortname: 'count_all_members',
47181 lang: _mainLocalizer.languageCode()
47183 var url = _apibase$1 + 'relation/roles?' + utilQsString(params);
47184 doRequest(url, params, true, callback, function (err, d) {
47188 var f = filterRoles(geometry);
47189 var result = d.data.filter(f).map(roleKey);
47190 _taginfoCache[url] = result;
47191 callback(null, result);
47195 docs: function docs(params, callback) {
47196 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47197 params = clean(setSort(params));
47198 var path = 'key/wiki_pages?';
47200 if (params.value) {
47201 path = 'tag/wiki_pages?';
47202 } else if (params.rtype) {
47203 path = 'relation/wiki_pages?';
47206 var url = _apibase$1 + path + utilQsString(params);
47207 doRequest(url, params, true, callback, function (err, d) {
47211 _taginfoCache[url] = d.data;
47212 callback(null, d.data);
47216 apibase: function apibase(_) {
47217 if (!arguments.length) return _apibase$1;
47224 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
47227 * @param {Geometry} geometry input geometry
47228 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47229 * @param {Object} [options={}] Optional Parameters
47230 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47231 * @param {string|number} [options.id] Identifier associated with the Feature
47232 * @returns {Feature} a GeoJSON Feature
47236 * "coordinates": [110, 50]
47239 * var feature = turf.feature(geometry);
47244 function feature(geom, properties, options) {
47245 if (options === void 0) {
47253 if (options.id === 0 || options.id) {
47254 feat.id = options.id;
47257 if (options.bbox) {
47258 feat.bbox = options.bbox;
47261 feat.properties = properties || {};
47262 feat.geometry = geom;
47266 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
47269 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47270 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47271 * @param {Object} [options={}] Optional Parameters
47272 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47273 * @param {string|number} [options.id] Identifier associated with the Feature
47274 * @returns {Feature<Polygon>} Polygon Feature
47276 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
47281 function polygon(coordinates, properties, options) {
47282 if (options === void 0) {
47286 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
47287 var ring = coordinates_1[_i];
47289 if (ring.length < 4) {
47290 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
47293 for (var j = 0; j < ring[ring.length - 1].length; j++) {
47294 // Check if first point of Polygon contains two numbers
47295 if (ring[ring.length - 1][j] !== ring[0][j]) {
47296 throw new Error("First and last Position are not equivalent.");
47303 coordinates: coordinates
47305 return feature(geom, properties, options);
47308 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
47311 * @param {Array<Array<number>>} coordinates an array of Positions
47312 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47313 * @param {Object} [options={}] Optional Parameters
47314 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47315 * @param {string|number} [options.id] Identifier associated with the Feature
47316 * @returns {Feature<LineString>} LineString Feature
47318 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
47319 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
47325 function lineString(coordinates, properties, options) {
47326 if (options === void 0) {
47330 if (coordinates.length < 2) {
47331 throw new Error("coordinates must be an array of two or more positions");
47335 type: "LineString",
47336 coordinates: coordinates
47338 return feature(geom, properties, options);
47341 * Creates a {@link Feature<MultiLineString>} based on a
47342 * coordinate array. Properties can be added optionally.
47344 * @name multiLineString
47345 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
47346 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47347 * @param {Object} [options={}] Optional Parameters
47348 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47349 * @param {string|number} [options.id] Identifier associated with the Feature
47350 * @returns {Feature<MultiLineString>} a MultiLineString feature
47351 * @throws {Error} if no coordinates are passed
47353 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
47358 function multiLineString(coordinates, properties, options) {
47359 if (options === void 0) {
47364 type: "MultiLineString",
47365 coordinates: coordinates
47367 return feature(geom, properties, options);
47370 * Creates a {@link Feature<MultiPolygon>} based on a
47371 * coordinate array. Properties can be added optionally.
47373 * @name multiPolygon
47374 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
47375 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47376 * @param {Object} [options={}] Optional Parameters
47377 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47378 * @param {string|number} [options.id] Identifier associated with the Feature
47379 * @returns {Feature<MultiPolygon>} a multipolygon feature
47380 * @throws {Error} if no coordinates are passed
47382 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
47388 function multiPolygon(coordinates, properties, options) {
47389 if (options === void 0) {
47394 type: "MultiPolygon",
47395 coordinates: coordinates
47397 return feature(geom, properties, options);
47401 * Get Geometry from Feature or Geometry Object
47403 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
47404 * @returns {Geometry|null} GeoJSON Geometry Object
47405 * @throws {Error} if geojson is not a Feature or Geometry Object
47408 * "type": "Feature",
47409 * "properties": {},
47412 * "coordinates": [110, 40]
47415 * var geom = turf.getGeom(point)
47416 * //={"type": "Point", "coordinates": [110, 40]}
47419 function getGeom(geojson) {
47420 if (geojson.type === "Feature") {
47421 return geojson.geometry;
47427 // Cohen-Sutherland line clippign algorithm, adapted to efficiently
47428 // handle polylines rather than just segments
47429 function lineclip(points, bbox, result) {
47430 var len = points.length,
47431 codeA = bitCode(points[0], bbox),
47438 if (!result) result = [];
47440 for (i = 1; i < len; i++) {
47443 codeB = lastCode = bitCode(b, bbox);
47446 if (!(codeA | codeB)) {
47450 if (codeB !== lastCode) {
47451 // segment went outside
47455 // start a new line
47459 } else if (i === len - 1) {
47464 } else if (codeA & codeB) {
47467 } else if (codeA) {
47468 // a outside, intersect with clip edge
47469 a = intersect(a, b, codeA, bbox);
47470 codeA = bitCode(a, bbox);
47473 b = intersect(a, b, codeB, bbox);
47474 codeB = bitCode(b, bbox);
47481 if (part.length) result.push(part);
47483 } // Sutherland-Hodgeman polygon clipping algorithm
47485 function polygonclip(points, bbox) {
47486 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
47488 for (edge = 1; edge <= 8; edge *= 2) {
47490 prev = points[points.length - 1];
47491 prevInside = !(bitCode(prev, bbox) & edge);
47493 for (i = 0; i < points.length; i++) {
47495 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
47497 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
47498 if (inside) result.push(p); // add a point if it's inside
47501 prevInside = inside;
47505 if (!points.length) break;
47509 } // intersect a segment against one of the 4 lines that make up the bbox
47511 function intersect(a, b, edge, bbox) {
47512 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] // top
47513 : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] // bottom
47514 : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] // right
47515 : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] // left
47517 } // bit code reflects the point position relative to the bbox:
47519 // top 1001 1000 1010
47520 // mid 0001 0000 0010
47521 // bottom 0101 0100 0110
47524 function bitCode(p, bbox) {
47526 if (p[0] < bbox[0]) code |= 1; // left
47527 else if (p[0] > bbox[2]) code |= 2; // right
47529 if (p[1] < bbox[1]) code |= 4; // bottom
47530 else if (p[1] > bbox[3]) code |= 8; // top
47536 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
47537 * [lineclip](https://github.com/mapbox/lineclip).
47538 * May result in degenerate edges when clipping Polygons.
47541 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
47542 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
47543 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
47545 * var bbox = [0, 0, 10, 10];
47546 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
47548 * var clipped = turf.bboxClip(poly, bbox);
47551 * var addToMap = [bbox, poly, clipped]
47554 function bboxClip(feature, bbox) {
47555 var geom = getGeom(feature);
47556 var type = geom.type;
47557 var properties = feature.type === "Feature" ? feature.properties : {};
47558 var coords = geom.coordinates;
47562 case "MultiLineString":
47565 if (type === "LineString") {
47569 coords.forEach(function (line) {
47570 lineclip(line, bbox, lines_1);
47573 if (lines_1.length === 1) {
47574 return lineString(lines_1[0], properties);
47577 return multiLineString(lines_1, properties);
47580 return polygon(clipPolygon(coords, bbox), properties);
47582 case "MultiPolygon":
47583 return multiPolygon(coords.map(function (poly) {
47584 return clipPolygon(poly, bbox);
47588 throw new Error("geometry " + type + " not supported");
47592 function clipPolygon(rings, bbox) {
47595 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
47596 var ring = rings_1[_i];
47597 var clipped = polygonclip(ring, bbox);
47599 if (clipped.length > 0) {
47600 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
47601 clipped.push(clipped[0]);
47604 if (clipped.length >= 4) {
47605 outRings.push(clipped);
47613 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
47614 if (!opts) opts = {};
47615 if (typeof opts === 'function') opts = {
47618 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
47620 var cmp = opts.cmp && function (f) {
47621 return function (node) {
47622 return function (a, b) {
47631 return f(aobj, bobj);
47637 return function stringify(node) {
47638 if (node && node.toJSON && typeof node.toJSON === 'function') {
47639 node = node.toJSON();
47642 if (node === undefined) return;
47643 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
47644 if (_typeof(node) !== 'object') return JSON.stringify(node);
47647 if (Array.isArray(node)) {
47650 for (i = 0; i < node.length; i++) {
47652 out += stringify(node[i]) || 'null';
47658 if (node === null) return 'null';
47660 if (seen.indexOf(node) !== -1) {
47661 if (cycles) return JSON.stringify('__cycle__');
47662 throw new TypeError('Converting circular structure to JSON');
47665 var seenIndex = seen.push(node) - 1;
47666 var keys = Object.keys(node).sort(cmp && cmp(node));
47669 for (i = 0; i < keys.length; i++) {
47671 var value = stringify(node[key]);
47672 if (!value) continue;
47673 if (out) out += ',';
47674 out += JSON.stringify(key) + ':' + value;
47677 seen.splice(seenIndex, 1);
47678 return '{' + out + '}';
47682 function DEFAULT_COMPARE(a, b) {
47683 return a > b ? 1 : a < b ? -1 : 0;
47686 var SplayTree = /*#__PURE__*/function () {
47687 function SplayTree() {
47688 var compare = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE;
47689 var noDuplicates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
47691 _classCallCheck(this, SplayTree);
47693 this._compare = compare;
47696 this._noDuplicates = !!noDuplicates;
47699 _createClass(SplayTree, [{
47701 value: function rotateLeft(x) {
47706 if (y.left) y.left.parent = x;
47707 y.parent = x.parent;
47710 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
47715 key: "rotateRight",
47716 value: function rotateRight(x) {
47721 if (y.right) y.right.parent = x;
47722 y.parent = x.parent;
47725 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
47726 if (y) y.right = x;
47731 value: function _splay(x) {
47736 if (p.left === x) this.rotateRight(p);else this.rotateLeft(p);
47737 } else if (p.left === x && p.parent.left === p) {
47738 this.rotateRight(p.parent);
47739 this.rotateRight(p);
47740 } else if (p.right === x && p.parent.right === p) {
47741 this.rotateLeft(p.parent);
47742 this.rotateLeft(p);
47743 } else if (p.left === x && p.parent.right === p) {
47744 this.rotateRight(p);
47745 this.rotateLeft(p);
47747 this.rotateLeft(p);
47748 this.rotateRight(p);
47754 value: function splay(x) {
47755 var p, gp, ggp, l, r;
47761 if (gp && gp.parent) {
47763 if (ggp.left === gp) ggp.left = x;else ggp.right = x;
47773 if (x === p.left) {
47776 if (gp.left === p) {
47780 gp.left.parent = gp;
47781 } else gp.left = null;
47790 } else gp.right = null;
47800 } else p.left = null;
47807 if (gp.right === p) {
47811 gp.right.parent = gp;
47812 } else gp.right = null;
47821 } else gp.left = null;
47831 } else p.right = null;
47840 value: function replace(u, v) {
47841 if (!u.parent) this._root = v;else if (u === u.parent.left) u.parent.left = v;else u.parent.right = v;
47842 if (v) v.parent = u.parent;
47846 value: function minNode() {
47847 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
47848 if (u) while (u.left) {
47855 value: function maxNode() {
47856 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
47857 if (u) while (u.right) {
47864 value: function insert(key, data) {
47865 var z = this._root;
47867 var comp = this._compare;
47870 if (this._noDuplicates) {
47873 cmp = comp(z.key, key);
47874 if (cmp === 0) return;else if (comp(z.key, key) < 0) z = z.right;else z = z.left;
47879 if (comp(z.key, key) < 0) z = z.right;else z = z.left;
47890 if (!p) this._root = z;else if (comp(p.key, z.key) < 0) p.right = z;else p.left = z;
47897 value: function find(key) {
47898 var z = this._root;
47899 var comp = this._compare;
47902 var cmp = comp(z.key, key);
47903 if (cmp < 0) z = z.right;else if (cmp > 0) z = z.left;else return z;
47909 * Whether the tree contains a node with the given key
47911 * @return {boolean} true/false
47916 value: function contains(key) {
47917 var node = this._root;
47918 var comparator = this._compare;
47921 var cmp = comparator(key, node.key);
47922 if (cmp === 0) return true;else if (cmp < 0) node = node.left;else node = node.right;
47929 value: function remove(key) {
47930 var z = this.find(key);
47931 if (!z) return false;
47933 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
47934 var y = this.minNode(z.right);
47936 if (y.parent !== z) {
47937 this.replace(y, y.right);
47939 y.right.parent = y;
47942 this.replace(z, y);
47951 value: function removeNode(z) {
47952 if (!z) return false;
47954 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
47955 var y = this.minNode(z.right);
47957 if (y.parent !== z) {
47958 this.replace(y, y.right);
47960 y.right.parent = y;
47963 this.replace(z, y);
47972 value: function erase(key) {
47973 var z = this.find(key);
47982 sMax = this.maxNode(s);
47988 if (s) sMax.right = t;else this._root = t;
47995 * Removes and returns the node with smallest key
48001 value: function pop() {
48002 var node = this._root,
48003 returnValue = null;
48006 while (node.left) {
48014 this.remove(node.key);
48017 return returnValue;
48019 /* eslint-disable class-methods-use-this */
48023 * @param {Node} node
48029 value: function next(node) {
48030 var successor = node;
48033 if (successor.right) {
48034 successor = successor.right;
48036 while (successor && successor.left) {
48037 successor = successor.left;
48040 successor = node.parent;
48042 while (successor && successor.right === node) {
48044 successor = successor.parent;
48053 * @param {Node} node
48059 value: function prev(node) {
48060 var predecessor = node;
48063 if (predecessor.left) {
48064 predecessor = predecessor.left;
48066 while (predecessor && predecessor.right) {
48067 predecessor = predecessor.right;
48070 predecessor = node.parent;
48072 while (predecessor && predecessor.left === node) {
48073 node = predecessor;
48074 predecessor = predecessor.parent;
48079 return predecessor;
48081 /* eslint-enable class-methods-use-this */
48084 * @param {forEachCallback} callback
48085 * @return {SplayTree}
48090 value: function forEach(callback) {
48091 var current = this._root;
48097 // Reach the left most Node of the current Node
48099 // Place pointer to a tree node on the stack
48100 // before traversing the node's left subtree
48102 current = current.left;
48104 // BackTrack from the empty subtree and visit the Node
48105 // at the top of the stack; however, if the stack is
48106 // empty you are done
48107 if (s.length > 0) {
48109 callback(current, i++); // We have visited the node and its left
48110 // subtree. Now, it's right subtree's turn
48112 current = current.right;
48113 } else done = true;
48120 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
48122 * @param {Key} high
48123 * @param {Function} fn
48125 * @return {SplayTree}
48130 value: function range(low, high, fn, ctx) {
48132 var compare = this._compare;
48133 var node = this._root,
48136 while (Q.length !== 0 || node) {
48142 cmp = compare(node.key, high);
48146 } else if (compare(node.key, low) >= 0) {
48147 if (fn.call(ctx, node)) return this; // stop if smth is returned
48157 * Returns all keys in order
48158 * @return {Array<Key>}
48163 value: function keys() {
48164 var current = this._root;
48172 current = current.left;
48174 if (s.length > 0) {
48176 r.push(current.key);
48177 current = current.right;
48178 } else done = true;
48185 * Returns `data` fields of all nodes in order.
48186 * @return {Array<Value>}
48191 value: function values() {
48192 var current = this._root;
48200 current = current.left;
48202 if (s.length > 0) {
48204 r.push(current.data);
48205 current = current.right;
48206 } else done = true;
48213 * Returns node at given index
48214 * @param {number} index
48220 value: function at(index) {
48221 // removed after a consideration, more misleading than useful
48222 // index = index % this.size;
48223 // if (index < 0) index = this.size - index;
48224 var current = this._root;
48232 current = current.left;
48234 if (s.length > 0) {
48236 if (i === index) return current;
48238 current = current.right;
48239 } else done = true;
48246 * Bulk-load items. Both array have to be same size
48247 * @param {Array<Key>} keys
48248 * @param {Array<Value>} [values]
48249 * @param {Boolean} [presort=false] Pre-sort keys and values, using
48250 * tree's comparator. Sorting is done
48252 * @return {AVLTree}
48257 value: function load() {
48258 var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
48259 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48260 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
48261 if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
48262 var size = keys.length;
48263 if (presort) sort(keys, values, 0, size - 1, this._compare);
48264 this._root = loadRecursive(null, keys, values, 0, size);
48270 value: function min() {
48271 var node = this.minNode(this._root);
48272 if (node) return node.key;else return null;
48276 value: function max() {
48277 var node = this.maxNode(this._root);
48278 if (node) return node.key;else return null;
48282 value: function isEmpty() {
48283 return this._root === null;
48287 get: function get() {
48291 * Create a tree and load it with items
48292 * @param {Array<Key>} keys
48293 * @param {Array<Value>?} [values]
48294 * @param {Function?} [comparator]
48295 * @param {Boolean?} [presort=false] Pre-sort keys and values, using
48296 * tree's comparator. Sorting is done
48298 * @param {Boolean?} [noDuplicates=false] Allow duplicates
48299 * @return {SplayTree}
48304 value: function createTree(keys, values, comparator, presort, noDuplicates) {
48305 return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
48312 function loadRecursive(parent, keys, values, start, end) {
48313 var size = end - start;
48316 var middle = start + Math.floor(size / 2);
48317 var key = keys[middle];
48318 var data = values[middle];
48324 node.left = loadRecursive(node, keys, values, start, middle);
48325 node.right = loadRecursive(node, keys, values, middle + 1, end);
48332 function sort(keys, values, left, right, compare) {
48333 if (left >= right) return;
48334 var pivot = keys[left + right >> 1];
48341 } while (compare(keys[i], pivot) < 0);
48345 } while (compare(keys[j], pivot) > 0);
48352 values[i] = values[j];
48356 sort(keys, values, left, j, compare);
48357 sort(keys, values, j + 1, right, compare);
48361 var NON_CONTRIBUTING = 1;
48362 var SAME_TRANSITION = 2;
48363 var DIFFERENT_TRANSITION = 3;
48365 var INTERSECTION = 0;
48367 var DIFFERENCE = 2;
48371 * @param {SweepEvent} event
48372 * @param {SweepEvent} prev
48373 * @param {Operation} operation
48376 function computeFields(event, prev, operation) {
48377 // compute inOut and otherInOut fields
48378 if (prev === null) {
48379 event.inOut = false;
48380 event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon
48382 if (event.isSubject === prev.isSubject) {
48383 event.inOut = !prev.inOut;
48384 event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon
48386 event.inOut = !prev.otherInOut;
48387 event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
48388 } // compute prevInResult field
48392 event.prevInResult = !inResult(prev, operation) || prev.isVertical() ? prev.prevInResult : prev;
48394 } // check if the line segment belongs to the Boolean operation
48397 var isInResult = inResult(event, operation);
48400 event.resultTransition = determineResultTransition(event, operation);
48402 event.resultTransition = 0;
48405 /* eslint-disable indent */
48407 function inResult(event, operation) {
48408 switch (event.type) {
48410 switch (operation) {
48412 return !event.otherInOut;
48415 return event.otherInOut;
48418 // return (event.isSubject && !event.otherInOut) ||
48419 // (!event.isSubject && event.otherInOut);
48420 return event.isSubject && event.otherInOut || !event.isSubject && !event.otherInOut;
48428 case SAME_TRANSITION:
48429 return operation === INTERSECTION || operation === UNION;
48431 case DIFFERENT_TRANSITION:
48432 return operation === DIFFERENCE;
48434 case NON_CONTRIBUTING:
48440 /* eslint-enable indent */
48443 function determineResultTransition(event, operation) {
48444 var thisIn = !event.inOut;
48445 var thatIn = !event.otherInOut;
48448 switch (operation) {
48450 isIn = thisIn && thatIn;
48454 isIn = thisIn || thatIn;
48458 isIn = thisIn ^ thatIn;
48462 if (event.isSubject) {
48463 isIn = thisIn && !thatIn;
48465 isIn = thatIn && !thisIn;
48471 return isIn ? +1 : -1;
48474 var SweepEvent = /*#__PURE__*/function () {
48478 * @class {SweepEvent}
48479 * @param {Array.<Number>} point
48480 * @param {Boolean} left
48481 * @param {SweepEvent=} otherEvent
48482 * @param {Boolean} isSubject
48483 * @param {Number} edgeType
48485 function SweepEvent(point, left, otherEvent, isSubject, edgeType) {
48486 _classCallCheck(this, SweepEvent);
48489 * Is left endpoint?
48494 * @type {Array.<Number>}
48497 this.point = point;
48499 * Other edge reference
48500 * @type {SweepEvent}
48503 this.otherEvent = otherEvent;
48505 * Belongs to source or clipping polygon
48509 this.isSubject = isSubject;
48511 * Edge contribution type
48515 this.type = edgeType || NORMAL;
48517 * In-out transition for the sweepline crossing polygon
48521 this.inOut = false;
48526 this.otherInOut = false;
48528 * Previous event in result?
48529 * @type {SweepEvent}
48532 this.prevInResult = null;
48534 * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
48538 this.resultTransition = 0; // connection step
48544 this.otherPos = -1;
48549 this.outputContourId = -1;
48550 this.isExteriorRing = true; // TODO: Looks unused, remove?
48553 * @param {Array.<Number>} p
48554 * @return {Boolean}
48558 _createClass(SweepEvent, [{
48560 value: function isBelow(p) {
48561 var p0 = this.point,
48562 p1 = this.otherEvent.point;
48563 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 :
48564 : (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;
48567 * @param {Array.<Number>} p
48568 * @return {Boolean}
48573 value: function isAbove(p) {
48574 return !this.isBelow(p);
48577 * @return {Boolean}
48582 value: function isVertical() {
48583 return this.point[0] === this.otherEvent.point[0];
48586 * Does event belong to result?
48587 * @return {Boolean}
48592 get: function get() {
48593 return this.resultTransition !== 0;
48597 value: function clone() {
48598 var copy = new SweepEvent(this.point, this.left, this.otherEvent, this.isSubject, this.type);
48599 copy.contourId = this.contourId;
48600 copy.resultTransition = this.resultTransition;
48601 copy.prevInResult = this.prevInResult;
48602 copy.isExteriorRing = this.isExteriorRing;
48603 copy.inOut = this.inOut;
48604 copy.otherInOut = this.otherInOut;
48612 function equals(p1, p2) {
48613 if (p1[0] === p2[0]) {
48614 if (p1[1] === p2[1]) {
48622 } // const EPSILON = 1e-9;
48623 // const abs = Math.abs;
48624 // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
48625 // Precision problem.
48627 // module.exports = function equals(p1, p2) {
48628 // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
48631 var epsilon$1 = 1.1102230246251565e-16;
48632 var splitter = 134217729;
48633 var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; // fast_expansion_sum_zeroelim routine from oritinal code
48635 function sum(elen, e, flen, f, h) {
48636 var Q, Qnew, hh, bvirt;
48642 if (fnow > enow === fnow > -enow) {
48644 enow = e[++eindex];
48647 fnow = f[++findex];
48652 if (eindex < elen && findex < flen) {
48653 if (fnow > enow === fnow > -enow) {
48655 hh = Q - (Qnew - enow);
48656 enow = e[++eindex];
48659 hh = Q - (Qnew - fnow);
48660 fnow = f[++findex];
48669 while (eindex < elen && findex < flen) {
48670 if (fnow > enow === fnow > -enow) {
48673 hh = Q - (Qnew - bvirt) + (enow - bvirt);
48674 enow = e[++eindex];
48678 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
48679 fnow = f[++findex];
48690 while (eindex < elen) {
48693 hh = Q - (Qnew - bvirt) + (enow - bvirt);
48694 enow = e[++eindex];
48702 while (findex < flen) {
48705 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
48706 fnow = f[++findex];
48714 if (Q !== 0 || hindex === 0) {
48720 function estimate(elen, e) {
48723 for (var i = 1; i < elen; i++) {
48730 return new Float64Array(n);
48733 var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
48734 var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
48735 var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
48742 function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
48743 var acxtail, acytail, bcxtail, bcytail;
48745 var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
48752 c = splitter * acx;
48753 ahi = c - (c - acx);
48755 c = splitter * bcy;
48756 bhi = c - (c - bcy);
48758 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
48760 c = splitter * acy;
48761 ahi = c - (c - acy);
48763 c = splitter * bcx;
48764 bhi = c - (c - bcx);
48766 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
48769 B[0] = s0 - (_i + bvirt) + (bvirt - t0);
48772 _0 = s1 - (_j - bvirt) + (_i - bvirt);
48775 B[1] = _0 - (_i + bvirt) + (bvirt - t1);
48778 B[2] = _j - (u3 - bvirt) + (_i - bvirt);
48780 var det = estimate(4, B);
48781 var errbound = ccwerrboundB * detsum;
48783 if (det >= errbound || -det >= errbound) {
48788 acxtail = ax - (acx + bvirt) + (bvirt - cx);
48790 bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
48792 acytail = ay - (acy + bvirt) + (bvirt - cy);
48794 bcytail = by - (bcy + bvirt) + (bvirt - cy);
48796 if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
48800 errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
48801 det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
48802 if (det >= errbound || -det >= errbound) return det;
48803 s1 = acxtail * bcy;
48804 c = splitter * acxtail;
48805 ahi = c - (c - acxtail);
48806 alo = acxtail - ahi;
48807 c = splitter * bcy;
48808 bhi = c - (c - bcy);
48810 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
48811 t1 = acytail * bcx;
48812 c = splitter * acytail;
48813 ahi = c - (c - acytail);
48814 alo = acytail - ahi;
48815 c = splitter * bcx;
48816 bhi = c - (c - bcx);
48818 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
48821 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
48824 _0 = s1 - (_j - bvirt) + (_i - bvirt);
48827 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
48830 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
48832 var C1len = sum(4, B, 4, u, C1);
48833 s1 = acx * bcytail;
48834 c = splitter * acx;
48835 ahi = c - (c - acx);
48837 c = splitter * bcytail;
48838 bhi = c - (c - bcytail);
48839 blo = bcytail - bhi;
48840 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
48841 t1 = acy * bcxtail;
48842 c = splitter * acy;
48843 ahi = c - (c - acy);
48845 c = splitter * bcxtail;
48846 bhi = c - (c - bcxtail);
48847 blo = bcxtail - bhi;
48848 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
48851 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
48854 _0 = s1 - (_j - bvirt) + (_i - bvirt);
48857 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
48860 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
48862 var C2len = sum(C1len, C1, 4, u, C2);
48863 s1 = acxtail * bcytail;
48864 c = splitter * acxtail;
48865 ahi = c - (c - acxtail);
48866 alo = acxtail - ahi;
48867 c = splitter * bcytail;
48868 bhi = c - (c - bcytail);
48869 blo = bcytail - bhi;
48870 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
48871 t1 = acytail * bcxtail;
48872 c = splitter * acytail;
48873 ahi = c - (c - acytail);
48874 alo = acytail - ahi;
48875 c = splitter * bcxtail;
48876 bhi = c - (c - bcxtail);
48877 blo = bcxtail - bhi;
48878 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
48881 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
48884 _0 = s1 - (_j - bvirt) + (_i - bvirt);
48887 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
48890 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
48892 var Dlen = sum(C2len, C2, 4, u, D);
48893 return D[Dlen - 1];
48896 function orient2d(ax, ay, bx, by, cx, cy) {
48897 var detleft = (ay - cy) * (bx - cx);
48898 var detright = (ax - cx) * (by - cy);
48899 var det = detleft - detright;
48900 if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) return det;
48901 var detsum = Math.abs(detleft + detright);
48902 if (Math.abs(det) >= ccwerrboundA * detsum) return det;
48903 return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
48907 * Signed area of the triangle (p0, p1, p2)
48908 * @param {Array.<Number>} p0
48909 * @param {Array.<Number>} p1
48910 * @param {Array.<Number>} p2
48914 function signedArea(p0, p1, p2) {
48915 var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
48916 if (res > 0) return -1;
48917 if (res < 0) return 1;
48922 * @param {SweepEvent} e1
48923 * @param {SweepEvent} e2
48927 function compareEvents(e1, e2) {
48929 var p2 = e2.point; // Different x-coordinate
48931 if (p1[0] > p2[0]) return 1;
48932 if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate
48933 // Event with lower y-coordinate is processed first
48935 if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
48936 return specialCases(e1, e2, p1);
48938 /* eslint-disable no-unused-vars */
48940 function specialCases(e1, e2, p1, p2) {
48941 // Same coordinates, but one is a left endpoint and the other is
48942 // a right endpoint. The right endpoint is processed first
48943 if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
48944 // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
48945 // Same coordinates, both events
48946 // are left endpoints or right endpoints.
48949 if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
48950 // the event associate to the bottom segment is processed first
48951 return !e1.isBelow(e2.otherEvent.point) ? 1 : -1;
48954 return !e1.isSubject && e2.isSubject ? 1 : -1;
48956 /* eslint-enable no-unused-vars */
48959 * @param {SweepEvent} se
48960 * @param {Array.<Number>} p
48961 * @param {Queue} queue
48965 function divideSegment(se, p, queue) {
48966 var r = new SweepEvent(p, false, se, se.isSubject);
48967 var l = new SweepEvent(p, true, se.otherEvent, se.isSubject);
48968 /* eslint-disable no-console */
48970 if (equals(se.point, se.otherEvent.point)) {
48971 console.warn('what is that, a collapsed segment?', se);
48973 /* eslint-enable no-console */
48976 r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event
48978 if (compareEvents(l, se.otherEvent) > 0) {
48979 se.otherEvent.left = true;
48981 } // avoid a rounding error. The left event would be processed after the right event
48982 // if (compareEvents(se, r) > 0) {}
48985 se.otherEvent.otherEvent = l;
48992 //const EPS = 1e-9;
48995 * Finds the magnitude of the cross product of two vectors (if we pretend
48996 * they're in three dimensions)
48998 * @param {Object} a First vector
48999 * @param {Object} b Second vector
49001 * @returns {Number} The magnitude of the cross product
49003 function crossProduct(a, b) {
49004 return a[0] * b[1] - a[1] * b[0];
49007 * Finds the dot product of two vectors.
49009 * @param {Object} a First vector
49010 * @param {Object} b Second vector
49012 * @returns {Number} The dot product
49016 function dotProduct(a, b) {
49017 return a[0] * b[0] + a[1] * b[1];
49020 * Finds the intersection (if any) between two line segments a and b, given the
49021 * line segments' end points a1, a2 and b1, b2.
49023 * This algorithm is based on Schneider and Eberly.
49024 * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
49027 * @param {Array.<Number>} a1 point of first line
49028 * @param {Array.<Number>} a2 point of first line
49029 * @param {Array.<Number>} b1 point of second line
49030 * @param {Array.<Number>} b2 point of second line
49031 * @param {Boolean=} noEndpointTouch whether to skip single touchpoints
49032 * (meaning connected segments) as
49034 * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
49035 * intersection. If they overlap, the two end points of the overlapping segment.
49040 function intersection (a1, a2, b1, b2, noEndpointTouch) {
49041 // The algorithm expects our lines in the form P + sd, where P is a point,
49042 // s is on the interval [0, 1], and d is a vector.
49043 // We are passed two points. P can be the first point of each pair. The
49044 // vector, then, could be thought of as the distance (in x and y components)
49045 // from the first point to the second point.
49046 // So first, let's make our vectors:
49047 var va = [a2[0] - a1[0], a2[1] - a1[1]];
49048 var vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form:
49050 /* eslint-disable arrow-body-style */
49052 function toPoint(p, s, d) {
49053 return [p[0] + s * d[0], p[1] + s * d[1]];
49055 /* eslint-enable arrow-body-style */
49056 // The rest is pretty much a straight port of the algorithm.
49059 var e = [b1[0] - a1[0], b1[1] - a1[1]];
49060 var kross = crossProduct(va, vb);
49061 var sqrKross = kross * kross;
49062 var sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb);
49063 // Check for line intersection. This works because of the properties of the
49064 // cross product -- specifically, two vectors are parallel if and only if the
49065 // cross product is the 0 vector. The full calculation involves relative error
49066 // to account for possible very small line segments. See Schneider & Eberly
49070 /* EPS * sqrLenB * sqLenA */
49072 // If they're not parallel, then (because these are line segments) they
49073 // still might not actually intersect. This code checks that the
49074 // intersection point of the lines is actually on both line segments.
49075 var s = crossProduct(e, vb) / kross;
49077 if (s < 0 || s > 1) {
49078 // not on line segment a
49082 var t = crossProduct(e, va) / kross;
49084 if (t < 0 || t > 1) {
49085 // not on line segment b
49089 if (s === 0 || s === 1) {
49090 // on an endpoint of line segment a
49091 return noEndpointTouch ? null : [toPoint(a1, s, va)];
49094 if (t === 0 || t === 1) {
49095 // on an endpoint of line segment b
49096 return noEndpointTouch ? null : [toPoint(b1, t, vb)];
49099 return [toPoint(a1, s, va)];
49100 } // If we've reached this point, then the lines are either parallel or the
49101 // same, but the segments could overlap partially or fully, or not at all.
49102 // So we need to find the overlap, if any. To do that, we can use e, which is
49103 // the (vector) difference between the two initial points. If this is parallel
49104 // with the line itself, then the two lines are the same line, and there will
49106 //const sqrLenE = dotProduct(e, e);
49109 kross = crossProduct(e, va);
49110 sqrKross = kross * kross;
49113 /* EPS * sqLenB * sqLenE */
49115 // Lines are just parallel, not the same. No overlap.
49119 var sa = dotProduct(va, e) / sqrLenA;
49120 var sb = sa + dotProduct(va, vb) / sqrLenA;
49121 var smin = Math.min(sa, sb);
49122 var smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from
49123 // Schneider & Eberly, just inlined into this function.
49125 if (smin <= 1 && smax >= 0) {
49126 // overlap on an end point
49128 return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
49132 return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
49135 if (noEndpointTouch && smin === 0 && smax === 1) return null; // There's overlap on a segment -- two points of intersection. Return both.
49137 return [toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va)];
49144 * @param {SweepEvent} se1
49145 * @param {SweepEvent} se2
49146 * @param {Queue} queue
49150 function possibleIntersection(se1, se2, queue) {
49151 // that disallows self-intersecting polygons,
49152 // did cost us half a day, so I'll leave it
49154 // if (se1.isSubject === se2.isSubject) return;
49155 var inter = intersection(se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
49156 var nintersections = inter ? inter.length : 0;
49157 if (nintersections === 0) return 0; // no intersection
49158 // the line segments intersect at an endpoint of both line segments
49160 if (nintersections === 1 && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) {
49164 if (nintersections === 2 && se1.isSubject === se2.isSubject) {
49165 // if(se1.contourId === se2.contourId){
49166 // console.warn('Edges of the same polygon overlap',
49167 // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
49169 //throw new Error('Edges of the same polygon overlap');
49171 } // The line segments associated to se1 and se2 intersect
49174 if (nintersections === 1) {
49175 // if the intersection point is not an endpoint of se1
49176 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
49177 divideSegment(se1, inter[0], queue);
49178 } // if the intersection point is not an endpoint of se2
49181 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
49182 divideSegment(se2, inter[0], queue);
49186 } // The line segments associated to se1 and se2 overlap
49190 var leftCoincide = false;
49191 var rightCoincide = false;
49193 if (equals(se1.point, se2.point)) {
49194 leftCoincide = true; // linked
49195 } else if (compareEvents(se1, se2) === 1) {
49196 events.push(se2, se1);
49198 events.push(se1, se2);
49201 if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
49202 rightCoincide = true;
49203 } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
49204 events.push(se2.otherEvent, se1.otherEvent);
49206 events.push(se1.otherEvent, se2.otherEvent);
49209 if (leftCoincide && rightCoincide || leftCoincide) {
49210 // both line segments are equal or share the left endpoint
49211 se2.type = NON_CONTRIBUTING;
49212 se1.type = se2.inOut === se1.inOut ? SAME_TRANSITION : DIFFERENT_TRANSITION;
49214 if (leftCoincide && !rightCoincide) {
49215 // honestly no idea, but changing events selection from [2, 1]
49216 // to [0, 1] fixes the overlapping self-intersecting polygons issue
49217 divideSegment(events[1].otherEvent, events[0].point, queue);
49221 } // the line segments share the right endpoint
49224 if (rightCoincide) {
49225 divideSegment(events[0], events[1].point, queue);
49227 } // no line segment includes totally the other one
49230 if (events[0] !== events[3].otherEvent) {
49231 divideSegment(events[0], events[1].point, queue);
49232 divideSegment(events[1], events[2].point, queue);
49234 } // one line segment includes the other one
49237 divideSegment(events[0], events[1].point, queue);
49238 divideSegment(events[3].otherEvent, events[2].point, queue);
49243 * @param {SweepEvent} le1
49244 * @param {SweepEvent} le2
49248 function compareSegments(le1, le2) {
49249 if (le1 === le2) return 0; // Segments are not collinear
49251 if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
49252 // If they share their left endpoint use the right endpoint to sort
49253 if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort
49255 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
49256 // into S after the line segment associated to e2 ?
49258 if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted
49259 // into S after the line segment associated to e1
49261 return le1.isBelow(le2.point) ? -1 : 1;
49264 if (le1.isSubject === le2.isSubject) {
49266 var p1 = le1.point,
49269 if (p1[0] === p2[0] && p1[1] === p2[1]
49270 /*equals(le1.point, le2.point)*/
49272 p1 = le1.otherEvent.point;
49273 p2 = le2.otherEvent.point;
49274 if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;else return le1.contourId > le2.contourId ? 1 : -1;
49277 // Segments are collinear, but belong to separate polygons
49278 return le1.isSubject ? -1 : 1;
49281 return compareEvents(le1, le2) === 1 ? 1 : -1;
49284 function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
49285 var sweepLine = new SplayTree(compareSegments);
49286 var sortedEvents = [];
49287 var rightbound = Math.min(sbbox[2], cbbox[2]);
49288 var prev, next, begin;
49290 while (eventQueue.length !== 0) {
49291 var event = eventQueue.pop();
49292 sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here
49294 if (operation === INTERSECTION && event.point[0] > rightbound || operation === DIFFERENCE && event.point[0] > sbbox[2]) {
49299 next = prev = sweepLine.insert(event);
49300 begin = sweepLine.minNode();
49301 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
49302 next = sweepLine.next(next);
49303 var prevEvent = prev ? prev.key : null;
49304 var prevprevEvent = void 0;
49305 computeFields(event, prevEvent, operation);
49308 if (possibleIntersection(event, next.key, eventQueue) === 2) {
49309 computeFields(event, prevEvent, operation);
49310 computeFields(event, next.key, operation);
49315 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
49316 var prevprev = prev;
49317 if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);else prevprev = null;
49318 prevprevEvent = prevprev ? prevprev.key : null;
49319 computeFields(prevEvent, prevprevEvent, operation);
49320 computeFields(event, prevEvent, operation);
49324 event = event.otherEvent;
49325 next = prev = sweepLine.find(event);
49327 if (prev && next) {
49328 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
49329 next = sweepLine.next(next);
49330 sweepLine.remove(event);
49332 if (next && prev) {
49333 possibleIntersection(prev.key, next.key, eventQueue);
49339 return sortedEvents;
49342 var Contour = /*#__PURE__*/function () {
49348 function Contour() {
49349 _classCallCheck(this, Contour);
49353 this.holeOf = null;
49357 _createClass(Contour, [{
49359 value: function isExterior() {
49360 return this.holeOf == null;
49368 * @param {Array.<SweepEvent>} sortedEvents
49369 * @return {Array.<SweepEvent>}
49372 function orderEvents(sortedEvents) {
49373 var event, i, len, tmp;
49374 var resultEvents = [];
49376 for (i = 0, len = sortedEvents.length; i < len; i++) {
49377 event = sortedEvents[i];
49379 if (event.left && event.inResult || !event.left && event.otherEvent.inResult) {
49380 resultEvents.push(event);
49382 } // Due to overlapping edges the resultEvents array can be not wholly sorted
49385 var sorted = false;
49390 for (i = 0, len = resultEvents.length; i < len; i++) {
49391 if (i + 1 < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
49392 tmp = resultEvents[i];
49393 resultEvents[i] = resultEvents[i + 1];
49394 resultEvents[i + 1] = tmp;
49400 for (i = 0, len = resultEvents.length; i < len; i++) {
49401 event = resultEvents[i];
49402 event.otherPos = i;
49403 } // imagine, the right event is found in the beginning of the queue,
49404 // when his left counterpart is not marked yet
49407 for (i = 0, len = resultEvents.length; i < len; i++) {
49408 event = resultEvents[i];
49411 tmp = event.otherPos;
49412 event.otherPos = event.otherEvent.otherPos;
49413 event.otherEvent.otherPos = tmp;
49417 return resultEvents;
49420 * @param {Number} pos
49421 * @param {Array.<SweepEvent>} resultEvents
49422 * @param {Object>} processed
49427 function nextPos(pos, resultEvents, processed, origPos) {
49428 var newPos = pos + 1,
49429 p = resultEvents[pos].point,
49431 var length = resultEvents.length;
49432 if (newPos < length) p1 = resultEvents[newPos].point;
49434 while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
49435 if (!processed[newPos]) {
49441 p1 = resultEvents[newPos].point;
49446 while (processed[newPos] && newPos > origPos) {
49453 function initializeContourFromContext(event, contours, contourId) {
49454 var contour = new Contour();
49456 if (event.prevInResult != null) {
49457 var prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id,
49458 // because we must have already processed it (i.e., assigned an output contour id)
49459 // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
49462 var lowerContourId = prevInResult.outputContourId;
49463 var lowerResultTransition = prevInResult.resultTransition;
49465 if (lowerResultTransition > 0) {
49466 // We are inside. Now we have to check if the thing below us is another hole or
49467 // an exterior contour.
49468 var lowerContour = contours[lowerContourId];
49470 if (lowerContour.holeOf != null) {
49471 // The lower contour is a hole => Connect the new contour as a hole to its parent,
49472 // and use same depth.
49473 var parentContourId = lowerContour.holeOf;
49474 contours[parentContourId].holeIds.push(contourId);
49475 contour.holeOf = parentContourId;
49476 contour.depth = contours[lowerContourId].depth;
49478 // The lower contour is an exterior contour => Connect the new contour as a hole,
49479 // and increment depth.
49480 contours[lowerContourId].holeIds.push(contourId);
49481 contour.holeOf = lowerContourId;
49482 contour.depth = contours[lowerContourId].depth + 1;
49485 // We are outside => this contour is an exterior contour of same depth.
49486 contour.holeOf = null;
49487 contour.depth = contours[lowerContourId].depth;
49490 // There is no lower/previous contour => this contour is an exterior contour of depth 0.
49491 contour.holeOf = null;
49498 * @param {Array.<SweepEvent>} sortedEvents
49499 * @return {Array.<*>} polygons
49503 function connectEdges(sortedEvents) {
49505 var resultEvents = orderEvents(sortedEvents); // "false"-filled array
49507 var processed = {};
49510 var _loop = function _loop() {
49511 if (processed[i]) {
49515 var contourId = contours.length;
49516 var contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID
49518 var markAsProcessed = function markAsProcessed(pos) {
49519 processed[pos] = true;
49520 resultEvents[pos].outputContourId = contourId;
49525 var initial = resultEvents[i].point;
49526 contour.points.push(initial);
49527 /* eslint no-constant-condition: "off" */
49530 markAsProcessed(pos);
49531 pos = resultEvents[pos].otherPos;
49532 markAsProcessed(pos);
49533 contour.points.push(resultEvents[pos].point);
49534 pos = nextPos(pos, resultEvents, processed, origPos);
49536 if (pos == origPos) {
49541 contours.push(contour);
49544 for (i = 0, len = resultEvents.length; i < len; i++) {
49545 var _ret = _loop();
49547 if (_ret === "continue") continue;
49553 var tinyqueue = TinyQueue;
49554 var _default = TinyQueue;
49556 function TinyQueue(data, compare) {
49557 if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
49558 this.data = data || [];
49559 this.length = this.data.length;
49560 this.compare = compare || defaultCompare$1;
49562 if (this.length > 0) {
49563 for (var i = (this.length >> 1) - 1; i >= 0; i--) {
49569 function defaultCompare$1(a, b) {
49570 return a < b ? -1 : a > b ? 1 : 0;
49573 TinyQueue.prototype = {
49574 push: function push(item) {
49575 this.data.push(item);
49578 this._up(this.length - 1);
49580 pop: function pop() {
49581 if (this.length === 0) return undefined;
49582 var top = this.data[0];
49585 if (this.length > 0) {
49586 this.data[0] = this.data[this.length];
49594 peek: function peek() {
49595 return this.data[0];
49597 _up: function _up(pos) {
49598 var data = this.data;
49599 var compare = this.compare;
49600 var item = data[pos];
49603 var parent = pos - 1 >> 1;
49604 var current = data[parent];
49605 if (compare(item, current) >= 0) break;
49606 data[pos] = current;
49612 _down: function _down(pos) {
49613 var data = this.data;
49614 var compare = this.compare;
49615 var halfLength = this.length >> 1;
49616 var item = data[pos];
49618 while (pos < halfLength) {
49619 var left = (pos << 1) + 1;
49620 var right = left + 1;
49621 var best = data[left];
49623 if (right < this.length && compare(data[right], best) < 0) {
49625 best = data[right];
49628 if (compare(best, item) >= 0) break;
49636 tinyqueue["default"] = _default;
49638 var max$5 = Math.max;
49639 var min$8 = Math.min;
49642 function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
49643 var i, len, s1, s2, e1, e2;
49645 for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
49646 s1 = contourOrHole[i];
49647 s2 = contourOrHole[i + 1];
49648 e1 = new SweepEvent(s1, false, undefined, isSubject);
49649 e2 = new SweepEvent(s2, false, e1, isSubject);
49650 e1.otherEvent = e2;
49652 if (s1[0] === s2[0] && s1[1] === s2[1]) {
49653 continue; // skip collapsed edges, or it breaks
49656 e1.contourId = e2.contourId = depth;
49658 if (!isExteriorRing) {
49659 e1.isExteriorRing = false;
49660 e2.isExteriorRing = false;
49663 if (compareEvents(e1, e2) > 0) {
49671 bbox[0] = min$8(bbox[0], x);
49672 bbox[1] = min$8(bbox[1], y);
49673 bbox[2] = max$5(bbox[2], x);
49674 bbox[3] = max$5(bbox[3], y); // Pushing it so the queue is sorted from left to right,
49675 // with object on the left having the highest priority.
49682 function fillQueue(subject, clipping, sbbox, cbbox, operation) {
49683 var eventQueue = new tinyqueue(null, compareEvents);
49684 var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
49686 for (i = 0, ii = subject.length; i < ii; i++) {
49687 polygonSet = subject[i];
49689 for (j = 0, jj = polygonSet.length; j < jj; j++) {
49690 isExteriorRing = j === 0;
49691 if (isExteriorRing) contourId++;
49692 processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
49696 for (i = 0, ii = clipping.length; i < ii; i++) {
49697 polygonSet = clipping[i];
49699 for (j = 0, jj = polygonSet.length; j < jj; j++) {
49700 isExteriorRing = j === 0;
49701 if (operation === DIFFERENCE) isExteriorRing = false;
49702 if (isExteriorRing) contourId++;
49703 processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
49712 function trivialOperation(subject, clipping, operation) {
49715 if (subject.length * clipping.length === 0) {
49716 if (operation === INTERSECTION) {
49718 } else if (operation === DIFFERENCE) {
49720 } else if (operation === UNION || operation === XOR) {
49721 result = subject.length === 0 ? clipping : subject;
49728 function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
49731 if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) {
49732 if (operation === INTERSECTION) {
49734 } else if (operation === DIFFERENCE) {
49736 } else if (operation === UNION || operation === XOR) {
49737 result = subject.concat(clipping);
49744 function _boolean(subject, clipping, operation) {
49745 if (typeof subject[0][0][0] === 'number') {
49746 subject = [subject];
49749 if (typeof clipping[0][0][0] === 'number') {
49750 clipping = [clipping];
49753 var trivial = trivialOperation(subject, clipping, operation);
49756 return trivial === EMPTY ? null : trivial;
49759 var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
49760 var cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time('fill queue');
49762 var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd('fill queue');
49764 trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
49767 return trivial === EMPTY ? null : trivial;
49768 } // console.time('subdivide edges');
49771 var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd('subdivide edges');
49772 // console.time('connect vertices');
49774 var contours = connectEdges(sortedEvents); //console.timeEnd('connect vertices');
49775 // Convert contours to polygons
49779 for (var i = 0; i < contours.length; i++) {
49780 var contour = contours[i];
49782 if (contour.isExterior()) {
49783 // The exterior ring goes first
49784 var rings = [contour.points]; // Followed by holes if any
49786 for (var j = 0; j < contour.holeIds.length; j++) {
49787 var holeId = contour.holeIds[j];
49788 rings.push(contours[holeId].points);
49791 polygons.push(rings);
49798 function union(subject, clipping) {
49799 return _boolean(subject, clipping, UNION);
49802 /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
49803 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
49805 var eLen = nBytes * 8 - mLen - 1;
49806 var eMax = (1 << eLen) - 1;
49807 var eBias = eMax >> 1;
49809 var i = isLE ? nBytes - 1 : 0;
49810 var d = isLE ? -1 : 1;
49811 var s = buffer[offset + i];
49813 e = s & (1 << -nBits) - 1;
49817 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49819 m = e & (1 << -nBits) - 1;
49823 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49827 } else if (e === eMax) {
49828 return m ? NaN : (s ? -1 : 1) * Infinity;
49830 m = m + Math.pow(2, mLen);
49834 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
49837 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
49839 var eLen = nBytes * 8 - mLen - 1;
49840 var eMax = (1 << eLen) - 1;
49841 var eBias = eMax >> 1;
49842 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
49843 var i = isLE ? 0 : nBytes - 1;
49844 var d = isLE ? 1 : -1;
49845 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
49846 value = Math.abs(value);
49848 if (isNaN(value) || value === Infinity) {
49849 m = isNaN(value) ? 1 : 0;
49852 e = Math.floor(Math.log(value) / Math.LN2);
49854 if (value * (c = Math.pow(2, -e)) < 1) {
49859 if (e + eBias >= 1) {
49862 value += rt * Math.pow(2, 1 - eBias);
49865 if (value * c >= 2) {
49870 if (e + eBias >= eMax) {
49873 } else if (e + eBias >= 1) {
49874 m = (value * c - 1) * Math.pow(2, mLen);
49877 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
49882 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
49887 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
49889 buffer[offset + i - d] |= s * 128;
49899 function Pbf(buf) {
49900 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
49903 this.length = this.buf.length;
49906 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
49908 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
49910 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
49912 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
49914 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
49915 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
49916 // data structures (which currently switch structure types at 12 bytes or more)
49918 var TEXT_DECODER_MIN_LENGTH = 12;
49919 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
49921 destroy: function destroy() {
49924 // === READING =================================================================
49925 readFields: function readFields(readField, result, end) {
49926 end = end || this.length;
49928 while (this.pos < end) {
49929 var val = this.readVarint(),
49931 startPos = this.pos;
49932 this.type = val & 0x7;
49933 readField(tag, result, this);
49934 if (this.pos === startPos) this.skip(val);
49939 readMessage: function readMessage(readField, result) {
49940 return this.readFields(readField, result, this.readVarint() + this.pos);
49942 readFixed32: function readFixed32() {
49943 var val = readUInt32(this.buf, this.pos);
49947 readSFixed32: function readSFixed32() {
49948 var val = readInt32(this.buf, this.pos);
49952 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
49953 readFixed64: function readFixed64() {
49954 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
49958 readSFixed64: function readSFixed64() {
49959 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
49963 readFloat: function readFloat() {
49964 var val = ieee754$1.read(this.buf, this.pos, true, 23, 4);
49968 readDouble: function readDouble() {
49969 var val = ieee754$1.read(this.buf, this.pos, true, 52, 8);
49973 readVarint: function readVarint(isSigned) {
49974 var buf = this.buf,
49977 b = buf[this.pos++];
49979 if (b < 0x80) return val;
49980 b = buf[this.pos++];
49981 val |= (b & 0x7f) << 7;
49982 if (b < 0x80) return val;
49983 b = buf[this.pos++];
49984 val |= (b & 0x7f) << 14;
49985 if (b < 0x80) return val;
49986 b = buf[this.pos++];
49987 val |= (b & 0x7f) << 21;
49988 if (b < 0x80) return val;
49990 val |= (b & 0x0f) << 28;
49991 return readVarintRemainder(val, isSigned, this);
49993 readVarint64: function readVarint64() {
49994 // for compatibility with v2.0.1
49995 return this.readVarint(true);
49997 readSVarint: function readSVarint() {
49998 var num = this.readVarint();
49999 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50001 readBoolean: function readBoolean() {
50002 return Boolean(this.readVarint());
50004 readString: function readString() {
50005 var end = this.readVarint() + this.pos;
50006 var pos = this.pos;
50009 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50010 // longer strings are fast with the built-in browser TextDecoder API
50011 return readUtf8TextDecoder(this.buf, pos, end);
50012 } // short strings are fast with our custom implementation
50015 return readUtf8(this.buf, pos, end);
50017 readBytes: function readBytes() {
50018 var end = this.readVarint() + this.pos,
50019 buffer = this.buf.subarray(this.pos, end);
50023 // verbose for performance reasons; doesn't affect gzipped size
50024 readPackedVarint: function readPackedVarint(arr, isSigned) {
50025 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50026 var end = readPackedEnd(this);
50029 while (this.pos < end) {
50030 arr.push(this.readVarint(isSigned));
50035 readPackedSVarint: function readPackedSVarint(arr) {
50036 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50037 var end = readPackedEnd(this);
50040 while (this.pos < end) {
50041 arr.push(this.readSVarint());
50046 readPackedBoolean: function readPackedBoolean(arr) {
50047 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50048 var end = readPackedEnd(this);
50051 while (this.pos < end) {
50052 arr.push(this.readBoolean());
50057 readPackedFloat: function readPackedFloat(arr) {
50058 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50059 var end = readPackedEnd(this);
50062 while (this.pos < end) {
50063 arr.push(this.readFloat());
50068 readPackedDouble: function readPackedDouble(arr) {
50069 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50070 var end = readPackedEnd(this);
50073 while (this.pos < end) {
50074 arr.push(this.readDouble());
50079 readPackedFixed32: function readPackedFixed32(arr) {
50080 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50081 var end = readPackedEnd(this);
50084 while (this.pos < end) {
50085 arr.push(this.readFixed32());
50090 readPackedSFixed32: function readPackedSFixed32(arr) {
50091 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50092 var end = readPackedEnd(this);
50095 while (this.pos < end) {
50096 arr.push(this.readSFixed32());
50101 readPackedFixed64: function readPackedFixed64(arr) {
50102 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
50103 var end = readPackedEnd(this);
50106 while (this.pos < end) {
50107 arr.push(this.readFixed64());
50112 readPackedSFixed64: function readPackedSFixed64(arr) {
50113 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
50114 var end = readPackedEnd(this);
50117 while (this.pos < end) {
50118 arr.push(this.readSFixed64());
50123 skip: function skip(val) {
50124 var type = val & 0x7;
50125 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);
50127 // === WRITING =================================================================
50128 writeTag: function writeTag(tag, type) {
50129 this.writeVarint(tag << 3 | type);
50131 realloc: function realloc(min) {
50132 var length = this.length || 16;
50134 while (length < this.pos + min) {
50138 if (length !== this.length) {
50139 var buf = new Uint8Array(length);
50142 this.length = length;
50145 finish: function finish() {
50146 this.length = this.pos;
50148 return this.buf.subarray(0, this.length);
50150 writeFixed32: function writeFixed32(val) {
50152 writeInt32(this.buf, val, this.pos);
50155 writeSFixed32: function writeSFixed32(val) {
50157 writeInt32(this.buf, val, this.pos);
50160 writeFixed64: function writeFixed64(val) {
50162 writeInt32(this.buf, val & -1, this.pos);
50163 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50166 writeSFixed64: function writeSFixed64(val) {
50168 writeInt32(this.buf, val & -1, this.pos);
50169 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50172 writeVarint: function writeVarint(val) {
50175 if (val > 0xfffffff || val < 0) {
50176 writeBigVarint(val, this);
50181 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
50182 if (val <= 0x7f) return;
50183 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50184 if (val <= 0x7f) return;
50185 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50186 if (val <= 0x7f) return;
50187 this.buf[this.pos++] = val >>> 7 & 0x7f;
50189 writeSVarint: function writeSVarint(val) {
50190 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
50192 writeBoolean: function writeBoolean(val) {
50193 this.writeVarint(Boolean(val));
50195 writeString: function writeString(str) {
50197 this.realloc(str.length * 4);
50198 this.pos++; // reserve 1 byte for short string length
50200 var startPos = this.pos; // write the string directly to the buffer and see how much was written
50202 this.pos = writeUtf8(this.buf, str, this.pos);
50203 var len = this.pos - startPos;
50204 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50206 this.pos = startPos - 1;
50207 this.writeVarint(len);
50210 writeFloat: function writeFloat(val) {
50212 ieee754$1.write(this.buf, val, this.pos, true, 23, 4);
50215 writeDouble: function writeDouble(val) {
50217 ieee754$1.write(this.buf, val, this.pos, true, 52, 8);
50220 writeBytes: function writeBytes(buffer) {
50221 var len = buffer.length;
50222 this.writeVarint(len);
50225 for (var i = 0; i < len; i++) {
50226 this.buf[this.pos++] = buffer[i];
50229 writeRawMessage: function writeRawMessage(fn, obj) {
50230 this.pos++; // reserve 1 byte for short message length
50231 // write the message directly to the buffer and see how much was written
50233 var startPos = this.pos;
50235 var len = this.pos - startPos;
50236 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50238 this.pos = startPos - 1;
50239 this.writeVarint(len);
50242 writeMessage: function writeMessage(tag, fn, obj) {
50243 this.writeTag(tag, Pbf.Bytes);
50244 this.writeRawMessage(fn, obj);
50246 writePackedVarint: function writePackedVarint(tag, arr) {
50247 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
50249 writePackedSVarint: function writePackedSVarint(tag, arr) {
50250 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
50252 writePackedBoolean: function writePackedBoolean(tag, arr) {
50253 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
50255 writePackedFloat: function writePackedFloat(tag, arr) {
50256 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
50258 writePackedDouble: function writePackedDouble(tag, arr) {
50259 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
50261 writePackedFixed32: function writePackedFixed32(tag, arr) {
50262 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
50264 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
50265 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
50267 writePackedFixed64: function writePackedFixed64(tag, arr) {
50268 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
50270 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
50271 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
50273 writeBytesField: function writeBytesField(tag, buffer) {
50274 this.writeTag(tag, Pbf.Bytes);
50275 this.writeBytes(buffer);
50277 writeFixed32Field: function writeFixed32Field(tag, val) {
50278 this.writeTag(tag, Pbf.Fixed32);
50279 this.writeFixed32(val);
50281 writeSFixed32Field: function writeSFixed32Field(tag, val) {
50282 this.writeTag(tag, Pbf.Fixed32);
50283 this.writeSFixed32(val);
50285 writeFixed64Field: function writeFixed64Field(tag, val) {
50286 this.writeTag(tag, Pbf.Fixed64);
50287 this.writeFixed64(val);
50289 writeSFixed64Field: function writeSFixed64Field(tag, val) {
50290 this.writeTag(tag, Pbf.Fixed64);
50291 this.writeSFixed64(val);
50293 writeVarintField: function writeVarintField(tag, val) {
50294 this.writeTag(tag, Pbf.Varint);
50295 this.writeVarint(val);
50297 writeSVarintField: function writeSVarintField(tag, val) {
50298 this.writeTag(tag, Pbf.Varint);
50299 this.writeSVarint(val);
50301 writeStringField: function writeStringField(tag, str) {
50302 this.writeTag(tag, Pbf.Bytes);
50303 this.writeString(str);
50305 writeFloatField: function writeFloatField(tag, val) {
50306 this.writeTag(tag, Pbf.Fixed32);
50307 this.writeFloat(val);
50309 writeDoubleField: function writeDoubleField(tag, val) {
50310 this.writeTag(tag, Pbf.Fixed64);
50311 this.writeDouble(val);
50313 writeBooleanField: function writeBooleanField(tag, val) {
50314 this.writeVarintField(tag, Boolean(val));
50318 function readVarintRemainder(l, s, p) {
50323 h = (b & 0x70) >> 4;
50324 if (b < 0x80) return toNum(l, h, s);
50326 h |= (b & 0x7f) << 3;
50327 if (b < 0x80) return toNum(l, h, s);
50329 h |= (b & 0x7f) << 10;
50330 if (b < 0x80) return toNum(l, h, s);
50332 h |= (b & 0x7f) << 17;
50333 if (b < 0x80) return toNum(l, h, s);
50335 h |= (b & 0x7f) << 24;
50336 if (b < 0x80) return toNum(l, h, s);
50338 h |= (b & 0x01) << 31;
50339 if (b < 0x80) return toNum(l, h, s);
50340 throw new Error('Expected varint not more than 10 bytes');
50343 function readPackedEnd(pbf) {
50344 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
50347 function toNum(low, high, isSigned) {
50349 return high * 0x100000000 + (low >>> 0);
50352 return (high >>> 0) * 0x100000000 + (low >>> 0);
50355 function writeBigVarint(val, pbf) {
50359 low = val % 0x100000000 | 0;
50360 high = val / 0x100000000 | 0;
50362 low = ~(-val % 0x100000000);
50363 high = ~(-val / 0x100000000);
50365 if (low ^ 0xffffffff) {
50369 high = high + 1 | 0;
50373 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
50374 throw new Error('Given varint doesn\'t fit into 10 bytes');
50378 writeBigVarintLow(low, high, pbf);
50379 writeBigVarintHigh(high, pbf);
50382 function writeBigVarintLow(low, high, pbf) {
50383 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50385 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50387 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50389 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50391 pbf.buf[pbf.pos] = low & 0x7f;
50394 function writeBigVarintHigh(high, pbf) {
50395 var lsb = (high & 0x07) << 4;
50396 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
50398 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50400 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50402 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50404 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50406 pbf.buf[pbf.pos++] = high & 0x7f;
50409 function makeRoomForExtraLength(startPos, len, pbf) {
50410 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
50412 pbf.realloc(extraLen);
50414 for (var i = pbf.pos - 1; i >= startPos; i--) {
50415 pbf.buf[i + extraLen] = pbf.buf[i];
50419 function _writePackedVarint(arr, pbf) {
50420 for (var i = 0; i < arr.length; i++) {
50421 pbf.writeVarint(arr[i]);
50425 function _writePackedSVarint(arr, pbf) {
50426 for (var i = 0; i < arr.length; i++) {
50427 pbf.writeSVarint(arr[i]);
50431 function _writePackedFloat(arr, pbf) {
50432 for (var i = 0; i < arr.length; i++) {
50433 pbf.writeFloat(arr[i]);
50437 function _writePackedDouble(arr, pbf) {
50438 for (var i = 0; i < arr.length; i++) {
50439 pbf.writeDouble(arr[i]);
50443 function _writePackedBoolean(arr, pbf) {
50444 for (var i = 0; i < arr.length; i++) {
50445 pbf.writeBoolean(arr[i]);
50449 function _writePackedFixed(arr, pbf) {
50450 for (var i = 0; i < arr.length; i++) {
50451 pbf.writeFixed32(arr[i]);
50455 function _writePackedSFixed(arr, pbf) {
50456 for (var i = 0; i < arr.length; i++) {
50457 pbf.writeSFixed32(arr[i]);
50461 function _writePackedFixed2(arr, pbf) {
50462 for (var i = 0; i < arr.length; i++) {
50463 pbf.writeFixed64(arr[i]);
50467 function _writePackedSFixed2(arr, pbf) {
50468 for (var i = 0; i < arr.length; i++) {
50469 pbf.writeSFixed64(arr[i]);
50471 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
50474 function readUInt32(buf, pos) {
50475 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
50478 function writeInt32(buf, val, pos) {
50480 buf[pos + 1] = val >>> 8;
50481 buf[pos + 2] = val >>> 16;
50482 buf[pos + 3] = val >>> 24;
50485 function readInt32(buf, pos) {
50486 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
50489 function readUtf8(buf, pos, end) {
50495 var c = null; // codepoint
50497 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
50498 if (i + bytesPerSequence > end) break;
50501 if (bytesPerSequence === 1) {
50505 } else if (bytesPerSequence === 2) {
50508 if ((b1 & 0xC0) === 0x80) {
50509 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
50515 } else if (bytesPerSequence === 3) {
50519 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
50520 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
50522 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
50526 } else if (bytesPerSequence === 4) {
50531 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
50532 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
50534 if (c <= 0xFFFF || c >= 0x110000) {
50542 bytesPerSequence = 1;
50543 } else if (c > 0xFFFF) {
50545 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
50546 c = 0xDC00 | c & 0x3FF;
50549 str += String.fromCharCode(c);
50550 i += bytesPerSequence;
50556 function readUtf8TextDecoder(buf, pos, end) {
50557 return utf8TextDecoder.decode(buf.subarray(pos, end));
50560 function writeUtf8(buf, str, pos) {
50561 for (var i = 0, c, lead; i < str.length; i++) {
50562 c = str.charCodeAt(i); // code point
50564 if (c > 0xD7FF && c < 0xE000) {
50573 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
50577 if (c > 0xDBFF || i + 1 === str.length) {
50598 buf[pos++] = c >> 0x6 | 0xC0;
50601 buf[pos++] = c >> 0xC | 0xE0;
50603 buf[pos++] = c >> 0x12 | 0xF0;
50604 buf[pos++] = c >> 0xC & 0x3F | 0x80;
50607 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
50610 buf[pos++] = c & 0x3F | 0x80;
50617 var pointGeometry = Point;
50619 * A standalone point geometry with useful accessor, comparison, and
50620 * modification methods.
50623 * @param {Number} x the x-coordinate. this could be longitude or screen
50624 * pixels, or any other sort of unit.
50625 * @param {Number} y the y-coordinate. this could be latitude or screen
50626 * pixels, or any other sort of unit.
50628 * var point = new Point(-77, 38);
50631 function Point(x, y) {
50636 Point.prototype = {
50638 * Clone this point, returning a new point that can be modified
50639 * without affecting the old one.
50640 * @return {Point} the clone
50642 clone: function clone() {
50643 return new Point(this.x, this.y);
50647 * Add this point's x & y coordinates to another point,
50648 * yielding a new point.
50649 * @param {Point} p the other point
50650 * @return {Point} output point
50652 add: function add(p) {
50653 return this.clone()._add(p);
50657 * Subtract this point's x & y coordinates to from point,
50658 * yielding a new point.
50659 * @param {Point} p the other point
50660 * @return {Point} output point
50662 sub: function sub(p) {
50663 return this.clone()._sub(p);
50667 * Multiply this point's x & y coordinates by point,
50668 * yielding a new point.
50669 * @param {Point} p the other point
50670 * @return {Point} output point
50672 multByPoint: function multByPoint(p) {
50673 return this.clone()._multByPoint(p);
50677 * Divide this point's x & y coordinates by point,
50678 * yielding a new point.
50679 * @param {Point} p the other point
50680 * @return {Point} output point
50682 divByPoint: function divByPoint(p) {
50683 return this.clone()._divByPoint(p);
50687 * Multiply this point's x & y coordinates by a factor,
50688 * yielding a new point.
50689 * @param {Point} k factor
50690 * @return {Point} output point
50692 mult: function mult(k) {
50693 return this.clone()._mult(k);
50697 * Divide this point's x & y coordinates by a factor,
50698 * yielding a new point.
50699 * @param {Point} k factor
50700 * @return {Point} output point
50702 div: function div(k) {
50703 return this.clone()._div(k);
50707 * Rotate this point around the 0, 0 origin by an angle a,
50709 * @param {Number} a angle to rotate around, in radians
50710 * @return {Point} output point
50712 rotate: function rotate(a) {
50713 return this.clone()._rotate(a);
50717 * Rotate this point around p point by an angle a,
50719 * @param {Number} a angle to rotate around, in radians
50720 * @param {Point} p Point to rotate around
50721 * @return {Point} output point
50723 rotateAround: function rotateAround(a, p) {
50724 return this.clone()._rotateAround(a, p);
50728 * Multiply this point by a 4x1 transformation matrix
50729 * @param {Array<Number>} m transformation matrix
50730 * @return {Point} output point
50732 matMult: function matMult(m) {
50733 return this.clone()._matMult(m);
50737 * Calculate this point but as a unit vector from 0, 0, meaning
50738 * that the distance from the resulting point to the 0, 0
50739 * coordinate will be equal to 1 and the angle from the resulting
50740 * point to the 0, 0 coordinate will be the same as before.
50741 * @return {Point} unit vector point
50743 unit: function unit() {
50744 return this.clone()._unit();
50748 * Compute a perpendicular point, where the new y coordinate
50749 * is the old x coordinate and the new x coordinate is the old y
50750 * coordinate multiplied by -1
50751 * @return {Point} perpendicular point
50753 perp: function perp() {
50754 return this.clone()._perp();
50758 * Return a version of this point with the x & y coordinates
50759 * rounded to integers.
50760 * @return {Point} rounded point
50762 round: function round() {
50763 return this.clone()._round();
50767 * Return the magitude of this point: this is the Euclidean
50768 * distance from the 0, 0 coordinate to this point's x and y
50770 * @return {Number} magnitude
50772 mag: function mag() {
50773 return Math.sqrt(this.x * this.x + this.y * this.y);
50777 * Judge whether this point is equal to another point, returning
50779 * @param {Point} other the other point
50780 * @return {boolean} whether the points are equal
50782 equals: function equals(other) {
50783 return this.x === other.x && this.y === other.y;
50787 * Calculate the distance from this point to another point
50788 * @param {Point} p the other point
50789 * @return {Number} distance
50791 dist: function dist(p) {
50792 return Math.sqrt(this.distSqr(p));
50796 * Calculate the distance from this point to another point,
50797 * without the square root step. Useful if you're comparing
50798 * relative distances.
50799 * @param {Point} p the other point
50800 * @return {Number} distance
50802 distSqr: function distSqr(p) {
50803 var dx = p.x - this.x,
50805 return dx * dx + dy * dy;
50809 * Get the angle from the 0, 0 coordinate to this point, in radians
50811 * @return {Number} angle
50813 angle: function angle() {
50814 return Math.atan2(this.y, this.x);
50818 * Get the angle from this point to another point, in radians
50819 * @param {Point} b the other point
50820 * @return {Number} angle
50822 angleTo: function angleTo(b) {
50823 return Math.atan2(this.y - b.y, this.x - b.x);
50827 * Get the angle between this point and another point, in radians
50828 * @param {Point} b the other point
50829 * @return {Number} angle
50831 angleWith: function angleWith(b) {
50832 return this.angleWithSep(b.x, b.y);
50836 * Find the angle of the two vectors, solving the formula for
50837 * the cross product a x b = |a||b|sin(θ) for θ.
50838 * @param {Number} x the x-coordinate
50839 * @param {Number} y the y-coordinate
50840 * @return {Number} the angle in radians
50842 angleWithSep: function angleWithSep(x, y) {
50843 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
50845 _matMult: function _matMult(m) {
50846 var x = m[0] * this.x + m[1] * this.y,
50847 y = m[2] * this.x + m[3] * this.y;
50852 _add: function _add(p) {
50857 _sub: function _sub(p) {
50862 _mult: function _mult(k) {
50867 _div: function _div(k) {
50872 _multByPoint: function _multByPoint(p) {
50877 _divByPoint: function _divByPoint(p) {
50882 _unit: function _unit() {
50883 this._div(this.mag());
50887 _perp: function _perp() {
50893 _rotate: function _rotate(angle) {
50894 var cos = Math.cos(angle),
50895 sin = Math.sin(angle),
50896 x = cos * this.x - sin * this.y,
50897 y = sin * this.x + cos * this.y;
50902 _rotateAround: function _rotateAround(angle, p) {
50903 var cos = Math.cos(angle),
50904 sin = Math.sin(angle),
50905 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
50906 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
50911 _round: function _round() {
50912 this.x = Math.round(this.x);
50913 this.y = Math.round(this.y);
50918 * Construct a point from an array if necessary, otherwise if the input
50919 * is already a Point, or an unknown type, return it unchanged
50920 * @param {Array<Number>|Point|*} a any kind of input value
50921 * @return {Point} constructed point, or passed-through value.
50924 * var point = Point.convert([0, 1]);
50925 * // is equivalent to
50926 * var point = new Point(0, 1);
50929 Point.convert = function (a) {
50930 if (a instanceof Point) {
50934 if (Array.isArray(a)) {
50935 return new Point(a[0], a[1]);
50941 var vectortilefeature = VectorTileFeature;
50943 function VectorTileFeature(pbf, end, extent, keys, values) {
50945 this.properties = {};
50946 this.extent = extent;
50947 this.type = 0; // Private
50950 this._geometry = -1;
50952 this._values = values;
50953 pbf.readFields(readFeature, this, end);
50956 function readFeature(tag, feature, pbf) {
50957 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;
50960 function readTag(pbf, feature) {
50961 var end = pbf.readVarint() + pbf.pos;
50963 while (pbf.pos < end) {
50964 var key = feature._keys[pbf.readVarint()],
50965 value = feature._values[pbf.readVarint()];
50967 feature.properties[key] = value;
50971 VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
50973 VectorTileFeature.prototype.loadGeometry = function () {
50974 var pbf = this._pbf;
50975 pbf.pos = this._geometry;
50976 var end = pbf.readVarint() + pbf.pos,
50984 while (pbf.pos < end) {
50986 var cmdLen = pbf.readVarint();
50987 cmd = cmdLen & 0x7;
50988 length = cmdLen >> 3;
50993 if (cmd === 1 || cmd === 2) {
50994 x += pbf.readSVarint();
50995 y += pbf.readSVarint();
50999 if (line) lines.push(line);
51003 line.push(new pointGeometry(x, y));
51004 } else if (cmd === 7) {
51005 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51007 line.push(line[0].clone()); // closePolygon
51010 throw new Error('unknown command ' + cmd);
51014 if (line) lines.push(line);
51018 VectorTileFeature.prototype.bbox = function () {
51019 var pbf = this._pbf;
51020 pbf.pos = this._geometry;
51021 var end = pbf.readVarint() + pbf.pos,
51031 while (pbf.pos < end) {
51033 var cmdLen = pbf.readVarint();
51034 cmd = cmdLen & 0x7;
51035 length = cmdLen >> 3;
51040 if (cmd === 1 || cmd === 2) {
51041 x += pbf.readSVarint();
51042 y += pbf.readSVarint();
51043 if (x < x1) x1 = x;
51044 if (x > x2) x2 = x;
51045 if (y < y1) y1 = y;
51046 if (y > y2) y2 = y;
51047 } else if (cmd !== 7) {
51048 throw new Error('unknown command ' + cmd);
51052 return [x1, y1, x2, y2];
51055 VectorTileFeature.prototype.toGeoJSON = function (x, y, z) {
51056 var size = this.extent * Math.pow(2, z),
51057 x0 = this.extent * x,
51058 y0 = this.extent * y,
51059 coords = this.loadGeometry(),
51060 type = VectorTileFeature.types[this.type],
51064 function project(line) {
51065 for (var j = 0; j < line.length; j++) {
51067 y2 = 180 - (p.y + y0) * 360 / size;
51068 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51072 switch (this.type) {
51076 for (i = 0; i < coords.length; i++) {
51077 points[i] = coords[i][0];
51085 for (i = 0; i < coords.length; i++) {
51086 project(coords[i]);
51092 coords = classifyRings(coords);
51094 for (i = 0; i < coords.length; i++) {
51095 for (j = 0; j < coords[i].length; j++) {
51096 project(coords[i][j]);
51103 if (coords.length === 1) {
51104 coords = coords[0];
51106 type = 'Multi' + type;
51113 coordinates: coords
51115 properties: this.properties
51118 if ('id' in this) {
51119 result.id = this.id;
51123 }; // classifies an array of rings into polygons with outer rings and holes
51126 function classifyRings(rings) {
51127 var len = rings.length;
51128 if (len <= 1) return [rings];
51133 for (var i = 0; i < len; i++) {
51134 var area = signedArea$1(rings[i]);
51135 if (area === 0) continue;
51136 if (ccw === undefined) ccw = area < 0;
51138 if (ccw === area < 0) {
51139 if (polygon) polygons.push(polygon);
51140 polygon = [rings[i]];
51142 polygon.push(rings[i]);
51146 if (polygon) polygons.push(polygon);
51150 function signedArea$1(ring) {
51153 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
51156 sum += (p2.x - p1.x) * (p1.y + p2.y);
51162 var vectortilelayer = VectorTileLayer;
51164 function VectorTileLayer(pbf, end) {
51168 this.extent = 4096;
51169 this.length = 0; // Private
51174 this._features = [];
51175 pbf.readFields(readLayer, this, end);
51176 this.length = this._features.length;
51179 function readLayer(tag, layer, pbf) {
51180 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));
51183 function readValueMessage(pbf) {
51185 end = pbf.readVarint() + pbf.pos;
51187 while (pbf.pos < end) {
51188 var tag = pbf.readVarint() >> 3;
51189 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;
51193 } // return feature `i` from this layer as a `VectorTileFeature`
51196 VectorTileLayer.prototype.feature = function (i) {
51197 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
51198 this._pbf.pos = this._features[i];
51200 var end = this._pbf.readVarint() + this._pbf.pos;
51202 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
51205 var vectortile = VectorTile;
51207 function VectorTile(pbf, end) {
51208 this.layers = pbf.readFields(readTile, {}, end);
51211 function readTile(tag, layers, pbf) {
51213 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
51214 if (layer.length) layers[layer.name] = layer;
51218 var VectorTile$1 = vectortile;
51219 var VectorTileFeature$1 = vectortilefeature;
51220 var VectorTileLayer$1 = vectortilelayer;
51222 VectorTile: VectorTile$1,
51223 VectorTileFeature: VectorTileFeature$1,
51224 VectorTileLayer: VectorTileLayer$1
51227 var tiler$7 = utilTiler().tileSize(512).margin(1);
51228 var dispatch$8 = dispatch('loadedData');
51232 function abortRequest$7(controller) {
51233 controller.abort();
51236 function vtToGeoJSON(data, tile, mergeCache) {
51237 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
51238 var layers = Object.keys(vectorTile$1.layers);
51240 if (!Array.isArray(layers)) {
51245 layers.forEach(function (layerID) {
51246 var layer = vectorTile$1.layers[layerID];
51249 for (var i = 0; i < layer.length; i++) {
51250 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51251 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
51253 if (geometry.type === 'Polygon') {
51254 geometry.type = 'MultiPolygon';
51255 geometry.coordinates = [geometry.coordinates];
51258 var isClipped = false; // Clip to tile bounds
51260 if (geometry.type === 'MultiPolygon') {
51261 var featureClip = bboxClip(feature, tile.extent.rectangle());
51263 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
51264 // feature = featureClip;
51268 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
51270 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
51271 } // Generate some unique IDs and add some metadata
51274 var featurehash = utilHashcode(fastJsonStableStringify(feature));
51275 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
51276 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
51277 feature.__featurehash__ = featurehash;
51278 feature.__propertyhash__ = propertyhash;
51279 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
51281 if (isClipped && geometry.type === 'MultiPolygon') {
51282 var merged = mergeCache[propertyhash];
51284 if (merged && merged.length) {
51285 var other = merged[0];
51286 var coords = union(feature.geometry.coordinates, other.geometry.coordinates);
51288 if (!coords || !coords.length) {
51289 continue; // something failed in martinez union
51292 merged.push(feature);
51294 for (var j = 0; j < merged.length; j++) {
51295 // all these features get...
51296 merged[j].geometry.coordinates = coords; // same coords
51298 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
51301 mergeCache[propertyhash] = [feature];
51310 function loadTile(source, tile) {
51311 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
51312 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
51313 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
51314 var subdomains = r.split(',');
51315 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
51317 var controller = new AbortController();
51318 source.inflight[tile.id] = controller;
51320 signal: controller.signal
51321 }).then(function (response) {
51322 if (!response.ok) {
51323 throw new Error(response.status + ' ' + response.statusText);
51326 source.loaded[tile.id] = [];
51327 delete source.inflight[tile.id];
51328 return response.arrayBuffer();
51329 }).then(function (data) {
51331 throw new Error('No Data');
51334 var z = tile.xyz[2];
51336 if (!source.canMerge[z]) {
51337 source.canMerge[z] = {}; // initialize mergeCache
51340 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
51341 dispatch$8.call('loadedData');
51342 })["catch"](function () {
51343 source.loaded[tile.id] = [];
51344 delete source.inflight[tile.id];
51348 var serviceVectorTile = {
51349 init: function init() {
51354 this.event = utilRebind(this, dispatch$8, 'on');
51356 reset: function reset() {
51357 for (var sourceID in _vtCache) {
51358 var source = _vtCache[sourceID];
51360 if (source && source.inflight) {
51361 Object.values(source.inflight).forEach(abortRequest$7);
51367 addSource: function addSource(sourceID, template) {
51368 _vtCache[sourceID] = {
51369 template: template,
51374 return _vtCache[sourceID];
51376 data: function data(sourceID, projection) {
51377 var source = _vtCache[sourceID];
51378 if (!source) return [];
51379 var tiles = tiler$7.getTiles(projection);
51383 for (var i = 0; i < tiles.length; i++) {
51384 var features = source.loaded[tiles[i].id];
51385 if (!features || !features.length) continue;
51387 for (var j = 0; j < features.length; j++) {
51388 var feature = features[j];
51389 var hash = feature.__featurehash__;
51390 if (seen[hash]) continue;
51391 seen[hash] = true; // return a shallow copy, because the hash may change
51392 // later if this feature gets merged with another
51394 results.push(Object.assign({}, feature)); // shallow copy
51400 loadTiles: function loadTiles(sourceID, template, projection) {
51401 var source = _vtCache[sourceID];
51404 source = this.addSource(sourceID, template);
51407 var tiles = tiler$7.getTiles(projection); // abort inflight requests that are no longer needed
51409 Object.keys(source.inflight).forEach(function (k) {
51410 var wanted = tiles.find(function (tile) {
51411 return k === tile.id;
51415 abortRequest$7(source.inflight[k]);
51416 delete source.inflight[k];
51419 tiles.forEach(function (tile) {
51420 loadTile(source, tile);
51423 cache: function cache() {
51428 var apibase$3 = 'https://www.wikidata.org/w/api.php?';
51429 var _wikidataCache = {};
51430 var serviceWikidata = {
51431 init: function init() {},
51432 reset: function reset() {
51433 _wikidataCache = {};
51435 // Search for Wikidata items matching the query
51436 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
51438 if (callback) callback('No query', {});
51442 var lang = this.languagesToQuery()[0];
51443 var url = apibase$3 + utilQsString({
51444 action: 'wbsearchentities',
51449 // the language to search
51451 // the language for the label and description in the result
51456 d3_json(url).then(function (result) {
51457 if (result && result.error) {
51458 throw new Error(result.error);
51461 if (callback) callback(null, result.search || {});
51462 })["catch"](function (err) {
51463 if (callback) callback(err.message, {});
51466 // Given a Wikipedia language and article title,
51467 // return an array of corresponding Wikidata entities.
51468 itemsByTitle: function itemsByTitle(lang, title, callback) {
51470 if (callback) callback('No title', {});
51474 lang = lang || 'en';
51475 var url = apibase$3 + utilQsString({
51476 action: 'wbgetentities',
51479 sites: lang.replace(/-/g, '_') + 'wiki',
51482 // shrink response by filtering to one language
51485 d3_json(url).then(function (result) {
51486 if (result && result.error) {
51487 throw new Error(result.error);
51490 if (callback) callback(null, result.entities || {});
51491 })["catch"](function (err) {
51492 if (callback) callback(err.message, {});
51495 languagesToQuery: function languagesToQuery() {
51496 return _mainLocalizer.localeCodes().map(function (code) {
51497 return code.toLowerCase();
51498 }).filter(function (code) {
51499 // HACK: en-us isn't a wikidata language. We should really be filtering by
51500 // the languages known to be supported by wikidata.
51501 return code !== 'en-us';
51504 entityByQID: function entityByQID(qid, callback) {
51506 callback('No qid', {});
51510 if (_wikidataCache[qid]) {
51511 if (callback) callback(null, _wikidataCache[qid]);
51515 var langs = this.languagesToQuery();
51516 var url = apibase$3 + utilQsString({
51517 action: 'wbgetentities',
51521 props: 'labels|descriptions|claims|sitelinks',
51522 sitefilter: langs.map(function (d) {
51525 languages: langs.join('|'),
51526 languagefallback: 1,
51529 d3_json(url).then(function (result) {
51530 if (result && result.error) {
51531 throw new Error(result.error);
51534 if (callback) callback(null, result.entities[qid] || {});
51535 })["catch"](function (err) {
51536 if (callback) callback(err.message, {});
51539 // Pass `params` object of the form:
51541 // qid: 'string' // brand wikidata (e.g. 'Q37158')
51544 // Get an result object used to display tag documentation
51546 // title: 'string',
51547 // description: 'string',
51548 // editURL: 'string',
51549 // imageURL: 'string',
51550 // wiki: { title: 'string', text: 'string', url: 'string' }
51553 getDocs: function getDocs(params, callback) {
51554 var langs = this.languagesToQuery();
51555 this.entityByQID(params.qid, function (err, entity) {
51556 if (err || !entity) {
51557 callback(err || 'No entity');
51565 var code = langs[i];
51567 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
51568 description = entity.descriptions[code];
51573 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
51577 description: description ? description.value : '',
51578 descriptionLocaleCode: description ? description.language : '',
51579 editURL: 'https://www.wikidata.org/wiki/' + entity.id
51582 if (entity.claims) {
51583 var imageroot = 'https://commons.wikimedia.org/w/index.php';
51584 var props = ['P154', 'P18']; // logo image, image
51588 for (i = 0; i < props.length; i++) {
51589 prop = entity.claims[props[i]];
51591 if (prop && Object.keys(prop).length > 0) {
51592 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
51595 result.imageURL = imageroot + '?' + utilQsString({
51596 title: 'Special:Redirect/file/' + image,
51605 if (entity.sitelinks) {
51606 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
51608 for (i = 0; i < langs.length; i++) {
51609 // check each, in order of preference
51610 var w = langs[i] + 'wiki';
51612 if (entity.sitelinks[w]) {
51613 var title = entity.sitelinks[w].title;
51614 var tKey = 'inspector.wiki_reference';
51616 if (!englishLocale && langs[i] === 'en') {
51617 // user's locale isn't English but
51618 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
51624 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
51631 callback(null, result);
51636 var endpoint = 'https://en.wikipedia.org/w/api.php?';
51637 var serviceWikipedia = {
51638 init: function init() {},
51639 reset: function reset() {},
51640 search: function search(lang, query, callback) {
51642 if (callback) callback('No Query', []);
51646 lang = lang || 'en';
51647 var url = endpoint.replace('en', lang) + utilQsString({
51651 srinfo: 'suggestion',
51656 d3_json(url).then(function (result) {
51657 if (result && result.error) {
51658 throw new Error(result.error);
51659 } else if (!result || !result.query || !result.query.search) {
51660 throw new Error('No Results');
51664 var titles = result.query.search.map(function (d) {
51667 callback(null, titles);
51669 })["catch"](function (err) {
51670 if (callback) callback(err, []);
51673 suggestions: function suggestions(lang, query, callback) {
51675 if (callback) callback('', []);
51679 lang = lang || 'en';
51680 var url = endpoint.replace('en', lang) + utilQsString({
51681 action: 'opensearch',
51688 d3_json(url).then(function (result) {
51689 if (result && result.error) {
51690 throw new Error(result.error);
51691 } else if (!result || result.length < 2) {
51692 throw new Error('No Results');
51695 if (callback) callback(null, result[1] || []);
51696 })["catch"](function (err) {
51697 if (callback) callback(err.message, []);
51700 translations: function translations(lang, title, callback) {
51702 if (callback) callback('No Title');
51706 var url = endpoint.replace('en', lang) + utilQsString({
51714 d3_json(url).then(function (result) {
51715 if (result && result.error) {
51716 throw new Error(result.error);
51717 } else if (!result || !result.query || !result.query.pages) {
51718 throw new Error('No Results');
51722 var list = result.query.pages[Object.keys(result.query.pages)[0]];
51723 var translations = {};
51725 if (list && list.langlinks) {
51726 list.langlinks.forEach(function (d) {
51727 translations[d.lang] = d['*'];
51731 callback(null, translations);
51733 })["catch"](function (err) {
51734 if (callback) callback(err.message);
51740 geocoder: serviceNominatim,
51741 keepRight: serviceKeepRight,
51742 improveOSM: serviceImproveOSM,
51743 osmose: serviceOsmose,
51744 mapillary: serviceMapillary,
51745 openstreetcam: serviceOpenstreetcam,
51747 osmWikibase: serviceOsmWikibase,
51748 maprules: serviceMapRules,
51749 streetside: serviceStreetside,
51750 taginfo: serviceTaginfo,
51751 vectorTile: serviceVectorTile,
51752 wikidata: serviceWikidata,
51753 wikipedia: serviceWikipedia
51756 function svgIcon(name, svgklass, useklass) {
51757 return function drawIcon(selection) {
51758 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);
51762 function uiNoteComments() {
51765 function noteComments(selection) {
51766 if (_note.isNew()) return; // don't draw .comments-container
51768 var comments = selection.selectAll('.comments-container').data([0]);
51769 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
51770 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
51771 commentEnter.append('div').attr('class', function (d) {
51772 return 'comment-avatar user-' + d.uid;
51773 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51774 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
51775 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
51776 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
51777 var selection = select(this);
51778 var osm = services.osm;
51780 if (osm && d.user) {
51781 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
51784 selection.html(function (d) {
51785 return d.user || _t.html('note.anonymous');
51788 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
51789 return _t('note.status.' + d.action, {
51790 when: localeDateString(d.date)
51793 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
51795 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
51796 comments.call(replaceAvatars);
51799 function replaceAvatars(selection) {
51800 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
51801 var osm = services.osm;
51802 if (showThirdPartyIcons !== 'true' || !osm) return;
51803 var uids = {}; // gather uids in the comment thread
51805 _note.comments.forEach(function (d) {
51806 if (d.uid) uids[d.uid] = true;
51809 Object.keys(uids).forEach(function (uid) {
51810 osm.loadUser(uid, function (err, user) {
51811 if (!user || !user.image_url) return;
51812 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);
51817 function localeDateString(s) {
51818 if (!s) return null;
51824 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
51826 var d = new Date(s);
51827 if (isNaN(d.getTime())) return null;
51828 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51831 noteComments.note = function (val) {
51832 if (!arguments.length) return _note;
51834 return noteComments;
51837 return noteComments;
51840 function uiNoteHeader() {
51843 function noteHeader(selection) {
51844 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
51845 return d.status + d.id;
51847 header.exit().remove();
51848 var headerEnter = header.enter().append('div').attr('class', 'note-header');
51849 var iconEnter = headerEnter.append('div').attr('class', function (d) {
51850 return 'note-header-icon ' + d.status;
51851 }).classed('new', function (d) {
51854 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
51855 iconEnter.each(function (d) {
51856 var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
51857 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
51859 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
51860 if (_note.isNew()) {
51861 return _t('note.new');
51864 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
51868 noteHeader.note = function (val) {
51869 if (!arguments.length) return _note;
51877 function uiNoteReport() {
51880 function noteReport(selection) {
51883 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
51884 url = services.osm.noteReportURL(_note);
51887 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
51889 link.exit().remove(); // enter
51891 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
51893 }).call(svgIcon('#iD-icon-out-link', 'inline'));
51894 linkEnter.append('span').html(_t.html('note.report'));
51897 noteReport.note = function (val) {
51898 if (!arguments.length) return _note;
51906 function uiViewOnOSM(context) {
51907 var _what; // an osmEntity or osmNote
51910 function viewOnOSM(selection) {
51913 if (_what instanceof osmEntity) {
51914 url = context.connection().entityURL(_what);
51915 } else if (_what instanceof osmNote) {
51916 url = context.connection().noteURL(_what);
51919 var data = !_what || _what.isNew() ? [] : [_what];
51920 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
51924 link.exit().remove(); // enter
51926 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
51927 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
51930 viewOnOSM.what = function (_) {
51931 if (!arguments.length) return _what;
51939 function uiNoteEditor(context) {
51940 var dispatch$1 = dispatch('change');
51941 var noteComments = uiNoteComments();
51942 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
51946 var _newNote; // var _fieldsArr;
51949 function noteEditor(selection) {
51950 var header = selection.selectAll('.header').data([0]);
51951 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
51952 headerEnter.append('button').attr('class', 'close').on('click', function () {
51953 context.enter(modeBrowse(context));
51954 }).call(svgIcon('#iD-icon-close'));
51955 headerEnter.append('h3').html(_t.html('note.title'));
51956 var body = selection.selectAll('.body').data([0]);
51957 body = body.enter().append('div').attr('class', 'body').merge(body);
51958 var editor = body.selectAll('.note-editor').data([0]);
51959 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
51960 var footer = selection.selectAll('.footer').data([0]);
51961 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
51963 var osm = services.osm;
51966 osm.on('change.note-save', function () {
51967 selection.call(noteEditor);
51972 function noteSaveSection(selection) {
51973 var isSelected = _note && _note.id === context.selectedNoteID();
51975 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
51976 return d.status + d.id;
51979 noteSave.exit().remove(); // enter
51981 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
51982 // if (_note.isNew()) {
51983 // var presets = presetManager;
51984 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
51986 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
51988 // _fieldsArr.forEach(function(field) {
51990 // .on('change', changeCategory);
51994 // .attr('class', 'note-category')
51995 // .call(formFields.fieldsArr(_fieldsArr));
51997 // function changeCategory() {
51998 // // NOTE: perhaps there is a better way to get value
51999 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
52000 // // store the unsaved category with the note itself
52001 // _note = _note.update({ newCategory: val });
52002 // var osm = services.osm;
52004 // osm.replaceNote(_note); // update note cache
52007 // .call(noteSaveButtons);
52010 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
52011 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
52013 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
52014 return d.newComment;
52015 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
52017 if (!commentTextarea.empty() && _newNote) {
52018 // autofocus the comment field for new notes
52019 commentTextarea.node().focus();
52023 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
52025 function keydown(d3_event) {
52026 if (!(d3_event.keyCode === 13 && // ↩ Return
52027 d3_event.metaKey)) return;
52028 var osm = services.osm;
52030 var hasAuth = osm.authenticated();
52031 if (!hasAuth) return;
52032 if (!_note.newComment) return;
52033 d3_event.preventDefault();
52034 select(this).on('keydown.note-input', null); // focus on button and submit
52036 window.setTimeout(function () {
52037 if (_note.isNew()) {
52038 noteSave.selectAll('.save-button').node().focus();
52041 noteSave.selectAll('.comment-button').node().focus();
52047 function changeInput() {
52048 var input = select(this);
52049 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
52051 _note = _note.update({
52054 var osm = services.osm;
52057 osm.replaceNote(_note); // update note cache
52060 noteSave.call(noteSaveButtons);
52064 function userDetails(selection) {
52065 var detailSection = selection.selectAll('.detail-section').data([0]);
52066 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
52067 var osm = services.osm;
52068 if (!osm) return; // Add warning if user is not logged in
52070 var hasAuth = osm.authenticated();
52071 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
52072 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
52073 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
52074 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
52075 authEnter.append('span').html(_t.html('note.login'));
52076 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) {
52077 d3_event.preventDefault();
52078 osm.authenticate();
52080 authEnter.transition().duration(200).style('opacity', 1);
52081 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
52082 prose.exit().remove();
52083 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
52084 osm.userDetails(function (err, user) {
52086 var userLink = select(document.createElement('div'));
52088 if (user.image_url) {
52089 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
52092 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
52093 prose.html(_t.html('note.upload_explanation_with_user', {
52094 user: userLink.html()
52099 function noteSaveButtons(selection) {
52100 var osm = services.osm;
52101 var hasAuth = osm && osm.authenticated();
52103 var isSelected = _note && _note.id === context.selectedNoteID();
52105 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
52106 return d.status + d.id;
52109 buttonSection.exit().remove(); // enter
52111 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
52113 if (_note.isNew()) {
52114 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
52115 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
52117 buttonEnter.append('button').attr('class', 'button status-button action');
52118 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
52122 buttonSection = buttonSection.merge(buttonEnter);
52123 buttonSection.select('.cancel-button') // select and propagate data
52124 .on('click.cancel', clickCancel);
52125 buttonSection.select('.save-button') // select and propagate data
52126 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
52127 buttonSection.select('.status-button') // select and propagate data
52128 .attr('disabled', hasAuth ? null : true).html(function (d) {
52129 var action = d.status === 'open' ? 'close' : 'open';
52130 var andComment = d.newComment ? '_comment' : '';
52131 return _t('note.' + action + andComment);
52132 }).on('click.status', clickStatus);
52133 buttonSection.select('.comment-button') // select and propagate data
52134 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
52136 function isSaveDisabled(d) {
52137 return hasAuth && d.status === 'open' && d.newComment ? null : true;
52141 function clickCancel(d3_event, d) {
52142 this.blur(); // avoid keeping focus on the button - #4641
52144 var osm = services.osm;
52150 context.enter(modeBrowse(context));
52151 dispatch$1.call('change');
52154 function clickSave(d3_event, d) {
52155 this.blur(); // avoid keeping focus on the button - #4641
52157 var osm = services.osm;
52160 osm.postNoteCreate(d, function (err, note) {
52161 dispatch$1.call('change', note);
52166 function clickStatus(d3_event, d) {
52167 this.blur(); // avoid keeping focus on the button - #4641
52169 var osm = services.osm;
52172 var setStatus = d.status === 'open' ? 'closed' : 'open';
52173 osm.postNoteUpdate(d, setStatus, function (err, note) {
52174 dispatch$1.call('change', note);
52179 function clickComment(d3_event, d) {
52180 this.blur(); // avoid keeping focus on the button - #4641
52182 var osm = services.osm;
52185 osm.postNoteUpdate(d, d.status, function (err, note) {
52186 dispatch$1.call('change', note);
52191 noteEditor.note = function (val) {
52192 if (!arguments.length) return _note;
52197 noteEditor.newNote = function (val) {
52198 if (!arguments.length) return _newNote;
52203 return utilRebind(noteEditor, dispatch$1, 'on');
52206 function modeSelectNote(context, selectedNoteID) {
52212 var _keybinding = utilKeybinding('select-note');
52214 var _noteEditor = uiNoteEditor(context).on('change', function () {
52215 context.map().pan([0, 0]); // trigger a redraw
52217 var note = checkSelectedID();
52219 context.ui().sidebar.show(_noteEditor.note(note));
52222 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
52223 var _newFeature = false;
52225 function checkSelectedID() {
52226 if (!services.osm) return;
52227 var note = services.osm.getNote(selectedNoteID);
52230 context.enter(modeBrowse(context));
52234 } // class the note as selected, or return to browse mode if the note is gone
52237 function selectNote(d3_event, drawn) {
52238 if (!checkSelectedID()) return;
52239 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
52241 if (selection.empty()) {
52242 // Return to browse mode if selected DOM elements have
52243 // disappeared because the user moved them out of view..
52244 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
52246 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52247 context.enter(modeBrowse(context));
52250 selection.classed('selected', true);
52251 context.selectedNoteID(selectedNoteID);
52256 if (context.container().select('.combobox').size()) return;
52257 context.enter(modeBrowse(context));
52260 mode.zoomToSelected = function () {
52261 if (!services.osm) return;
52262 var note = services.osm.getNote(selectedNoteID);
52265 context.map().centerZoomEase(note.loc, 20);
52269 mode.newFeature = function (val) {
52270 if (!arguments.length) return _newFeature;
52275 mode.enter = function () {
52276 var note = checkSelectedID();
52279 _behaviors.forEach(context.install);
52281 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
52283 select(document).call(_keybinding);
52285 var sidebar = context.ui().sidebar;
52286 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
52288 sidebar.expand(sidebar.intersects(note.extent()));
52289 context.map().on('drawn.select', selectNote);
52292 mode.exit = function () {
52293 _behaviors.forEach(context.uninstall);
52295 select(document).call(_keybinding.unbind);
52296 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
52297 context.map().on('drawn.select', null);
52298 context.ui().sidebar.hide();
52299 context.selectedNoteID(null);
52305 function modeDragNote(context) {
52310 var edit = behaviorEdit(context);
52312 var _nudgeInterval;
52316 var _note; // most current note.. dragged note may have stale datum.
52319 function startNudge(d3_event, nudge) {
52320 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
52321 _nudgeInterval = window.setInterval(function () {
52322 context.map().pan(nudge);
52323 doMove(d3_event, nudge);
52327 function stopNudge() {
52328 if (_nudgeInterval) {
52329 window.clearInterval(_nudgeInterval);
52330 _nudgeInterval = null;
52334 function origin(note) {
52335 return context.projection(note.loc);
52338 function start(d3_event, note) {
52340 var osm = services.osm;
52343 // Get latest note from cache.. The marker may have a stale datum bound to it
52344 // and dragging it around can sometimes delete the users note comment.
52345 _note = osm.getNote(_note.id);
52348 context.surface().selectAll('.note-' + _note.id).classed('active', true);
52349 context.perform(actionNoop());
52350 context.enter(mode);
52351 context.selectedNoteID(_note.id);
52354 function move(d3_event, entity, point) {
52355 d3_event.stopPropagation();
52356 _lastLoc = context.projection.invert(point);
52358 var nudge = geoViewportEdge(point, context.map().dimensions());
52361 startNudge(d3_event, nudge);
52367 function doMove(d3_event, nudge) {
52368 nudge = nudge || [0, 0];
52369 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
52370 var currMouse = geoVecSubtract(currPoint, nudge);
52371 var loc = context.projection.invert(currMouse);
52372 _note = _note.move(loc);
52373 var osm = services.osm;
52376 osm.replaceNote(_note); // update note cache
52379 context.replace(actionNoop()); // trigger redraw
52383 context.replace(actionNoop()); // trigger redraw
52385 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
52388 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);
52390 mode.enter = function () {
52391 context.install(edit);
52394 mode.exit = function () {
52395 context.ui().sidebar.hover.cancel();
52396 context.uninstall(edit);
52397 context.surface().selectAll('.active').classed('active', false);
52401 mode.behavior = drag;
52405 function uiDataHeader() {
52408 function dataHeader(selection) {
52409 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
52410 return d.__featurehash__;
52412 header.exit().remove();
52413 var headerEnter = header.enter().append('div').attr('class', 'data-header');
52414 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
52415 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
52416 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
52419 dataHeader.datum = function (val) {
52420 if (!arguments.length) return _datum;
52428 // It is keyed on the `value` of the entry. Data should be an array of objects like:
52430 // value: 'string value', // required
52431 // display: 'label html' // optional
52432 // title: 'hover text' // optional
52433 // terms: ['search terms'] // optional
52436 var _comboHideTimerID;
52438 function uiCombobox(context, klass) {
52439 var dispatch$1 = dispatch('accept', 'cancel');
52440 var container = context.container();
52441 var _suggestions = [];
52444 var _selected = null;
52445 var _canAutocomplete = true;
52446 var _caseSensitive = false;
52447 var _cancelFetch = false;
52451 var _mouseEnterHandler, _mouseLeaveHandler;
52453 var _fetcher = function _fetcher(val, cb) {
52454 cb(_data.filter(function (d) {
52455 var terms = d.terms || [];
52456 terms.push(d.value);
52457 return terms.some(function (term) {
52458 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
52463 var combobox = function combobox(input, attachTo) {
52464 if (!input || input.empty()) return;
52465 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 () {
52466 var parent = this.parentNode;
52467 var sibling = this.nextSibling;
52468 select(parent).selectAll('.combobox-caret').filter(function (d) {
52469 return d === input.node();
52470 }).data([input.node()]).enter().insert('div', function () {
52472 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
52473 d3_event.preventDefault(); // don't steal focus from input
52475 input.node().focus(); // focus the input as if it was clicked
52477 mousedown(d3_event);
52478 }).on('mouseup.combo-caret', function (d3_event) {
52479 d3_event.preventDefault(); // don't steal focus from input
52485 function mousedown(d3_event) {
52486 if (d3_event.button !== 0) return; // left click only
52488 _tDown = +new Date(); // clear selection
52490 var start = input.property('selectionStart');
52491 var end = input.property('selectionEnd');
52493 if (start !== end) {
52494 var val = utilGetSetValue(input);
52495 input.node().setSelectionRange(val.length, val.length);
52499 input.on('mouseup.combo-input', mouseup);
52502 function mouseup(d3_event) {
52503 input.on('mouseup.combo-input', null);
52504 if (d3_event.button !== 0) return; // left click only
52506 if (input.node() !== document.activeElement) return; // exit if this input is not focused
52508 var start = input.property('selectionStart');
52509 var end = input.property('selectionEnd');
52510 if (start !== end) return; // exit if user is selecting
52511 // not showing or showing for a different field - try to show it.
52513 var combo = container.selectAll('.combobox');
52515 if (combo.empty() || combo.datum() !== input.node()) {
52516 var tOrig = _tDown;
52517 window.setTimeout(function () {
52518 if (tOrig !== _tDown) return; // exit if user double clicked
52520 fetchComboData('', function () {
52531 fetchComboData(''); // prefetch values (may warm taginfo cache)
52535 _comboHideTimerID = window.setTimeout(hide, 75);
52539 hide(); // remove any existing
52541 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) {
52542 // prevent moving focus out of the input field
52543 d3_event.preventDefault();
52545 container.on('scroll.combo-scroll', render, true);
52549 if (_comboHideTimerID) {
52550 window.clearTimeout(_comboHideTimerID);
52551 _comboHideTimerID = undefined;
52554 container.selectAll('.combobox').remove();
52555 container.on('scroll.combo-scroll', null);
52558 function keydown(d3_event) {
52559 var shown = !container.selectAll('.combobox').empty();
52560 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
52562 switch (d3_event.keyCode) {
52563 case 8: // ⌫ Backspace
52567 d3_event.stopPropagation();
52570 input.on('input.combo-input', function () {
52571 var start = input.property('selectionStart');
52572 input.node().setSelectionRange(start, start);
52573 input.on('input.combo-input', change);
52584 d3_event.preventDefault();
52585 d3_event.stopPropagation();
52590 if (tagName === 'textarea' && !shown) return;
52591 d3_event.preventDefault();
52593 if (tagName === 'input' && !shown) {
52602 if (tagName === 'textarea' && !shown) return;
52603 d3_event.preventDefault();
52605 if (tagName === 'input' && !shown) {
52614 function keyup(d3_event) {
52615 switch (d3_event.keyCode) {
52626 } // Called whenever the input value is changed (e.g. on typing)
52629 function change() {
52630 fetchComboData(value(), function () {
52632 var val = input.property('value');
52634 if (_suggestions.length) {
52635 if (input.property('selectionEnd') === val.length) {
52636 _selected = tryAutocomplete();
52645 var combo = container.selectAll('.combobox');
52647 if (combo.empty()) {
52656 } // Called when the user presses up/down arrows to navigate the list
52659 function nav(dir) {
52660 if (_suggestions.length) {
52661 // try to determine previously selected index..
52664 for (var i = 0; i < _suggestions.length; i++) {
52665 if (_selected && _suggestions[i].value === _selected) {
52669 } // pick new _selected
52672 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
52673 _selected = _suggestions[index].value;
52674 input.property('value', _selected);
52681 function ensureVisible() {
52682 var combo = container.selectAll('.combobox');
52683 if (combo.empty()) return;
52684 var containerRect = container.node().getBoundingClientRect();
52685 var comboRect = combo.node().getBoundingClientRect();
52687 if (comboRect.bottom > containerRect.bottom) {
52688 var node = attachTo ? attachTo.node() : input.node();
52689 node.scrollIntoView({
52690 behavior: 'instant',
52694 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
52697 var selected = combo.selectAll('.combobox-option.selected').node();
52700 selected.scrollIntoView({
52701 behavior: 'smooth',
52708 var value = input.property('value');
52709 var start = input.property('selectionStart');
52710 var end = input.property('selectionEnd');
52712 if (start && end) {
52713 value = value.substring(0, start);
52719 function fetchComboData(v, cb) {
52720 _cancelFetch = false;
52722 _fetcher.call(input, v, function (results) {
52723 // already chose a value, don't overwrite or autocomplete it
52724 if (_cancelFetch) return;
52725 _suggestions = results;
52726 results.forEach(function (d) {
52727 _fetched[d.value] = d;
52736 function tryAutocomplete() {
52737 if (!_canAutocomplete) return;
52738 var val = _caseSensitive ? value() : value().toLowerCase();
52739 if (!val) return; // Don't autocomplete if user is typing a number - #4935
52741 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
52742 var bestIndex = -1;
52744 for (var i = 0; i < _suggestions.length; i++) {
52745 var suggestion = _suggestions[i].value;
52746 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
52748 if (compare === val) {
52750 break; // otherwise lock in the first result that starts with the search string..
52751 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
52756 if (bestIndex !== -1) {
52757 var bestVal = _suggestions[bestIndex].value;
52758 input.property('value', bestVal);
52759 input.node().setSelectionRange(val.length, bestVal.length);
52764 function render() {
52765 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
52770 var shown = !container.selectAll('.combobox').empty();
52771 if (!shown) return;
52772 var combo = container.selectAll('.combobox');
52773 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
52776 options.exit().remove(); // enter/update
52778 options.enter().append('a').attr('class', 'combobox-option').attr('title', function (d) {
52780 }).html(function (d) {
52781 return d.display || d.value;
52782 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
52783 return d.value === _selected;
52784 }).on('click.combo-option', accept).order();
52785 var node = attachTo ? attachTo.node() : input.node();
52786 var containerRect = container.node().getBoundingClientRect();
52787 var rect = node.getBoundingClientRect();
52788 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
52789 } // Dispatches an 'accept' event
52790 // Then hides the combobox.
52793 function accept(d3_event, d) {
52794 _cancelFetch = true;
52795 var thiz = input.node();
52798 // user clicked on a suggestion
52799 utilGetSetValue(input, d.value); // replace field contents
52801 utilTriggerEvent(input, 'change');
52802 } // clear (and keep) selection
52805 var val = utilGetSetValue(input);
52806 thiz.setSelectionRange(val.length, val.length);
52808 dispatch$1.call('accept', thiz, d, val);
52810 } // Dispatches an 'cancel' event
52811 // Then hides the combobox.
52814 function cancel() {
52815 _cancelFetch = true;
52816 var thiz = input.node(); // clear (and remove) selection, and replace field contents
52818 var val = utilGetSetValue(input);
52819 var start = input.property('selectionStart');
52820 var end = input.property('selectionEnd');
52821 val = val.slice(0, start) + val.slice(end);
52822 utilGetSetValue(input, val);
52823 thiz.setSelectionRange(val.length, val.length);
52824 dispatch$1.call('cancel', thiz);
52829 combobox.canAutocomplete = function (val) {
52830 if (!arguments.length) return _canAutocomplete;
52831 _canAutocomplete = val;
52835 combobox.caseSensitive = function (val) {
52836 if (!arguments.length) return _caseSensitive;
52837 _caseSensitive = val;
52841 combobox.data = function (val) {
52842 if (!arguments.length) return _data;
52847 combobox.fetcher = function (val) {
52848 if (!arguments.length) return _fetcher;
52853 combobox.minItems = function (val) {
52854 if (!arguments.length) return _minItems;
52859 combobox.itemsMouseEnter = function (val) {
52860 if (!arguments.length) return _mouseEnterHandler;
52861 _mouseEnterHandler = val;
52865 combobox.itemsMouseLeave = function (val) {
52866 if (!arguments.length) return _mouseLeaveHandler;
52867 _mouseLeaveHandler = val;
52871 return utilRebind(combobox, dispatch$1, 'on');
52874 uiCombobox.off = function (input, context) {
52875 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);
52876 context.container().on('scroll.combo-scroll', null);
52879 // hide class, which sets display=none, and a d3 transition for opacity.
52880 // this will cause blinking when called repeatedly, so check that the
52881 // value actually changes between calls.
52883 function uiToggle(show, callback) {
52884 return function (selection) {
52885 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
52886 select(this).classed('hide', !show).style('opacity', null);
52887 if (callback) callback.apply(this);
52892 function uiDisclosure(context, key, expandedDefault) {
52893 var dispatch$1 = dispatch('toggled');
52897 var _label = utilFunctor('');
52899 var _updatePreference = true;
52901 var _content = function _content() {};
52903 var disclosure = function disclosure(selection) {
52904 if (_expanded === undefined || _expanded === null) {
52905 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
52906 var preference = corePreferences('disclosure.' + key + '.expanded');
52907 _expanded = preference === null ? !!expandedDefault : preference === 'true';
52910 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
52912 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
52913 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
52915 hideToggle = hideToggleEnter.merge(hideToggle);
52916 hideToggle.on('click', toggle).classed('expanded', _expanded);
52917 hideToggle.selectAll('.hide-toggle-text').html(_label());
52918 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
52919 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
52921 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
52924 wrap.call(_content);
52927 function toggle(d3_event) {
52928 d3_event.preventDefault();
52929 _expanded = !_expanded;
52931 if (_updatePreference) {
52932 corePreferences('disclosure.' + key + '.expanded', _expanded);
52935 hideToggle.classed('expanded', _expanded);
52936 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
52937 wrap.call(uiToggle(_expanded));
52940 wrap.call(_content);
52943 dispatch$1.call('toggled', this, _expanded);
52947 disclosure.label = function (val) {
52948 if (!arguments.length) return _label;
52949 _label = utilFunctor(val);
52953 disclosure.expanded = function (val) {
52954 if (!arguments.length) return _expanded;
52959 disclosure.updatePreference = function (val) {
52960 if (!arguments.length) return _updatePreference;
52961 _updatePreference = val;
52965 disclosure.content = function (val) {
52966 if (!arguments.length) return _content;
52971 return utilRebind(disclosure, dispatch$1, 'on');
52974 // Can be labeled and collapsible.
52976 function uiSection(id, context) {
52977 var _classes = utilFunctor('');
52979 var _shouldDisplay;
52987 var _expandedByDefault = utilFunctor(true);
52989 var _disclosureContent;
52991 var _disclosureExpanded;
52993 var _containerSelection = select(null);
52999 section.classes = function (val) {
53000 if (!arguments.length) return _classes;
53001 _classes = utilFunctor(val);
53005 section.label = function (val) {
53006 if (!arguments.length) return _label;
53007 _label = utilFunctor(val);
53011 section.expandedByDefault = function (val) {
53012 if (!arguments.length) return _expandedByDefault;
53013 _expandedByDefault = utilFunctor(val);
53017 section.shouldDisplay = function (val) {
53018 if (!arguments.length) return _shouldDisplay;
53019 _shouldDisplay = utilFunctor(val);
53023 section.content = function (val) {
53024 if (!arguments.length) return _content;
53029 section.disclosureContent = function (val) {
53030 if (!arguments.length) return _disclosureContent;
53031 _disclosureContent = val;
53035 section.disclosureExpanded = function (val) {
53036 if (!arguments.length) return _disclosureExpanded;
53037 _disclosureExpanded = val;
53039 }; // may be called multiple times
53042 section.render = function (selection) {
53043 _containerSelection = selection.selectAll('.section-' + id).data([0]);
53045 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
53047 _containerSelection = sectionEnter.merge(_containerSelection);
53049 _containerSelection.call(renderContent);
53052 section.reRender = function () {
53053 _containerSelection.call(renderContent);
53056 section.selection = function () {
53057 return _containerSelection;
53060 section.disclosure = function () {
53061 return _disclosure;
53062 }; // may be called multiple times
53065 function renderContent(selection) {
53066 if (_shouldDisplay) {
53067 var shouldDisplay = _shouldDisplay();
53069 selection.classed('hide', !shouldDisplay);
53071 if (!shouldDisplay) {
53072 selection.html('');
53077 if (_disclosureContent) {
53078 if (!_disclosure) {
53079 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
53080 /*.on('toggled', function(expanded) {
53081 if (expanded) { selection.node().parentNode.scrollTop += 200; }
53083 .content(_disclosureContent);
53086 if (_disclosureExpanded !== undefined) {
53087 _disclosure.expanded(_disclosureExpanded);
53089 _disclosureExpanded = undefined;
53092 selection.call(_disclosure);
53097 selection.call(_content);
53105 // key: 'string', // required
53106 // value: 'string' // optional
53110 // qid: 'string' // brand wikidata (e.g. 'Q37158')
53114 function uiTagReference(what) {
53115 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
53116 var tagReference = {};
53118 var _button = select(null);
53120 var _body = select(null);
53127 if (!wikibase) return;
53129 _button.classed('tag-reference-loading', true);
53131 wikibase.getDocs(what, gotDocs);
53134 function gotDocs(err, docs) {
53137 if (!docs || !docs.title) {
53138 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
53144 if (docs.imageURL) {
53145 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
53147 }).on('error', function () {
53148 select(this).remove();
53155 _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'));
53158 _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));
53159 } // Add link to info about "good changeset comments" - #2923
53162 if (what.key === 'comment') {
53163 _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'));
53170 _button.classed('tag-reference-loading', false);
53172 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
53176 _button.selectAll('svg.icon use').each(function () {
53177 var iconUse = select(this);
53179 if (iconUse.attr('href') === '#iD-icon-info') {
53180 iconUse.attr('href', '#iD-icon-info-filled');
53186 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
53187 _body.classed('expanded', false);
53192 _button.selectAll('svg.icon use').each(function () {
53193 var iconUse = select(this);
53195 if (iconUse.attr('href') === '#iD-icon-info-filled') {
53196 iconUse.attr('href', '#iD-icon-info');
53201 tagReference.button = function (selection, klass, iconName) {
53202 _button = selection.selectAll('.tag-reference-button').data([0]);
53203 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
53205 _button.on('click', function (d3_event) {
53206 d3_event.stopPropagation();
53207 d3_event.preventDefault();
53208 this.blur(); // avoid keeping focus on the button - #4641
53212 } else if (_loaded) {
53220 tagReference.body = function (selection) {
53221 var itemID = what.qid || what.key + '-' + (what.value || '');
53222 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
53226 _body.exit().remove();
53228 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
53230 if (_showing === false) {
53235 tagReference.showing = function (val) {
53236 if (!arguments.length) return _showing;
53238 return tagReference;
53241 return tagReference;
53244 function uiSectionRawTagEditor(id, context) {
53245 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
53246 var count = Object.keys(_tags).filter(function (d) {
53249 return _t('inspector.title_count', {
53250 title: _t.html('inspector.tags'),
53253 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
53254 var taginfo = services.taginfo;
53255 var dispatch$1 = dispatch('change');
53256 var availableViews = [{
53258 icon: '#fas-th-list'
53261 icon: '#fas-i-cursor'
53264 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
53267 var _readOnlyTags = []; // the keys in the order we want them to display
53269 var _orderedKeys = [];
53270 var _showBlank = false;
53271 var _pendingChange = null;
53281 var _didInteract = false;
53283 function interacted() {
53284 _didInteract = true;
53287 function renderDisclosureContent(wrap) {
53288 // remove deleted keys
53289 _orderedKeys = _orderedKeys.filter(function (key) {
53290 return _tags[key] !== undefined;
53291 }); // When switching to a different entity or changing the state (hover/select)
53292 // reorder the keys alphabetically.
53293 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
53294 // Otherwise leave their order alone - #5857, #5927
53296 var all = Object.keys(_tags).sort();
53297 var missingKeys = utilArrayDifference(all, _orderedKeys);
53299 for (var i in missingKeys) {
53300 _orderedKeys.push(missingKeys[i]);
53301 } // assemble row data
53304 var rowData = _orderedKeys.map(function (key, i) {
53310 }); // append blank row last, if necessary
53313 if (!rowData.length || _showBlank) {
53314 _showBlank = false;
53316 index: rowData.length,
53323 var options = wrap.selectAll('.raw-tag-options').data([0]);
53324 options.exit().remove();
53325 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
53326 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
53329 optionEnter.append('button').attr('class', function (d) {
53330 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
53331 }).attr('title', function (d) {
53332 return _t('icons.' + d.id);
53333 }).on('click', function (d3_event, d) {
53335 corePreferences('raw-tag-editor-view', d.id);
53336 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
53337 return datum === d;
53339 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
53340 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
53341 }).each(function (d) {
53342 select(this).call(svgIcon(d.icon));
53343 }); // View as Text
53345 var textData = rowsToText(rowData);
53346 var textarea = wrap.selectAll('.tag-text').data([0]);
53347 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);
53348 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
53350 var list = wrap.selectAll('.tag-list').data([0]);
53351 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
53353 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
53354 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
53355 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
53357 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
53360 var items = list.selectAll('.tag-row').data(rowData, function (d) {
53363 items.exit().each(unbind).remove(); // Enter
53365 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
53366 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
53367 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);
53368 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);
53369 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
53371 items = items.merge(itemsEnter).sort(function (a, b) {
53372 return a.index - b.index;
53374 items.each(function (d) {
53375 var row = select(this);
53376 var key = row.select('input.key'); // propagate bound data
53378 var value = row.select('input.value'); // propagate bound data
53380 if (_entityIDs && taginfo && _state !== 'hover') {
53381 bindTypeahead(key, value);
53384 var referenceOptions = {
53388 if (typeof d.value === 'string') {
53389 referenceOptions.value = d.value;
53392 var reference = uiTagReference(referenceOptions);
53394 if (_state === 'hover') {
53395 reference.showing(false);
53398 row.select('.inner-wrap') // propagate bound data
53399 .call(reference.button);
53400 row.call(reference.body);
53401 row.select('button.remove'); // propagate bound data
53403 items.selectAll('input.key').attr('title', function (d) {
53405 }).call(utilGetSetValue, function (d) {
53407 }).attr('readonly', function (d) {
53408 return isReadOnly(d) || typeof d.value !== 'string' || null;
53410 items.selectAll('input.value').attr('title', function (d) {
53411 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
53412 }).classed('mixed', function (d) {
53413 return Array.isArray(d.value);
53414 }).attr('placeholder', function (d) {
53415 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
53416 }).call(utilGetSetValue, function (d) {
53417 return typeof d.value === 'string' ? d.value : '';
53418 }).attr('readonly', function (d) {
53419 return isReadOnly(d) || null;
53421 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
53424 function isReadOnly(d) {
53425 for (var i = 0; i < _readOnlyTags.length; i++) {
53426 if (d.key.match(_readOnlyTags[i]) !== null) {
53434 function setTextareaHeight() {
53435 if (_tagView !== 'text') return;
53436 var selection = select(this);
53437 var matches = selection.node().value.match(/\n/g);
53438 var lineCount = 2 + Number(matches && matches.length);
53439 var lineHeight = 20;
53440 selection.style('height', lineCount * lineHeight + 'px');
53443 function stringify(s) {
53444 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
53447 function unstringify(s) {
53451 if (s.length < 1 || s.charAt(0) !== '"') {
53455 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
53459 return JSON.parse(leading + s + trailing);
53462 function rowsToText(rows) {
53463 var str = rows.filter(function (row) {
53464 return row.key && row.key.trim() !== '';
53465 }).map(function (row) {
53466 var rawVal = row.value;
53467 if (typeof rawVal !== 'string') rawVal = '*';
53468 var val = rawVal ? stringify(rawVal) : '';
53469 return stringify(row.key) + '=' + val;
53472 if (_state !== 'hover' && str.length) {
53479 function textChanged() {
53480 var newText = this.value.trim();
53482 newText.split('\n').forEach(function (row) {
53483 var m = row.match(/^\s*([^=]+)=(.*)$/);
53486 var k = context.cleanTagKey(unstringify(m[1].trim()));
53487 var v = context.cleanTagValue(unstringify(m[2].trim()));
53491 var tagDiff = utilTagDiff(_tags, newTags);
53492 if (!tagDiff.length) return;
53493 _pendingChange = _pendingChange || {};
53494 tagDiff.forEach(function (change) {
53497 })) return; // skip unchanged multiselection placeholders
53499 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
53501 if (change.type === '-') {
53502 _pendingChange[change.key] = undefined;
53503 } else if (change.type === '+') {
53504 _pendingChange[change.key] = change.newVal || '';
53508 if (Object.keys(_pendingChange).length === 0) {
53509 _pendingChange = null;
53516 function pushMore(d3_event) {
53517 // if pressing Tab on the last value field with content, add a blank row
53518 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
53523 function bindTypeahead(key, value) {
53524 if (isReadOnly(key.datum())) return;
53526 if (Array.isArray(value.datum().value)) {
53527 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
53528 var keyString = utilGetSetValue(key);
53529 if (!_tags[keyString]) return;
53531 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
53543 var geometry = context.graph().geometry(_entityIDs[0]);
53544 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
53547 geometry: geometry,
53549 }, function (err, data) {
53551 var filtered = data.filter(function (d) {
53552 return _tags[d.value] === undefined;
53554 callback(sort(value, filtered));
53558 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
53561 key: utilGetSetValue(key),
53562 geometry: geometry,
53564 }, function (err, data) {
53565 if (!err) callback(sort(value, data));
53569 function sort(value, data) {
53570 var sameletter = [];
53573 for (var i = 0; i < data.length; i++) {
53574 if (data[i].value.substring(0, value.length) === value) {
53575 sameletter.push(data[i]);
53577 other.push(data[i]);
53581 return sameletter.concat(other);
53585 function unbind() {
53586 var row = select(this);
53587 row.selectAll('input.key').call(uiCombobox.off, context);
53588 row.selectAll('input.value').call(uiCombobox.off, context);
53591 function keyChange(d3_event, d) {
53592 if (select(this).attr('readonly')) return;
53593 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
53595 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
53596 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
53605 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
53606 // new key is already in use, switch focus to the existing row
53607 this.value = kOld; // reset the key
53609 section.selection().selectAll('.tag-list input.value').each(function (d) {
53610 if (d.key === kNew) {
53611 // send focus to that other value combo instead
53612 var input = select(this).node();
53620 var row = this.parentNode.parentNode;
53621 var inputVal = select(row).selectAll('input.value');
53622 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
53623 _pendingChange = _pendingChange || {};
53626 _pendingChange[kOld] = undefined;
53629 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
53631 var existingKeyIndex = _orderedKeys.indexOf(kOld);
53633 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
53634 d.key = kNew; // update datum to avoid exit/enter on tag update
53638 utilGetSetValue(inputVal, vNew);
53642 function valueChange(d3_event, d) {
53643 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
53645 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
53647 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
53648 _pendingChange = _pendingChange || {};
53649 _pendingChange[d.key] = context.cleanTagValue(this.value);
53653 function removeTag(d3_event, d) {
53654 if (isReadOnly(d)) return;
53656 if (d.key === '') {
53657 // removing the blank row
53658 _showBlank = false;
53659 section.reRender();
53661 // remove the key from the ordered key index
53662 _orderedKeys = _orderedKeys.filter(function (key) {
53663 return key !== d.key;
53665 _pendingChange = _pendingChange || {};
53666 _pendingChange[d.key] = undefined;
53671 function addTag() {
53672 // Delay render in case this click is blurring an edited combo.
53673 // Without the setTimeout, the `content` render would wipe out the pending tag change.
53674 window.setTimeout(function () {
53676 section.reRender();
53677 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
53681 function scheduleChange() {
53682 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
53683 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
53685 window.setTimeout(function () {
53686 if (!_pendingChange) return;
53687 dispatch$1.call('change', this, entityIDs, _pendingChange);
53688 _pendingChange = null;
53692 section.state = function (val) {
53693 if (!arguments.length) return _state;
53695 if (_state !== val) {
53703 section.presets = function (val) {
53704 if (!arguments.length) return _presets;
53707 if (_presets && _presets.length && _presets[0].isFallback()) {
53708 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
53709 } else if (!_didInteract) {
53710 section.disclosureExpanded(null);
53716 section.tags = function (val) {
53717 if (!arguments.length) return _tags;
53722 section.entityIDs = function (val) {
53723 if (!arguments.length) return _entityIDs;
53725 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
53731 }; // pass an array of regular expressions to test against the tag key
53734 section.readOnlyTags = function (val) {
53735 if (!arguments.length) return _readOnlyTags;
53736 _readOnlyTags = val;
53740 return utilRebind(section, dispatch$1, 'on');
53743 function uiDataEditor(context) {
53744 var dataHeader = uiDataHeader();
53745 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
53749 function dataEditor(selection) {
53750 var header = selection.selectAll('.header').data([0]);
53751 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
53752 headerEnter.append('button').attr('class', 'close').on('click', function () {
53753 context.enter(modeBrowse(context));
53754 }).call(svgIcon('#iD-icon-close'));
53755 headerEnter.append('h3').html(_t.html('map_data.title'));
53756 var body = selection.selectAll('.body').data([0]);
53757 body = body.enter().append('div').attr('class', 'body').merge(body);
53758 var editor = body.selectAll('.data-editor').data([0]); // enter/update
53760 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
53761 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
53763 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);
53766 dataEditor.datum = function (val) {
53767 if (!arguments.length) return _datum;
53775 function modeSelectData(context, selectedDatum) {
53780 var keybinding = utilKeybinding('select-data');
53781 var dataEditor = uiDataEditor(context);
53782 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
53784 function selectData(d3_event, drawn) {
53785 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
53787 if (selection.empty()) {
53788 // Return to browse mode if selected DOM elements have
53789 // disappeared because the user moved them out of view..
53790 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
53792 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
53793 context.enter(modeBrowse(context));
53796 selection.classed('selected', true);
53801 if (context.container().select('.combobox').size()) return;
53802 context.enter(modeBrowse(context));
53805 mode.zoomToSelected = function () {
53806 var extent = geoExtent(d3_geoBounds(selectedDatum));
53807 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
53810 mode.enter = function () {
53811 behaviors.forEach(context.install);
53812 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
53813 select(document).call(keybinding);
53815 var sidebar = context.ui().sidebar;
53816 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
53818 var extent = geoExtent(d3_geoBounds(selectedDatum));
53819 sidebar.expand(sidebar.intersects(extent));
53820 context.map().on('drawn.select-data', selectData);
53823 mode.exit = function () {
53824 behaviors.forEach(context.uninstall);
53825 select(document).call(keybinding.unbind);
53826 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
53827 context.map().on('drawn.select-data', null);
53828 context.ui().sidebar.hide();
53834 function uiImproveOsmComments() {
53837 function issueComments(selection) {
53838 // make the div immediately so it appears above the buttons
53839 var comments = selection.selectAll('.comments-container').data([0]);
53840 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
53842 services.improveOSM.getComments(_qaItem).then(function (d) {
53843 if (!d.comments) return; // nothing to do here
53845 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
53846 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
53847 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
53848 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
53849 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
53850 var osm = services.osm;
53851 var selection = select(this);
53853 if (osm && d.username) {
53854 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
53857 selection.html(function (d) {
53861 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
53862 return _t.html('note.status.commented', {
53863 when: localeDateString(d.timestamp)
53866 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
53869 })["catch"](function (err) {
53870 console.log(err); // eslint-disable-line no-console
53874 function localeDateString(s) {
53875 if (!s) return null;
53881 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
53883 if (isNaN(d.getTime())) return null;
53884 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
53887 issueComments.issue = function (val) {
53888 if (!arguments.length) return _qaItem;
53890 return issueComments;
53893 return issueComments;
53896 function uiImproveOsmDetails(context) {
53899 function issueDetail(d) {
53900 if (d.desc) return d.desc;
53901 var issueKey = d.issueKey;
53902 d.replacements = d.replacements || {};
53903 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
53905 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
53908 function improveOsmDetails(selection) {
53909 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
53910 return "".concat(d.id, "-").concat(d.status || 0);
53912 details.exit().remove();
53913 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
53915 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
53916 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
53917 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
53919 var relatedEntities = [];
53920 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
53921 var link = select(this);
53922 var isObjectLink = link.classed('error_object_link');
53923 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
53924 var entity = context.hasEntity(entityID);
53925 relatedEntities.push(entityID); // Add click handler
53927 link.on('mouseenter', function () {
53928 utilHighlightEntities([entityID], true, context);
53929 }).on('mouseleave', function () {
53930 utilHighlightEntities([entityID], false, context);
53931 }).on('click', function (d3_event) {
53932 d3_event.preventDefault();
53933 utilHighlightEntities([entityID], false, context);
53934 var osmlayer = context.layers().layer('osm');
53936 if (!osmlayer.enabled()) {
53937 osmlayer.enabled(true);
53940 context.map().centerZoom(_qaItem.loc, 20);
53943 context.enter(modeSelect(context, [entityID]));
53945 context.loadEntity(entityID, function () {
53946 context.enter(modeSelect(context, [entityID]));
53949 }); // Replace with friendly name if possible
53950 // (The entity may not yet be loaded into the graph)
53953 var name = utilDisplayName(entity); // try to use common name
53955 if (!name && !isObjectLink) {
53956 var preset = _mainPresetIndex.match(entity, context.graph());
53957 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
53961 this.innerText = name;
53964 }); // Don't hide entities related to this error - #5880
53966 context.features().forceVisible(relatedEntities);
53967 context.map().pan([0, 0]); // trigger a redraw
53970 improveOsmDetails.issue = function (val) {
53971 if (!arguments.length) return _qaItem;
53973 return improveOsmDetails;
53976 return improveOsmDetails;
53979 function uiImproveOsmHeader() {
53982 function issueTitle(d) {
53983 var issueKey = d.issueKey;
53984 d.replacements = d.replacements || {};
53985 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
53987 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
53990 function improveOsmHeader(selection) {
53991 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
53992 return "".concat(d.id, "-").concat(d.status || 0);
53994 header.exit().remove();
53995 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
53996 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
53998 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
53999 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
54001 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');
54002 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
54003 var picon = d.icon;
54008 var isMaki = /^maki-/.test(picon);
54009 return "#".concat(picon).concat(isMaki ? '-11' : '');
54012 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54015 improveOsmHeader.issue = function (val) {
54016 if (!arguments.length) return _qaItem;
54018 return improveOsmHeader;
54021 return improveOsmHeader;
54024 function uiImproveOsmEditor(context) {
54025 var dispatch$1 = dispatch('change');
54026 var qaDetails = uiImproveOsmDetails(context);
54027 var qaComments = uiImproveOsmComments();
54028 var qaHeader = uiImproveOsmHeader();
54032 function improveOsmEditor(selection) {
54033 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
54034 headerEnter.append('button').attr('class', 'close').on('click', function () {
54035 return context.enter(modeBrowse(context));
54036 }).call(svgIcon('#iD-icon-close'));
54037 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
54038 var body = selection.selectAll('.body').data([0]);
54039 body = body.enter().append('div').attr('class', 'body').merge(body);
54040 var editor = body.selectAll('.qa-editor').data([0]);
54041 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);
54044 function improveOsmSaveSection(selection) {
54045 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54047 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
54048 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
54049 return "".concat(d.id, "-").concat(d.status || 0);
54052 saveSection.exit().remove(); // enter
54054 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
54055 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
54056 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
54057 return d.newComment;
54058 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
54060 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
54062 function changeInput() {
54063 var input = select(this);
54064 var val = input.property('value').trim();
54068 } // store the unsaved comment with the issue itself
54071 _qaItem = _qaItem.update({
54074 var qaService = services.improveOSM;
54077 qaService.replaceItem(_qaItem);
54080 saveSection.call(qaSaveButtons);
54084 function qaSaveButtons(selection) {
54085 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54087 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
54088 return d.status + d.id;
54091 buttonSection.exit().remove(); // enter
54093 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
54094 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
54095 buttonEnter.append('button').attr('class', 'button close-button action');
54096 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
54098 buttonSection = buttonSection.merge(buttonEnter);
54099 buttonSection.select('.comment-button').attr('disabled', function (d) {
54100 return d.newComment ? null : true;
54101 }).on('click.comment', function (d3_event, d) {
54102 this.blur(); // avoid keeping focus on the button - #4641
54104 var qaService = services.improveOSM;
54107 qaService.postUpdate(d, function (err, item) {
54108 return dispatch$1.call('change', item);
54112 buttonSection.select('.close-button').html(function (d) {
54113 var andComment = d.newComment ? '_comment' : '';
54114 return _t.html("QA.keepRight.close".concat(andComment));
54115 }).on('click.close', function (d3_event, d) {
54116 this.blur(); // avoid keeping focus on the button - #4641
54118 var qaService = services.improveOSM;
54121 d.newStatus = 'SOLVED';
54122 qaService.postUpdate(d, function (err, item) {
54123 return dispatch$1.call('change', item);
54127 buttonSection.select('.ignore-button').html(function (d) {
54128 var andComment = d.newComment ? '_comment' : '';
54129 return _t.html("QA.keepRight.ignore".concat(andComment));
54130 }).on('click.ignore', function (d3_event, d) {
54131 this.blur(); // avoid keeping focus on the button - #4641
54133 var qaService = services.improveOSM;
54136 d.newStatus = 'INVALID';
54137 qaService.postUpdate(d, function (err, item) {
54138 return dispatch$1.call('change', item);
54142 } // NOTE: Don't change method name until UI v3 is merged
54145 improveOsmEditor.error = function (val) {
54146 if (!arguments.length) return _qaItem;
54148 return improveOsmEditor;
54151 return utilRebind(improveOsmEditor, dispatch$1, 'on');
54154 function uiKeepRightDetails(context) {
54157 function issueDetail(d) {
54158 var itemType = d.itemType,
54159 parentIssueType = d.parentIssueType;
54160 var unknown = _t.html('inspector.unknown');
54161 var replacements = d.replacements || {};
54162 replacements["default"] = unknown; // special key `default` works as a fallback string
54164 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
54166 if (detail === unknown) {
54167 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
54173 function keepRightDetails(selection) {
54174 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
54175 return "".concat(d.id, "-").concat(d.status || 0);
54177 details.exit().remove();
54178 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
54180 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54181 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
54182 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
54184 var relatedEntities = [];
54185 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
54186 var link = select(this);
54187 var isObjectLink = link.classed('error_object_link');
54188 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
54189 var entity = context.hasEntity(entityID);
54190 relatedEntities.push(entityID); // Add click handler
54192 link.on('mouseenter', function () {
54193 utilHighlightEntities([entityID], true, context);
54194 }).on('mouseleave', function () {
54195 utilHighlightEntities([entityID], false, context);
54196 }).on('click', function (d3_event) {
54197 d3_event.preventDefault();
54198 utilHighlightEntities([entityID], false, context);
54199 var osmlayer = context.layers().layer('osm');
54201 if (!osmlayer.enabled()) {
54202 osmlayer.enabled(true);
54205 context.map().centerZoomEase(_qaItem.loc, 20);
54208 context.enter(modeSelect(context, [entityID]));
54210 context.loadEntity(entityID, function () {
54211 context.enter(modeSelect(context, [entityID]));
54214 }); // Replace with friendly name if possible
54215 // (The entity may not yet be loaded into the graph)
54218 var name = utilDisplayName(entity); // try to use common name
54220 if (!name && !isObjectLink) {
54221 var preset = _mainPresetIndex.match(entity, context.graph());
54222 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
54226 this.innerText = name;
54229 }); // Don't hide entities related to this issue - #5880
54231 context.features().forceVisible(relatedEntities);
54232 context.map().pan([0, 0]); // trigger a redraw
54235 keepRightDetails.issue = function (val) {
54236 if (!arguments.length) return _qaItem;
54238 return keepRightDetails;
54241 return keepRightDetails;
54244 function uiKeepRightHeader() {
54247 function issueTitle(d) {
54248 var itemType = d.itemType,
54249 parentIssueType = d.parentIssueType;
54250 var unknown = _t.html('inspector.unknown');
54251 var replacements = d.replacements || {};
54252 replacements["default"] = unknown; // special key `default` works as a fallback string
54254 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
54256 if (title === unknown) {
54257 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
54263 function keepRightHeader(selection) {
54264 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
54265 return "".concat(d.id, "-").concat(d.status || 0);
54267 header.exit().remove();
54268 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
54269 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
54272 iconEnter.append('div').attr('class', function (d) {
54273 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
54274 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
54275 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54278 keepRightHeader.issue = function (val) {
54279 if (!arguments.length) return _qaItem;
54281 return keepRightHeader;
54284 return keepRightHeader;
54287 function uiViewOnKeepRight() {
54290 function viewOnKeepRight(selection) {
54293 if (services.keepRight && _qaItem instanceof QAItem) {
54294 url = services.keepRight.issueURL(_qaItem);
54297 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
54299 link.exit().remove(); // enter
54301 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
54302 .attr('href', function (d) {
54304 }).call(svgIcon('#iD-icon-out-link', 'inline'));
54305 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
54308 viewOnKeepRight.what = function (val) {
54309 if (!arguments.length) return _qaItem;
54311 return viewOnKeepRight;
54314 return viewOnKeepRight;
54317 function uiKeepRightEditor(context) {
54318 var dispatch$1 = dispatch('change');
54319 var qaDetails = uiKeepRightDetails(context);
54320 var qaHeader = uiKeepRightHeader();
54324 function keepRightEditor(selection) {
54325 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
54326 headerEnter.append('button').attr('class', 'close').on('click', function () {
54327 return context.enter(modeBrowse(context));
54328 }).call(svgIcon('#iD-icon-close'));
54329 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
54330 var body = selection.selectAll('.body').data([0]);
54331 body = body.enter().append('div').attr('class', 'body').merge(body);
54332 var editor = body.selectAll('.qa-editor').data([0]);
54333 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
54334 var footer = selection.selectAll('.footer').data([0]);
54335 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
54338 function keepRightSaveSection(selection) {
54339 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54341 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
54342 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
54343 return "".concat(d.id, "-").concat(d.status || 0);
54346 saveSection.exit().remove(); // enter
54348 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
54349 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
54350 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
54351 return d.newComment || d.comment;
54352 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
54354 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
54356 function changeInput() {
54357 var input = select(this);
54358 var val = input.property('value').trim();
54360 if (val === _qaItem.comment) {
54362 } // store the unsaved comment with the issue itself
54365 _qaItem = _qaItem.update({
54368 var qaService = services.keepRight;
54371 qaService.replaceItem(_qaItem); // update keepright cache
54374 saveSection.call(qaSaveButtons);
54378 function qaSaveButtons(selection) {
54379 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54381 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
54382 return d.status + d.id;
54385 buttonSection.exit().remove(); // enter
54387 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
54388 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
54389 buttonEnter.append('button').attr('class', 'button close-button action');
54390 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
54392 buttonSection = buttonSection.merge(buttonEnter);
54393 buttonSection.select('.comment-button') // select and propagate data
54394 .attr('disabled', function (d) {
54395 return d.newComment ? null : true;
54396 }).on('click.comment', function (d3_event, d) {
54397 this.blur(); // avoid keeping focus on the button - #4641
54399 var qaService = services.keepRight;
54402 qaService.postUpdate(d, function (err, item) {
54403 return dispatch$1.call('change', item);
54407 buttonSection.select('.close-button') // select and propagate data
54408 .html(function (d) {
54409 var andComment = d.newComment ? '_comment' : '';
54410 return _t.html("QA.keepRight.close".concat(andComment));
54411 }).on('click.close', function (d3_event, d) {
54412 this.blur(); // avoid keeping focus on the button - #4641
54414 var qaService = services.keepRight;
54417 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
54419 qaService.postUpdate(d, function (err, item) {
54420 return dispatch$1.call('change', item);
54424 buttonSection.select('.ignore-button') // select and propagate data
54425 .html(function (d) {
54426 var andComment = d.newComment ? '_comment' : '';
54427 return _t.html("QA.keepRight.ignore".concat(andComment));
54428 }).on('click.ignore', function (d3_event, d) {
54429 this.blur(); // avoid keeping focus on the button - #4641
54431 var qaService = services.keepRight;
54434 d.newStatus = 'ignore'; // ignore permanently (false positive)
54436 qaService.postUpdate(d, function (err, item) {
54437 return dispatch$1.call('change', item);
54441 } // NOTE: Don't change method name until UI v3 is merged
54444 keepRightEditor.error = function (val) {
54445 if (!arguments.length) return _qaItem;
54447 return keepRightEditor;
54450 return utilRebind(keepRightEditor, dispatch$1, 'on');
54453 function uiOsmoseDetails(context) {
54456 function issueString(d, type) {
54457 if (!d) return ''; // Issue strings are cached from Osmose API
54459 var s = services.osmose.getStrings(d.itemType);
54460 return type in s ? s[type] : '';
54463 function osmoseDetails(selection) {
54464 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
54465 return "".concat(d.id, "-").concat(d.status || 0);
54467 details.exit().remove();
54468 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
54470 if (issueString(_qaItem, 'detail')) {
54471 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54472 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
54473 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
54474 return issueString(d, 'detail');
54475 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
54476 } // Elements (populated later as data is requested)
54479 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54480 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
54482 if (issueString(_qaItem, 'fix')) {
54483 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54485 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
54487 _div.append('p').html(function (d) {
54488 return issueString(d, 'fix');
54489 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
54490 } // Common Pitfalls (mustn't exist for every issue type)
54493 if (issueString(_qaItem, 'trap')) {
54494 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54496 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
54498 _div2.append('p').html(function (d) {
54499 return issueString(d, 'trap');
54500 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
54501 } // Save current item to check if UI changed by time request resolves
54504 var thisItem = _qaItem;
54505 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
54506 // No details to add if there are no associated issue elements
54507 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
54509 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
54512 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
54513 detailsDiv.append('p').html(function (d) {
54515 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
54516 } // Create list of linked issue elements
54519 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
54520 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
54522 }).each(function () {
54523 var link = select(this);
54524 var entityID = this.textContent;
54525 var entity = context.hasEntity(entityID); // Add click handler
54527 link.on('mouseenter', function () {
54528 utilHighlightEntities([entityID], true, context);
54529 }).on('mouseleave', function () {
54530 utilHighlightEntities([entityID], false, context);
54531 }).on('click', function (d3_event) {
54532 d3_event.preventDefault();
54533 utilHighlightEntities([entityID], false, context);
54534 var osmlayer = context.layers().layer('osm');
54536 if (!osmlayer.enabled()) {
54537 osmlayer.enabled(true);
54540 context.map().centerZoom(d.loc, 20);
54543 context.enter(modeSelect(context, [entityID]));
54545 context.loadEntity(entityID, function () {
54546 context.enter(modeSelect(context, [entityID]));
54549 }); // Replace with friendly name if possible
54550 // (The entity may not yet be loaded into the graph)
54553 var name = utilDisplayName(entity); // try to use common name
54556 var preset = _mainPresetIndex.match(entity, context.graph());
54557 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
54561 this.innerText = name;
54564 }); // Don't hide entities related to this issue - #5880
54566 context.features().forceVisible(d.elems);
54567 context.map().pan([0, 0]); // trigger a redraw
54568 })["catch"](function (err) {
54569 console.log(err); // eslint-disable-line no-console
54573 osmoseDetails.issue = function (val) {
54574 if (!arguments.length) return _qaItem;
54576 return osmoseDetails;
54579 return osmoseDetails;
54582 function uiOsmoseHeader() {
54585 function issueTitle(d) {
54586 var unknown = _t('inspector.unknown');
54587 if (!d) return unknown; // Issue titles supplied by Osmose
54589 var s = services.osmose.getStrings(d.itemType);
54590 return 'title' in s ? s.title : unknown;
54593 function osmoseHeader(selection) {
54594 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
54595 return "".concat(d.id, "-").concat(d.status || 0);
54597 header.exit().remove();
54598 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
54599 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
54601 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
54602 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
54604 svgEnter.append('polygon').attr('fill', function (d) {
54605 return services.osmose.getColor(d.item);
54606 }).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');
54607 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
54608 var picon = d.icon;
54613 var isMaki = /^maki-/.test(picon);
54614 return "#".concat(picon).concat(isMaki ? '-11' : '');
54617 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54620 osmoseHeader.issue = function (val) {
54621 if (!arguments.length) return _qaItem;
54623 return osmoseHeader;
54626 return osmoseHeader;
54629 function uiViewOnOsmose() {
54632 function viewOnOsmose(selection) {
54635 if (services.osmose && _qaItem instanceof QAItem) {
54636 url = services.osmose.itemURL(_qaItem);
54639 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
54641 link.exit().remove(); // enter
54643 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
54644 .attr('href', function (d) {
54646 }).call(svgIcon('#iD-icon-out-link', 'inline'));
54647 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
54650 viewOnOsmose.what = function (val) {
54651 if (!arguments.length) return _qaItem;
54653 return viewOnOsmose;
54656 return viewOnOsmose;
54659 function uiOsmoseEditor(context) {
54660 var dispatch$1 = dispatch('change');
54661 var qaDetails = uiOsmoseDetails(context);
54662 var qaHeader = uiOsmoseHeader();
54666 function osmoseEditor(selection) {
54667 var header = selection.selectAll('.header').data([0]);
54668 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
54669 headerEnter.append('button').attr('class', 'close').on('click', function () {
54670 return context.enter(modeBrowse(context));
54671 }).call(svgIcon('#iD-icon-close'));
54672 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
54673 var body = selection.selectAll('.body').data([0]);
54674 body = body.enter().append('div').attr('class', 'body').merge(body);
54675 var editor = body.selectAll('.qa-editor').data([0]);
54676 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
54677 var footer = selection.selectAll('.footer').data([0]);
54678 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
54681 function osmoseSaveSection(selection) {
54682 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54684 var isShown = _qaItem && isSelected;
54685 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
54686 return "".concat(d.id, "-").concat(d.status || 0);
54689 saveSection.exit().remove(); // enter
54691 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
54693 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
54696 function qaSaveButtons(selection) {
54697 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54699 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
54700 return d.status + d.id;
54703 buttonSection.exit().remove(); // enter
54705 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
54706 buttonEnter.append('button').attr('class', 'button close-button action');
54707 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
54709 buttonSection = buttonSection.merge(buttonEnter);
54710 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
54711 this.blur(); // avoid keeping focus on the button - #4641
54713 var qaService = services.osmose;
54716 d.newStatus = 'done';
54717 qaService.postUpdate(d, function (err, item) {
54718 return dispatch$1.call('change', item);
54722 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
54723 this.blur(); // avoid keeping focus on the button - #4641
54725 var qaService = services.osmose;
54728 d.newStatus = 'false';
54729 qaService.postUpdate(d, function (err, item) {
54730 return dispatch$1.call('change', item);
54734 } // NOTE: Don't change method name until UI v3 is merged
54737 osmoseEditor.error = function (val) {
54738 if (!arguments.length) return _qaItem;
54740 return osmoseEditor;
54743 return utilRebind(osmoseEditor, dispatch$1, 'on');
54746 function modeSelectError(context, selectedErrorID, selectedErrorService) {
54748 id: 'select-error',
54751 var keybinding = utilKeybinding('select-error');
54752 var errorService = services[selectedErrorService];
54755 switch (selectedErrorService) {
54757 errorEditor = uiImproveOsmEditor(context).on('change', function () {
54758 context.map().pan([0, 0]); // trigger a redraw
54760 var error = checkSelectedID();
54761 if (!error) return;
54762 context.ui().sidebar.show(errorEditor.error(error));
54767 errorEditor = uiKeepRightEditor(context).on('change', function () {
54768 context.map().pan([0, 0]); // trigger a redraw
54770 var error = checkSelectedID();
54771 if (!error) return;
54772 context.ui().sidebar.show(errorEditor.error(error));
54777 errorEditor = uiOsmoseEditor(context).on('change', function () {
54778 context.map().pan([0, 0]); // trigger a redraw
54780 var error = checkSelectedID();
54781 if (!error) return;
54782 context.ui().sidebar.show(errorEditor.error(error));
54787 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
54789 function checkSelectedID() {
54790 if (!errorService) return;
54791 var error = errorService.getError(selectedErrorID);
54794 context.enter(modeBrowse(context));
54800 mode.zoomToSelected = function () {
54801 if (!errorService) return;
54802 var error = errorService.getError(selectedErrorID);
54805 context.map().centerZoomEase(error.loc, 20);
54809 mode.enter = function () {
54810 var error = checkSelectedID();
54811 if (!error) return;
54812 behaviors.forEach(context.install);
54813 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
54814 select(document).call(keybinding);
54816 var sidebar = context.ui().sidebar;
54817 sidebar.show(errorEditor.error(error));
54818 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
54820 function selectError(d3_event, drawn) {
54821 if (!checkSelectedID()) return;
54822 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
54824 if (selection.empty()) {
54825 // Return to browse mode if selected DOM elements have
54826 // disappeared because the user moved them out of view..
54827 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
54829 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
54830 context.enter(modeBrowse(context));
54833 selection.classed('selected', true);
54834 context.selectedErrorID(selectedErrorID);
54839 if (context.container().select('.combobox').size()) return;
54840 context.enter(modeBrowse(context));
54844 mode.exit = function () {
54845 behaviors.forEach(context.uninstall);
54846 select(document).call(keybinding.unbind);
54847 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
54848 context.map().on('drawn.select-error', null);
54849 context.ui().sidebar.hide();
54850 context.selectedErrorID(null);
54851 context.features().forceVisible([]);
54857 function behaviorSelect(context) {
54858 var _tolerancePx = 4; // see also behaviorDrag
54860 var _lastMouseEvent = null;
54861 var _showMenu = false;
54862 var _downPointers = {};
54863 var _longPressTimeout = null;
54864 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
54866 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
54868 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
54870 function keydown(d3_event) {
54871 if (d3_event.keyCode === 32) {
54872 // don't react to spacebar events during text input
54873 var activeNode = document.activeElement;
54874 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
54877 if (d3_event.keyCode === 93 || // context menu key
54878 d3_event.keyCode === 32) {
54880 d3_event.preventDefault();
54883 if (d3_event.repeat) return; // ignore repeated events for held keys
54884 // if any key is pressed the user is probably doing something other than long-pressing
54888 if (d3_event.shiftKey) {
54889 context.surface().classed('behavior-multiselect', true);
54892 if (d3_event.keyCode === 32) {
54894 if (!_downPointers.spacebar && _lastMouseEvent) {
54896 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
54897 _downPointers.spacebar = {
54898 firstEvent: _lastMouseEvent,
54899 lastEvent: _lastMouseEvent
54905 function keyup(d3_event) {
54908 if (!d3_event.shiftKey) {
54909 context.surface().classed('behavior-multiselect', false);
54912 if (d3_event.keyCode === 93) {
54913 // context menu key
54914 d3_event.preventDefault();
54915 _lastInteractionType = 'menukey';
54916 contextmenu(d3_event);
54917 } else if (d3_event.keyCode === 32) {
54919 var pointer = _downPointers.spacebar;
54922 delete _downPointers.spacebar;
54923 if (pointer.done) return;
54924 d3_event.preventDefault();
54925 _lastInteractionType = 'spacebar';
54926 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
54931 function pointerdown(d3_event) {
54932 var id = (d3_event.pointerId || 'mouse').toString();
54934 if (d3_event.buttons && d3_event.buttons !== 1) return;
54935 context.ui().closeEditMenu();
54936 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
54937 _downPointers[id] = {
54938 firstEvent: d3_event,
54939 lastEvent: d3_event
54943 function didLongPress(id, interactionType) {
54944 var pointer = _downPointers[id];
54945 if (!pointer) return;
54947 for (var i in _downPointers) {
54948 // don't allow this or any currently down pointer to trigger another click
54949 _downPointers[i].done = true;
54950 } // treat long presses like right-clicks
54953 _longPressTimeout = null;
54954 _lastInteractionType = interactionType;
54956 click(pointer.firstEvent, pointer.lastEvent, id);
54959 function pointermove(d3_event) {
54960 var id = (d3_event.pointerId || 'mouse').toString();
54962 if (_downPointers[id]) {
54963 _downPointers[id].lastEvent = d3_event;
54966 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
54967 _lastMouseEvent = d3_event;
54969 if (_downPointers.spacebar) {
54970 _downPointers.spacebar.lastEvent = d3_event;
54975 function pointerup(d3_event) {
54976 var id = (d3_event.pointerId || 'mouse').toString();
54977 var pointer = _downPointers[id];
54978 if (!pointer) return;
54979 delete _downPointers[id];
54981 if (_multiselectionPointerId === id) {
54982 _multiselectionPointerId = null;
54985 if (pointer.done) return;
54986 click(pointer.firstEvent, d3_event, id);
54989 function pointercancel(d3_event) {
54990 var id = (d3_event.pointerId || 'mouse').toString();
54991 if (!_downPointers[id]) return;
54992 delete _downPointers[id];
54994 if (_multiselectionPointerId === id) {
54995 _multiselectionPointerId = null;
54999 function contextmenu(d3_event) {
55000 d3_event.preventDefault();
55002 if (!+d3_event.clientX && !+d3_event.clientY) {
55003 if (_lastMouseEvent) {
55004 d3_event.sourceEvent = _lastMouseEvent;
55009 _lastMouseEvent = d3_event;
55010 _lastInteractionType = 'rightclick';
55014 click(d3_event, d3_event);
55017 function click(firstEvent, lastEvent, pointerId) {
55019 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
55020 // are transformed when drag-panning.
55022 var pointGetter = utilFastMouse(mapNode);
55023 var p1 = pointGetter(firstEvent);
55024 var p2 = pointGetter(lastEvent);
55025 var dist = geoVecLength(p1, p2);
55027 if (dist > _tolerancePx || !mapContains(lastEvent)) {
55032 var targetDatum = lastEvent.target.__data__;
55033 var multiselectEntityId;
55035 if (!_multiselectionPointerId) {
55036 // If a different pointer than the one triggering this click is down on a
55037 // feature, treat this and all future clicks as multiselection until that
55038 // pointer is raised.
55039 var selectPointerInfo = pointerDownOnSelection(pointerId);
55041 if (selectPointerInfo) {
55042 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
55044 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
55045 _downPointers[selectPointerInfo.pointerId].done = true;
55047 } // support multiselect if data is already selected
55050 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
55051 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
55052 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
55053 _multiselectionPointerId && !multiselectEntityId);
55055 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
55057 function mapContains(event) {
55058 var rect = mapNode.getBoundingClientRect();
55059 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
55062 function pointerDownOnSelection(skipPointerId) {
55063 var mode = context.mode();
55064 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
55066 for (var pointerId in _downPointers) {
55067 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
55068 var pointerInfo = _downPointers[pointerId];
55069 var p1 = pointGetter(pointerInfo.firstEvent);
55070 var p2 = pointGetter(pointerInfo.lastEvent);
55071 if (geoVecLength(p1, p2) > _tolerancePx) continue;
55072 var datum = pointerInfo.firstEvent.target.__data__;
55073 var entity = datum && datum.properties && datum.properties.entity || datum;
55074 if (context.graph().hasEntity(entity.id)) return {
55075 pointerId: pointerId,
55076 entityId: entity.id,
55077 selected: selectedIDs.indexOf(entity.id) !== -1
55085 function processClick(datum, isMultiselect, point, alsoSelectId) {
55086 var mode = context.mode();
55087 var showMenu = _showMenu;
55088 var interactionType = _lastInteractionType;
55089 var entity = datum && datum.properties && datum.properties.entity;
55090 if (entity) datum = entity;
55092 if (datum && datum.type === 'midpoint') {
55093 // treat targeting midpoints as if targeting the parent way
55094 datum = datum.parents[0];
55099 if (datum instanceof osmEntity) {
55100 // targeting an entity
55101 var selectedIDs = context.selectedIDs();
55102 context.selectedNoteID(null);
55103 context.selectedErrorID(null);
55105 if (!isMultiselect) {
55106 // don't change the selection if we're toggling the menu atop a multiselection
55107 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
55108 if (alsoSelectId === datum.id) alsoSelectId = null;
55109 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
55110 // selected since listeners may expect `context.enter` events,
55111 // e.g. in the walkthrough
55113 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
55114 context.enter(newMode);
55117 if (selectedIDs.indexOf(datum.id) !== -1) {
55118 // clicked entity is already in the selectedIDs list..
55120 // deselect clicked entity, then reenter select mode or return to browse mode..
55121 selectedIDs = selectedIDs.filter(function (id) {
55122 return id !== datum.id;
55124 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
55125 context.enter(newMode);
55128 // clicked entity is not in the selected list, add it..
55129 selectedIDs = selectedIDs.concat([datum.id]);
55130 newMode = mode.selectedIDs(selectedIDs);
55131 context.enter(newMode);
55134 } else if (datum && datum.__featurehash__ && !isMultiselect) {
55135 // targeting custom data
55136 context.selectedNoteID(null).enter(modeSelectData(context, datum));
55137 } else if (datum instanceof osmNote && !isMultiselect) {
55138 // targeting a note
55139 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
55140 } else if (datum instanceof QAItem & !isMultiselect) {
55141 // targeting an external QA issue
55142 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
55144 // targeting nothing
55145 context.selectedNoteID(null);
55146 context.selectedErrorID(null);
55148 if (!isMultiselect && mode.id !== 'browse') {
55149 context.enter(modeBrowse(context));
55153 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
55155 if (showMenu) context.ui().showEditMenu(point, interactionType);
55159 function cancelLongPress() {
55160 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
55161 _longPressTimeout = null;
55164 function resetProperties() {
55167 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
55170 function behavior(selection) {
55172 _lastMouseEvent = context.map().lastPointerEvent();
55173 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) {
55174 // Edge and IE really like to show the contextmenu on the
55175 // menubar when user presses a keyboard menu button
55176 // even after we've already preventdefaulted the key event.
55179 if (+e.clientX === 0 && +e.clientY === 0) {
55180 d3_event.preventDefault();
55183 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
55184 /*if (d3_event && d3_event.shiftKey) {
55186 .classed('behavior-multiselect', true);
55190 behavior.off = function (selection) {
55192 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);
55193 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
55194 context.surface().classed('behavior-multiselect', false);
55200 function behaviorDrawWay(context, wayID, mode, startGraph) {
55201 var dispatch$1 = dispatch('rejectedSelfIntersection');
55202 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
55214 var _pointerHasMoved = false; // The osmNode to be placed.
55215 // This is temporary and just follows the mouse cursor until an "add" event occurs.
55219 var _didResolveTempEdit = false;
55221 function createDrawNode(loc) {
55222 // don't make the draw node until we actually need it
55223 _drawNode = osmNode({
55226 context.pauseChangeDispatch();
55227 context.replace(function actionAddDrawNode(graph) {
55228 // add the draw node to the graph and insert it into the way
55229 var way = graph.entity(wayID);
55230 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
55232 context.resumeChangeDispatch();
55233 setActiveElements();
55236 function removeDrawNode() {
55237 context.pauseChangeDispatch();
55238 context.replace(function actionDeleteDrawNode(graph) {
55239 var way = graph.entity(wayID);
55240 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
55242 _drawNode = undefined;
55243 context.resumeChangeDispatch();
55246 function keydown(d3_event) {
55247 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
55248 if (context.surface().classed('nope')) {
55249 context.surface().classed('nope-suppressed', true);
55252 context.surface().classed('nope', false).classed('nope-disabled', true);
55256 function keyup(d3_event) {
55257 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
55258 if (context.surface().classed('nope-suppressed')) {
55259 context.surface().classed('nope', true);
55262 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
55266 function allowsVertex(d) {
55267 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
55269 // - `mode/drag_node.js` `doMove()`
55270 // - `behavior/draw.js` `click()`
55271 // - `behavior/draw_way.js` `move()`
55274 function move(d3_event, datum) {
55275 var loc = context.map().mouseCoordinates();
55276 if (!_drawNode) createDrawNode(loc);
55277 context.surface().classed('nope-disabled', d3_event.altKey);
55278 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
55279 var targetNodes = datum && datum.properties && datum.properties.nodes;
55282 // snap to node/vertex - a point target with `.loc`
55284 } else if (targetNodes) {
55285 // snap to way - a line target with `.nodes`
55286 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
55293 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
55294 _drawNode = context.entity(_drawNode.id);
55296 /* includeDrawNode */
55298 } // Check whether this edit causes the geometry to break.
55299 // If so, class the surface with a nope cursor.
55300 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
55303 function checkGeometry(includeDrawNode) {
55304 var nopeDisabled = context.surface().classed('nope-disabled');
55305 var isInvalid = isInvalidGeometry(includeDrawNode);
55307 if (nopeDisabled) {
55308 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
55310 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
55314 function isInvalidGeometry(includeDrawNode) {
55315 var testNode = _drawNode; // we only need to test the single way we're drawing
55317 var parentWay = context.graph().entity(wayID);
55318 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
55320 if (includeDrawNode) {
55321 if (parentWay.isClosed()) {
55322 // don't test the last segment for closed ways - #4655
55323 // (still test the first segment)
55327 // discount the draw node
55328 if (parentWay.isClosed()) {
55329 if (nodes.length < 3) return false;
55330 if (_drawNode) nodes.splice(-2, 1);
55331 testNode = nodes[nodes.length - 2];
55333 // there's nothing we need to test if we ignore the draw node on open ways
55338 return testNode && geoHasSelfIntersections(nodes, testNode.id);
55341 function undone() {
55342 // undoing removed the temp edit
55343 _didResolveTempEdit = true;
55344 context.pauseChangeDispatch();
55347 if (context.graph() === startGraph) {
55348 // We've undone back to the initial state before we started drawing.
55349 // Just exit the draw mode without undoing whatever we did before
55350 // we entered the draw mode.
55351 nextMode = modeSelect(context, [wayID]);
55353 // The `undo` only removed the temporary edit, so here we have to
55354 // manually undo to actually remove the last node we added. We can't
55355 // use the `undo` function since the initial "add" graph doesn't have
55356 // an annotation and so cannot be undone to.
55357 context.pop(1); // continue drawing
55360 } // clear the redo stack by adding and removing a blank edit
55363 context.perform(actionNoop());
55365 context.resumeChangeDispatch();
55366 context.enter(nextMode);
55369 function setActiveElements() {
55370 if (!_drawNode) return;
55371 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
55374 function resetToStartGraph() {
55375 while (context.graph() !== startGraph) {
55380 var drawWay = function drawWay(surface) {
55381 _drawNode = undefined;
55382 _didResolveTempEdit = false;
55383 _origWay = context.entity(wayID);
55384 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] : _origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1];
55385 _wayGeometry = _origWay.geometry(context.graph());
55386 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
55387 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
55388 // We must make sure to replace or remove it later.
55390 context.pauseChangeDispatch();
55391 context.perform(actionNoop(), _annotation);
55392 context.resumeChangeDispatch();
55393 behavior.hover().initialNodeID(_headNodeID);
55394 behavior.on('move', function () {
55395 _pointerHasMoved = true;
55396 move.apply(this, arguments);
55397 }).on('down', function () {
55398 move.apply(this, arguments);
55399 }).on('downcancel', function () {
55400 if (_drawNode) removeDrawNode();
55401 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
55402 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
55403 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
55404 setActiveElements();
55405 surface.call(behavior);
55406 context.history().on('undone.draw', undone);
55409 drawWay.off = function (surface) {
55410 if (!_didResolveTempEdit) {
55411 // Drawing was interrupted unexpectedly.
55412 // This can happen if the user changes modes,
55413 // clicks geolocate button, a hashchange event occurs, etc.
55414 context.pauseChangeDispatch();
55415 resetToStartGraph();
55416 context.resumeChangeDispatch();
55419 _drawNode = undefined;
55420 _nodeIndex = undefined;
55421 context.map().on('drawn.draw', null);
55422 surface.call(behavior.off).selectAll('.active').classed('active', false);
55423 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
55424 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
55425 context.history().on('undone.draw', null);
55428 function attemptAdd(d, loc, doAdd) {
55430 // move the node to the final loc in case move wasn't called
55431 // consistently (e.g. on touch devices)
55432 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
55433 _drawNode = context.entity(_drawNode.id);
55435 createDrawNode(loc);
55439 /* includeDrawNode */
55442 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
55443 if (!_pointerHasMoved) {
55444 // prevent the temporary draw node from appearing on touch devices
55448 dispatch$1.call('rejectedSelfIntersection', this);
55449 return; // can't click here
55452 context.pauseChangeDispatch();
55453 doAdd(); // we just replaced the temporary edit with the real one
55455 _didResolveTempEdit = true;
55456 context.resumeChangeDispatch();
55457 context.enter(mode);
55458 } // Accept the current position of the drawing node
55461 drawWay.add = function (loc, d) {
55462 attemptAdd(d, loc, function () {// don't need to do anything extra
55464 }; // Connect the way to an existing way
55467 drawWay.addWay = function (loc, edge, d) {
55468 attemptAdd(d, loc, function () {
55469 context.replace(actionAddMidpoint({
55472 }, _drawNode), _annotation);
55474 }; // Connect the way to an existing node
55477 drawWay.addNode = function (node, d) {
55478 // finish drawing if the mapper targets the prior node
55479 if (node.id === _headNodeID || // or the first node when drawing an area
55480 _origWay.isClosed() && node.id === _origWay.first()) {
55485 attemptAdd(d, node.loc, function () {
55486 context.replace(function actionReplaceDrawNode(graph) {
55487 // remove the temporary draw node and insert the existing node
55488 // at the same index
55489 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
55490 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
55493 }; // Finish the draw operation, removing the temporary edit.
55494 // If the way has enough nodes to be valid, it's selected.
55495 // Otherwise, delete everything and return to browse mode.
55498 drawWay.finish = function () {
55499 checkGeometry(false
55500 /* includeDrawNode */
55503 if (context.surface().classed('nope')) {
55504 dispatch$1.call('rejectedSelfIntersection', this);
55505 return; // can't click here
55508 context.pauseChangeDispatch(); // remove the temporary edit
55511 _didResolveTempEdit = true;
55512 context.resumeChangeDispatch();
55513 var way = context.hasEntity(wayID);
55515 if (!way || way.isDegenerate()) {
55520 window.setTimeout(function () {
55521 context.map().dblclickZoomEnable(true);
55523 var isNewFeature = !mode.isContinuing;
55524 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
55525 }; // Cancel the draw operation, delete everything, and return to browse mode.
55528 drawWay.cancel = function () {
55529 context.pauseChangeDispatch();
55530 resetToStartGraph();
55531 context.resumeChangeDispatch();
55532 window.setTimeout(function () {
55533 context.map().dblclickZoomEnable(true);
55535 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
55536 context.enter(modeBrowse(context));
55539 drawWay.nodeIndex = function (val) {
55540 if (!arguments.length) return _nodeIndex;
55545 drawWay.activeID = function () {
55546 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
55551 return utilRebind(drawWay, dispatch$1, 'on');
55554 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
55559 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
55560 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
55562 mode.wayID = wayID;
55563 mode.isContinuing = continuing;
55565 mode.enter = function () {
55566 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
55567 context.install(behavior);
55570 mode.exit = function () {
55571 context.uninstall(behavior);
55574 mode.selectedIDs = function () {
55578 mode.activeID = function () {
55579 return behavior && behavior.activeID() || [];
55585 function operationContinue(context, selectedIDs) {
55586 var _entities = selectedIDs.map(function (id) {
55587 return context.graph().entity(id);
55590 var _geometries = Object.assign({
55593 }, utilArrayGroupBy(_entities, function (entity) {
55594 return entity.geometry(context.graph());
55597 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
55599 function candidateWays() {
55600 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
55601 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
55605 var _candidates = candidateWays();
55607 var operation = function operation() {
55608 var candidate = _candidates[0];
55609 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
55612 operation.relatedEntityIds = function () {
55613 return _candidates.length ? [_candidates[0].id] : [];
55616 operation.available = function () {
55617 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
55620 operation.disabled = function () {
55621 if (_candidates.length === 0) {
55622 return 'not_eligible';
55623 } else if (_candidates.length > 1) {
55630 operation.tooltip = function () {
55631 var disable = operation.disabled();
55632 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
55635 operation.annotation = function () {
55636 return _t('operations.continue.annotation.line');
55639 operation.id = 'continue';
55640 operation.keys = [_t('operations.continue.key')];
55641 operation.title = _t('operations.continue.title');
55642 operation.behavior = behaviorOperation(context).which(operation);
55646 function operationCopy(context, selectedIDs) {
55647 function getFilteredIdsToCopy() {
55648 return selectedIDs.filter(function (selectedID) {
55649 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
55651 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
55655 var operation = function operation() {
55656 var graph = context.graph();
55657 var selected = groupEntities(getFilteredIdsToCopy(), graph);
55663 for (i = 0; i < selected.relation.length; i++) {
55664 entity = selected.relation[i];
55666 if (!skip[entity.id] && entity.isComplete(graph)) {
55667 canCopy.push(entity.id);
55668 skip = getDescendants(entity.id, graph, skip);
55672 for (i = 0; i < selected.way.length; i++) {
55673 entity = selected.way[i];
55675 if (!skip[entity.id]) {
55676 canCopy.push(entity.id);
55677 skip = getDescendants(entity.id, graph, skip);
55681 for (i = 0; i < selected.node.length; i++) {
55682 entity = selected.node[i];
55684 if (!skip[entity.id]) {
55685 canCopy.push(entity.id);
55689 context.copyIDs(canCopy);
55691 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
55692 // store the anchor coordinates if copying more than a single node
55693 context.copyLonLat(context.projection.invert(_point));
55695 context.copyLonLat(null);
55699 function groupEntities(ids, graph) {
55700 var entities = ids.map(function (id) {
55701 return graph.entity(id);
55703 return Object.assign({
55707 }, utilArrayGroupBy(entities, 'type'));
55710 function getDescendants(id, graph, descendants) {
55711 var entity = graph.entity(id);
55713 descendants = descendants || {};
55715 if (entity.type === 'relation') {
55716 children = entity.members.map(function (m) {
55719 } else if (entity.type === 'way') {
55720 children = entity.nodes;
55725 for (var i = 0; i < children.length; i++) {
55726 if (!descendants[children[i]]) {
55727 descendants[children[i]] = true;
55728 descendants = getDescendants(children[i], graph, descendants);
55732 return descendants;
55735 operation.available = function () {
55736 return getFilteredIdsToCopy().length > 0;
55739 operation.disabled = function () {
55740 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
55742 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
55743 return 'too_large';
55749 operation.availableForKeypress = function () {
55750 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
55752 return !selection || !selection.toString();
55755 operation.tooltip = function () {
55756 var disable = operation.disabled();
55757 return disable ? _t('operations.copy.' + disable, {
55758 n: selectedIDs.length
55759 }) : _t('operations.copy.description', {
55760 n: selectedIDs.length
55764 operation.annotation = function () {
55765 return _t('operations.copy.annotation', {
55766 n: selectedIDs.length
55772 operation.point = function (val) {
55777 operation.id = 'copy';
55778 operation.keys = [uiCmd('⌘C')];
55779 operation.title = _t('operations.copy.title');
55780 operation.behavior = behaviorOperation(context).which(operation);
55784 function operationDisconnect(context, selectedIDs) {
55785 var _vertexIDs = [];
55787 var _otherIDs = [];
55789 selectedIDs.forEach(function (id) {
55790 var entity = context.entity(id);
55792 if (entity.type === 'way') {
55794 } else if (entity.geometry(context.graph()) === 'vertex') {
55795 _vertexIDs.push(id);
55797 _otherIDs.push(id);
55802 _descriptionID = '',
55803 _annotationID = 'features';
55805 var _disconnectingVertexIds = [];
55806 var _disconnectingWayIds = [];
55808 if (_vertexIDs.length > 0) {
55809 // At the selected vertices, disconnect the selected ways, if any, else
55810 // disconnect all connected ways
55811 _disconnectingVertexIds = _vertexIDs;
55813 _vertexIDs.forEach(function (vertexID) {
55814 var action = actionDisconnect(vertexID);
55816 if (_wayIDs.length > 0) {
55817 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
55818 var way = context.entity(wayID);
55819 return way.nodes.indexOf(vertexID) !== -1;
55822 action.limitWays(waysIDsForVertex);
55825 _actions.push(action);
55827 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
55832 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
55833 return _wayIDs.indexOf(id) === -1;
55835 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
55837 if (_wayIDs.length === 1) {
55838 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
55840 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
55842 } else if (_wayIDs.length > 0) {
55843 // Disconnect the selected ways from each other, if they're connected,
55844 // else disconnect them from all connected ways
55845 var ways = _wayIDs.map(function (id) {
55846 return context.entity(id);
55849 var nodes = utilGetAllNodes(_wayIDs, context.graph());
55850 _coords = nodes.map(function (n) {
55852 }); // actions for connected nodes shared by at least two selected ways
55854 var sharedActions = [];
55855 var sharedNodes = []; // actions for connected nodes
55857 var unsharedActions = [];
55858 var unsharedNodes = [];
55859 nodes.forEach(function (node) {
55860 var action = actionDisconnect(node.id).limitWays(_wayIDs);
55862 if (action.disabled(context.graph()) !== 'not_connected') {
55865 for (var i in ways) {
55868 if (way.nodes.indexOf(node.id) !== -1) {
55872 if (count > 1) break;
55876 sharedActions.push(action);
55877 sharedNodes.push(node);
55879 unsharedActions.push(action);
55880 unsharedNodes.push(node);
55884 _descriptionID += 'no_points.';
55885 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
55887 if (sharedActions.length) {
55888 // if any nodes are shared, only disconnect the selected ways from each other
55889 _actions = sharedActions;
55890 _disconnectingVertexIds = sharedNodes.map(function (node) {
55893 _descriptionID += 'conjoined';
55894 _annotationID = 'from_each_other';
55896 // if no nodes are shared, disconnect the selected ways from all connected ways
55897 _actions = unsharedActions;
55898 _disconnectingVertexIds = unsharedNodes.map(function (node) {
55902 if (_wayIDs.length === 1) {
55903 _descriptionID += context.graph().geometry(_wayIDs[0]);
55905 _descriptionID += 'separate';
55910 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
55912 var operation = function operation() {
55913 context.perform(function (graph) {
55914 return _actions.reduce(function (graph, action) {
55915 return action(graph);
55917 }, operation.annotation());
55918 context.validator().validate();
55921 operation.relatedEntityIds = function () {
55922 if (_vertexIDs.length) {
55923 return _disconnectingWayIds;
55926 return _disconnectingVertexIds;
55929 operation.available = function () {
55930 if (_actions.length === 0) return false;
55931 if (_otherIDs.length !== 0) return false;
55932 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
55933 return _vertexIDs.some(function (vertexID) {
55934 var way = context.entity(wayID);
55935 return way.nodes.indexOf(vertexID) !== -1;
55941 operation.disabled = function () {
55944 for (var actionIndex in _actions) {
55945 reason = _actions[actionIndex].disabled(context.graph());
55946 if (reason) return reason;
55949 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
55950 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
55951 } else if (_coords && someMissing()) {
55952 return 'not_downloaded';
55953 } else if (selectedIDs.some(context.hasHiddenConnections)) {
55954 return 'connected_to_hidden';
55959 function someMissing() {
55960 if (context.inIntro()) return false;
55961 var osm = context.connection();
55964 var missing = _coords.filter(function (loc) {
55965 return !osm.isDataLoaded(loc);
55968 if (missing.length) {
55969 missing.forEach(function (loc) {
55970 context.loadTileAtLoc(loc);
55980 operation.tooltip = function () {
55981 var disable = operation.disabled();
55984 return _t('operations.disconnect.' + disable);
55987 return _t('operations.disconnect.description.' + _descriptionID);
55990 operation.annotation = function () {
55991 return _t('operations.disconnect.annotation.' + _annotationID);
55994 operation.id = 'disconnect';
55995 operation.keys = [_t('operations.disconnect.key')];
55996 operation.title = _t('operations.disconnect.title');
55997 operation.behavior = behaviorOperation(context).which(operation);
56001 function operationDowngrade(context, selectedIDs) {
56002 var _affectedFeatureCount = 0;
56004 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
56006 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
56008 function downgradeTypeForEntityIDs(entityIds) {
56010 _affectedFeatureCount = 0;
56012 for (var i in entityIds) {
56013 var entityID = entityIds[i];
56014 var type = downgradeTypeForEntityID(entityID);
56017 _affectedFeatureCount += 1;
56019 if (downgradeType && type !== downgradeType) {
56020 if (downgradeType !== 'generic' && type !== 'generic') {
56021 downgradeType = 'building_address';
56023 downgradeType = 'generic';
56026 downgradeType = type;
56031 return downgradeType;
56034 function downgradeTypeForEntityID(entityID) {
56035 var graph = context.graph();
56036 var entity = graph.entity(entityID);
56037 var preset = _mainPresetIndex.match(entity, graph);
56038 if (!preset || preset.isFallback()) return null;
56040 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
56041 return key.match(/^addr:.{1,}/);
56046 var geometry = entity.geometry(graph);
56048 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
56052 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
56059 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
56060 var addressKeysToKeep = ['source'];
56062 var operation = function operation() {
56063 context.perform(function (graph) {
56064 for (var i in selectedIDs) {
56065 var entityID = selectedIDs[i];
56066 var type = downgradeTypeForEntityID(entityID);
56067 if (!type) continue;
56068 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
56070 for (var key in tags) {
56071 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
56073 if (type === 'building') {
56074 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
56077 if (type !== 'generic') {
56078 if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
56084 graph = actionChangeTags(entityID, tags)(graph);
56088 }, operation.annotation());
56089 context.validator().validate(); // refresh the select mode to enable the delete operation
56091 context.enter(modeSelect(context, selectedIDs));
56094 operation.available = function () {
56095 return _downgradeType;
56098 operation.disabled = function () {
56099 if (selectedIDs.some(hasWikidataTag)) {
56100 return 'has_wikidata_tag';
56105 function hasWikidataTag(id) {
56106 var entity = context.entity(id);
56107 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
56111 operation.tooltip = function () {
56112 var disable = operation.disabled();
56113 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
56116 operation.annotation = function () {
56119 if (_downgradeType === 'building_address') {
56120 suffix = 'generic';
56122 suffix = _downgradeType;
56125 return _t('operations.downgrade.annotation.' + suffix, {
56126 n: _affectedFeatureCount
56130 operation.id = 'downgrade';
56131 operation.keys = [uiCmd('⌫')];
56132 operation.title = _t('operations.downgrade.title');
56133 operation.behavior = behaviorOperation(context).which(operation);
56137 function operationExtract(context, selectedIDs) {
56138 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
56140 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
56141 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
56142 }).filter(Boolean));
56144 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
56148 var _actions = selectedIDs.map(function (entityID) {
56149 var graph = context.graph();
56150 var entity = graph.hasEntity(entityID);
56151 if (!entity || !entity.hasInterestingTags()) return null;
56152 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
56154 if (entity.type !== 'node') {
56155 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
56157 if (preset.geometry.indexOf('point') === -1) return null;
56160 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
56161 return actionExtract(entityID);
56162 }).filter(Boolean);
56164 var operation = function operation() {
56165 var combinedAction = function combinedAction(graph) {
56166 _actions.forEach(function (action) {
56167 graph = action(graph);
56173 context.perform(combinedAction, operation.annotation()); // do the extract
56175 var extractedNodeIDs = _actions.map(function (action) {
56176 return action.getExtractedNodeID();
56179 context.enter(modeSelect(context, extractedNodeIDs));
56182 operation.available = function () {
56183 return _actions.length && selectedIDs.length === _actions.length;
56186 operation.disabled = function () {
56187 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
56188 return 'too_large';
56189 } else if (selectedIDs.some(function (entityID) {
56190 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
56192 return 'connected_to_hidden';
56198 operation.tooltip = function () {
56199 var disableReason = operation.disabled();
56201 if (disableReason) {
56202 return _t('operations.extract.' + disableReason + '.' + _amount);
56204 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
56208 operation.annotation = function () {
56209 return _t('operations.extract.annotation', {
56210 n: selectedIDs.length
56214 operation.id = 'extract';
56215 operation.keys = [_t('operations.extract.key')];
56216 operation.title = _t('operations.extract.title');
56217 operation.behavior = behaviorOperation(context).which(operation);
56221 function operationMerge(context, selectedIDs) {
56222 var _action = getAction();
56224 function getAction() {
56225 // prefer a non-disabled action first
56226 var join = actionJoin(selectedIDs);
56227 if (!join.disabled(context.graph())) return join;
56228 var merge = actionMerge(selectedIDs);
56229 if (!merge.disabled(context.graph())) return merge;
56230 var mergePolygon = actionMergePolygon(selectedIDs);
56231 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
56232 var mergeNodes = actionMergeNodes(selectedIDs);
56233 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
56235 if (join.disabled(context.graph()) !== 'not_eligible') return join;
56236 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
56237 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
56241 var operation = function operation() {
56242 if (operation.disabled()) return;
56243 context.perform(_action, operation.annotation());
56244 context.validator().validate();
56245 var resultIDs = selectedIDs.filter(context.hasEntity);
56247 if (resultIDs.length > 1) {
56248 var interestingIDs = resultIDs.filter(function (id) {
56249 return context.entity(id).hasInterestingTags();
56251 if (interestingIDs.length) resultIDs = interestingIDs;
56254 context.enter(modeSelect(context, resultIDs));
56257 operation.available = function () {
56258 return selectedIDs.length >= 2;
56261 operation.disabled = function () {
56262 var actionDisabled = _action.disabled(context.graph());
56264 if (actionDisabled) return actionDisabled;
56265 var osm = context.connection();
56267 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
56268 return 'too_many_vertices';
56274 operation.tooltip = function () {
56275 var disabled = operation.disabled();
56278 if (disabled === 'restriction') {
56279 return _t('operations.merge.restriction', {
56280 relation: _mainPresetIndex.item('type/restriction').name()
56284 return _t('operations.merge.' + disabled);
56287 return _t('operations.merge.description');
56290 operation.annotation = function () {
56291 return _t('operations.merge.annotation', {
56292 n: selectedIDs.length
56296 operation.id = 'merge';
56297 operation.keys = [_t('operations.merge.key')];
56298 operation.title = _t('operations.merge.title');
56299 operation.behavior = behaviorOperation(context).which(operation);
56303 function operationPaste(context) {
56306 var operation = function operation() {
56307 if (!_pastePoint) return;
56308 var oldIDs = context.copyIDs();
56309 if (!oldIDs.length) return;
56310 var projection = context.projection;
56311 var extent = geoExtent();
56312 var oldGraph = context.copyGraph();
56314 var action = actionCopyEntities(oldIDs, oldGraph);
56315 context.perform(action);
56316 var copies = action.copies();
56317 var originals = new Set();
56318 Object.values(copies).forEach(function (entity) {
56319 originals.add(entity.id);
56322 for (var id in copies) {
56323 var oldEntity = oldGraph.entity(id);
56324 var newEntity = copies[id];
56326 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
56329 var parents = context.graph().parentWays(newEntity);
56330 var parentCopied = parents.some(function (parent) {
56331 return originals.has(parent.id);
56334 if (!parentCopied) {
56335 newIDs.push(newEntity.id);
56337 } // Use the location of the copy operation to offset the paste location,
56338 // or else use the center of the pasted extent
56341 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
56342 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
56344 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
56345 context.enter(modeSelect(context, newIDs));
56348 operation.point = function (val) {
56353 operation.available = function () {
56354 return context.mode().id === 'browse';
56357 operation.disabled = function () {
56358 return !context.copyIDs().length;
56361 operation.tooltip = function () {
56362 var oldGraph = context.copyGraph();
56363 var ids = context.copyIDs();
56366 return _t('operations.paste.nothing_copied');
56369 return _t('operations.paste.description', {
56370 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
56375 operation.annotation = function () {
56376 var ids = context.copyIDs();
56377 return _t('operations.paste.annotation', {
56382 operation.id = 'paste';
56383 operation.keys = [uiCmd('⌘V')];
56384 operation.title = _t('operations.paste.title');
56388 function operationReverse(context, selectedIDs) {
56389 var operation = function operation() {
56390 context.perform(function combinedReverseAction(graph) {
56391 actions().forEach(function (action) {
56392 graph = action(graph);
56395 }, operation.annotation());
56396 context.validator().validate();
56399 function actions(situation) {
56400 return selectedIDs.map(function (entityID) {
56401 var entity = context.hasEntity(entityID);
56402 if (!entity) return null;
56404 if (situation === 'toolbar') {
56405 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
56408 var geometry = entity.geometry(context.graph());
56409 if (entity.type !== 'node' && geometry !== 'line') return null;
56410 var action = actionReverse(entityID);
56411 if (action.disabled(context.graph())) return null;
56413 }).filter(Boolean);
56416 function reverseTypeID() {
56417 var acts = actions();
56418 var nodeActionCount = acts.filter(function (act) {
56419 var entity = context.hasEntity(act.entityID());
56420 return entity && entity.type === 'node';
56422 if (nodeActionCount === 0) return 'line';
56423 if (nodeActionCount === acts.length) return 'point';
56427 operation.available = function (situation) {
56428 return actions(situation).length > 0;
56431 operation.disabled = function () {
56435 operation.tooltip = function () {
56436 return _t('operations.reverse.description.' + reverseTypeID());
56439 operation.annotation = function () {
56440 var acts = actions();
56441 return _t('operations.reverse.annotation.' + reverseTypeID(), {
56446 operation.id = 'reverse';
56447 operation.keys = [_t('operations.reverse.key')];
56448 operation.title = _t('operations.reverse.title');
56449 operation.behavior = behaviorOperation(context).which(operation);
56453 function operationSplit(context, selectedIDs) {
56454 var _vertexIds = selectedIDs.filter(function (id) {
56455 return context.graph().geometry(id) === 'vertex';
56458 var _selectedWayIds = selectedIDs.filter(function (id) {
56459 var entity = context.graph().hasEntity(id);
56460 return entity && entity.type === 'way';
56463 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
56465 var _action = actionSplit(_vertexIds);
56468 var _geometry = 'feature';
56469 var _waysAmount = 'single';
56471 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
56473 if (_isAvailable) {
56474 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
56475 _ways = _action.ways(context.graph());
56476 var geometries = {};
56478 _ways.forEach(function (way) {
56479 geometries[way.geometry(context.graph())] = true;
56482 if (Object.keys(geometries).length === 1) {
56483 _geometry = Object.keys(geometries)[0];
56486 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
56489 var operation = function operation() {
56490 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
56492 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
56493 // filter out relations that may have had member additions
56494 return context.entity(id).type === 'way';
56497 context.enter(modeSelect(context, idsToSelect));
56500 operation.relatedEntityIds = function () {
56501 return _selectedWayIds.length ? [] : _ways.map(function (way) {
56506 operation.available = function () {
56507 return _isAvailable;
56510 operation.disabled = function () {
56511 var reason = _action.disabled(context.graph());
56515 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56516 return 'connected_to_hidden';
56522 operation.tooltip = function () {
56523 var disable = operation.disabled();
56524 if (disable) return _t('operations.split.' + disable);
56525 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
56528 operation.annotation = function () {
56529 return _t('operations.split.annotation.' + _geometry, {
56534 operation.id = 'split';
56535 operation.keys = [_t('operations.split.key')];
56536 operation.title = _t('operations.split.title');
56537 operation.behavior = behaviorOperation(context).which(operation);
56541 function operationStraighten(context, selectedIDs) {
56542 var _wayIDs = selectedIDs.filter(function (id) {
56543 return id.charAt(0) === 'w';
56546 var _nodeIDs = selectedIDs.filter(function (id) {
56547 return id.charAt(0) === 'n';
56550 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
56552 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
56554 var _coords = _nodes.map(function (n) {
56558 var _extent = utilTotalExtent(selectedIDs, context.graph());
56560 var _action = chooseAction();
56564 function chooseAction() {
56565 // straighten selected nodes
56566 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
56567 _geometry = 'point';
56568 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
56569 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
56570 var startNodeIDs = [];
56571 var endNodeIDs = [];
56573 for (var i = 0; i < selectedIDs.length; i++) {
56574 var entity = context.entity(selectedIDs[i]);
56576 if (entity.type === 'node') {
56578 } else if (entity.type !== 'way' || entity.isClosed()) {
56579 return null; // exit early, can't straighten these
56582 startNodeIDs.push(entity.first());
56583 endNodeIDs.push(entity.last());
56584 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
56587 startNodeIDs = startNodeIDs.filter(function (n) {
56588 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
56590 endNodeIDs = endNodeIDs.filter(function (n) {
56591 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
56592 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
56594 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
56596 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
56599 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
56601 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
56603 if (_nodeIDs.length) {
56604 // If we're only straightenting between two points, we only need that extent visible
56605 _extent = utilTotalExtent(_nodeIDs, context.graph());
56608 _geometry = 'line';
56609 return actionStraightenWay(selectedIDs, context.projection);
56615 function operation() {
56616 if (!_action) return;
56617 context.perform(_action, operation.annotation());
56618 window.setTimeout(function () {
56619 context.validator().validate();
56620 }, 300); // after any transition
56623 operation.available = function () {
56624 return Boolean(_action);
56627 operation.disabled = function () {
56628 var reason = _action.disabled(context.graph());
56632 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
56633 return 'too_large';
56634 } else if (someMissing()) {
56635 return 'not_downloaded';
56636 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56637 return 'connected_to_hidden';
56642 function someMissing() {
56643 if (context.inIntro()) return false;
56644 var osm = context.connection();
56647 var missing = _coords.filter(function (loc) {
56648 return !osm.isDataLoaded(loc);
56651 if (missing.length) {
56652 missing.forEach(function (loc) {
56653 context.loadTileAtLoc(loc);
56663 operation.tooltip = function () {
56664 var disable = operation.disabled();
56665 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
56668 operation.annotation = function () {
56669 return _t('operations.straighten.annotation.' + _geometry, {
56670 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
56674 operation.id = 'straighten';
56675 operation.keys = [_t('operations.straighten.key')];
56676 operation.title = _t('operations.straighten.title');
56677 operation.behavior = behaviorOperation(context).which(operation);
56681 var Operations = /*#__PURE__*/Object.freeze({
56683 operationCircularize: operationCircularize,
56684 operationContinue: operationContinue,
56685 operationCopy: operationCopy,
56686 operationDelete: operationDelete,
56687 operationDisconnect: operationDisconnect,
56688 operationDowngrade: operationDowngrade,
56689 operationExtract: operationExtract,
56690 operationMerge: operationMerge,
56691 operationMove: operationMove,
56692 operationOrthogonalize: operationOrthogonalize,
56693 operationPaste: operationPaste,
56694 operationReflectShort: operationReflectShort,
56695 operationReflectLong: operationReflectLong,
56696 operationReverse: operationReverse,
56697 operationRotate: operationRotate,
56698 operationSplit: operationSplit,
56699 operationStraighten: operationStraighten
56702 var _relatedParent;
56704 function modeSelect(context, selectedIDs) {
56709 var keybinding = utilKeybinding('select');
56711 var _breatheBehavior = behaviorBreathe();
56713 var _modeDragNode = modeDragNode(context);
56715 var _selectBehavior;
56717 var _behaviors = [];
56718 var _operations = [];
56719 var _newFeature = false;
56720 var _follow = false;
56722 function singular() {
56723 if (selectedIDs && selectedIDs.length === 1) {
56724 return context.hasEntity(selectedIDs[0]);
56728 function selectedEntities() {
56729 return selectedIDs.map(function (id) {
56730 return context.hasEntity(id);
56731 }).filter(Boolean);
56734 function checkSelectedIDs() {
56737 if (Array.isArray(selectedIDs)) {
56738 ids = selectedIDs.filter(function (id) {
56739 return context.hasEntity(id);
56744 context.enter(modeBrowse(context));
56746 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
56747 // switch between single- and multi-select UI
56748 context.enter(modeSelect(context, ids));
56754 } // find the common parent ways for nextVertex, previousVertex
56757 function commonParents() {
56758 var graph = context.graph();
56759 var commonParents = [];
56761 for (var i = 0; i < selectedIDs.length; i++) {
56762 var entity = context.hasEntity(selectedIDs[i]);
56764 if (!entity || entity.geometry(graph) !== 'vertex') {
56765 return []; // selection includes some not vertices
56768 var currParents = graph.parentWays(entity).map(function (w) {
56772 if (!commonParents.length) {
56773 commonParents = currParents;
56777 commonParents = utilArrayIntersection(commonParents, currParents);
56779 if (!commonParents.length) {
56784 return commonParents;
56787 function singularParent() {
56788 var parents = commonParents();
56790 if (!parents || parents.length === 0) {
56791 _relatedParent = null;
56793 } // relatedParent is used when we visit a vertex with multiple
56794 // parents, and we want to remember which parent line we started on.
56797 if (parents.length === 1) {
56798 _relatedParent = parents[0]; // remember this parent for later
56800 return _relatedParent;
56803 if (parents.indexOf(_relatedParent) !== -1) {
56804 return _relatedParent; // prefer the previously seen parent
56810 mode.selectedIDs = function (val) {
56811 if (!arguments.length) return selectedIDs;
56816 mode.zoomToSelected = function () {
56817 context.map().zoomToEase(selectedEntities());
56820 mode.newFeature = function (val) {
56821 if (!arguments.length) return _newFeature;
56826 mode.selectBehavior = function (val) {
56827 if (!arguments.length) return _selectBehavior;
56828 _selectBehavior = val;
56832 mode.follow = function (val) {
56833 if (!arguments.length) return _follow;
56838 function loadOperations() {
56839 _operations.forEach(function (operation) {
56840 if (operation.behavior) {
56841 context.uninstall(operation.behavior);
56845 _operations = Object.values(Operations).map(function (o) {
56846 return o(context, selectedIDs);
56847 }).filter(function (o) {
56848 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
56849 }).concat([// group copy/downgrade/delete operation together at the end of the list
56850 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
56851 return operation.available();
56854 _operations.forEach(function (operation) {
56855 if (operation.behavior) {
56856 context.install(operation.behavior);
56858 }); // remove any displayed menu
56861 context.ui().closeEditMenu();
56864 mode.operations = function () {
56865 return _operations;
56868 mode.enter = function () {
56869 if (!checkSelectedIDs()) return;
56870 context.features().forceVisible(selectedIDs);
56872 _modeDragNode.restoreSelectedIDs(selectedIDs);
56876 if (!_behaviors.length) {
56877 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
56878 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
56881 _behaviors.forEach(context.install);
56883 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) {
56884 return uiCmd('⇧' + key);
56885 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
56886 return uiCmd('⇧⌥' + key);
56887 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
56888 return uiCmd('⇧' + key);
56889 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
56890 return uiCmd('⇧⌥' + key);
56891 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], nextParent).on('⎋', esc, true);
56892 select(document).call(keybinding);
56893 context.ui().sidebar.select(selectedIDs, _newFeature);
56894 context.history().on('change.select', function () {
56895 loadOperations(); // reselect after change in case relation members were removed or added
56898 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
56899 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
56902 _breatheBehavior.restartIfNeeded(context.surface());
56904 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
56908 var extent = geoExtent();
56909 var graph = context.graph();
56910 selectedIDs.forEach(function (id) {
56911 var entity = context.entity(id);
56913 extent._extend(entity.extent(graph));
56915 var loc = extent.center();
56916 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
56921 function nudgeSelection(delta) {
56922 return function () {
56923 // prevent nudging during low zoom selection
56924 if (!context.map().withinEditableZoom()) return;
56925 var moveOp = operationMove(context, selectedIDs);
56927 if (moveOp.disabled()) {
56928 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
56930 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
56931 context.validator().validate();
56936 function scaleSelection(factor) {
56937 return function () {
56938 // prevent scaling during low zoom selection
56939 if (!context.map().withinEditableZoom()) return;
56940 var nodes = utilGetAllNodes(selectedIDs, context.graph());
56941 var isUp = factor > 1; // can only scale if multiple nodes are selected
56943 if (nodes.length <= 1) return;
56944 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
56945 // object, but we don't want an actual scale operation at this point.
56947 function scalingDisabled() {
56949 return 'too_small';
56950 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
56951 return 'too_large';
56952 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
56953 return 'not_downloaded';
56954 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56955 return 'connected_to_hidden';
56960 function tooSmall() {
56961 if (isUp) return false;
56962 var dLon = Math.abs(extent[1][0] - extent[0][0]);
56963 var dLat = Math.abs(extent[1][1] - extent[0][1]);
56964 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
56967 function someMissing() {
56968 if (context.inIntro()) return false;
56969 var osm = context.connection();
56972 var missing = nodes.filter(function (n) {
56973 return !osm.isDataLoaded(n.loc);
56976 if (missing.length) {
56977 missing.forEach(function (loc) {
56978 context.loadTileAtLoc(loc);
56987 function incompleteRelation(id) {
56988 var entity = context.entity(id);
56989 return entity.type === 'relation' && !entity.isComplete(context.graph());
56993 var disabled = scalingDisabled();
56996 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
56997 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
56999 var pivot = context.projection(extent.center());
57000 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
57001 n: selectedIDs.length
57003 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
57004 context.validator().validate();
57009 function didDoubleUp(d3_event, loc) {
57010 if (!context.map().withinEditableZoom()) return;
57011 var target = select(d3_event.target);
57012 var datum = target.datum();
57013 var entity = datum && datum.properties && datum.properties.entity;
57014 if (!entity) return;
57016 if (entity instanceof osmWay && target.classed('target')) {
57017 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
57018 var prev = entity.nodes[choice.index - 1];
57019 var next = entity.nodes[choice.index];
57020 context.perform(actionAddMidpoint({
57023 }, osmNode()), _t('operations.add.annotation.vertex'));
57024 } else if (entity.type === 'midpoint') {
57025 context.perform(actionAddMidpoint({
57028 }, osmNode()), _t('operations.add.annotation.vertex'));
57032 function selectElements() {
57033 if (!checkSelectedIDs()) return;
57034 var surface = context.surface();
57035 surface.selectAll('.selected-member').classed('selected-member', false);
57036 surface.selectAll('.selected').classed('selected', false);
57037 surface.selectAll('.related').classed('related', false);
57040 if (_relatedParent) {
57041 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
57044 if (context.map().withinEditableZoom()) {
57045 // Apply selection styling if not in wide selection
57046 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
57047 /* skipMultipolgonMembers */
57048 )).classed('selected-member', true);
57049 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
57054 if (context.container().select('.combobox').size()) return;
57055 context.enter(modeBrowse(context));
57058 function firstVertex(d3_event) {
57059 d3_event.preventDefault();
57060 var entity = singular();
57061 var parent = singularParent();
57064 if (entity && entity.type === 'way') {
57066 } else if (parent) {
57067 way = context.entity(parent);
57071 context.enter(modeSelect(context, [way.first()]).follow(true));
57075 function lastVertex(d3_event) {
57076 d3_event.preventDefault();
57077 var entity = singular();
57078 var parent = singularParent();
57081 if (entity && entity.type === 'way') {
57083 } else if (parent) {
57084 way = context.entity(parent);
57088 context.enter(modeSelect(context, [way.last()]).follow(true));
57092 function previousVertex(d3_event) {
57093 d3_event.preventDefault();
57094 var parent = singularParent();
57095 if (!parent) return;
57096 var way = context.entity(parent);
57097 var length = way.nodes.length;
57098 var curr = way.nodes.indexOf(selectedIDs[0]);
57103 } else if (way.isClosed()) {
57104 index = length - 2;
57107 if (index !== -1) {
57108 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
57112 function nextVertex(d3_event) {
57113 d3_event.preventDefault();
57114 var parent = singularParent();
57115 if (!parent) return;
57116 var way = context.entity(parent);
57117 var length = way.nodes.length;
57118 var curr = way.nodes.indexOf(selectedIDs[0]);
57121 if (curr < length - 1) {
57123 } else if (way.isClosed()) {
57127 if (index !== -1) {
57128 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
57132 function nextParent(d3_event) {
57133 d3_event.preventDefault();
57134 var parents = commonParents();
57135 if (!parents || parents.length < 2) return;
57136 var index = parents.indexOf(_relatedParent);
57138 if (index < 0 || index > parents.length - 2) {
57139 _relatedParent = parents[0];
57141 _relatedParent = parents[index + 1];
57144 var surface = context.surface();
57145 surface.selectAll('.related').classed('related', false);
57147 if (_relatedParent) {
57148 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
57153 mode.exit = function () {
57154 _newFeature = false;
57156 _operations.forEach(function (operation) {
57157 if (operation.behavior) {
57158 context.uninstall(operation.behavior);
57164 _behaviors.forEach(context.uninstall);
57166 select(document).call(keybinding.unbind);
57167 context.ui().closeEditMenu();
57168 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
57169 var surface = context.surface();
57170 surface.selectAll('.selected-member').classed('selected-member', false);
57171 surface.selectAll('.selected').classed('selected', false);
57172 surface.selectAll('.highlighted').classed('highlighted', false);
57173 surface.selectAll('.related').classed('related', false);
57174 context.map().on('drawn.select', null);
57175 context.ui().sidebar.hide();
57176 context.features().forceVisible([]);
57177 var entity = singular();
57179 if (_newFeature && entity && entity.type === 'relation' && // no tags
57180 Object.keys(entity.tags).length === 0 && // no parent relations
57181 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
57182 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
57183 // the user added this relation but didn't edit it at all, so just delete it
57184 var deleteAction = actionDeleteRelation(entity.id, true
57185 /* don't delete untagged members */
57187 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
57194 function uiLasso(context) {
57195 var group, polygon;
57196 lasso.coordinates = [];
57198 function lasso(selection) {
57199 context.container().classed('lasso', true);
57200 group = selection.append('g').attr('class', 'lasso hide');
57201 polygon = group.append('path').attr('class', 'lasso-path');
57202 group.call(uiToggle(true));
57207 polygon.data([lasso.coordinates]).attr('d', function (d) {
57208 return 'M' + d.join(' L') + ' Z';
57213 lasso.extent = function () {
57214 return lasso.coordinates.reduce(function (extent, point) {
57215 return extent.extend(geoExtent(point));
57219 lasso.p = function (_) {
57220 if (!arguments.length) return lasso;
57221 lasso.coordinates.push(_);
57226 lasso.close = function () {
57228 group.call(uiToggle(false, function () {
57229 select(this).remove();
57233 context.container().classed('lasso', false);
57239 function behaviorLasso(context) {
57240 // use pointer events on supported platforms; fallback to mouse events
57241 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
57243 var behavior = function behavior(selection) {
57246 function pointerdown(d3_event) {
57247 var button = 0; // left
57249 if (d3_event.button === button && d3_event.shiftKey === true) {
57251 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
57252 d3_event.stopPropagation();
57256 function pointermove() {
57258 lasso = uiLasso(context);
57259 context.surface().call(lasso);
57262 lasso.p(context.map().mouse());
57265 function normalize(a, b) {
57266 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])]];
57269 function lassoed() {
57270 if (!lasso) return [];
57271 var graph = context.graph();
57274 if (context.map().editableDataEnabled(true
57275 /* skipZoomCheck */
57276 ) && context.map().isInWideSelection()) {
57277 // only select from the visible nodes
57278 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
57279 } else if (!context.map().editableDataEnabled()) {
57283 var bounds = lasso.extent().map(context.projection.invert);
57284 var extent = geoExtent(normalize(bounds[0], bounds[1]));
57285 var intersects = context.history().intersects(extent).filter(function (entity) {
57286 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
57287 }); // sort the lassoed nodes as best we can
57289 intersects.sort(function (node1, node2) {
57290 var parents1 = graph.parentWays(node1);
57291 var parents2 = graph.parentWays(node2);
57293 if (parents1.length && parents2.length) {
57294 // both nodes are vertices
57295 var sharedParents = utilArrayIntersection(parents1, parents2);
57297 if (sharedParents.length) {
57298 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
57300 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
57302 // vertices do not share a way; group them by their respective parent ways
57303 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
57305 } else if (parents1.length || parents2.length) {
57306 // only one node is a vertex; sort standalone points before vertices
57307 return parents1.length - parents2.length;
57308 } // both nodes are standalone points; sort left to right
57311 return node1.loc[0] - node2.loc[0];
57313 return intersects.map(function (entity) {
57318 function pointerup() {
57319 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
57320 if (!lasso) return;
57321 var ids = lassoed();
57325 context.enter(modeSelect(context, ids));
57329 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
57332 behavior.off = function (selection) {
57333 selection.on(_pointerPrefix + 'down.lasso', null);
57339 function modeBrowse(context) {
57343 title: _t('modes.browse.title'),
57344 description: _t('modes.browse.description')
57348 var _selectBehavior;
57350 var _behaviors = [];
57352 mode.selectBehavior = function (val) {
57353 if (!arguments.length) return _selectBehavior;
57354 _selectBehavior = val;
57358 mode.enter = function () {
57359 if (!_behaviors.length) {
57360 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
57361 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
57364 _behaviors.forEach(context.install); // Get focus on the body.
57367 if (document.activeElement && document.activeElement.blur) {
57368 document.activeElement.blur();
57372 context.ui().sidebar.show(sidebar);
57374 context.ui().sidebar.select(null);
57378 mode.exit = function () {
57379 context.ui().sidebar.hover.cancel();
57381 _behaviors.forEach(context.uninstall);
57384 context.ui().sidebar.hide();
57388 mode.sidebar = function (_) {
57389 if (!arguments.length) return sidebar;
57394 mode.operations = function () {
57395 return [operationPaste(context)];
57401 function behaviorAddWay(context) {
57402 var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
57403 var draw = behaviorDraw(context);
57405 function behavior(surface) {
57406 draw.on('click', function () {
57407 dispatch$1.apply('start', this, arguments);
57408 }).on('clickWay', function () {
57409 dispatch$1.apply('startFromWay', this, arguments);
57410 }).on('clickNode', function () {
57411 dispatch$1.apply('startFromNode', this, arguments);
57412 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
57413 context.map().dblclickZoomEnable(false);
57414 surface.call(draw);
57417 behavior.off = function (surface) {
57418 surface.call(draw.off);
57421 behavior.cancel = function () {
57422 window.setTimeout(function () {
57423 context.map().dblclickZoomEnable(true);
57425 context.enter(modeBrowse(context));
57428 return utilRebind(behavior, dispatch$1, 'on');
57431 function behaviorHash(context) {
57432 // cached window.location.hash
57433 var _cachedHash = null; // allowable latitude range
57435 var _latitudeLimit = 90 - 1e-8;
57437 function computedHashParameters() {
57438 var map = context.map();
57439 var center = map.center();
57440 var zoom = map.zoom();
57441 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
57442 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
57443 var newParams = {};
57444 delete oldParams.id;
57445 var selected = context.selectedIDs().filter(function (id) {
57446 return context.hasEntity(id);
57449 if (selected.length) {
57450 newParams.id = selected.join(',');
57453 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
57454 return Object.assign(oldParams, newParams);
57457 function computedHash() {
57458 return '#' + utilQsString(computedHashParameters(), true);
57461 function computedTitle(includeChangeCount) {
57462 var baseTitle = context.documentTitleBase() || 'iD';
57466 var selected = context.selectedIDs().filter(function (id) {
57467 return context.hasEntity(id);
57470 if (selected.length) {
57471 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
57473 if (selected.length > 1) {
57474 contextual = _t('title.labeled_and_more', {
57475 labeled: firstLabel,
57476 count: selected.length - 1
57479 contextual = firstLabel;
57482 titleID = 'context';
57485 if (includeChangeCount) {
57486 changeCount = context.history().difference().summary().length;
57488 if (changeCount > 0) {
57489 titleID = contextual ? 'changes_context' : 'changes';
57494 return _t('title.format.' + titleID, {
57495 changes: changeCount,
57497 context: contextual
57504 function updateTitle(includeChangeCount) {
57505 if (!context.setsDocumentTitle()) return;
57506 var newTitle = computedTitle(includeChangeCount);
57508 if (document.title !== newTitle) {
57509 document.title = newTitle;
57513 function updateHashIfNeeded() {
57514 if (context.inIntro()) return;
57515 var latestHash = computedHash();
57517 if (_cachedHash !== latestHash) {
57518 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
57519 // though unavoidably creating a browser history entry
57521 window.history.replaceState(null, computedTitle(false
57522 /* includeChangeCount */
57523 ), latestHash); // set the title we want displayed for the browser tab/window
57526 /* includeChangeCount */
57531 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
57533 var _throttledUpdateTitle = throttle(function () {
57535 /* includeChangeCount */
57539 function hashchange() {
57540 // ignore spurious hashchange events
57541 if (window.location.hash === _cachedHash) return;
57542 _cachedHash = window.location.hash;
57543 var q = utilStringQs(_cachedHash);
57544 var mapArgs = (q.map || '').split('/').map(Number);
57546 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
57547 // replace bogus hash
57548 updateHashIfNeeded();
57550 // don't update if the new hash already reflects the state of iD
57551 if (_cachedHash === computedHash()) return;
57552 var mode = context.mode();
57553 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
57555 if (q.id && mode) {
57556 var ids = q.id.split(',').filter(function (id) {
57557 return context.hasEntity(id);
57560 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
57561 context.enter(modeSelect(context, ids));
57566 var center = context.map().center();
57567 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
57568 var maxdist = 500; // Don't allow the hash location to change too much while drawing
57569 // This can happen if the user accidentally hit the back button. #3996
57571 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
57572 context.enter(modeBrowse(context));
57578 function behavior() {
57579 context.map().on('move.behaviorHash', _throttledUpdate);
57580 context.history().on('change.behaviorHash', _throttledUpdateTitle);
57581 context.on('enter.behaviorHash', _throttledUpdate);
57582 select(window).on('hashchange.behaviorHash', hashchange);
57584 if (window.location.hash) {
57585 var q = utilStringQs(window.location.hash);
57588 //if (!context.history().hasRestorableChanges()) {
57589 // targeting specific features: download, select, and zoom to them
57590 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
57593 if (q.walkthrough === 'true') {
57594 behavior.startWalkthrough = true;
57598 behavior.hadHash = true;
57602 updateTitle(false);
57606 behavior.off = function () {
57607 _throttledUpdate.cancel();
57609 _throttledUpdateTitle.cancel();
57611 context.map().on('move.behaviorHash', null);
57612 context.on('enter.behaviorHash', null);
57613 select(window).on('hashchange.behaviorHash', null);
57614 window.location.hash = '';
57621 iD.coreDifference represents the difference between two graphs.
57622 It knows how to calculate the set of entities that were
57623 created, modified, or deleted, and also contains the logic
57624 for recursively extending a difference to the complete set
57625 of entities that will require a redraw, taking into account
57626 child and parent relationships.
57629 function coreDifference(base, head) {
57631 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
57635 function checkEntityID(id) {
57636 var h = head.entities[id];
57637 var b = base.entities[id];
57638 if (h === b) return;
57639 if (_changes[id]) return;
57646 _didChange.deletion = true;
57655 _didChange.addition = true;
57660 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
57665 _didChange.geometry = true;
57666 _didChange.properties = true;
57670 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
57675 _didChange.geometry = true;
57678 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
57683 _didChange.geometry = true;
57686 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
57691 _didChange.properties = true;
57697 // HOT CODE: there can be many thousands of downloaded entities, so looping
57698 // through them all can become a performance bottleneck. Optimize by
57699 // resolving duplicates and using a basic `for` loop
57700 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
57702 for (var i = 0; i < ids.length; i++) {
57703 checkEntityID(ids[i]);
57709 _diff.length = function length() {
57710 return Object.keys(_changes).length;
57713 _diff.changes = function changes() {
57717 _diff.didChange = _didChange; // pass true to include affected relation members
57719 _diff.extantIDs = function extantIDs(includeRelMembers) {
57720 var result = new Set();
57721 Object.keys(_changes).forEach(function (id) {
57722 if (_changes[id].head) {
57726 var h = _changes[id].head;
57727 var b = _changes[id].base;
57728 var entity = h || b;
57730 if (includeRelMembers && entity.type === 'relation') {
57731 var mh = h ? h.members.map(function (m) {
57734 var mb = b ? b.members.map(function (m) {
57737 utilArrayUnion(mh, mb).forEach(function (memberID) {
57738 if (head.hasEntity(memberID)) {
57739 result.add(memberID);
57744 return Array.from(result);
57747 _diff.modified = function modified() {
57749 Object.values(_changes).forEach(function (change) {
57750 if (change.base && change.head) {
57751 result.push(change.head);
57757 _diff.created = function created() {
57759 Object.values(_changes).forEach(function (change) {
57760 if (!change.base && change.head) {
57761 result.push(change.head);
57767 _diff.deleted = function deleted() {
57769 Object.values(_changes).forEach(function (change) {
57770 if (change.base && !change.head) {
57771 result.push(change.base);
57777 _diff.summary = function summary() {
57779 var keys = Object.keys(_changes);
57781 for (var i = 0; i < keys.length; i++) {
57782 var change = _changes[keys[i]];
57784 if (change.head && change.head.geometry(head) !== 'vertex') {
57785 addEntity(change.head, head, change.base ? 'modified' : 'created');
57786 } else if (change.base && change.base.geometry(base) !== 'vertex') {
57787 addEntity(change.base, base, 'deleted');
57788 } else if (change.base && change.head) {
57790 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
57791 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
57794 addParents(change.head);
57797 if (retagged || moved && change.head.hasInterestingTags()) {
57798 addEntity(change.head, head, 'modified');
57800 } else if (change.head && change.head.hasInterestingTags()) {
57802 addEntity(change.head, head, 'created');
57803 } else if (change.base && change.base.hasInterestingTags()) {
57805 addEntity(change.base, base, 'deleted');
57809 return Object.values(relevant);
57811 function addEntity(entity, graph, changeType) {
57812 relevant[entity.id] = {
57815 changeType: changeType
57819 function addParents(entity) {
57820 var parents = head.parentWays(entity);
57822 for (var j = parents.length - 1; j >= 0; j--) {
57823 var parent = parents[j];
57825 if (!(parent.id in relevant)) {
57826 addEntity(parent, head, 'modified');
57830 }; // returns complete set of entities that require a redraw
57831 // (optionally within given `extent`)
57834 _diff.complete = function complete(extent) {
57838 for (id in _changes) {
57839 change = _changes[id];
57840 var h = change.head;
57841 var b = change.base;
57842 var entity = h || b;
57844 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) continue;
57847 if (entity.type === 'way') {
57848 var nh = h ? h.nodes : [];
57849 var nb = b ? b.nodes : [];
57851 diff = utilArrayDifference(nh, nb);
57853 for (i = 0; i < diff.length; i++) {
57854 result[diff[i]] = head.hasEntity(diff[i]);
57857 diff = utilArrayDifference(nb, nh);
57859 for (i = 0; i < diff.length; i++) {
57860 result[diff[i]] = head.hasEntity(diff[i]);
57864 if (entity.type === 'relation' && entity.isMultipolygon()) {
57865 var mh = h ? h.members.map(function (m) {
57868 var mb = b ? b.members.map(function (m) {
57871 var ids = utilArrayUnion(mh, mb);
57873 for (i = 0; i < ids.length; i++) {
57874 var member = head.hasEntity(ids[i]);
57875 if (!member) continue; // not downloaded
57877 if (extent && !member.intersects(extent, head)) continue; // not visible
57879 result[ids[i]] = member;
57883 addParents(head.parentWays(entity), result);
57884 addParents(head.parentRelations(entity), result);
57889 function addParents(parents, result) {
57890 for (var i = 0; i < parents.length; i++) {
57891 var parent = parents[i];
57892 if (parent.id in result) continue;
57893 result[parent.id] = parent;
57894 addParents(head.parentRelations(parent), result);
57902 function coreTree(head) {
57903 // tree for entities
57904 var _rtree = new RBush();
57906 var _bboxes = {}; // maintain a separate tree for granular way segments
57908 var _segmentsRTree = new RBush();
57910 var _segmentsBBoxes = {};
57911 var _segmentsByWayId = {};
57914 function entityBBox(entity) {
57915 var bbox = entity.extent(head).bbox();
57916 bbox.id = entity.id;
57917 _bboxes[entity.id] = bbox;
57921 function segmentBBox(segment) {
57922 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
57924 if (!extent) return null;
57925 var bbox = extent.bbox();
57926 bbox.segment = segment;
57927 _segmentsBBoxes[segment.id] = bbox;
57931 function removeEntity(entity) {
57932 _rtree.remove(_bboxes[entity.id]);
57934 delete _bboxes[entity.id];
57936 if (_segmentsByWayId[entity.id]) {
57937 _segmentsByWayId[entity.id].forEach(function (segment) {
57938 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
57940 delete _segmentsBBoxes[segment.id];
57943 delete _segmentsByWayId[entity.id];
57947 function loadEntities(entities) {
57948 _rtree.load(entities.map(entityBBox));
57951 entities.forEach(function (entity) {
57952 if (entity.segments) {
57953 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
57955 _segmentsByWayId[entity.id] = entitySegments;
57956 segments = segments.concat(entitySegments);
57959 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
57962 function updateParents(entity, insertions, memo) {
57963 head.parentWays(entity).forEach(function (way) {
57964 if (_bboxes[way.id]) {
57966 insertions[way.id] = way;
57969 updateParents(way, insertions, memo);
57971 head.parentRelations(entity).forEach(function (relation) {
57972 if (memo[entity.id]) return;
57973 memo[entity.id] = true;
57975 if (_bboxes[relation.id]) {
57976 removeEntity(relation);
57977 insertions[relation.id] = relation;
57980 updateParents(relation, insertions, memo);
57984 tree.rebase = function (entities, force) {
57985 var insertions = {};
57987 for (var i = 0; i < entities.length; i++) {
57988 var entity = entities[i];
57989 if (!entity.visible) continue;
57991 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
57994 } else if (_bboxes[entity.id]) {
57995 removeEntity(entity);
57999 insertions[entity.id] = entity;
58000 updateParents(entity, insertions, {});
58003 loadEntities(Object.values(insertions));
58007 function updateToGraph(graph) {
58008 if (graph === head) return;
58009 var diff = coreDifference(head, graph);
58011 var changed = diff.didChange;
58012 if (!changed.addition && !changed.deletion && !changed.geometry) return;
58013 var insertions = {};
58015 if (changed.deletion) {
58016 diff.deleted().forEach(function (entity) {
58017 removeEntity(entity);
58021 if (changed.geometry) {
58022 diff.modified().forEach(function (entity) {
58023 removeEntity(entity);
58024 insertions[entity.id] = entity;
58025 updateParents(entity, insertions, {});
58029 if (changed.addition) {
58030 diff.created().forEach(function (entity) {
58031 insertions[entity.id] = entity;
58035 loadEntities(Object.values(insertions));
58036 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
58039 tree.intersects = function (extent, graph) {
58040 updateToGraph(graph);
58041 return _rtree.search(extent.bbox()).map(function (bbox) {
58042 return graph.entity(bbox.id);
58044 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
58047 tree.waySegments = function (extent, graph) {
58048 updateToGraph(graph);
58049 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
58050 return bbox.segment;
58057 function uiModal(selection, blocking) {
58060 var keybinding = utilKeybinding('modal');
58061 var previous = selection.select('div.modal');
58062 var animate = previous.empty();
58063 previous.transition().duration(200).style('opacity', 0).remove();
58064 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
58066 shaded.close = function () {
58067 shaded.transition().duration(200).style('opacity', 0).remove();
58068 modal.transition().duration(200).style('top', '0px');
58069 select(document).call(keybinding.unbind);
58072 var modal = shaded.append('div').attr('class', 'modal fillL');
58073 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
58076 shaded.on('click.remove-modal', function (d3_event) {
58077 if (d3_event.target === _this) {
58081 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
58082 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
58083 select(document).call(keybinding);
58086 modal.append('div').attr('class', 'content');
58087 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
58090 shaded.transition().style('opacity', 1);
58092 shaded.style('opacity', 1);
58097 function moveFocusToFirst() {
58098 var node = modal // there are additional rules about what's focusable, but this suits our purposes
58099 .select('a, button, input:not(.keytrap), select, textarea').node();
58104 select(this).node().blur();
58108 function moveFocusToLast() {
58109 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
58111 if (nodes.length) {
58112 nodes[nodes.length - 1].focus();
58114 select(this).node().blur();
58119 function uiLoading(context) {
58120 var _modalSelection = select(null);
58123 var _blocking = false;
58125 var loading = function loading(selection) {
58126 _modalSelection = uiModal(selection, _blocking);
58128 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
58130 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
58131 loadertext.append('h3').html(_message);
58133 _modalSelection.select('button.close').attr('class', 'hide');
58138 loading.message = function (val) {
58139 if (!arguments.length) return _message;
58144 loading.blocking = function (val) {
58145 if (!arguments.length) return _blocking;
58150 loading.close = function () {
58151 _modalSelection.remove();
58154 loading.isShown = function () {
58155 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
58161 function coreHistory(context) {
58162 var dispatch$1 = dispatch('reset', 'change', 'merge', 'restore', 'undone', 'redone');
58164 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
58167 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
58169 var duration = 150;
58170 var _imageryUsed = [];
58171 var _photoOverlaysUsed = [];
58172 var _checkpoints = {};
58180 var _tree; // internal _act, accepts list of actions and eased time
58183 function _act(actions, t) {
58184 actions = Array.prototype.slice.call(actions);
58187 if (typeof actions[actions.length - 1] !== 'function') {
58188 annotation = actions.pop();
58191 var graph = _stack[_index].graph;
58193 for (var i = 0; i < actions.length; i++) {
58194 graph = actions[i](graph, t);
58199 annotation: annotation,
58200 imageryUsed: _imageryUsed,
58201 photoOverlaysUsed: _photoOverlaysUsed,
58202 transform: context.projection.transform(),
58203 selectedIDs: context.selectedIDs()
58205 } // internal _perform with eased time
58208 function _perform(args, t) {
58209 var previous = _stack[_index].graph;
58210 _stack = _stack.slice(0, _index + 1);
58212 var actionResult = _act(args, t);
58214 _stack.push(actionResult);
58217 return change(previous);
58218 } // internal _replace with eased time
58221 function _replace(args, t) {
58222 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
58224 var actionResult = _act(args, t);
58226 _stack[_index] = actionResult;
58227 return change(previous);
58228 } // internal _overwrite with eased time
58231 function _overwrite(args, t) {
58232 var previous = _stack[_index].graph;
58240 _stack = _stack.slice(0, _index + 1);
58242 var actionResult = _act(args, t);
58244 _stack.push(actionResult);
58247 return change(previous);
58248 } // determine difference and dispatch a change event
58251 function change(previous) {
58252 var difference = coreDifference(previous, history.graph());
58254 if (!_pausedGraph) {
58255 dispatch$1.call('change', this, difference);
58259 } // iD uses namespaced keys so multiple installations do not conflict
58262 function getKey(n) {
58263 return 'iD_' + window.location.origin + '_' + n;
58267 graph: function graph() {
58268 return _stack[_index].graph;
58270 tree: function tree() {
58273 base: function base() {
58274 return _stack[0].graph;
58276 merge: function merge(entities
58279 var stack = _stack.map(function (state) {
58280 return state.graph;
58283 _stack[0].graph.rebase(entities, stack, false);
58285 _tree.rebase(entities, false);
58287 dispatch$1.call('merge', this, entities);
58289 perform: function perform() {
58290 // complete any transition already in progress
58291 select(document).interrupt('history.perform');
58292 var transitionable = false;
58293 var action0 = arguments[0];
58295 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
58296 transitionable = !!action0.transitionable;
58299 if (transitionable) {
58300 var origArguments = arguments;
58301 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
58302 return function (t) {
58303 if (t < 1) _overwrite([action0], t);
58305 }).on('start', function () {
58306 _perform([action0], 0);
58307 }).on('end interrupt', function () {
58308 _overwrite(origArguments, 1);
58311 return _perform(arguments);
58314 replace: function replace() {
58315 select(document).interrupt('history.perform');
58316 return _replace(arguments, 1);
58318 // Same as calling pop and then perform
58319 overwrite: function overwrite() {
58320 select(document).interrupt('history.perform');
58321 return _overwrite(arguments, 1);
58323 pop: function pop(n) {
58324 select(document).interrupt('history.perform');
58325 var previous = _stack[_index].graph;
58327 if (isNaN(+n) || +n < 0) {
58331 while (n-- > 0 && _index > 0) {
58337 return change(previous);
58339 // Back to the previous annotated state or _index = 0.
58340 undo: function undo() {
58341 select(document).interrupt('history.perform');
58342 var previousStack = _stack[_index];
58343 var previous = previousStack.graph;
58345 while (_index > 0) {
58347 if (_stack[_index].annotation) break;
58350 dispatch$1.call('undone', this, _stack[_index], previousStack);
58351 return change(previous);
58353 // Forward to the next annotated state.
58354 redo: function redo() {
58355 select(document).interrupt('history.perform');
58356 var previousStack = _stack[_index];
58357 var previous = previousStack.graph;
58358 var tryIndex = _index;
58360 while (tryIndex < _stack.length - 1) {
58363 if (_stack[tryIndex].annotation) {
58365 dispatch$1.call('redone', this, _stack[_index], previousStack);
58370 return change(previous);
58372 pauseChangeDispatch: function pauseChangeDispatch() {
58373 if (!_pausedGraph) {
58374 _pausedGraph = _stack[_index].graph;
58377 resumeChangeDispatch: function resumeChangeDispatch() {
58378 if (_pausedGraph) {
58379 var previous = _pausedGraph;
58380 _pausedGraph = null;
58381 return change(previous);
58384 undoAnnotation: function undoAnnotation() {
58388 if (_stack[i].annotation) return _stack[i].annotation;
58392 redoAnnotation: function redoAnnotation() {
58393 var i = _index + 1;
58395 while (i <= _stack.length - 1) {
58396 if (_stack[i].annotation) return _stack[i].annotation;
58400 // Returns the entities from the active graph with bounding boxes
58401 // overlapping the given `extent`.
58402 intersects: function intersects(extent) {
58403 return _tree.intersects(extent, _stack[_index].graph);
58405 difference: function difference() {
58406 var base = _stack[0].graph;
58407 var head = _stack[_index].graph;
58408 return coreDifference(base, head);
58410 changes: function changes(action) {
58411 var base = _stack[0].graph;
58412 var head = _stack[_index].graph;
58415 head = action(head);
58418 var difference = coreDifference(base, head);
58420 modified: difference.modified(),
58421 created: difference.created(),
58422 deleted: difference.deleted()
58425 hasChanges: function hasChanges() {
58426 return this.difference().length() > 0;
58428 imageryUsed: function imageryUsed(sources) {
58430 _imageryUsed = sources;
58435 _stack.slice(1, _index + 1).forEach(function (state) {
58436 state.imageryUsed.forEach(function (source) {
58437 if (source !== 'Custom') {
58443 return Array.from(s);
58446 photoOverlaysUsed: function photoOverlaysUsed(sources) {
58448 _photoOverlaysUsed = sources;
58453 _stack.slice(1, _index + 1).forEach(function (state) {
58454 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
58455 state.photoOverlaysUsed.forEach(function (photoOverlay) {
58456 s.add(photoOverlay);
58461 return Array.from(s);
58464 // save the current history state
58465 checkpoint: function checkpoint(key) {
58466 _checkpoints[key] = {
58472 // restore history state to a given checkpoint or reset completely
58473 reset: function reset(key) {
58474 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
58475 _stack = _checkpoints[key].stack;
58476 _index = _checkpoints[key].index;
58482 _tree = coreTree(_stack[0].graph);
58486 dispatch$1.call('reset');
58487 dispatch$1.call('change');
58490 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
58493 // 1. Start the walkthrough.
58494 // 2. Get to a "free editing" tutorial step
58495 // 3. Make your edits to the walkthrough map
58496 // 4. In your browser dev console run:
58497 // `id.history().toIntroGraph()`
58498 // 5. This outputs stringified JSON to the browser console
58499 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
58500 toIntroGraph: function toIntroGraph() {
58507 var graph = this.graph();
58508 var baseEntities = {}; // clone base entities..
58510 Object.values(graph.base().entities).forEach(function (entity) {
58511 var copy = copyIntroEntity(entity);
58512 baseEntities[copy.id] = copy;
58513 }); // replace base entities with head entities..
58515 Object.keys(graph.entities).forEach(function (id) {
58516 var entity = graph.entities[id];
58519 var copy = copyIntroEntity(entity);
58520 baseEntities[copy.id] = copy;
58522 delete baseEntities[id];
58524 }); // swap temporary for permanent ids..
58526 Object.values(baseEntities).forEach(function (entity) {
58527 if (Array.isArray(entity.nodes)) {
58528 entity.nodes = entity.nodes.map(function (node) {
58529 return permIDs[node] || node;
58533 if (Array.isArray(entity.members)) {
58534 entity.members = entity.members.map(function (member) {
58535 member.id = permIDs[member.id] || member.id;
58540 return JSON.stringify({
58541 dataIntroGraph: baseEntities
58544 function copyIntroEntity(source) {
58545 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
58547 if (copy.tags && !Object.keys(copy.tags)) {
58551 if (Array.isArray(copy.loc)) {
58552 copy.loc[0] = +copy.loc[0].toFixed(6);
58553 copy.loc[1] = +copy.loc[1].toFixed(6);
58556 var match = source.id.match(/([nrw])-\d*/); // temporary id
58558 if (match !== null) {
58559 var nrw = match[1];
58563 permID = nrw + ++nextID[nrw];
58564 } while (baseEntities.hasOwnProperty(permID));
58566 copy.id = permIDs[source.id] = permID;
58572 toJSON: function toJSON() {
58573 if (!this.hasChanges()) return;
58574 var allEntities = {};
58575 var baseEntities = {};
58576 var base = _stack[0];
58578 var s = _stack.map(function (i) {
58581 Object.keys(i.graph.entities).forEach(function (id) {
58582 var entity = i.graph.entities[id];
58585 var key = osmEntity.key(entity);
58586 allEntities[key] = entity;
58587 modified.push(key);
58590 } // make sure that the originals of changed or deleted entities get merged
58591 // into the base of the _stack after restoring the data from JSON.
58594 if (id in base.graph.entities) {
58595 baseEntities[id] = base.graph.entities[id];
58598 if (entity && entity.nodes) {
58599 // get originals of pre-existing child nodes
58600 entity.nodes.forEach(function (nodeID) {
58601 if (nodeID in base.graph.entities) {
58602 baseEntities[nodeID] = base.graph.entities[nodeID];
58605 } // get originals of parent entities too
58608 var baseParents = base.graph._parentWays[id];
58611 baseParents.forEach(function (parentID) {
58612 if (parentID in base.graph.entities) {
58613 baseEntities[parentID] = base.graph.entities[parentID];
58619 if (modified.length) x.modified = modified;
58620 if (deleted.length) x.deleted = deleted;
58621 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
58622 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
58623 if (i.annotation) x.annotation = i.annotation;
58624 if (i.transform) x.transform = i.transform;
58625 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
58629 return JSON.stringify({
58631 entities: Object.values(allEntities),
58632 baseEntities: Object.values(baseEntities),
58634 nextIDs: osmEntity.id.next,
58636 // note the time the changes were saved
58637 timestamp: new Date().getTime()
58640 fromJSON: function fromJSON(json, loadChildNodes) {
58641 var h = JSON.parse(json);
58642 var loadComplete = true;
58643 osmEntity.id.next = h.nextIDs;
58646 if (h.version === 2 || h.version === 3) {
58647 var allEntities = {};
58648 h.entities.forEach(function (entity) {
58649 allEntities[osmEntity.key(entity)] = osmEntity(entity);
58652 if (h.version === 3) {
58653 // This merges originals for changed entities into the base of
58654 // the _stack even if the current _stack doesn't have them (for
58655 // example when iD has been restarted in a different region)
58656 var baseEntities = h.baseEntities.map(function (d) {
58657 return osmEntity(d);
58660 var stack = _stack.map(function (state) {
58661 return state.graph;
58664 _stack[0].graph.rebase(baseEntities, stack, true);
58666 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
58667 // childnodes that would normally have been downloaded with it.. #2142
58670 if (loadChildNodes) {
58671 var osm = context.connection();
58672 var baseWays = baseEntities.filter(function (e) {
58673 return e.type === 'way';
58675 var nodeIDs = baseWays.reduce(function (acc, way) {
58676 return utilArrayUnion(acc, way.nodes);
58678 var missing = nodeIDs.filter(function (n) {
58679 return !_stack[0].graph.hasEntity(n);
58682 if (missing.length && osm) {
58683 loadComplete = false;
58684 context.map().redrawEnable(false);
58685 var loading = uiLoading(context).blocking(true);
58686 context.container().call(loading);
58688 var childNodesLoaded = function childNodesLoaded(err, result) {
58690 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
58691 var visibles = visibleGroups["true"] || []; // alive nodes
58693 var invisibles = visibleGroups["false"] || []; // deleted nodes
58695 if (visibles.length) {
58696 var visibleIDs = visibles.map(function (entity) {
58700 var stack = _stack.map(function (state) {
58701 return state.graph;
58704 missing = utilArrayDifference(missing, visibleIDs);
58706 _stack[0].graph.rebase(visibles, stack, true);
58708 _tree.rebase(visibles, true);
58709 } // fetch older versions of nodes that were deleted..
58712 invisibles.forEach(function (entity) {
58713 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
58717 if (err || !missing.length) {
58719 context.map().redrawEnable(true);
58720 dispatch$1.call('change');
58721 dispatch$1.call('restore', this);
58725 osm.loadMultiple(missing, childNodesLoaded);
58730 _stack = h.stack.map(function (d) {
58735 d.modified.forEach(function (key) {
58736 entity = allEntities[key];
58737 entities[entity.id] = entity;
58742 d.deleted.forEach(function (id) {
58743 entities[id] = undefined;
58748 graph: coreGraph(_stack[0].graph).load(entities),
58749 annotation: d.annotation,
58750 imageryUsed: d.imageryUsed,
58751 photoOverlaysUsed: d.photoOverlaysUsed,
58752 transform: d.transform,
58753 selectedIDs: d.selectedIDs
58757 // original version
58758 _stack = h.stack.map(function (d) {
58761 for (var i in d.entities) {
58762 var entity = d.entities[i];
58763 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
58766 d.graph = coreGraph(_stack[0].graph).load(entities);
58771 var transform = _stack[_index].transform;
58774 context.map().transformEase(transform, 0); // 0 = immediate, no easing
58777 if (loadComplete) {
58778 dispatch$1.call('change');
58779 dispatch$1.call('restore', this);
58784 lock: function lock() {
58785 return _lock.lock();
58787 unlock: function unlock() {
58790 save: function save() {
58791 if (_lock.locked() && // don't overwrite existing, unresolved changes
58792 !_hasUnresolvedRestorableChanges) {
58793 corePreferences(getKey('saved_history'), history.toJSON() || null);
58798 // delete the history version saved in localStorage
58799 clearSaved: function clearSaved() {
58800 context.debouncedSave.cancel();
58802 if (_lock.locked()) {
58803 _hasUnresolvedRestorableChanges = false;
58804 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
58806 corePreferences('comment', null);
58807 corePreferences('hashtags', null);
58808 corePreferences('source', null);
58813 savedHistoryJSON: function savedHistoryJSON() {
58814 return corePreferences(getKey('saved_history'));
58816 hasRestorableChanges: function hasRestorableChanges() {
58817 return _hasUnresolvedRestorableChanges;
58819 // load history from a version stored in localStorage
58820 restore: function restore() {
58821 if (_lock.locked()) {
58822 _hasUnresolvedRestorableChanges = false;
58823 var json = this.savedHistoryJSON();
58824 if (json) history.fromJSON(json, true);
58830 return utilRebind(history, dispatch$1, 'on');
58834 * Look for roads that can be connected to other roads with a short extension
58837 function validationAlmostJunction(context) {
58838 var type = 'almost_junction';
58839 var EXTEND_TH_METERS = 5;
58840 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
58842 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
58844 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
58846 function isHighway(entity) {
58847 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
58850 function isTaggedAsNotContinuing(node) {
58851 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
58854 var validation = function checkAlmostJunction(entity, graph) {
58855 if (!isHighway(entity)) return [];
58856 if (entity.isDegenerate()) return [];
58857 var tree = context.history().tree();
58858 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
58860 extendableNodeInfos.forEach(function (extendableNodeInfo) {
58861 issues.push(new validationIssue({
58863 subtype: 'highway-highway',
58864 severity: 'warning',
58865 message: function message(context) {
58866 var entity1 = context.hasEntity(this.entityIds[0]);
58868 if (this.entityIds[0] === this.entityIds[2]) {
58869 return entity1 ? _t.html('issues.almost_junction.self.message', {
58870 feature: utilDisplayLabel(entity1, context.graph())
58873 var entity2 = context.hasEntity(this.entityIds[2]);
58874 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
58875 feature: utilDisplayLabel(entity1, context.graph()),
58876 feature2: utilDisplayLabel(entity2, context.graph())
58880 reference: showReference,
58881 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
58882 loc: extendableNodeInfo.node.loc,
58883 hash: JSON.stringify(extendableNodeInfo.node.loc),
58885 midId: extendableNodeInfo.mid.id,
58886 edge: extendableNodeInfo.edge,
58887 cross_loc: extendableNodeInfo.cross_loc
58889 dynamicFixes: makeFixes
58894 function makeFixes(context) {
58895 var fixes = [new validationIssueFix({
58896 icon: 'iD-icon-abutment',
58897 title: _t.html('issues.fix.connect_features.title'),
58898 onClick: function onClick(context) {
58899 var annotation = _t('issues.fix.connect_almost_junction.annotation');
58901 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
58902 endNodeId = _this$issue$entityIds[1],
58903 crossWayId = _this$issue$entityIds[2];
58905 var midNode = context.entity(this.issue.data.midId);
58906 var endNode = context.entity(endNodeId);
58907 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
58909 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
58911 if (nearEndNodes.length > 0) {
58912 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
58915 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
58920 var targetEdge = this.issue.data.edge;
58921 var crossLoc = this.issue.data.cross_loc;
58922 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
58923 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
58925 if (closestNodeInfo.distance < WELD_TH_METERS) {
58926 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
58928 context.perform(actionAddMidpoint({
58931 }, endNode), annotation);
58935 var node = context.hasEntity(this.entityIds[1]);
58937 if (node && !node.hasInterestingTags()) {
58938 // node has no descriptive tags, suggest noexit fix
58939 fixes.push(new validationIssueFix({
58940 icon: 'maki-barrier',
58941 title: _t.html('issues.fix.tag_as_disconnected.title'),
58942 onClick: function onClick(context) {
58943 var nodeID = this.issue.entityIds[1];
58944 var tags = Object.assign({}, context.entity(nodeID).tags);
58945 tags.noexit = 'yes';
58946 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
58954 function showReference(selection) {
58955 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
58958 function isExtendableCandidate(node, way) {
58959 // can not accurately test vertices on tiles not downloaded from osm - #5938
58960 var osm = services.osm;
58962 if (osm && !osm.isDataLoaded(node.loc)) {
58966 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
58970 var occurrences = 0;
58972 for (var index in way.nodes) {
58973 if (way.nodes[index] === node.id) {
58976 if (occurrences > 1) {
58985 function findConnectableEndNodesByExtension(way) {
58987 if (way.isClosed()) return results;
58989 var indices = [0, way.nodes.length - 1];
58990 indices.forEach(function (nodeIndex) {
58991 var nodeID = way.nodes[nodeIndex];
58992 var node = graph.entity(nodeID);
58993 if (!isExtendableCandidate(node, way)) return;
58994 var connectionInfo = canConnectByExtend(way, nodeIndex);
58995 if (!connectionInfo) return;
58996 testNodes = graph.childNodes(way).slice(); // shallow copy
58998 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
59000 if (geoHasSelfIntersections(testNodes, nodeID)) return;
59001 results.push(connectionInfo);
59006 function findNearbyEndNodes(node, way) {
59007 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
59008 return graph.entity(d);
59009 }).filter(function (d) {
59010 // Node cannot be near to itself, but other endnode of same way could be
59011 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
59015 function findSmallJoinAngle(midNode, tipNode, endNodes) {
59016 // Both nodes could be close, so want to join whichever is closest to collinear
59018 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
59020 endNodes.forEach(function (endNode) {
59021 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
59022 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
59023 var diff = Math.max(a1, a2) - Math.min(a1, a2);
59025 if (diff < minAngle) {
59030 /* Threshold set by considering right angle triangle
59031 based on node joining threshold and extension distance */
59033 if (minAngle <= SIG_ANGLE_TH) return joinTo;
59037 function hasTag(tags, key) {
59038 return tags[key] !== undefined && tags[key] !== 'no';
59041 function canConnectWays(way, way2) {
59042 // allow self-connections
59043 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
59045 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
59046 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
59048 var layer1 = way.tags.layer || '0',
59049 layer2 = way2.tags.layer || '0';
59050 if (layer1 !== layer2) return false;
59051 var level1 = way.tags.level || '0',
59052 level2 = way2.tags.level || '0';
59053 if (level1 !== level2) return false;
59057 function canConnectByExtend(way, endNodeIdx) {
59058 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
59060 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
59062 var tipNode = graph.entity(tipNid);
59063 var midNode = graph.entity(midNid);
59064 var lon = tipNode.loc[0];
59065 var lat = tipNode.loc[1];
59066 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
59067 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
59068 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
59070 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
59071 var t = EXTEND_TH_METERS / edgeLen + 1.0;
59072 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
59074 var segmentInfos = tree.waySegments(queryExtent, graph);
59076 for (var i = 0; i < segmentInfos.length; i++) {
59077 var segmentInfo = segmentInfos[i];
59078 var way2 = graph.entity(segmentInfo.wayId);
59079 if (!isHighway(way2)) continue;
59080 if (!canConnectWays(way, way2)) continue;
59081 var nAid = segmentInfo.nodes[0],
59082 nBid = segmentInfo.nodes[1];
59083 if (nAid === tipNid || nBid === tipNid) continue;
59084 var nA = graph.entity(nAid),
59085 nB = graph.entity(nBid);
59086 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
59093 edge: [nA.id, nB.id],
59094 cross_loc: crossLoc
59103 validation.type = type;
59107 function validationCloseNodes(context) {
59108 var type = 'close_nodes';
59109 var pointThresholdMeters = 0.2;
59111 var validation = function validation(entity, graph) {
59112 if (entity.type === 'node') {
59113 return getIssuesForNode(entity);
59114 } else if (entity.type === 'way') {
59115 return getIssuesForWay(entity);
59120 function getIssuesForNode(node) {
59121 var parentWays = graph.parentWays(node);
59123 if (parentWays.length) {
59124 return getIssuesForVertex(node, parentWays);
59126 return getIssuesForDetachedPoint(node);
59130 function wayTypeFor(way) {
59131 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
59132 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
59133 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
59134 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
59135 var parentRelations = graph.parentRelations(way);
59137 for (var i in parentRelations) {
59138 var relation = parentRelations[i];
59139 if (relation.tags.type === 'boundary') return 'boundary';
59141 if (relation.isMultipolygon()) {
59142 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
59143 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
59150 function shouldCheckWay(way) {
59151 // don't flag issues where merging would create degenerate ways
59152 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
59153 var bbox = way.extent(graph).bbox();
59154 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
59156 if (hypotenuseMeters < 1.5) return false;
59160 function getIssuesForWay(way) {
59161 if (!shouldCheckWay(way)) return [];
59163 nodes = graph.childNodes(way);
59165 for (var i = 0; i < nodes.length - 1; i++) {
59166 var node1 = nodes[i];
59167 var node2 = nodes[i + 1];
59168 var issue = getWayIssueIfAny(node1, node2, way);
59169 if (issue) issues.push(issue);
59175 function getIssuesForVertex(node, parentWays) {
59178 function checkForCloseness(node1, node2, way) {
59179 var issue = getWayIssueIfAny(node1, node2, way);
59180 if (issue) issues.push(issue);
59183 for (var i = 0; i < parentWays.length; i++) {
59184 var parentWay = parentWays[i];
59185 if (!shouldCheckWay(parentWay)) continue;
59186 var lastIndex = parentWay.nodes.length - 1;
59188 for (var j = 0; j < parentWay.nodes.length; j++) {
59190 if (parentWay.nodes[j - 1] === node.id) {
59191 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
59195 if (j !== lastIndex) {
59196 if (parentWay.nodes[j + 1] === node.id) {
59197 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
59206 function thresholdMetersForWay(way) {
59207 if (!shouldCheckWay(way)) return 0;
59208 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
59210 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
59212 if (wayType === 'indoor') return 0.01;
59213 if (wayType === 'building') return 0.05;
59214 if (wayType === 'path') return 0.1;
59218 function getIssuesForDetachedPoint(node) {
59220 var lon = node.loc[0];
59221 var lat = node.loc[1];
59222 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
59223 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
59224 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
59225 var intersected = context.history().tree().intersects(queryExtent, graph);
59227 for (var j = 0; j < intersected.length; j++) {
59228 var nearby = intersected[j];
59229 if (nearby.id === node.id) continue;
59230 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
59232 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
59233 // allow very close points if tags indicate the z-axis might vary
59237 'addr:housenumber': true,
59240 var zAxisDifferentiates = false;
59242 for (var key in zAxisKeys) {
59243 var nodeValue = node.tags[key] || '0';
59244 var nearbyValue = nearby.tags[key] || '0';
59246 if (nodeValue !== nearbyValue) {
59247 zAxisDifferentiates = true;
59252 if (zAxisDifferentiates) continue;
59253 issues.push(new validationIssue({
59255 subtype: 'detached',
59256 severity: 'warning',
59257 message: function message(context) {
59258 var entity = context.hasEntity(this.entityIds[0]),
59259 entity2 = context.hasEntity(this.entityIds[1]);
59260 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
59261 feature: utilDisplayLabel(entity, context.graph()),
59262 feature2: utilDisplayLabel(entity2, context.graph())
59265 reference: showReference,
59266 entityIds: [node.id, nearby.id],
59267 dynamicFixes: function dynamicFixes() {
59268 return [new validationIssueFix({
59269 icon: 'iD-operation-disconnect',
59270 title: _t.html('issues.fix.move_points_apart.title')
59271 }), new validationIssueFix({
59272 icon: 'iD-icon-layers',
59273 title: _t.html('issues.fix.use_different_layers_or_levels.title')
59282 function showReference(selection) {
59283 var referenceText = _t('issues.close_nodes.detached.reference');
59284 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
59288 function getWayIssueIfAny(node1, node2, way) {
59289 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
59293 if (node1.loc !== node2.loc) {
59294 var parentWays1 = graph.parentWays(node1);
59295 var parentWays2 = new Set(graph.parentWays(node2));
59296 var sharedWays = parentWays1.filter(function (parentWay) {
59297 return parentWays2.has(parentWay);
59299 var thresholds = sharedWays.map(function (parentWay) {
59300 return thresholdMetersForWay(parentWay);
59302 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
59303 var distance = geoSphericalDistance(node1.loc, node2.loc);
59304 if (distance > threshold) return null;
59307 return new validationIssue({
59309 subtype: 'vertices',
59310 severity: 'warning',
59311 message: function message(context) {
59312 var entity = context.hasEntity(this.entityIds[0]);
59313 return entity ? _t.html('issues.close_nodes.message', {
59314 way: utilDisplayLabel(entity, context.graph())
59317 reference: showReference,
59318 entityIds: [way.id, node1.id, node2.id],
59320 dynamicFixes: function dynamicFixes() {
59321 return [new validationIssueFix({
59322 icon: 'iD-icon-plus',
59323 title: _t.html('issues.fix.merge_points.title'),
59324 onClick: function onClick(context) {
59325 var entityIds = this.issue.entityIds;
59326 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
59327 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
59329 }), new validationIssueFix({
59330 icon: 'iD-operation-disconnect',
59331 title: _t.html('issues.fix.move_points_apart.title')
59336 function showReference(selection) {
59337 var referenceText = _t('issues.close_nodes.reference');
59338 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
59343 validation.type = type;
59347 function validationCrossingWays(context) {
59348 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
59350 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
59351 if (getFeatureType(way, graph) === null) {
59352 // if the way doesn't match a feature type, check its parent relations
59353 var parentRels = graph.parentRelations(way);
59355 for (var i = 0; i < parentRels.length; i++) {
59356 var rel = parentRels[i];
59358 if (getFeatureType(rel, graph) !== null) {
59367 function hasTag(tags, key) {
59368 return tags[key] !== undefined && tags[key] !== 'no';
59371 function taggedAsIndoor(tags) {
59372 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
59375 function allowsBridge(featureType) {
59376 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
59379 function allowsTunnel(featureType) {
59380 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
59384 var ignoredBuildings = {
59391 function getFeatureType(entity, graph) {
59392 var geometry = entity.geometry(graph);
59393 if (geometry !== 'line' && geometry !== 'area') return null;
59394 var tags = entity.tags;
59395 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
59396 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
59398 if (geometry !== 'line') return null;
59399 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
59400 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
59404 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
59405 // assume 0 by default
59406 var level1 = tags1.level || '0';
59407 var level2 = tags2.level || '0';
59409 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
59410 // assume features don't interact if they're indoor on different levels
59412 } // assume 0 by default; don't use way.layer() since we account for structures here
59415 var layer1 = tags1.layer || '0';
59416 var layer2 = tags2.layer || '0';
59418 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
59419 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
59420 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
59422 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
59423 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
59425 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
59426 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
59427 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
59429 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
59430 } 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
59433 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
59434 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
59436 if (featureType1 === 'building' || featureType2 === 'building') {
59437 // for building crossings, different layers are enough
59438 if (layer1 !== layer2) return true;
59442 } // highway values for which we shouldn't recommend connecting to waterways
59445 var highwaysDisallowingFords = {
59447 motorway_link: true,
59451 primary_link: true,
59453 secondary_link: true
59455 var nonCrossingHighways = {
59459 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
59460 var featureType1 = getFeatureType(entity1, graph);
59461 var featureType2 = getFeatureType(entity2, graph);
59462 var geometry1 = entity1.geometry(graph);
59463 var geometry2 = entity2.geometry(graph);
59464 var bothLines = geometry1 === 'line' && geometry2 === 'line';
59466 if (featureType1 === featureType2) {
59467 if (featureType1 === 'highway') {
59468 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
59469 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
59471 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
59472 // one feature is a path but not both
59473 var roadFeature = entity1IsPath ? entity2 : entity1;
59475 if (nonCrossingHighways[roadFeature.tags.highway]) {
59476 // don't mark path connections with certain roads as crossings
59480 var pathFeature = entity1IsPath ? entity1 : entity2;
59482 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
59483 // if the path is a crossing, match the crossing type
59484 return bothLines ? {
59485 highway: 'crossing',
59486 crossing: pathFeature.tags.crossing
59488 } // don't add a `crossing` subtag to ambiguous crossings
59491 return bothLines ? {
59492 highway: 'crossing'
59499 if (featureType1 === 'waterway') return {};
59500 if (featureType1 === 'railway') return {};
59502 var featureTypes = [featureType1, featureType2];
59504 if (featureTypes.indexOf('highway') !== -1) {
59505 if (featureTypes.indexOf('railway') !== -1) {
59506 if (!bothLines) return {};
59507 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
59509 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
59510 // path-tram connections use this tag
59511 if (isTram) return {
59512 railway: 'tram_crossing'
59513 }; // other path-rail connections use this tag
59516 railway: 'crossing'
59519 // path-tram connections use this tag
59520 if (isTram) return {
59521 railway: 'tram_level_crossing'
59522 }; // other road-rail connections use this tag
59525 railway: 'level_crossing'
59530 if (featureTypes.indexOf('waterway') !== -1) {
59531 // do not allow fords on structures
59532 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
59533 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
59535 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
59536 // do not allow fords on major highways
59540 return bothLines ? {
59550 function findCrossingsByWay(way1, graph, tree) {
59551 var edgeCrossInfos = [];
59552 if (way1.type !== 'way') return edgeCrossInfos;
59553 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
59554 var way1FeatureType = getFeatureType(taggedFeature1, graph);
59555 if (way1FeatureType === null) return edgeCrossInfos;
59556 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
59560 var n1, n2, nA, nB, nAId, nBId;
59561 var segment1, segment2;
59563 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
59564 var way1Nodes = graph.childNodes(way1);
59565 var comparedWays = {};
59567 for (i = 0; i < way1Nodes.length - 1; i++) {
59569 n2 = way1Nodes[i + 1];
59570 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
59571 // of overlapping ways
59573 segmentInfos = tree.waySegments(extent, graph);
59575 for (j = 0; j < segmentInfos.length; j++) {
59576 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
59578 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
59580 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
59582 comparedWays[segment2Info.wayId] = true;
59583 way2 = graph.hasEntity(segment2Info.wayId);
59584 if (!way2) continue;
59585 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
59587 way2FeatureType = getFeatureType(taggedFeature2, graph);
59589 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
59591 } // create only one issue for building crossings
59594 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
59595 nAId = segment2Info.nodes[0];
59596 nBId = segment2Info.nodes[1];
59598 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
59599 // n1 or n2 is a connection node; skip
59603 nA = graph.hasEntity(nAId);
59605 nB = graph.hasEntity(nBId);
59607 segment1 = [n1.loc, n2.loc];
59608 segment2 = [nA.loc, nB.loc];
59609 var point = geoLineIntersection(segment1, segment2);
59612 edgeCrossInfos.push({
59615 featureType: way1FeatureType,
59616 edge: [n1.id, n2.id]
59619 featureType: way2FeatureType,
59620 edge: [nA.id, nB.id]
59626 checkedSingleCrossingWays[way2.id] = true;
59633 return edgeCrossInfos;
59636 function waysToCheck(entity, graph) {
59637 var featureType = getFeatureType(entity, graph);
59638 if (!featureType) return [];
59640 if (entity.type === 'way') {
59642 } else if (entity.type === 'relation') {
59643 return entity.members.reduce(function (array, member) {
59644 if (member.type === 'way' && ( // only look at geometry ways
59645 !member.role || member.role === 'outer' || member.role === 'inner')) {
59646 var entity = graph.hasEntity(member.id); // don't add duplicates
59648 if (entity && array.indexOf(entity) === -1) {
59649 array.push(entity);
59660 var validation = function checkCrossingWays(entity, graph) {
59661 var tree = context.history().tree();
59662 var ways = waysToCheck(entity, graph);
59663 var issues = []; // declare these here to reduce garbage collection
59665 var wayIndex, crossingIndex, crossings;
59667 for (wayIndex in ways) {
59668 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
59670 for (crossingIndex in crossings) {
59671 issues.push(createIssue(crossings[crossingIndex], graph));
59678 function createIssue(crossing, graph) {
59679 // use the entities with the tags that define the feature type
59680 crossing.wayInfos.sort(function (way1Info, way2Info) {
59681 var type1 = way1Info.featureType;
59682 var type2 = way2Info.featureType;
59684 if (type1 === type2) {
59685 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
59686 } else if (type1 === 'waterway') {
59688 } else if (type2 === 'waterway') {
59692 return type1 < type2;
59694 var entities = crossing.wayInfos.map(function (wayInfo) {
59695 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
59697 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
59698 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
59699 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
59700 var featureType1 = crossing.wayInfos[0].featureType;
59701 var featureType2 = crossing.wayInfos[1].featureType;
59702 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
59703 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
59704 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
59705 var subtype = [featureType1, featureType2].sort().join('-');
59706 var crossingTypeID = subtype;
59708 if (isCrossingIndoors) {
59709 crossingTypeID = 'indoor-indoor';
59710 } else if (isCrossingTunnels) {
59711 crossingTypeID = 'tunnel-tunnel';
59712 } else if (isCrossingBridges) {
59713 crossingTypeID = 'bridge-bridge';
59716 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
59717 crossingTypeID += '_connectable';
59720 return new validationIssue({
59723 severity: 'warning',
59724 message: function message(context) {
59725 var graph = context.graph();
59726 var entity1 = graph.hasEntity(this.entityIds[0]),
59727 entity2 = graph.hasEntity(this.entityIds[1]);
59728 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
59729 feature: utilDisplayLabel(entity1, graph),
59730 feature2: utilDisplayLabel(entity2, graph)
59733 reference: showReference,
59734 entityIds: entities.map(function (entity) {
59739 featureTypes: featureTypes,
59740 connectionTags: connectionTags
59742 // differentiate based on the loc since two ways can cross multiple times
59743 hash: crossing.crossPoint.toString() + // if the edges change then so does the fix
59744 edges.slice().sort(function (edge1, edge2) {
59745 // order to assure hash is deterministic
59746 return edge1[0] < edge2[0] ? -1 : 1;
59747 }).toString() + // ensure the correct connection tags are added in the fix
59748 JSON.stringify(connectionTags),
59749 loc: crossing.crossPoint,
59750 dynamicFixes: function dynamicFixes(context) {
59751 var mode = context.mode();
59752 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
59753 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
59754 var selectedFeatureType = this.data.featureTypes[selectedIndex];
59755 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
59758 if (connectionTags) {
59759 fixes.push(makeConnectWaysFix(this.data.connectionTags));
59762 if (isCrossingIndoors) {
59763 fixes.push(new validationIssueFix({
59764 icon: 'iD-icon-layers',
59765 title: _t.html('issues.fix.use_different_levels.title')
59767 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
59768 fixes.push(makeChangeLayerFix('higher'));
59769 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
59770 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
59771 // don't recommend adding bridges to waterways since they're uncommon
59772 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
59773 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
59774 } // don't recommend adding tunnels under waterways since they're uncommon
59777 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
59779 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
59780 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
59782 } // repositioning the features is always an option
59785 fixes.push(new validationIssueFix({
59786 icon: 'iD-operation-move',
59787 title: _t.html('issues.fix.reposition_features.title')
59793 function showReference(selection) {
59794 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
59798 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
59799 return new validationIssueFix({
59801 title: _t.html('issues.fix.' + fixTitleID + '.title'),
59802 onClick: function onClick(context) {
59803 var mode = context.mode();
59804 if (!mode || mode.id !== 'select') return;
59805 var selectedIDs = mode.selectedIDs();
59806 if (selectedIDs.length !== 1) return;
59807 var selectedWayID = selectedIDs[0];
59808 if (!context.hasEntity(selectedWayID)) return;
59809 var resultWayIDs = [selectedWayID];
59810 var edge, crossedEdge, crossedWayID;
59812 if (this.issue.entityIds[0] === selectedWayID) {
59813 edge = this.issue.data.edges[0];
59814 crossedEdge = this.issue.data.edges[1];
59815 crossedWayID = this.issue.entityIds[1];
59817 edge = this.issue.data.edges[1];
59818 crossedEdge = this.issue.data.edges[0];
59819 crossedWayID = this.issue.entityIds[0];
59822 var crossingLoc = this.issue.loc;
59823 var projection = context.projection;
59825 var action = function actionAddStructure(graph) {
59826 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
59827 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
59829 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
59831 if (!structLengthMeters) {
59832 // if no explicit width is set, approximate the width based on the tags
59833 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
59836 if (structLengthMeters) {
59837 if (getFeatureType(crossedWay, graph) === 'railway') {
59838 // bridges over railways are generally much longer than the rail bed itself, compensate
59839 structLengthMeters *= 2;
59842 // should ideally never land here since all rail/water/road tags should have an implied width
59843 structLengthMeters = 8;
59846 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
59847 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
59848 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
59849 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
59851 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
59853 structLengthMeters += 4; // clamp the length to a reasonable range
59855 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
59857 function geomToProj(geoPoint) {
59858 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
59861 function projToGeom(projPoint) {
59862 var lat = geoMetersToLat(projPoint[1]);
59863 return [geoMetersToLon(projPoint[0], lat), lat];
59866 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
59867 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
59868 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
59869 var projectedCrossingLoc = geomToProj(crossingLoc);
59870 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
59872 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
59873 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
59874 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
59877 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
59878 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
59881 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
59882 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
59883 }; // avoid creating very short edges from splitting too close to another node
59886 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
59888 function determineEndpoint(edge, endNode, locGetter) {
59890 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
59891 // the maximum length of this side of the structure
59893 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
59895 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
59896 // the edge is long enough to insert a new node
59897 // the loc that would result in the full expected length
59898 var idealNodeLoc = locGetter(idealLengthMeters);
59899 newNode = osmNode();
59900 graph = actionAddMidpoint({
59903 }, newNode)(graph);
59906 endNode.parentIntersectionWays(graph).forEach(function (way) {
59907 way.nodes.forEach(function (nodeID) {
59908 if (nodeID === endNode.id) {
59909 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
59918 if (edgeCount >= 3) {
59919 // the end node is a junction, try to leave a segment
59920 // between it and the structure - #7202
59921 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
59923 if (insetLength > minEdgeLengthMeters) {
59924 var insetNodeLoc = locGetter(insetLength);
59925 newNode = osmNode();
59926 graph = actionAddMidpoint({
59929 }, newNode)(graph);
59932 } // if the edge is too short to subdivide as desired, then
59933 // just bound the structure at the existing end node
59936 if (!newNode) newNode = endNode;
59937 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
59940 graph = splitAction(graph);
59942 if (splitAction.getCreatedWayIDs().length) {
59943 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
59949 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
59950 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
59951 var structureWay = resultWayIDs.map(function (id) {
59952 return graph.entity(id);
59953 }).find(function (way) {
59954 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
59956 var tags = Object.assign({}, structureWay.tags); // copy tags
59958 if (bridgeOrTunnel === 'bridge') {
59959 tags.bridge = 'yes';
59962 var tunnelValue = 'yes';
59964 if (getFeatureType(structureWay, graph) === 'waterway') {
59965 // use `tunnel=culvert` for waterways by default
59966 tunnelValue = 'culvert';
59969 tags.tunnel = tunnelValue;
59971 } // apply the structure tags to the way
59974 graph = actionChangeTags(structureWay.id, tags)(graph);
59978 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
59979 context.enter(modeSelect(context, resultWayIDs));
59984 function makeConnectWaysFix(connectionTags) {
59985 var fixTitleID = 'connect_features';
59987 if (connectionTags.ford) {
59988 fixTitleID = 'connect_using_ford';
59991 return new validationIssueFix({
59992 icon: 'iD-icon-crossing',
59993 title: _t.html('issues.fix.' + fixTitleID + '.title'),
59994 onClick: function onClick(context) {
59995 var loc = this.issue.loc;
59996 var connectionTags = this.issue.data.connectionTags;
59997 var edges = this.issue.data.edges;
59998 context.perform(function actionConnectCrossingWays(graph) {
59999 // create the new node for the points
60000 var node = osmNode({
60002 tags: connectionTags
60004 graph = graph.replace(node);
60005 var nodesToMerge = [node.id];
60006 var mergeThresholdInMeters = 0.75;
60007 edges.forEach(function (edge) {
60008 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60009 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc); // if there is already a point nearby, use that
60011 if (closestNodeInfo.distance < mergeThresholdInMeters) {
60012 nodesToMerge.push(closestNodeInfo.node.id); // else add the new node to the way
60014 graph = actionAddMidpoint({
60021 if (nodesToMerge.length > 1) {
60022 // if we're using nearby nodes, merge them with the new node
60023 graph = actionMergeNodes(nodesToMerge, loc)(graph);
60027 }, _t('issues.fix.connect_crossing_features.annotation'));
60032 function makeChangeLayerFix(higherOrLower) {
60033 return new validationIssueFix({
60034 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
60035 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
60036 onClick: function onClick(context) {
60037 var mode = context.mode();
60038 if (!mode || mode.id !== 'select') return;
60039 var selectedIDs = mode.selectedIDs();
60040 if (selectedIDs.length !== 1) return;
60041 var selectedID = selectedIDs[0];
60042 if (!this.issue.entityIds.some(function (entityId) {
60043 return entityId === selectedID;
60045 var entity = context.hasEntity(selectedID);
60046 if (!entity) return;
60047 var tags = Object.assign({}, entity.tags); // shallow copy
60049 var layer = tags.layer && Number(tags.layer);
60051 if (layer && !isNaN(layer)) {
60052 if (higherOrLower === 'higher') {
60058 if (higherOrLower === 'higher') {
60065 tags.layer = layer.toString();
60066 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
60071 validation.type = type;
60075 function validationDisconnectedWay() {
60076 var type = 'disconnected_way';
60078 function isTaggedAsHighway(entity) {
60079 return osmRoutableHighwayTagValues[entity.tags.highway];
60082 var validation = function checkDisconnectedWay(entity, graph) {
60083 var routingIslandWays = routingIslandForEntity(entity);
60084 if (!routingIslandWays) return [];
60085 return [new validationIssue({
60087 subtype: 'highway',
60088 severity: 'warning',
60089 message: function message(context) {
60090 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
60091 var label = entity && utilDisplayLabel(entity, context.graph());
60092 return _t.html('issues.disconnected_way.routable.message', {
60093 count: this.entityIds.length,
60097 reference: showReference,
60098 entityIds: Array.from(routingIslandWays).map(function (way) {
60101 dynamicFixes: makeFixes
60104 function makeFixes(context) {
60106 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
60108 if (singleEntity) {
60109 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
60110 var textDirection = _mainLocalizer.textDirection();
60111 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
60112 if (startFix) fixes.push(startFix);
60113 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
60114 if (endFix) fixes.push(endFix);
60117 if (!fixes.length) {
60118 fixes.push(new validationIssueFix({
60119 title: _t.html('issues.fix.connect_feature.title')
60123 fixes.push(new validationIssueFix({
60124 icon: 'iD-operation-delete',
60125 title: _t.html('issues.fix.delete_feature.title'),
60126 entityIds: [singleEntity.id],
60127 onClick: function onClick(context) {
60128 var id = this.issue.entityIds[0];
60129 var operation = operationDelete(context, [id]);
60131 if (!operation.disabled()) {
60137 fixes.push(new validationIssueFix({
60138 title: _t.html('issues.fix.connect_features.title')
60145 function showReference(selection) {
60146 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
60149 function routingIslandForEntity(entity) {
60150 var routingIsland = new Set(); // the interconnected routable features
60152 var waysToCheck = []; // the queue of remaining routable ways to traverse
60154 function queueParentWays(node) {
60155 graph.parentWays(node).forEach(function (parentWay) {
60156 if (!routingIsland.has(parentWay) && // only check each feature once
60157 isRoutableWay(parentWay, false)) {
60158 // only check routable features
60159 routingIsland.add(parentWay);
60160 waysToCheck.push(parentWay);
60165 if (entity.type === 'way' && isRoutableWay(entity, true)) {
60166 routingIsland.add(entity);
60167 waysToCheck.push(entity);
60168 } else if (entity.type === 'node' && isRoutableNode(entity)) {
60169 routingIsland.add(entity);
60170 queueParentWays(entity);
60172 // this feature isn't routable, cannot be a routing island
60176 while (waysToCheck.length) {
60177 var wayToCheck = waysToCheck.pop();
60178 var childNodes = graph.childNodes(wayToCheck);
60180 for (var i in childNodes) {
60181 var vertex = childNodes[i];
60183 if (isConnectedVertex(vertex)) {
60184 // found a link to the wider network, not a routing island
60188 if (isRoutableNode(vertex)) {
60189 routingIsland.add(vertex);
60192 queueParentWays(vertex);
60194 } // no network link found, this is a routing island, return its members
60197 return routingIsland;
60200 function isConnectedVertex(vertex) {
60201 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
60202 var osm = services.osm;
60203 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
60205 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
60206 if (vertex.tags.amenity === 'parking_entrance') return true;
60210 function isRoutableNode(node) {
60211 // treat elevators as distinct features in the highway network
60212 if (node.tags.highway === 'elevator') return true;
60216 function isRoutableWay(way, ignoreInnerWays) {
60217 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
60218 return graph.parentRelations(way).some(function (parentRelation) {
60219 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
60220 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
60225 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
60226 var vertex = graph.hasEntity(vertexID);
60227 if (!vertex || vertex.tags.noexit === 'yes') return null;
60228 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
60229 return new validationIssueFix({
60230 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
60231 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
60232 entityIds: [vertexID],
60233 onClick: function onClick(context) {
60234 var wayId = this.issue.entityIds[0];
60235 var way = context.hasEntity(wayId);
60236 var vertexId = this.entityIds[0];
60237 var vertex = context.hasEntity(vertexId);
60238 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
60240 var map = context.map();
60242 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
60243 map.zoomToEase(vertex);
60246 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
60252 validation.type = type;
60256 function validationFormatting() {
60257 var type = 'invalid_format';
60259 var validation = function validation(entity) {
60262 function isValidEmail(email) {
60263 // Emails in OSM are going to be official so they should be pretty simple
60264 // Using negated lists to better support all possible unicode characters (#6494)
60265 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
60267 return !email || valid_email.test(email);
60270 function isSchemePresent(url) {
60271 var valid_scheme = /^https?:\/\//i;
60272 return (!url || valid_scheme.test(url));
60277 function showReferenceEmail(selection) {
60278 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
60281 function showReferenceWebsite(selection) {
60282 selection.selectAll('.issue-reference')
60286 .attr('class', 'issue-reference')
60287 .html(t.html('issues.invalid_format.website.reference'));
60289 if (entity.tags.website) {
60290 // Multiple websites are possible
60291 // If ever we support ES6, arrow functions make this nicer
60292 var websites = entity.tags.website
60294 .map(function(s) { return s.trim(); })
60295 .filter(function(x) { return !isSchemePresent(x); });
60296 if (websites.length) {
60297 issues.push(new validationIssue({
60299 subtype: 'website',
60300 severity: 'warning',
60301 message: function(context) {
60302 var entity = context.hasEntity(this.entityIds[0]);
60303 return entity ? t.html('issues.invalid_format.website.message' + this.data,
60304 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
60306 reference: showReferenceWebsite,
60307 entityIds: [entity.id],
60308 hash: websites.join(),
60309 data: (websites.length > 1) ? '_multi' : ''
60316 if (entity.tags.email) {
60317 // Multiple emails are possible
60318 var emails = entity.tags.email.split(';').map(function (s) {
60320 }).filter(function (x) {
60321 return !isValidEmail(x);
60324 if (emails.length) {
60325 issues.push(new validationIssue({
60328 severity: 'warning',
60329 message: function message(context) {
60330 var entity = context.hasEntity(this.entityIds[0]);
60331 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
60332 feature: utilDisplayLabel(entity, context.graph()),
60333 email: emails.join(', ')
60336 reference: showReferenceEmail,
60337 entityIds: [entity.id],
60338 hash: emails.join(),
60339 data: emails.length > 1 ? '_multi' : ''
60347 validation.type = type;
60351 function validationHelpRequest(context) {
60352 var type = 'help_request';
60354 var validation = function checkFixmeTag(entity) {
60355 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
60357 if (entity.version === undefined) return [];
60359 if (entity.v !== undefined) {
60360 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
60362 if (!baseEntity || !baseEntity.tags.fixme) return [];
60365 return [new validationIssue({
60367 subtype: 'fixme_tag',
60368 severity: 'warning',
60369 message: function message(context) {
60370 var entity = context.hasEntity(this.entityIds[0]);
60371 return entity ? _t.html('issues.fixme_tag.message', {
60372 feature: utilDisplayLabel(entity, context.graph())
60375 dynamicFixes: function dynamicFixes() {
60376 return [new validationIssueFix({
60377 title: _t.html('issues.fix.address_the_concern.title')
60380 reference: showReference,
60381 entityIds: [entity.id]
60384 function showReference(selection) {
60385 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
60389 validation.type = type;
60393 function validationImpossibleOneway() {
60394 var type = 'impossible_oneway';
60396 var validation = function checkImpossibleOneway(entity, graph) {
60397 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
60398 if (entity.isClosed()) return [];
60399 if (!typeForWay(entity)) return [];
60400 if (!isOneway(entity)) return [];
60401 var firstIssues = issuesForNode(entity, entity.first());
60402 var lastIssues = issuesForNode(entity, entity.last());
60403 return firstIssues.concat(lastIssues);
60405 function typeForWay(way) {
60406 if (way.geometry(graph) !== 'line') return null;
60407 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
60408 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
60412 function isOneway(way) {
60413 if (way.tags.oneway === 'yes') return true;
60414 if (way.tags.oneway) return false;
60416 for (var key in way.tags) {
60417 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
60425 function nodeOccursMoreThanOnce(way, nodeID) {
60426 var occurrences = 0;
60428 for (var index in way.nodes) {
60429 if (way.nodes[index] === nodeID) {
60431 if (occurrences > 1) return true;
60438 function isConnectedViaOtherTypes(way, node) {
60439 var wayType = typeForWay(way);
60441 if (wayType === 'highway') {
60442 // entrances are considered connected
60443 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
60444 if (node.tags.amenity === 'parking_entrance') return true;
60445 } else if (wayType === 'waterway') {
60446 if (node.id === way.first()) {
60447 // multiple waterways may start at the same spring
60448 if (node.tags.natural === 'spring') return true;
60450 // multiple waterways may end at the same drain
60451 if (node.tags.manhole === 'drain') return true;
60455 return graph.parentWays(node).some(function (parentWay) {
60456 if (parentWay.id === way.id) return false;
60458 if (wayType === 'highway') {
60459 // allow connections to highway areas
60460 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
60462 if (parentWay.tags.route === 'ferry') return true;
60463 return graph.parentRelations(parentWay).some(function (parentRelation) {
60464 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
60466 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
60468 } else if (wayType === 'waterway') {
60469 // multiple waterways may start or end at a water body at the same node
60470 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
60477 function issuesForNode(way, nodeID) {
60478 var isFirst = nodeID === way.first();
60479 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
60481 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
60482 var osm = services.osm;
60483 if (!osm) return [];
60484 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
60486 if (!node || !osm.isDataLoaded(node.loc)) return [];
60487 if (isConnectedViaOtherTypes(way, node)) return [];
60488 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
60489 if (parentWay.id === way.id) return false;
60490 return typeForWay(parentWay) === wayType;
60491 }); // assume it's okay for waterways to start or end disconnected for now
60493 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
60494 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
60495 return isOneway(attachedWay);
60496 }); // ignore if the way is connected to some non-oneway features
60498 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
60500 if (attachedOneways.length) {
60501 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
60502 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
60503 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
60506 if (connectedEndpointsOkay) return [];
60509 var placement = isFirst ? 'start' : 'end',
60510 messageID = wayType + '.',
60511 referenceID = wayType + '.';
60513 if (wayType === 'waterway') {
60514 messageID += 'connected.' + placement;
60515 referenceID += 'connected';
60517 messageID += placement;
60518 referenceID += placement;
60521 return [new validationIssue({
60524 severity: 'warning',
60525 message: function message(context) {
60526 var entity = context.hasEntity(this.entityIds[0]);
60527 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
60528 feature: utilDisplayLabel(entity, context.graph())
60531 reference: getReference(referenceID),
60532 entityIds: [way.id, node.id],
60533 dynamicFixes: function dynamicFixes() {
60536 if (attachedOneways.length) {
60537 fixes.push(new validationIssueFix({
60538 icon: 'iD-operation-reverse',
60539 title: _t.html('issues.fix.reverse_feature.title'),
60540 entityIds: [way.id],
60541 onClick: function onClick(context) {
60542 var id = this.issue.entityIds[0];
60543 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
60550 if (node.tags.noexit !== 'yes') {
60551 var textDirection = _mainLocalizer.textDirection();
60552 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
60553 fixes.push(new validationIssueFix({
60554 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
60555 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
60556 onClick: function onClick(context) {
60557 var entityID = this.issue.entityIds[0];
60558 var vertexID = this.issue.entityIds[1];
60559 var way = context.entity(entityID);
60560 var vertex = context.entity(vertexID);
60561 continueDrawing(way, vertex, context);
60571 function getReference(referenceID) {
60572 return function showReference(selection) {
60573 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
60579 function continueDrawing(way, vertex, context) {
60580 // make sure the vertex is actually visible and editable
60581 var map = context.map();
60583 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
60584 map.zoomToEase(vertex);
60587 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
60590 validation.type = type;
60594 function validationIncompatibleSource() {
60595 var type = 'incompatible_source';
60596 var invalidSources = [{
60599 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
60602 var validation = function checkIncompatibleSource(entity) {
60603 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
60604 if (!entitySources) return [];
60606 invalidSources.forEach(function (invalidSource) {
60607 var hasInvalidSource = entitySources.some(function (source) {
60608 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
60609 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
60612 if (!hasInvalidSource) return;
60613 issues.push(new validationIssue({
60615 severity: 'warning',
60616 message: function message(context) {
60617 var entity = context.hasEntity(this.entityIds[0]);
60618 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
60619 feature: utilDisplayLabel(entity, context.graph())
60622 reference: getReference(invalidSource.id),
60623 entityIds: [entity.id],
60624 dynamicFixes: function dynamicFixes() {
60625 return [new validationIssueFix({
60626 title: _t.html('issues.fix.remove_proprietary_data.title')
60633 function getReference(id) {
60634 return function showReference(selection) {
60635 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
60640 validation.type = type;
60644 function validationMaprules() {
60645 var type = 'maprules';
60647 var validation = function checkMaprules(entity, graph) {
60648 if (!services.maprules) return [];
60649 var rules = services.maprules.validationRules();
60652 for (var i = 0; i < rules.length; i++) {
60653 var rule = rules[i];
60654 rule.findIssues(entity, graph, issues);
60660 validation.type = type;
60664 function validationMismatchedGeometry() {
60665 var type = 'mismatched_geometry';
60667 function tagSuggestingLineIsArea(entity) {
60668 if (entity.type !== 'way' || entity.isClosed()) return null;
60669 var tagSuggestingArea = entity.tagSuggestingArea();
60671 if (!tagSuggestingArea) {
60675 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
60676 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
60678 if (asLine && asArea && asLine === asArea) {
60679 // these tags also allow lines and making this an area wouldn't matter
60683 return tagSuggestingArea;
60686 function makeConnectEndpointsFixOnClick(way, graph) {
60687 // must have at least three nodes to close this automatically
60688 if (way.nodes.length < 3) return null;
60689 var nodes = graph.childNodes(way),
60691 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
60693 if (firstToLastDistanceMeters < 0.75) {
60694 testNodes = nodes.slice(); // shallow copy
60697 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
60699 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
60700 return function (context) {
60701 var way = context.entity(this.issue.entityIds[0]);
60702 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
60705 } // if the points were not merged, attempt to close the way
60708 testNodes = nodes.slice(); // shallow copy
60710 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
60712 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
60713 return function (context) {
60714 var wayId = this.issue.entityIds[0];
60715 var way = context.entity(wayId);
60716 var nodeId = way.nodes[0];
60717 var index = way.nodes.length;
60718 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
60723 function lineTaggedAsAreaIssue(entity) {
60724 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
60725 if (!tagSuggestingArea) return null;
60726 return new validationIssue({
60728 subtype: 'area_as_line',
60729 severity: 'warning',
60730 message: function message(context) {
60731 var entity = context.hasEntity(this.entityIds[0]);
60732 return entity ? _t.html('issues.tag_suggests_area.message', {
60733 feature: utilDisplayLabel(entity, 'area'),
60735 tags: tagSuggestingArea
60739 reference: showReference,
60740 entityIds: [entity.id],
60741 hash: JSON.stringify(tagSuggestingArea),
60742 dynamicFixes: function dynamicFixes(context) {
60744 var entity = context.entity(this.entityIds[0]);
60745 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
60746 fixes.push(new validationIssueFix({
60747 title: _t.html('issues.fix.connect_endpoints.title'),
60748 onClick: connectEndsOnClick
60750 fixes.push(new validationIssueFix({
60751 icon: 'iD-operation-delete',
60752 title: _t.html('issues.fix.remove_tag.title'),
60753 onClick: function onClick(context) {
60754 var entityId = this.issue.entityIds[0];
60755 var entity = context.entity(entityId);
60756 var tags = Object.assign({}, entity.tags); // shallow copy
60758 for (var key in tagSuggestingArea) {
60762 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
60769 function showReference(selection) {
60770 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
60774 function vertexTaggedAsPointIssue(entity, graph) {
60775 // we only care about nodes
60776 if (entity.type !== 'node') return null; // ignore tagless points
60778 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
60780 if (entity.isOnAddressLine(graph)) return null;
60781 var geometry = entity.geometry(graph);
60782 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
60784 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
60785 return new validationIssue({
60787 subtype: 'vertex_as_point',
60788 severity: 'warning',
60789 message: function message(context) {
60790 var entity = context.hasEntity(this.entityIds[0]);
60791 return entity ? _t.html('issues.vertex_as_point.message', {
60792 feature: utilDisplayLabel(entity, 'vertex')
60795 reference: function showReference(selection) {
60796 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
60798 entityIds: [entity.id]
60800 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
60801 return new validationIssue({
60803 subtype: 'point_as_vertex',
60804 severity: 'warning',
60805 message: function message(context) {
60806 var entity = context.hasEntity(this.entityIds[0]);
60807 return entity ? _t.html('issues.point_as_vertex.message', {
60808 feature: utilDisplayLabel(entity, 'point')
60811 reference: function showReference(selection) {
60812 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
60814 entityIds: [entity.id],
60815 dynamicFixes: function dynamicFixes(context) {
60816 var entityId = this.entityIds[0];
60817 var extractOnClick = null;
60819 if (!context.hasHiddenConnections(entityId)) {
60820 extractOnClick = function extractOnClick(context) {
60821 var entityId = this.issue.entityIds[0];
60822 var action = actionExtract(entityId);
60823 context.perform(action, _t('operations.extract.annotation', {
60825 })); // re-enter mode to trigger updates
60827 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
60831 return [new validationIssueFix({
60832 icon: 'iD-operation-extract',
60833 title: _t.html('issues.fix.extract_point.title'),
60834 onClick: extractOnClick
60843 function unclosedMultipolygonPartIssues(entity, graph) {
60844 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
60845 !entity.isComplete(graph)) return null;
60846 var sequences = osmJoinWays(entity.members, graph);
60849 for (var i in sequences) {
60850 var sequence = sequences[i];
60851 if (!sequence.nodes) continue;
60852 var firstNode = sequence.nodes[0];
60853 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
60855 if (firstNode === lastNode) continue;
60856 var issue = new validationIssue({
60858 subtype: 'unclosed_multipolygon_part',
60859 severity: 'warning',
60860 message: function message(context) {
60861 var entity = context.hasEntity(this.entityIds[0]);
60862 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
60863 feature: utilDisplayLabel(entity, context.graph())
60866 reference: showReference,
60867 loc: sequence.nodes[0].loc,
60868 entityIds: [entity.id],
60869 hash: sequence.map(function (way) {
60873 issues.push(issue);
60878 function showReference(selection) {
60879 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
60883 var validation = function checkMismatchedGeometry(entity, graph) {
60884 var issues = [vertexTaggedAsPointIssue(entity, graph), lineTaggedAsAreaIssue(entity)];
60885 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
60886 return issues.filter(Boolean);
60889 validation.type = type;
60893 function validationMissingRole() {
60894 var type = 'missing_role';
60896 var validation = function checkMissingRole(entity, graph) {
60899 if (entity.type === 'way') {
60900 graph.parentRelations(entity).forEach(function (relation) {
60901 if (!relation.isMultipolygon()) return;
60902 var member = relation.memberById(entity.id);
60904 if (member && isMissingRole(member)) {
60905 issues.push(makeIssue(entity, relation, member));
60908 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
60909 entity.indexedMembers().forEach(function (member) {
60910 var way = graph.hasEntity(member.id);
60912 if (way && isMissingRole(member)) {
60913 issues.push(makeIssue(way, entity, member));
60921 function isMissingRole(member) {
60922 return !member.role || !member.role.trim().length;
60925 function makeIssue(way, relation, member) {
60926 return new validationIssue({
60928 severity: 'warning',
60929 message: function message(context) {
60930 var member = context.hasEntity(this.entityIds[1]),
60931 relation = context.hasEntity(this.entityIds[0]);
60932 return member && relation ? _t.html('issues.missing_role.message', {
60933 member: utilDisplayLabel(member, context.graph()),
60934 relation: utilDisplayLabel(relation, context.graph())
60937 reference: showReference,
60938 entityIds: [relation.id, way.id],
60942 hash: member.index.toString(),
60943 dynamicFixes: function dynamicFixes() {
60944 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
60945 icon: 'iD-operation-delete',
60946 title: _t.html('issues.fix.remove_from_relation.title'),
60947 onClick: function onClick(context) {
60948 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
60956 function showReference(selection) {
60957 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
60961 function makeAddRoleFix(role) {
60962 return new validationIssueFix({
60963 title: _t.html('issues.fix.set_as_' + role + '.title'),
60964 onClick: function onClick(context) {
60965 var oldMember = this.issue.data.member;
60967 id: this.issue.entityIds[1],
60968 type: oldMember.type,
60971 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
60978 validation.type = type;
60982 function validationMissingTag(context) {
60983 var type = 'missing_tag';
60985 function hasDescriptiveTags(entity, graph) {
60986 var keys = Object.keys(entity.tags).filter(function (k) {
60987 if (k === 'area' || k === 'name') {
60990 return osmIsInterestingTag(k);
60994 if (entity.type === 'relation' && keys.length === 1 && entity.tags.type === 'multipolygon') {
60995 // this relation's only interesting tag just says its a multipolygon,
60996 // which is not descriptive enough
60997 // It's okay for a simple multipolygon to have no descriptive tags
60998 // if its outer way has them (old model, see `outdated_tags.js`)
60999 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
61002 return keys.length > 0;
61005 function isUnknownRoad(entity) {
61006 return entity.type === 'way' && entity.tags.highway === 'road';
61009 function isUntypedRelation(entity) {
61010 return entity.type === 'relation' && !entity.tags.type;
61013 var validation = function checkMissingTag(entity, graph) {
61015 var osm = context.connection();
61016 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
61018 if (!isUnloadedNode && // allow untagged nodes that are part of ways
61019 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
61020 !entity.hasParentRelations(graph)) {
61021 if (Object.keys(entity.tags).length === 0) {
61023 } else if (!hasDescriptiveTags(entity, graph)) {
61024 subtype = 'descriptive';
61025 } else if (isUntypedRelation(entity)) {
61026 subtype = 'relation_type';
61028 } // flag an unknown road even if it's a member of a relation
61031 if (!subtype && isUnknownRoad(entity)) {
61032 subtype = 'highway_classification';
61035 if (!subtype) return [];
61036 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
61037 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
61039 var canDelete = entity.version === undefined || entity.v !== undefined;
61040 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
61041 return [new validationIssue({
61044 severity: severity,
61045 message: function message(context) {
61046 var entity = context.hasEntity(this.entityIds[0]);
61047 return entity ? _t.html('issues.' + messageID + '.message', {
61048 feature: utilDisplayLabel(entity, context.graph())
61051 reference: showReference,
61052 entityIds: [entity.id],
61053 dynamicFixes: function dynamicFixes(context) {
61055 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
61056 fixes.push(new validationIssueFix({
61057 icon: 'iD-icon-search',
61058 title: _t.html('issues.fix.' + selectFixType + '.title'),
61059 onClick: function onClick(context) {
61060 context.ui().sidebar.showPresetList();
61064 var id = this.entityIds[0];
61065 var operation = operationDelete(context, [id]);
61066 var disabledReasonID = operation.disabled();
61068 if (!disabledReasonID) {
61069 deleteOnClick = function deleteOnClick(context) {
61070 var id = this.issue.entityIds[0];
61071 var operation = operationDelete(context, [id]);
61073 if (!operation.disabled()) {
61079 fixes.push(new validationIssueFix({
61080 icon: 'iD-operation-delete',
61081 title: _t.html('issues.fix.delete_feature.title'),
61082 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
61083 onClick: deleteOnClick
61089 function showReference(selection) {
61090 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
61094 validation.type = type;
61098 var simplify = function simplify(str) {
61099 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());
61103 // kvnd: "amenity/fast_food|Thaï Express~(North America)",
61104 // kvn: "amenity/fast_food|Thaï Express",
61105 // kv: "amenity/fast_food",
61108 // n: "Thaï Express",
61109 // d: "(North America)",
61110 // nsimple: "thaiexpress",
61111 // kvnnsimple: "amenity/fast_food|thaiexpress"
61114 var to_parts = function to_parts(kvnd) {
61117 var kvndparts = kvnd.split('~', 2);
61118 if (kvndparts.length > 1) parts.d = kvndparts[1];
61119 parts.kvn = kvndparts[0];
61120 var kvnparts = parts.kvn.split('|', 2);
61121 if (kvnparts.length > 1) parts.n = kvnparts[1];
61122 parts.kv = kvnparts[0];
61123 var kvparts = parts.kv.split('/', 2);
61124 parts.k = kvparts[0];
61125 parts.v = kvparts[1];
61126 parts.nsimple = simplify(parts.n);
61127 parts.kvnsimple = parts.kv + '|' + parts.nsimple;
61131 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"]};
61133 matchGroups: matchGroups
61136 var matchGroups$1 = require$$0.matchGroups;
61138 var matcher$1 = function matcher() {
61139 var _warnings = []; // array of match conflict pairs
61141 var _ambiguous = {};
61142 var _matchIndex = {};
61143 var matcher = {}; // Create an index of all the keys/simplenames for fast matching
61145 matcher.buildMatchIndex = function (brands) {
61146 // two passes - once for primary names, once for secondary/alternate names
61147 Object.keys(brands).forEach(function (kvnd) {
61148 return insertNames(kvnd, 'primary');
61150 Object.keys(brands).forEach(function (kvnd) {
61151 return insertNames(kvnd, 'secondary');
61154 function insertNames(kvnd, which) {
61155 var obj = brands[kvnd];
61156 var parts = to_parts(kvnd); // Exit early for ambiguous names in the second pass.
61157 // They were collected in the first pass and we don't gather alt names for them.
61159 if (which === 'secondary' && parts.d) return;
61161 if (obj.countryCodes) {
61162 parts.countryCodes = obj.countryCodes.slice(); // copy
61165 var nomatches = obj.nomatch || [];
61167 if (nomatches.some(function (s) {
61170 console.log("WARNING match/nomatch conflict for ".concat(kvnd));
61174 var match_kv = [parts.kv].concat(obj.matchTags || []).concat(["".concat(parts.k, "/yes"), "building/yes"]) // #3454 - match some generic tags
61175 .map(function (s) {
61176 return s.toLowerCase();
61178 var match_nsimple = [];
61180 if (which === 'primary') {
61181 match_nsimple = [parts.n].concat(obj.matchNames || []).concat(obj.tags.official_name || []) // #2732 - match alternate names
61183 } else if (which === 'secondary') {
61184 match_nsimple = [].concat(obj.tags.alt_name || []) // #2732 - match alternate names
61185 .concat(obj.tags.short_name || []) // #2732 - match alternate names
61189 if (!match_nsimple.length) return; // nothing to do
61191 match_kv.forEach(function (kv) {
61192 match_nsimple.forEach(function (nsimple) {
61194 // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
61195 // FIXME: Name collisions will overwrite the initial entry (ok for now)
61196 if (!_ambiguous[kv]) _ambiguous[kv] = {};
61197 _ambiguous[kv][nsimple] = parts;
61199 // Names we mostly expect to be unique..
61200 if (!_matchIndex[kv]) _matchIndex[kv] = {};
61201 var m = _matchIndex[kv][nsimple];
61204 // There already is a match for this name, skip it
61205 // Warn if we detect collisions in a primary name.
61206 // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
61207 if (which === 'primary' && !/\/yes$/.test(kv)) {
61208 _warnings.push([m.kvnd, "".concat(kvnd, " (").concat(kv, "/").concat(nsimple, ")")]);
61211 _matchIndex[kv][nsimple] = parts; // insert
61217 }; // pass a `key`, `value`, `name` and return the best match,
61218 // `countryCode` optional (if supplied, must match that too)
61221 matcher.matchKVN = function (key, value, name, countryCode) {
61222 return matcher.matchParts(to_parts("".concat(key, "/").concat(value, "|").concat(name)), countryCode);
61223 }; // pass a parts object and return the best match,
61224 // `countryCode` optional (if supplied, must match that too)
61227 matcher.matchParts = function (parts, countryCode) {
61229 var inGroup = false; // fixme: we currently return a single match for ambiguous
61231 match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
61232 if (match && matchesCountryCode(match)) return match; // try to return an exact match
61234 match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
61235 if (match && matchesCountryCode(match)) return match; // look in match groups
61237 for (var mg in matchGroups$1) {
61238 var matchGroup = matchGroups$1[mg];
61242 for (var i = 0; i < matchGroup.length; i++) {
61243 var otherkv = matchGroup[i].toLowerCase();
61246 inGroup = otherkv === parts.kv;
61250 // fixme: we currently return a single match for ambiguous
61251 match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
61255 match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
61258 if (match && !matchesCountryCode(match)) {
61262 if (inGroup && match) {
61270 function matchesCountryCode(match) {
61271 if (!countryCode) return true;
61272 if (!match.countryCodes) return true;
61273 return match.countryCodes.indexOf(countryCode) !== -1;
61277 matcher.getWarnings = function () {
61284 var fromCharCode = String.fromCharCode;
61285 var nativeFromCodePoint = String.fromCodePoint;
61287 // length should be 1, old FF problem
61288 var INCORRECT_LENGTH = !!nativeFromCodePoint && nativeFromCodePoint.length != 1;
61290 // `String.fromCodePoint` method
61291 // https://tc39.es/ecma262/#sec-string.fromcodepoint
61292 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
61293 // eslint-disable-next-line no-unused-vars -- required for `.length`
61294 fromCodePoint: function fromCodePoint(x) {
61296 var length = arguments.length;
61299 while (length > i) {
61300 code = +arguments[i++];
61301 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
61302 elements.push(code < 0x10000
61303 ? fromCharCode(code)
61304 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
61306 } return elements.join('');
61310 var quickselect$2 = createCommonjsModule(function (module, exports) {
61311 (function (global, factory) {
61312 module.exports = factory() ;
61313 })(commonjsGlobal, function () {
61315 function quickselect(arr, k, left, right, compare) {
61316 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
61319 function quickselectStep(arr, k, left, right, compare) {
61320 while (right > left) {
61321 if (right - left > 600) {
61322 var n = right - left + 1;
61323 var m = k - left + 1;
61324 var z = Math.log(n);
61325 var s = 0.5 * Math.exp(2 * z / 3);
61326 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
61327 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
61328 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
61329 quickselectStep(arr, k, newLeft, newRight, compare);
61335 swap(arr, left, k);
61336 if (compare(arr[right], t) > 0) swap(arr, left, right);
61343 while (compare(arr[i], t) < 0) {
61347 while (compare(arr[j], t) > 0) {
61352 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
61354 swap(arr, j, right);
61356 if (j <= k) left = j + 1;
61357 if (k <= j) right = j - 1;
61361 function swap(arr, i, j) {
61367 function defaultCompare(a, b) {
61368 return a < b ? -1 : a > b ? 1 : 0;
61371 return quickselect;
61375 var rbush_1 = rbush;
61376 var _default$1 = rbush;
61378 function rbush(maxEntries, format) {
61379 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
61381 this._maxEntries = Math.max(4, maxEntries || 9);
61382 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
61385 this._initFormat(format);
61391 rbush.prototype = {
61392 all: function all() {
61393 return this._all(this.data, []);
61395 search: function search(bbox) {
61396 var node = this.data,
61398 toBBox = this.toBBox;
61399 if (!intersects$1(bbox, node)) return result;
61400 var nodesToSearch = [],
61407 for (i = 0, len = node.children.length; i < len; i++) {
61408 child = node.children[i];
61409 childBBox = node.leaf ? toBBox(child) : child;
61411 if (intersects$1(bbox, childBBox)) {
61412 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
61416 node = nodesToSearch.pop();
61421 collides: function collides(bbox) {
61422 var node = this.data,
61423 toBBox = this.toBBox;
61424 if (!intersects$1(bbox, node)) return false;
61425 var nodesToSearch = [],
61432 for (i = 0, len = node.children.length; i < len; i++) {
61433 child = node.children[i];
61434 childBBox = node.leaf ? toBBox(child) : child;
61436 if (intersects$1(bbox, childBBox)) {
61437 if (node.leaf || contains$1(bbox, childBBox)) return true;
61438 nodesToSearch.push(child);
61442 node = nodesToSearch.pop();
61447 load: function load(data) {
61448 if (!(data && data.length)) return this;
61450 if (data.length < this._minEntries) {
61451 for (var i = 0, len = data.length; i < len; i++) {
61452 this.insert(data[i]);
61456 } // recursively build the tree with the given data from scratch using OMT algorithm
61459 var node = this._build(data.slice(), 0, data.length - 1, 0);
61461 if (!this.data.children.length) {
61462 // save as is if tree is empty
61464 } else if (this.data.height === node.height) {
61465 // split root if trees have the same height
61466 this._splitRoot(this.data, node);
61468 if (this.data.height < node.height) {
61469 // swap trees if inserted one is bigger
61470 var tmpNode = this.data;
61473 } // insert the small tree into the large tree at appropriate level
61476 this._insert(node, this.data.height - node.height - 1, true);
61481 insert: function insert(item) {
61482 if (item) this._insert(item, this.data.height - 1);
61485 clear: function clear() {
61486 this.data = createNode$1([]);
61489 remove: function remove(item, equalsFn) {
61490 if (!item) return this;
61491 var node = this.data,
61492 bbox = this.toBBox(item),
61498 goingUp; // depth-first iterative tree traversal
61500 while (node || path.length) {
61504 parent = path[path.length - 1];
61510 // check current node
61511 index = findItem$1(item, node.children, equalsFn);
61513 if (index !== -1) {
61514 // item found, remove the item and condense tree upwards
61515 node.children.splice(index, 1);
61518 this._condense(path);
61524 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
61530 node = node.children[0];
61531 } else if (parent) {
61534 node = parent.children[i];
61536 } else node = null; // nothing found
61542 toBBox: function toBBox(item) {
61545 compareMinX: compareNodeMinX$1,
61546 compareMinY: compareNodeMinY$1,
61547 toJSON: function toJSON() {
61550 fromJSON: function fromJSON(data) {
61554 _all: function _all(node, result) {
61555 var nodesToSearch = [];
61558 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
61559 node = nodesToSearch.pop();
61564 _build: function _build(items, left, right, height) {
61565 var N = right - left + 1,
61566 M = this._maxEntries,
61570 // reached leaf level; return leaf
61571 node = createNode$1(items.slice(left, right + 1));
61572 calcBBox$1(node, this.toBBox);
61577 // target height of the bulk-loaded tree
61578 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
61580 M = Math.ceil(N / Math.pow(M, height - 1));
61583 node = createNode$1([]);
61585 node.height = height; // split the items into M mostly square tiles
61587 var N2 = Math.ceil(N / M),
61588 N1 = N2 * Math.ceil(Math.sqrt(M)),
61593 multiSelect$1(items, left, right, N1, this.compareMinX);
61595 for (i = left; i <= right; i += N1) {
61596 right2 = Math.min(i + N1 - 1, right);
61597 multiSelect$1(items, i, right2, N2, this.compareMinY);
61599 for (j = i; j <= right2; j += N2) {
61600 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
61602 node.children.push(this._build(items, j, right3, height - 1));
61606 calcBBox$1(node, this.toBBox);
61609 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
61610 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
61614 if (node.leaf || path.length - 1 === level) break;
61615 minArea = minEnlargement = Infinity;
61617 for (i = 0, len = node.children.length; i < len; i++) {
61618 child = node.children[i];
61619 area = bboxArea$1(child);
61620 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
61622 if (enlargement < minEnlargement) {
61623 minEnlargement = enlargement;
61624 minArea = area < minArea ? area : minArea;
61625 targetNode = child;
61626 } else if (enlargement === minEnlargement) {
61627 // otherwise choose one with the smallest area
61628 if (area < minArea) {
61630 targetNode = child;
61635 node = targetNode || node.children[0];
61640 _insert: function _insert(item, level, isNode) {
61641 var toBBox = this.toBBox,
61642 bbox = isNode ? item : toBBox(item),
61643 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
61645 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
61648 node.children.push(item);
61649 extend$3(node, bbox); // split on node overflow; propagate upwards if necessary
61651 while (level >= 0) {
61652 if (insertPath[level].children.length > this._maxEntries) {
61653 this._split(insertPath, level);
61657 } // adjust bboxes along the insertion path
61660 this._adjustParentBBoxes(bbox, insertPath, level);
61662 // split overflowed node into two
61663 _split: function _split(insertPath, level) {
61664 var node = insertPath[level],
61665 M = node.children.length,
61666 m = this._minEntries;
61668 this._chooseSplitAxis(node, m, M);
61670 var splitIndex = this._chooseSplitIndex(node, m, M);
61672 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
61673 newNode.height = node.height;
61674 newNode.leaf = node.leaf;
61675 calcBBox$1(node, this.toBBox);
61676 calcBBox$1(newNode, this.toBBox);
61677 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
61679 _splitRoot: function _splitRoot(node, newNode) {
61681 this.data = createNode$1([node, newNode]);
61682 this.data.height = node.height + 1;
61683 this.data.leaf = false;
61684 calcBBox$1(this.data, this.toBBox);
61686 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
61687 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
61688 minOverlap = minArea = Infinity;
61690 for (i = m; i <= M - m; i++) {
61691 bbox1 = distBBox$1(node, 0, i, this.toBBox);
61692 bbox2 = distBBox$1(node, i, M, this.toBBox);
61693 overlap = intersectionArea$1(bbox1, bbox2);
61694 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
61696 if (overlap < minOverlap) {
61697 minOverlap = overlap;
61699 minArea = area < minArea ? area : minArea;
61700 } else if (overlap === minOverlap) {
61701 // otherwise choose distribution with minimum area
61702 if (area < minArea) {
61711 // sorts node children by the best axis for split
61712 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
61713 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
61714 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
61715 xMargin = this._allDistMargin(node, m, M, compareMinX),
61716 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
61717 // otherwise it's already sorted by minY
61720 if (xMargin < yMargin) node.children.sort(compareMinX);
61722 // total margin of all possible split distributions where each node is at least m full
61723 _allDistMargin: function _allDistMargin(node, m, M, compare) {
61724 node.children.sort(compare);
61725 var toBBox = this.toBBox,
61726 leftBBox = distBBox$1(node, 0, m, toBBox),
61727 rightBBox = distBBox$1(node, M - m, M, toBBox),
61728 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
61732 for (i = m; i < M - m; i++) {
61733 child = node.children[i];
61734 extend$3(leftBBox, node.leaf ? toBBox(child) : child);
61735 margin += bboxMargin$1(leftBBox);
61738 for (i = M - m - 1; i >= m; i--) {
61739 child = node.children[i];
61740 extend$3(rightBBox, node.leaf ? toBBox(child) : child);
61741 margin += bboxMargin$1(rightBBox);
61746 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
61747 // adjust bboxes along the given tree path
61748 for (var i = level; i >= 0; i--) {
61749 extend$3(path[i], bbox);
61752 _condense: function _condense(path) {
61753 // go through the path, removing empty nodes and updating bboxes
61754 for (var i = path.length - 1, siblings; i >= 0; i--) {
61755 if (path[i].children.length === 0) {
61757 siblings = path[i - 1].children;
61758 siblings.splice(siblings.indexOf(path[i]), 1);
61759 } else this.clear();
61760 } else calcBBox$1(path[i], this.toBBox);
61763 _initFormat: function _initFormat(format) {
61764 // data format (minX, minY, maxX, maxY accessors)
61765 // uses eval-type function compilation instead of just accepting a toBBox function
61766 // because the algorithms are very sensitive to sorting functions performance,
61767 // so they should be dead simple and without inner calls
61768 var compareArr = ['return a', ' - b', ';'];
61769 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
61770 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
61771 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
61775 function findItem$1(item, items, equalsFn) {
61776 if (!equalsFn) return items.indexOf(item);
61778 for (var i = 0; i < items.length; i++) {
61779 if (equalsFn(item, items[i])) return i;
61783 } // calculate node's bbox from bboxes of its children
61786 function calcBBox$1(node, toBBox) {
61787 distBBox$1(node, 0, node.children.length, toBBox, node);
61788 } // min bounding rectangle of node children from k to p-1
61791 function distBBox$1(node, k, p, toBBox, destNode) {
61792 if (!destNode) destNode = createNode$1(null);
61793 destNode.minX = Infinity;
61794 destNode.minY = Infinity;
61795 destNode.maxX = -Infinity;
61796 destNode.maxY = -Infinity;
61798 for (var i = k, child; i < p; i++) {
61799 child = node.children[i];
61800 extend$3(destNode, node.leaf ? toBBox(child) : child);
61806 function extend$3(a, b) {
61807 a.minX = Math.min(a.minX, b.minX);
61808 a.minY = Math.min(a.minY, b.minY);
61809 a.maxX = Math.max(a.maxX, b.maxX);
61810 a.maxY = Math.max(a.maxY, b.maxY);
61814 function compareNodeMinX$1(a, b) {
61815 return a.minX - b.minX;
61818 function compareNodeMinY$1(a, b) {
61819 return a.minY - b.minY;
61822 function bboxArea$1(a) {
61823 return (a.maxX - a.minX) * (a.maxY - a.minY);
61826 function bboxMargin$1(a) {
61827 return a.maxX - a.minX + (a.maxY - a.minY);
61830 function enlargedArea$1(a, b) {
61831 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));
61834 function intersectionArea$1(a, b) {
61835 var minX = Math.max(a.minX, b.minX),
61836 minY = Math.max(a.minY, b.minY),
61837 maxX = Math.min(a.maxX, b.maxX),
61838 maxY = Math.min(a.maxY, b.maxY);
61839 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
61842 function contains$1(a, b) {
61843 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
61846 function intersects$1(a, b) {
61847 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
61850 function createNode$1(children) {
61852 children: children,
61860 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
61861 // combines selection algorithm with binary divide & conquer approach
61864 function multiSelect$1(arr, left, right, n, compare) {
61865 var stack = [left, right],
61868 while (stack.length) {
61869 right = stack.pop();
61870 left = stack.pop();
61871 if (right - left <= n) continue;
61872 mid = left + Math.ceil((right - left) / n / 2) * n;
61873 quickselect$2(arr, mid, left, right, compare);
61874 stack.push(left, mid, mid, right);
61877 rbush_1["default"] = _default$1;
61879 var lineclip_1 = lineclip$1;
61880 lineclip$1.polyline = lineclip$1;
61881 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
61882 // handle polylines rather than just segments
61884 function lineclip$1(points, bbox, result) {
61885 var len = points.length,
61886 codeA = bitCode$1(points[0], bbox),
61893 if (!result) result = [];
61895 for (i = 1; i < len; i++) {
61898 codeB = lastCode = bitCode$1(b, bbox);
61901 if (!(codeA | codeB)) {
61905 if (codeB !== lastCode) {
61906 // segment went outside
61910 // start a new line
61914 } else if (i === len - 1) {
61919 } else if (codeA & codeB) {
61922 } else if (codeA) {
61923 // a outside, intersect with clip edge
61924 a = intersect$1(a, b, codeA, bbox);
61925 codeA = bitCode$1(a, bbox);
61928 b = intersect$1(a, b, codeB, bbox);
61929 codeB = bitCode$1(b, bbox);
61936 if (part.length) result.push(part);
61938 } // Sutherland-Hodgeman polygon clipping algorithm
61941 function polygonclip$1(points, bbox) {
61942 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
61944 for (edge = 1; edge <= 8; edge *= 2) {
61946 prev = points[points.length - 1];
61947 prevInside = !(bitCode$1(prev, bbox) & edge);
61949 for (i = 0; i < points.length; i++) {
61951 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
61953 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
61954 if (inside) result.push(p); // add a point if it's inside
61957 prevInside = inside;
61961 if (!points.length) break;
61965 } // intersect a segment against one of the 4 lines that make up the bbox
61968 function intersect$1(a, b, edge, bbox) {
61969 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
61970 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
61971 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
61972 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
61974 } // bit code reflects the point position relative to the bbox:
61976 // top 1001 1000 1010
61977 // mid 0001 0000 0010
61978 // bottom 0101 0100 0110
61981 function bitCode$1(p, bbox) {
61983 if (p[0] < bbox[0]) code |= 1; // left
61984 else if (p[0] > bbox[2]) code |= 2; // right
61986 if (p[1] < bbox[1]) code |= 4; // bottom
61987 else if (p[1] > bbox[3]) code |= 8; // top
61992 var whichPolygon_1 = whichPolygon;
61994 function whichPolygon(data) {
61997 for (var i = 0; i < data.features.length; i++) {
61998 var feature = data.features[i];
61999 var coords = feature.geometry.coordinates;
62001 if (feature.geometry.type === 'Polygon') {
62002 bboxes.push(treeItem(coords, feature.properties));
62003 } else if (feature.geometry.type === 'MultiPolygon') {
62004 for (var j = 0; j < coords.length; j++) {
62005 bboxes.push(treeItem(coords[j], feature.properties));
62010 var tree = rbush_1().load(bboxes);
62012 function query(p, multi) {
62014 result = tree.search({
62021 for (var i = 0; i < result.length; i++) {
62022 if (insidePolygon(result[i].coords, p)) {
62023 if (multi) output.push(result[i].props);else return result[i].props;
62027 return multi && output.length ? output : null;
62032 query.bbox = function queryBBox(bbox) {
62034 var result = tree.search({
62041 for (var i = 0; i < result.length; i++) {
62042 if (polygonIntersectsBBox(result[i].coords, bbox)) {
62043 output.push(result[i].props);
62053 function polygonIntersectsBBox(polygon, bbox) {
62054 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
62055 if (insidePolygon(polygon, bboxCenter)) return true;
62057 for (var i = 0; i < polygon.length; i++) {
62058 if (lineclip_1(polygon[i], bbox).length > 0) return true;
62062 } // ray casting algorithm for detecting if point is in polygon
62065 function insidePolygon(rings, p) {
62066 var inside = false;
62068 for (var i = 0, len = rings.length; i < len; i++) {
62069 var ring = rings[i];
62071 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
62072 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
62079 function rayIntersect(p, p1, p2) {
62080 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];
62083 function treeItem(coords, props) {
62093 for (var i = 0; i < coords[0].length; i++) {
62094 var p = coords[0][i];
62095 item.minX = Math.min(item.minX, p[0]);
62096 item.minY = Math.min(item.minY, p[1]);
62097 item.maxX = Math.max(item.maxX, p[0]);
62098 item.maxY = Math.max(item.maxY, p[1]);
62104 var type = "FeatureCollection";
62105 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]]]]}}];
62111 var borders = rawBorders;
62112 var whichPolygonGetter = {};
62113 var featuresByCode = {};
62114 var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
62115 var levels = ['subterritory', 'territory', 'country', 'intermediateRegion', 'subregion', 'region', 'union', 'world'];
62116 loadDerivedDataAndCaches(borders);
62118 function loadDerivedDataAndCaches(borders) {
62119 var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
62120 var geometryFeatures = [];
62122 for (var i in borders.features) {
62123 var _feature = borders.features[i];
62124 _feature.properties.id = _feature.properties.iso1A2 || _feature.properties.m49;
62126 loadIsoStatus(_feature);
62127 loadLevel(_feature);
62128 loadGroups(_feature);
62129 loadRoadSpeedUnit(_feature);
62130 loadDriveSide(_feature);
62131 loadFlag(_feature);
62132 cacheFeatureByIDs(_feature);
62133 if (_feature.geometry) geometryFeatures.push(_feature);
62136 for (var _i in borders.features) {
62137 var _feature2 = borders.features[_i];
62139 _feature2.properties.groups.sort(function (groupID1, groupID2) {
62140 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
62143 loadMembersForGroupsOf(_feature2);
62146 var geometryOnlyCollection = {
62147 type: 'RegionFeatureCollection',
62148 features: geometryFeatures
62150 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
62152 function loadGroups(feature) {
62153 var props = feature.properties;
62155 if (!props.groups) {
62159 if (props.country) {
62160 props.groups.push(props.country);
62163 if (props.m49 !== '001') {
62164 props.groups.push('001');
62168 function loadM49(feature) {
62169 var props = feature.properties;
62171 if (!props.m49 && props.iso1N3) {
62172 props.m49 = props.iso1N3;
62176 function loadIsoStatus(feature) {
62177 var props = feature.properties;
62179 if (!props.isoStatus && props.iso1A2) {
62180 props.isoStatus = 'official';
62184 function loadLevel(feature) {
62185 var props = feature.properties;
62186 if (props.level) return;
62188 if (!props.country) {
62189 props.level = 'country';
62190 } else if (props.isoStatus === 'official') {
62191 props.level = 'territory';
62193 props.level = 'subterritory';
62197 function loadRoadSpeedUnit(feature) {
62198 var props = feature.properties;
62200 if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
62201 props.roadSpeedUnit = 'km/h';
62205 function loadDriveSide(feature) {
62206 var props = feature.properties;
62208 if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
62209 props.driveSide = 'right';
62213 function loadFlag(feature) {
62214 if (!feature.properties.iso1A2) return;
62215 var flag = feature.properties.iso1A2.replace(/./g, function (_char) {
62216 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
62218 feature.properties.emojiFlag = flag;
62221 function loadMembersForGroupsOf(feature) {
62222 var featureID = feature.properties.id;
62223 var standardizedGroupIDs = [];
62225 for (var j in feature.properties.groups) {
62226 var groupID = feature.properties.groups[j];
62227 var groupFeature = featuresByCode[groupID];
62228 standardizedGroupIDs.push(groupFeature.properties.id);
62230 if (groupFeature.properties.members) {
62231 groupFeature.properties.members.push(featureID);
62233 groupFeature.properties.members = [featureID];
62237 feature.properties.groups = standardizedGroupIDs;
62240 function cacheFeatureByIDs(feature) {
62241 for (var k in identifierProps) {
62242 var prop = identifierProps[k];
62243 var id = prop && feature.properties[prop];
62246 id = id.replace(idFilterRegex, '').toUpperCase();
62247 featuresByCode[id] = feature;
62251 if (feature.properties.aliases) {
62252 for (var j in feature.properties.aliases) {
62253 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
62254 featuresByCode[alias] = feature;
62260 function locArray(loc) {
62261 if (Array.isArray(loc)) {
62263 } else if (loc.coordinates) {
62264 return loc.coordinates;
62267 return loc.geometry.coordinates;
62270 function smallestFeature(loc) {
62271 var query = locArray(loc);
62272 var featureProperties = whichPolygonGetter(query);
62273 if (!featureProperties) return null;
62274 return featuresByCode[featureProperties.id];
62277 function countryFeature(loc) {
62278 var feature = smallestFeature(loc);
62279 if (!feature) return null;
62280 var countryCode = feature.properties.country || feature.properties.iso1A2;
62281 return featuresByCode[countryCode];
62284 function featureForLoc(loc, opts) {
62285 if (opts && opts.level && opts.level !== 'country') {
62286 var features = featuresContaining(loc);
62287 var targetLevel = opts.level;
62288 var targetLevelIndex = levels.indexOf(targetLevel);
62289 if (targetLevelIndex === -1) return null;
62291 for (var i in features) {
62292 var _feature3 = features[i];
62294 if (_feature3.properties.level === targetLevel || levels.indexOf(_feature3.properties.level) > targetLevelIndex) {
62302 return countryFeature(loc);
62305 function featureForID(id) {
62308 if (typeof id === 'number') {
62309 stringID = id.toString();
62311 if (stringID.length === 1) {
62312 stringID = '00' + stringID;
62313 } else if (stringID.length === 2) {
62314 stringID = '0' + stringID;
62317 stringID = id.replace(idFilterRegex, '').toUpperCase();
62320 return featuresByCode[stringID] || null;
62323 function smallestOrMatchingFeature(query) {
62324 if (_typeof(query) === 'object') {
62325 return smallestFeature(query);
62328 return featureForID(query);
62331 function feature$1(query, opts) {
62332 if (_typeof(query) === 'object') {
62333 return featureForLoc(query, opts);
62336 return featureForID(query);
62338 function iso1A2Code(query, opts) {
62339 var match = feature$1(query, opts);
62340 if (!match) return null;
62341 return match.properties.iso1A2 || null;
62343 function featuresContaining(query, strict) {
62344 var feature = smallestOrMatchingFeature(query);
62345 if (!feature) return [];
62348 if (!strict || _typeof(query) === 'object') {
62349 features.push(feature);
62352 var properties = feature.properties;
62354 for (var i in properties.groups) {
62355 var groupID = properties.groups[i];
62356 features.push(featuresByCode[groupID]);
62361 function roadSpeedUnit(query) {
62362 var feature = smallestOrMatchingFeature(query);
62363 return feature && feature.properties.roadSpeedUnit || null;
62366 var _dataDeprecated;
62370 function validationOutdatedTags() {
62371 var type = 'outdated_tags';
62372 var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office']; // A concern here in switching to async data means that `_dataDeprecated`
62373 // and `_nsi` will not be available at first, so the data on early tiles
62374 // may not have tags validated fully.
62375 // initialize deprecated tags array
62377 _mainFileFetcher.get('deprecated').then(function (d) {
62378 return _dataDeprecated = d;
62379 })["catch"](function () {
62382 _mainFileFetcher.get('nsi_brands').then(function (d) {
62385 matcher: matcher$1(),
62388 }; // initialize name-suggestion-index matcher
62390 _nsi.matcher.buildMatchIndex(d.brands); // index all known wikipedia and wikidata tags
62393 Object.keys(d.brands).forEach(function (kvnd) {
62394 var brand = d.brands[kvnd];
62395 var wd = brand.tags['brand:wikidata'];
62396 var wp = brand.tags['brand:wikipedia'];
62399 _nsi.wikidata[wd] = kvnd;
62403 _nsi.wikipedia[wp] = kvnd;
62407 })["catch"](function () {
62411 function oldTagIssues(entity, graph) {
62412 var oldTags = Object.assign({}, entity.tags); // shallow copy
62414 var preset = _mainPresetIndex.match(entity, graph);
62415 var subtype = 'deprecated_tags';
62416 if (!preset) return []; // upgrade preset..
62418 if (preset.replacement) {
62419 var newPreset = _mainPresetIndex.item(preset.replacement);
62420 graph = actionChangePreset(entity.id, preset, newPreset, true
62421 /* skip field defaults */
62423 entity = graph.entity(entity.id);
62424 preset = newPreset;
62425 } // upgrade tags..
62428 if (_dataDeprecated) {
62429 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
62431 if (deprecatedTags.length) {
62432 deprecatedTags.forEach(function (tag) {
62433 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
62435 entity = graph.entity(entity.id);
62437 } // add missing addTags..
62440 var newTags = Object.assign({}, entity.tags); // shallow copy
62442 if (preset.tags !== preset.addTags) {
62443 Object.keys(preset.addTags).forEach(function (k) {
62445 if (preset.addTags[k] === '*') {
62446 newTags[k] = 'yes';
62448 newTags[k] = preset.addTags[k];
62455 // Do `wikidata` or `wikipedia` identify this entity as a brand? #6416
62456 // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
62459 if (newTags.wikidata) {
62460 // try matching `wikidata`
62461 isBrand = _nsi.wikidata[newTags.wikidata];
62464 if (!isBrand && newTags.wikipedia) {
62465 // fallback to `wikipedia`
62466 isBrand = _nsi.wikipedia[newTags.wikipedia];
62469 if (isBrand && !newTags.office) {
62470 // but avoid doing this for corporate offices
62471 if (newTags.wikidata) {
62472 newTags['brand:wikidata'] = newTags.wikidata;
62473 delete newTags.wikidata;
62476 if (newTags.wikipedia) {
62477 newTags['brand:wikipedia'] = newTags.wikipedia;
62478 delete newTags.wikipedia;
62479 } // I considered setting `name` and other tags here, but they aren't unique per wikidata
62480 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
62481 // So users will really need to use a preset or assign `name` themselves.
62483 } // try key/value|name match against name-suggestion-index
62486 if (newTags.name) {
62487 for (var i = 0; i < nsiKeys.length; i++) {
62488 var k = nsiKeys[i];
62489 if (!newTags[k]) continue;
62490 var center = entity.extent(graph).center();
62491 var countryCode = iso1A2Code(center);
62493 var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
62495 if (!match) continue; // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
62497 if (match.d) continue;
62498 var brand = _nsi.brands[match.kvnd];
62500 if (brand && brand.tags['brand:wikidata'] && brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
62501 subtype = 'noncanonical_brand';
62502 var keepTags = ['takeaway'].reduce(function (acc, k) {
62504 acc[k] = newTags[k];
62509 nsiKeys.forEach(function (k) {
62510 return delete newTags[k];
62512 Object.assign(newTags, brand.tags, keepTags);
62517 } // determine diff
62520 var tagDiff = utilTagDiff(oldTags, newTags);
62521 if (!tagDiff.length) return [];
62522 var isOnlyAddingTags = tagDiff.every(function (d) {
62523 return d.type === '+';
62527 if (subtype === 'noncanonical_brand') {
62528 prefix = 'noncanonical_brand.';
62529 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
62530 subtype = 'incomplete_tags';
62531 prefix = 'incomplete.';
62532 } // don't allow autofixing brand tags
62535 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
62536 return [new validationIssue({
62539 severity: 'warning',
62540 message: showMessage,
62541 reference: showReference,
62542 entityIds: [entity.id],
62543 hash: JSON.stringify(tagDiff),
62544 dynamicFixes: function dynamicFixes() {
62545 return [new validationIssueFix({
62546 autoArgs: autoArgs,
62547 title: _t.html('issues.fix.upgrade_tags.title'),
62548 onClick: function onClick(context) {
62549 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
62555 function doUpgrade(graph) {
62556 var currEntity = graph.hasEntity(entity.id);
62557 if (!currEntity) return graph;
62558 var newTags = Object.assign({}, currEntity.tags); // shallow copy
62560 tagDiff.forEach(function (diff) {
62561 if (diff.type === '-') {
62562 delete newTags[diff.key];
62563 } else if (diff.type === '+') {
62564 newTags[diff.key] = diff.newVal;
62567 return actionChangeTags(currEntity.id, newTags)(graph);
62570 function showMessage(context) {
62571 var currEntity = context.hasEntity(entity.id);
62572 if (!currEntity) return '';
62573 var messageID = "issues.outdated_tags.".concat(prefix, "message");
62575 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
62576 messageID += '_incomplete';
62579 return _t.html(messageID, {
62580 feature: utilDisplayLabel(currEntity, context.graph())
62584 function showReference(selection) {
62585 var enter = selection.selectAll('.issue-reference').data([0]).enter();
62586 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
62587 enter.append('strong').html(_t.html('issues.suggested'));
62588 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) {
62589 var klass = d.type === '+' ? 'add' : 'remove';
62590 return "tagDiff-cell tagDiff-cell-".concat(klass);
62591 }).html(function (d) {
62597 function oldMultipolygonIssues(entity, graph) {
62598 var multipolygon, outerWay;
62600 if (entity.type === 'relation') {
62601 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
62602 multipolygon = entity;
62603 } else if (entity.type === 'way') {
62604 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
62610 if (!multipolygon || !outerWay) return [];
62611 return [new validationIssue({
62613 subtype: 'old_multipolygon',
62614 severity: 'warning',
62615 message: showMessage,
62616 reference: showReference,
62617 entityIds: [outerWay.id, multipolygon.id],
62618 dynamicFixes: function dynamicFixes() {
62619 return [new validationIssueFix({
62620 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
62621 title: _t.html('issues.fix.move_tags.title'),
62622 onClick: function onClick(context) {
62623 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
62629 function doUpgrade(graph) {
62630 var currMultipolygon = graph.hasEntity(multipolygon.id);
62631 var currOuterWay = graph.hasEntity(outerWay.id);
62632 if (!currMultipolygon || !currOuterWay) return graph;
62633 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
62634 graph = graph.replace(currMultipolygon);
62635 return actionChangeTags(currOuterWay.id, {})(graph);
62638 function showMessage(context) {
62639 var currMultipolygon = context.hasEntity(multipolygon.id);
62640 if (!currMultipolygon) return '';
62641 return _t.html('issues.old_multipolygon.message', {
62642 multipolygon: utilDisplayLabel(currMultipolygon, context.graph())
62646 function showReference(selection) {
62647 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
62651 var validation = function checkOutdatedTags(entity, graph) {
62652 var issues = oldMultipolygonIssues(entity, graph);
62653 if (!issues.length) issues = oldTagIssues(entity, graph);
62657 validation.type = type;
62661 function validationPrivateData() {
62662 var type = 'private_data'; // assume that some buildings are private
62664 var privateBuildingValues = {
62670 semidetached_house: true,
62671 static_caravan: true
62672 }; // but they might be public if they have one of these other tags
62682 }; // these tags may contain personally identifying info
62684 var personalTags = {
62685 'contact:email': true,
62686 'contact:fax': true,
62687 'contact:phone': true,
62693 var validation = function checkPrivateData(entity) {
62694 var tags = entity.tags;
62695 if (!tags.building || !privateBuildingValues[tags.building]) return [];
62698 for (var k in tags) {
62699 if (publicKeys[k]) return []; // probably a public feature
62701 if (!personalTags[k]) {
62702 keepTags[k] = tags[k];
62706 var tagDiff = utilTagDiff(tags, keepTags);
62707 if (!tagDiff.length) return [];
62708 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
62709 return [new validationIssue({
62711 severity: 'warning',
62712 message: showMessage,
62713 reference: showReference,
62714 entityIds: [entity.id],
62715 dynamicFixes: function dynamicFixes() {
62716 return [new validationIssueFix({
62717 icon: 'iD-operation-delete',
62718 title: _t.html('issues.fix.' + fixID + '.title'),
62719 onClick: function onClick(context) {
62720 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
62726 function doUpgrade(graph) {
62727 var currEntity = graph.hasEntity(entity.id);
62728 if (!currEntity) return graph;
62729 var newTags = Object.assign({}, currEntity.tags); // shallow copy
62731 tagDiff.forEach(function (diff) {
62732 if (diff.type === '-') {
62733 delete newTags[diff.key];
62734 } else if (diff.type === '+') {
62735 newTags[diff.key] = diff.newVal;
62738 return actionChangeTags(currEntity.id, newTags)(graph);
62741 function showMessage(context) {
62742 var currEntity = context.hasEntity(this.entityIds[0]);
62743 if (!currEntity) return '';
62744 return _t.html('issues.private_data.contact.message', {
62745 feature: utilDisplayLabel(currEntity, context.graph())
62749 function showReference(selection) {
62750 var enter = selection.selectAll('.issue-reference').data([0]).enter();
62751 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
62752 enter.append('strong').html(_t.html('issues.suggested'));
62753 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) {
62754 var klass = d.type === '+' ? 'add' : 'remove';
62755 return 'tagDiff-cell tagDiff-cell-' + klass;
62756 }).html(function (d) {
62762 validation.type = type;
62766 var _discardNameRegexes = [];
62767 function validationSuspiciousName() {
62768 var type = 'suspicious_name';
62769 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
62770 // be available at first, so the data on early tiles may not have tags validated fully.
62772 _mainFileFetcher.get('nsi_filters').then(function (filters) {
62773 // known list of generic names (e.g. "bar")
62774 _discardNameRegexes = filters.discardNames.map(function (discardName) {
62775 return new RegExp(discardName, 'i');
62777 })["catch"](function () {
62781 function isDiscardedSuggestionName(lowercaseName) {
62782 return _discardNameRegexes.some(function (regex) {
62783 return regex.test(lowercaseName);
62785 } // test if the name is just the key or tag value (e.g. "park")
62788 function nameMatchesRawTag(lowercaseName, tags) {
62789 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
62790 var key = keysToTestForGenericValues[i];
62791 var val = tags[key];
62794 val = val.toLowerCase();
62796 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
62805 function isGenericName(name, tags) {
62806 name = name.toLowerCase();
62807 return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
62810 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
62811 return new validationIssue({
62813 subtype: 'generic_name',
62814 severity: 'warning',
62815 message: function message(context) {
62816 var entity = context.hasEntity(this.entityIds[0]);
62817 if (!entity) return '';
62818 var preset = _mainPresetIndex.match(entity, context.graph());
62819 var langName = langCode && _mainLocalizer.languageName(langCode);
62820 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
62821 feature: preset.name(),
62826 reference: showReference,
62827 entityIds: [entityId],
62828 hash: nameKey + '=' + genericName,
62829 dynamicFixes: function dynamicFixes() {
62830 return [new validationIssueFix({
62831 icon: 'iD-operation-delete',
62832 title: _t.html('issues.fix.remove_the_name.title'),
62833 onClick: function onClick(context) {
62834 var entityId = this.issue.entityIds[0];
62835 var entity = context.entity(entityId);
62836 var tags = Object.assign({}, entity.tags); // shallow copy
62838 delete tags[nameKey];
62839 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
62845 function showReference(selection) {
62846 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
62850 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
62851 return new validationIssue({
62853 subtype: 'not_name',
62854 severity: 'warning',
62855 message: function message(context) {
62856 var entity = context.hasEntity(this.entityIds[0]);
62857 if (!entity) return '';
62858 var preset = _mainPresetIndex.match(entity, context.graph());
62859 var langName = langCode && _mainLocalizer.languageName(langCode);
62860 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
62861 feature: preset.name(),
62862 name: incorrectName,
62866 reference: showReference,
62867 entityIds: [entityId],
62868 hash: nameKey + '=' + incorrectName,
62869 dynamicFixes: function dynamicFixes() {
62870 return [new validationIssueFix({
62871 icon: 'iD-operation-delete',
62872 title: _t.html('issues.fix.remove_the_name.title'),
62873 onClick: function onClick(context) {
62874 var entityId = this.issue.entityIds[0];
62875 var entity = context.entity(entityId);
62876 var tags = Object.assign({}, entity.tags); // shallow copy
62878 delete tags[nameKey];
62879 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
62885 function showReference(selection) {
62886 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
62890 var validation = function checkGenericName(entity) {
62891 // a generic name is okay if it's a known brand or entity
62892 if (entity.hasWikidata()) return [];
62894 var notNames = (entity.tags['not:name'] || '').split(';');
62896 for (var key in entity.tags) {
62897 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
62899 var langCode = m.length >= 2 ? m[1] : null;
62900 var value = entity.tags[key];
62902 if (notNames.length) {
62903 for (var i in notNames) {
62904 var notName = notNames[i];
62906 if (notName && value === notName) {
62907 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
62913 if (isGenericName(value, entity.tags)) {
62914 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
62921 validation.type = type;
62925 function validationUnsquareWay(context) {
62926 var type = 'unsquare_way';
62927 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
62928 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
62930 var epsilon = 0.05;
62931 var nodeThreshold = 10;
62933 function isBuilding(entity, graph) {
62934 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
62935 return entity.tags.building && entity.tags.building !== 'no';
62938 var validation = function checkUnsquareWay(entity, graph) {
62939 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
62941 if (entity.tags.nonsquare === 'yes') return [];
62942 var isClosed = entity.isClosed();
62943 if (!isClosed) return []; // this building has bigger problems
62944 // don't flag ways with lots of nodes since they are likely detail-mapped
62946 var nodes = graph.childNodes(entity).slice(); // shallow copy
62948 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
62949 // ignore if not all nodes are fully downloaded
62951 var osm = services.osm;
62952 if (!osm || nodes.some(function (node) {
62953 return !osm.isDataLoaded(node.loc);
62954 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
62956 var hasConnectedSquarableWays = nodes.some(function (node) {
62957 return graph.parentWays(node).some(function (way) {
62958 if (way.id === entity.id) return false;
62959 if (isBuilding(way, graph)) return true;
62960 return graph.parentRelations(way).some(function (parentRelation) {
62961 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
62965 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
62967 var storedDegreeThreshold = corePreferences('validate-square-degrees');
62968 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
62969 var points = nodes.map(function (node) {
62970 return context.projection(node.loc);
62972 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
62973 var autoArgs; // don't allow autosquaring features linked to wikidata
62975 if (!entity.tags.wikidata) {
62976 // use same degree threshold as for detection
62977 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
62978 autoAction.transitionable = false; // when autofixing, do it instantly
62980 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
62985 return [new validationIssue({
62987 subtype: 'building',
62988 severity: 'warning',
62989 message: function message(context) {
62990 var entity = context.hasEntity(this.entityIds[0]);
62991 return entity ? _t.html('issues.unsquare_way.message', {
62992 feature: utilDisplayLabel(entity, context.graph())
62995 reference: showReference,
62996 entityIds: [entity.id],
62997 hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
62998 dynamicFixes: function dynamicFixes() {
62999 return [new validationIssueFix({
63000 icon: 'iD-operation-orthogonalize',
63001 title: _t.html('issues.fix.square_feature.title'),
63002 autoArgs: autoArgs,
63003 onClick: function onClick(context, completionHandler) {
63004 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
63006 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
63008 })); // run after the squaring transition (currently 150ms)
63010 window.setTimeout(function () {
63011 completionHandler();
63016 new validationIssueFix({
63017 title: t.html('issues.fix.tag_as_unsquare.title'),
63018 onClick: function(context) {
63019 var entityId = this.issue.entityIds[0];
63020 var entity = context.entity(entityId);
63021 var tags = Object.assign({}, entity.tags); // shallow copy
63022 tags.nonsquare = 'yes';
63024 actionChangeTags(entityId, tags),
63025 t('issues.fix.tag_as_unsquare.annotation')
63034 function showReference(selection) {
63035 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
63039 validation.type = type;
63043 var Validations = /*#__PURE__*/Object.freeze({
63045 validationAlmostJunction: validationAlmostJunction,
63046 validationCloseNodes: validationCloseNodes,
63047 validationCrossingWays: validationCrossingWays,
63048 validationDisconnectedWay: validationDisconnectedWay,
63049 validationFormatting: validationFormatting,
63050 validationHelpRequest: validationHelpRequest,
63051 validationImpossibleOneway: validationImpossibleOneway,
63052 validationIncompatibleSource: validationIncompatibleSource,
63053 validationMaprules: validationMaprules,
63054 validationMismatchedGeometry: validationMismatchedGeometry,
63055 validationMissingRole: validationMissingRole,
63056 validationMissingTag: validationMissingTag,
63057 validationOutdatedTags: validationOutdatedTags,
63058 validationPrivateData: validationPrivateData,
63059 validationSuspiciousName: validationSuspiciousName,
63060 validationUnsquareWay: validationUnsquareWay
63063 function coreValidator(context) {
63064 var dispatch$1 = dispatch('validated', 'focusedIssue');
63065 var validator = utilRebind({}, dispatch$1, 'on');
63067 var _disabledRules = {};
63068 var _ignoredIssueIDs = {}; // issue.id -> true
63070 var _baseCache = validationCache(); // issues before any user edits
63073 var _headCache = validationCache(); // issues after all user edits
63076 var _validatedGraph = null;
63078 var _deferred = new Set(); //
63079 // initialize the validator rulesets
63083 validator.init = function () {
63084 Object.values(Validations).forEach(function (validation) {
63085 if (typeof validation !== 'function') return;
63086 var fn = validation(context);
63090 var disabledRules = corePreferences('validate-disabledRules');
63092 if (disabledRules) {
63093 disabledRules.split(',').forEach(function (key) {
63094 _disabledRules[key] = true;
63099 function reset(resetIgnored) {
63100 Array.from(_deferred).forEach(function (handle) {
63101 window.cancelIdleCallback(handle);
63103 _deferred["delete"](handle);
63104 }); // clear caches
63106 if (resetIgnored) _ignoredIssueIDs = {};
63107 _baseCache = validationCache();
63108 _headCache = validationCache();
63109 _validatedGraph = null;
63111 // clear caches, called whenever iD resets after a save
63115 validator.reset = function () {
63119 validator.resetIgnoredIssues = function () {
63120 _ignoredIssueIDs = {}; // reload UI
63122 dispatch$1.call('validated');
63123 }; // must update issues when the user changes the unsquare thereshold
63126 validator.reloadUnsquareIssues = function () {
63127 reloadUnsquareIssues(_headCache, context.graph());
63128 reloadUnsquareIssues(_baseCache, context.history().base());
63129 dispatch$1.call('validated');
63132 function reloadUnsquareIssues(cache, graph) {
63133 var checkUnsquareWay = _rules.unsquare_way;
63134 if (typeof checkUnsquareWay !== 'function') return; // uncache existing
63136 cache.uncacheIssuesOfType('unsquare_way');
63137 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), graph) // everywhere
63138 .filter(function (entity) {
63139 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
63140 }); // rerun for all buildings
63142 buildings.forEach(function (entity) {
63143 var detected = checkUnsquareWay(entity, graph);
63144 if (detected.length !== 1) return;
63145 var issue = detected[0];
63147 if (!cache.issuesByEntityID[entity.id]) {
63148 cache.issuesByEntityID[entity.id] = new Set();
63151 cache.issuesByEntityID[entity.id].add(issue.id);
63152 cache.issuesByIssueID[issue.id] = issue;
63155 // what: 'all', // 'all' or 'edited'
63156 // where: 'all', // 'all' or 'visible'
63157 // includeIgnored: false // true, false, or 'only'
63158 // includeDisabledRules: false // true, false, or 'only'
63162 validator.getIssues = function (options) {
63163 var opts = Object.assign({
63166 includeIgnored: false,
63167 includeDisabledRules: false
63169 var issues = Object.values(_headCache.issuesByIssueID);
63170 var view = context.map().extent();
63171 return issues.filter(function (issue) {
63172 if (!issue) return false;
63173 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
63174 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
63175 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
63176 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; // Sanity check: This issue may be for an entity that not longer exists.
63177 // If we detect this, uncache and return false so it is not included..
63179 var entityIds = issue.entityIds || [];
63181 for (var i = 0; i < entityIds.length; i++) {
63182 var entityId = entityIds[i];
63184 if (!context.hasEntity(entityId)) {
63185 delete _headCache.issuesByEntityID[entityId];
63186 delete _headCache.issuesByIssueID[issue.id];
63191 if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
63193 if (opts.where === 'visible') {
63194 var extent = issue.extent(context.graph());
63195 if (!view.intersects(extent)) return false;
63202 validator.getResolvedIssues = function () {
63203 var baseIssues = Object.values(_baseCache.issuesByIssueID);
63204 return baseIssues.filter(function (issue) {
63205 return !_headCache.issuesByIssueID[issue.id];
63209 validator.focusIssue = function (issue) {
63210 var extent = issue.extent(context.graph());
63213 var setZoom = Math.max(context.map().zoom(), 19);
63214 context.map().unobscuredCenterZoomEase(extent.center(), setZoom); // select the first entity
63216 if (issue.entityIds && issue.entityIds.length) {
63217 window.setTimeout(function () {
63218 var ids = issue.entityIds;
63219 context.enter(modeSelect(context, [ids[0]]));
63220 dispatch$1.call('focusedIssue', this, issue);
63221 }, 250); // after ease
63226 validator.getIssuesBySeverity = function (options) {
63227 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
63228 groups.error = groups.error || [];
63229 groups.warning = groups.warning || [];
63231 }; // show some issue types in a particular order
63234 var orderedIssueTypes = [// flag missing data first
63235 'missing_tag', 'missing_role', // then flag identity issues
63236 'outdated_tags', 'mismatched_geometry', // flag geometry issues where fixing them might solve connectivity issues
63237 'crossing_ways', 'almost_junction', // then flag connectivity issues
63238 'disconnected_way', 'impossible_oneway']; // returns the issues that the given entity IDs have in common, matching the given options
63240 validator.getSharedEntityIssues = function (entityIDs, options) {
63241 var cache = _headCache; // gather the issues that are common to all the entities
63243 var issueIDs = entityIDs.reduce(function (acc, entityID) {
63244 var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
63247 return new Set(entityIssueIDs);
63250 return new Set(_toConsumableArray(acc).filter(function (elem) {
63251 return entityIssueIDs.has(elem);
63254 var opts = options || {};
63255 return Array.from(issueIDs).map(function (id) {
63256 return cache.issuesByIssueID[id];
63257 }).filter(function (issue) {
63258 if (!issue) return false;
63259 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
63260 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
63261 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
63262 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
63264 }).sort(function (issue1, issue2) {
63265 if (issue1.type === issue2.type) {
63266 // issues of the same type, sort deterministically
63267 return issue1.id < issue2.id ? -1 : 1;
63270 var index1 = orderedIssueTypes.indexOf(issue1.type);
63271 var index2 = orderedIssueTypes.indexOf(issue2.type);
63273 if (index1 !== -1 && index2 !== -1) {
63274 // both issue types have explicit sort orders
63275 return index1 - index2;
63276 } else if (index1 === -1 && index2 === -1) {
63277 // neither issue type has an explicit sort order, sort by type
63278 return issue1.type < issue2.type ? -1 : 1;
63280 // order explicit types before everything else
63281 return index1 !== -1 ? -1 : 1;
63286 validator.getEntityIssues = function (entityID, options) {
63287 return validator.getSharedEntityIssues([entityID], options);
63290 validator.getRuleKeys = function () {
63291 return Object.keys(_rules);
63294 validator.isRuleEnabled = function (key) {
63295 return !_disabledRules[key];
63298 validator.toggleRule = function (key) {
63299 if (_disabledRules[key]) {
63300 delete _disabledRules[key];
63302 _disabledRules[key] = true;
63305 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
63306 validator.validate();
63309 validator.disableRules = function (keys) {
63310 _disabledRules = {};
63311 keys.forEach(function (k) {
63312 _disabledRules[k] = true;
63314 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
63315 validator.validate();
63318 validator.ignoreIssue = function (id) {
63319 _ignoredIssueIDs[id] = true;
63321 // Run validation on a single entity for the given graph
63325 function validateEntity(entity, graph) {
63326 var entityIssues = []; // runs validation and appends resulting issues
63328 function runValidation(key) {
63329 var fn = _rules[key];
63331 if (typeof fn !== 'function') {
63332 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
63337 var detected = fn(entity, graph);
63338 entityIssues = entityIssues.concat(detected);
63342 Object.keys(_rules).forEach(runValidation);
63343 return entityIssues;
63346 function entityIDsToValidate(entityIDs, graph) {
63347 var processedIDs = new Set();
63348 return entityIDs.reduce(function (acc, entityID) {
63349 // keep redundancy check separate from `acc` because an `entityID`
63350 // could have been added to `acc` as a related entity through an earlier pass
63351 if (processedIDs.has(entityID)) return acc;
63352 processedIDs.add(entityID);
63353 var entity = graph.hasEntity(entityID);
63354 if (!entity) return acc;
63356 var checkParentRels = [entity];
63358 if (entity.type === 'node') {
63359 graph.parentWays(entity).forEach(function (parentWay) {
63360 acc.add(parentWay.id); // include parent ways
63362 checkParentRels.push(parentWay);
63364 } else if (entity.type === 'relation') {
63365 entity.members.forEach(function (member) {
63366 acc.add(member.id); // include members
63368 } else if (entity.type === 'way') {
63369 entity.nodes.forEach(function (nodeID) {
63370 acc.add(nodeID); // include child nodes
63372 graph._parentWays[nodeID].forEach(function (wayID) {
63373 acc.add(wayID); // include connected ways
63378 checkParentRels.forEach(function (entity) {
63379 // include parent relations
63380 if (entity.type !== 'relation') {
63381 // but not super-relations
63382 graph.parentRelations(entity).forEach(function (parentRelation) {
63383 acc.add(parentRelation.id);
63390 // Run validation for several entities, supplied `entityIDs`,
63391 // against `graph` for the given `cache`
63395 function validateEntities(entityIDs, graph, cache) {
63396 // clear caches for existing issues related to these entities
63397 entityIDs.forEach(cache.uncacheEntityID); // detect new issues and update caches
63399 entityIDs.forEach(function (entityID) {
63400 var entity = graph.hasEntity(entityID); // don't validate deleted entities
63402 if (!entity) return;
63403 var issues = validateEntity(entity, graph);
63404 cache.cacheIssues(issues);
63407 // Validates anything that has changed since the last time it was run.
63408 // Also updates the "validatedGraph" to be the current graph
63409 // and dispatches a `validated` event when finished.
63413 validator.validate = function () {
63414 var currGraph = context.graph();
63415 _validatedGraph = _validatedGraph || context.history().base();
63417 if (currGraph === _validatedGraph) {
63418 dispatch$1.call('validated');
63422 var oldGraph = _validatedGraph;
63423 var difference = coreDifference(oldGraph, currGraph);
63424 _validatedGraph = currGraph;
63425 var createdAndModifiedEntityIDs = difference.extantIDs(true); // created/modified (true = w/relation members)
63427 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph); // check modified and deleted entities against the old graph in order to update their related entities
63428 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
63430 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified()).map(function (entity) {
63433 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph); // concat the sets
63435 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
63436 validateEntities(entityIDsToCheck, context.graph(), _headCache);
63437 dispatch$1.call('validated');
63440 context.history().on('reset.validator', function () {
63441 // cached issues aren't valid any longer if the history has been reset
63443 validator.validate();
63444 }); // WHEN TO RUN VALIDATION:
63445 // When graph changes:
63447 context.history().on('restore.validator', validator.validate) // restore saved history
63448 .on('undone.validator', validator.validate) // undo
63449 .on('redone.validator', validator.validate); // redo
63450 // but not on 'change' (e.g. while drawing)
63451 // When user changes editing modes:
63453 context.on('exit.validator', validator.validate); // When merging fetched data:
63455 context.history().on('merge.validator', function (entities) {
63456 if (!entities) return;
63457 var handle = window.requestIdleCallback(function () {
63458 var entityIDs = entities.map(function (entity) {
63461 var headGraph = context.graph();
63462 validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
63463 var baseGraph = context.history().base();
63464 validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
63465 dispatch$1.call('validated');
63468 _deferred.add(handle);
63473 function validationCache() {
63475 issuesByIssueID: {},
63476 // issue.id -> issue
63477 issuesByEntityID: {} // entity.id -> set(issue.id)
63481 cache.cacheIssues = function (issues) {
63482 issues.forEach(function (issue) {
63483 var entityIds = issue.entityIds || [];
63484 entityIds.forEach(function (entityId) {
63485 if (!cache.issuesByEntityID[entityId]) {
63486 cache.issuesByEntityID[entityId] = new Set();
63489 cache.issuesByEntityID[entityId].add(issue.id);
63491 cache.issuesByIssueID[issue.id] = issue;
63495 cache.uncacheIssue = function (issue) {
63496 // When multiple entities are involved (e.g. crossing_ways),
63497 // remove this issue from the other entity caches too..
63498 var entityIds = issue.entityIds || [];
63499 entityIds.forEach(function (entityId) {
63500 if (cache.issuesByEntityID[entityId]) {
63501 cache.issuesByEntityID[entityId]["delete"](issue.id);
63504 delete cache.issuesByIssueID[issue.id];
63507 cache.uncacheIssues = function (issues) {
63508 issues.forEach(cache.uncacheIssue);
63511 cache.uncacheIssuesOfType = function (type) {
63512 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
63513 return issue.type === type;
63515 cache.uncacheIssues(issuesOfType);
63517 // Remove a single entity and all its related issues from the caches
63521 cache.uncacheEntityID = function (entityID) {
63522 var issueIDs = cache.issuesByEntityID[entityID];
63523 if (!issueIDs) return;
63524 issueIDs.forEach(function (issueID) {
63525 var issue = cache.issuesByIssueID[issueID];
63528 cache.uncacheIssue(issue);
63530 delete cache.issuesByIssueID[issueID];
63533 delete cache.issuesByEntityID[entityID];
63539 function coreUploader(context) {
63540 var dispatch$1 = dispatch( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
63541 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
63542 'saveEnded', // dispatched after the result event has been dispatched
63543 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
63544 'progressChanged', // Each save results in one of these outcomes:
63545 'resultNoChanges', // upload wasn't attempted since there were no edits
63546 'resultErrors', // upload failed due to errors
63547 'resultConflicts', // upload failed due to data conflicts
63548 'resultSuccess' // upload completed without errors
63550 var _isSaving = false;
63551 var _conflicts = [];
63556 var _discardTags = {};
63557 _mainFileFetcher.get('discarded').then(function (d) {
63559 })["catch"](function () {
63562 var uploader = utilRebind({}, dispatch$1, 'on');
63564 uploader.isSaving = function () {
63568 uploader.save = function (changeset, tryAgain, checkConflicts) {
63569 // Guard against accidentally entering save code twice - #4641
63570 if (_isSaving && !tryAgain) {
63574 var osm = context.connection();
63575 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
63576 // This can happen if they were logged in from before, but the tokens are no longer valid.
63578 if (!osm.authenticated()) {
63579 osm.authenticate(function (err) {
63581 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
63589 dispatch$1.call('saveStarted', this);
63592 var history = context.history();
63594 _errors = []; // Store original changes, in case user wants to download them as an .osc file
63596 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
63597 // Any conflict resolutions will be done as `history.replace`
63598 // Remember to pop this later if needed
63601 history.perform(actionNoop());
63602 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
63605 if (!checkConflicts) {
63606 upload(changeset); // Do the full (slow) conflict check..
63608 performFullConflictCheck(changeset);
63612 function performFullConflictCheck(changeset) {
63613 var osm = context.connection();
63615 var history = context.history();
63616 var localGraph = context.graph();
63617 var remoteGraph = coreGraph(history.base(), true);
63618 var summary = history.difference().summary();
63621 for (var i = 0; i < summary.length; i++) {
63622 var item = summary[i];
63624 if (item.changeType === 'modified') {
63625 _toCheck.push(item.entity.id);
63629 var _toLoad = withChildNodes(_toCheck, localGraph);
63632 var _toLoadCount = 0;
63633 var _toLoadTotal = _toLoad.length;
63635 if (_toCheck.length) {
63636 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
63638 _toLoad.forEach(function (id) {
63639 _loaded[id] = false;
63642 osm.loadMultiple(_toLoad, loaded);
63649 function withChildNodes(ids, graph) {
63650 var s = new Set(ids);
63651 ids.forEach(function (id) {
63652 var entity = graph.entity(id);
63653 if (entity.type !== 'way') return;
63654 graph.childNodes(entity).forEach(function (child) {
63655 if (child.version !== undefined) {
63660 return Array.from(s);
63661 } // Reload modified entities into an alternate graph and check for conflicts..
63664 function loaded(err, result) {
63665 if (_errors.length) return;
63669 msg: err.message || err.responseText,
63670 details: [_t('save.status_code', {
63675 didResultInErrors();
63678 result.data.forEach(function (entity) {
63679 remoteGraph.replace(entity);
63680 _loaded[entity.id] = true;
63681 _toLoad = _toLoad.filter(function (val) {
63682 return val !== entity.id;
63684 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
63685 // need to also load children that aren't already being checked..
63689 if (entity.type === 'way') {
63690 for (i = 0; i < entity.nodes.length; i++) {
63691 id = entity.nodes[i];
63693 if (_loaded[id] === undefined) {
63694 _loaded[id] = false;
63698 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
63699 for (i = 0; i < entity.members.length; i++) {
63700 id = entity.members[i].id;
63702 if (_loaded[id] === undefined) {
63703 _loaded[id] = false;
63709 _toLoadCount += result.data.length;
63710 _toLoadTotal += loadMore.length;
63711 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
63713 if (loadMore.length) {
63714 _toLoad.push.apply(_toLoad, loadMore);
63716 osm.loadMultiple(loadMore, loaded);
63719 if (!_toLoad.length) {
63726 function detectConflicts() {
63727 function choice(id, text, _action) {
63731 action: function action() {
63732 history.replace(_action);
63737 function formatUser(d) {
63738 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
63741 function entityName(entity) {
63742 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
63745 function sameVersions(local, remote) {
63746 if (local.version !== remote.version) return false;
63748 if (local.type === 'way') {
63749 var children = utilArrayUnion(local.nodes, remote.nodes);
63751 for (var i = 0; i < children.length; i++) {
63752 var a = localGraph.hasEntity(children[i]);
63753 var b = remoteGraph.hasEntity(children[i]);
63754 if (a && b && a.version !== b.version) return false;
63761 _toCheck.forEach(function (id) {
63762 var local = localGraph.entity(id);
63763 var remote = remoteGraph.entity(id);
63764 if (sameVersions(local, remote)) return;
63765 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
63766 history.replace(merge);
63767 var mergeConflicts = merge.conflicts();
63768 if (!mergeConflicts.length) return; // merged safely
63770 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
63771 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
63772 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
63773 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
63777 name: entityName(local),
63778 details: mergeConflicts,
63780 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
63786 function upload(changeset) {
63787 var osm = context.connection();
63791 msg: 'No OSM Service'
63795 if (_conflicts.length) {
63796 didResultInConflicts(changeset);
63797 } else if (_errors.length) {
63798 didResultInErrors();
63800 var history = context.history();
63801 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
63803 if (changes.modified.length || changes.created.length || changes.deleted.length) {
63804 dispatch$1.call('willAttemptUpload', this);
63805 osm.putChangeset(changeset, changes, uploadCallback);
63807 // changes were insignificant or reverted by user
63808 didResultInNoChanges();
63813 function uploadCallback(err, changeset) {
63815 if (err.status === 409) {
63817 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
63820 msg: err.message || err.responseText,
63821 details: [_t('save.status_code', {
63826 didResultInErrors();
63829 didResultInSuccess(changeset);
63833 function didResultInNoChanges() {
63834 dispatch$1.call('resultNoChanges', this);
63836 context.flush(); // reset iD
63839 function didResultInErrors() {
63840 context.history().pop();
63841 dispatch$1.call('resultErrors', this, _errors);
63845 function didResultInConflicts(changeset) {
63846 _conflicts.sort(function (a, b) {
63847 return b.id.localeCompare(a.id);
63850 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
63854 function didResultInSuccess(changeset) {
63855 // delete the edit stack cached to local storage
63856 context.history().clearSaved();
63857 dispatch$1.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
63859 window.setTimeout(function () {
63861 context.flush(); // reset iD
63865 function endSave() {
63867 dispatch$1.call('saveEnded', this);
63870 uploader.cancelConflictResolution = function () {
63871 context.history().pop();
63874 uploader.processResolvedConflicts = function (changeset) {
63875 var history = context.history();
63877 for (var i = 0; i < _conflicts.length; i++) {
63878 if (_conflicts[i].chosen === 1) {
63879 // user chose "use theirs"
63880 var entity = context.hasEntity(_conflicts[i].id);
63882 if (entity && entity.type === 'way') {
63883 var children = utilArrayUniq(entity.nodes);
63885 for (var j = 0; j < children.length; j++) {
63886 history.replace(actionRevert(children[j]));
63890 history.replace(actionRevert(_conflicts[i].id));
63894 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
63897 uploader.reset = function () {};
63902 var abs$4 = Math.abs;
63903 var exp$2 = Math.exp;
63906 var FORCED$g = fails(function () {
63907 return Math.sinh(-2e-17) != -2e-17;
63910 // `Math.sinh` method
63911 // https://tc39.es/ecma262/#sec-math.sinh
63912 // V8 near Chromium 38 has a problem with very small numbers
63913 _export({ target: 'Math', stat: true, forced: FORCED$g }, {
63914 sinh: function sinh(x) {
63915 return abs$4(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp$2(x - 1) - exp$2(-x - 1)) * (E / 2);
63919 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
63921 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
63922 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
63925 function localeDateString(s) {
63926 if (!s) return null;
63932 var d = new Date(s);
63933 if (isNaN(d.getTime())) return null;
63934 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
63937 function vintageRange(vintage) {
63940 if (vintage.start || vintage.end) {
63941 s = vintage.start || '?';
63943 if (vintage.start !== vintage.end) {
63944 s += ' - ' + (vintage.end || '?');
63951 function rendererBackgroundSource(data) {
63952 var source = Object.assign({}, data); // shallow copy
63954 var _offset = [0, 0];
63955 var _name = source.name;
63956 var _description = source.description;
63958 var _best = !!source.best;
63960 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
63962 source.tileSize = data.tileSize || 256;
63963 source.zoomExtent = data.zoomExtent || [0, 22];
63964 source.overzoom = data.overzoom !== false;
63966 source.offset = function (val) {
63967 if (!arguments.length) return _offset;
63972 source.nudge = function (val, zoomlevel) {
63973 _offset[0] += val[0] / Math.pow(2, zoomlevel);
63974 _offset[1] += val[1] / Math.pow(2, zoomlevel);
63978 source.name = function () {
63979 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
63980 return _t('imagery.' + id_safe + '.name', {
63985 source.label = function () {
63986 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
63987 return _t.html('imagery.' + id_safe + '.name', {
63992 source.description = function () {
63993 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
63994 return _t.html('imagery.' + id_safe + '.description', {
63995 "default": _description
63999 source.best = function () {
64003 source.area = function () {
64004 if (!data.polygon) return Number.MAX_VALUE; // worldwide
64006 var area = d3_geoArea({
64007 type: 'MultiPolygon',
64008 coordinates: [data.polygon]
64010 return isNaN(area) ? 0 : area;
64013 source.imageryUsed = function () {
64014 return _name || source.id;
64017 source.template = function (val) {
64018 if (!arguments.length) return _template;
64020 if (source.id === 'custom') {
64027 source.url = function (coord) {
64028 var result = _template;
64029 if (result === '') return result; // source 'none'
64030 // Guess a type based on the tokens present in the template
64031 // (This is for 'custom' source, where we don't know)
64033 if (!source.type) {
64034 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
64035 source.type = 'wms';
64036 source.projection = 'EPSG:3857'; // guess
64037 } else if (/\{(x|y)\}/.test(_template)) {
64038 source.type = 'tms';
64039 } else if (/\{u\}/.test(_template)) {
64040 source.type = 'bing';
64044 if (source.type === 'wms') {
64045 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
64046 //polyfill for IE11, PhantomJS
64047 var sinh = Math.sinh || function (x) {
64048 var y = Math.exp(x);
64049 return (y - 1 / y) / 2;
64052 var zoomSize = Math.pow(2, z);
64053 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
64054 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
64056 switch (source.projection) {
64059 x: lon * 180 / Math.PI,
64060 y: lat * 180 / Math.PI
64064 // EPSG:3857 and synonyms
64065 var mercCoords = mercatorRaw(lon, lat);
64067 x: 20037508.34 / Math.PI * mercCoords[0],
64068 y: 20037508.34 / Math.PI * mercCoords[1]
64073 var tileSize = source.tileSize;
64074 var projection = source.projection;
64075 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
64076 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
64077 result = result.replace(/\{(\w+)\}/g, function (token, key) {
64087 return projection.replace(/^EPSG:/, '');
64090 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
64091 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
64092 /VERSION=1.3|CRS={proj}/.test(source.template())) {
64093 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
64095 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
64114 } else if (source.type === 'tms') {
64115 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
64116 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
64117 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
64118 } else if (source.type === 'bing') {
64119 result = result.replace('{u}', function () {
64122 for (var zoom = coord[2]; zoom > 0; zoom--) {
64124 var mask = 1 << zoom - 1;
64125 if ((coord[0] & mask) !== 0) b++;
64126 if ((coord[1] & mask) !== 0) b += 2;
64132 } // these apply to any type..
64135 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
64136 var subdomains = r.split(',');
64137 return subdomains[(coord[0] + coord[1]) % subdomains.length];
64142 source.validZoom = function (z) {
64143 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
64146 source.isLocatorOverlay = function () {
64147 return source.id === 'mapbox_locator_overlay';
64149 /* hides a source from the list, but leaves it available for use */
64152 source.isHidden = function () {
64153 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
64156 source.copyrightNotices = function () {};
64158 source.getMetadata = function (center, tileCoord, callback) {
64160 start: localeDateString(source.startDate),
64161 end: localeDateString(source.endDate)
64163 vintage.range = vintageRange(vintage);
64167 callback(null, metadata);
64173 rendererBackgroundSource.Bing = function (data, dispatch) {
64174 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
64175 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
64176 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
64177 var bing = rendererBackgroundSource(data); // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
64179 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
64181 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
64184 var providers = [];
64185 d3_json(url).then(function (json) {
64186 providers = json.resourceSets[0].resources[0].imageryProviders.map(function (provider) {
64188 attribution: provider.attribution,
64189 areas: provider.coverageAreas.map(function (area) {
64191 zoom: [area.zoomMin, area.zoomMax],
64192 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
64197 dispatch.call('change');
64198 })["catch"](function () {
64202 bing.copyrightNotices = function (zoom, extent) {
64203 zoom = Math.min(zoom, 21);
64204 return providers.filter(function (provider) {
64205 return provider.areas.some(function (area) {
64206 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
64208 }).map(function (provider) {
64209 return provider.attribution;
64213 bing.getMetadata = function (center, tileCoord, callback) {
64214 var tileID = tileCoord.slice(0, 3).join('/');
64215 var zoom = Math.min(tileCoord[2], 21);
64216 var centerPoint = center[1] + ',' + center[0]; // lat,lng
64218 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
64219 if (inflight[tileID]) return;
64221 if (!cache[tileID]) {
64222 cache[tileID] = {};
64225 if (cache[tileID] && cache[tileID].metadata) {
64226 return callback(null, cache[tileID].metadata);
64229 inflight[tileID] = true;
64230 d3_json(url).then(function (result) {
64231 delete inflight[tileID];
64234 throw new Error('Unknown Error');
64238 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
64239 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
64241 vintage.range = vintageRange(vintage);
64245 cache[tileID].metadata = metadata;
64246 if (callback) callback(null, metadata);
64247 })["catch"](function (err) {
64248 delete inflight[tileID];
64249 if (callback) callback(err.message);
64253 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
64257 rendererBackgroundSource.Esri = function (data) {
64258 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
64259 if (data.template.match(/blankTile/) === null) {
64260 data.template = data.template + '?blankTile=false';
64263 var esri = rendererBackgroundSource(data);
64267 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
64268 // https://developers.arcgis.com/documentation/tiled-elevation-service/
64271 esri.fetchTilemap = function (center) {
64272 // skip if we have already fetched a tilemap within 5km
64273 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
64274 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
64276 var z = 20; // first generate a random url using the template
64278 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
64280 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
64281 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
64283 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
64285 d3_json(tilemapUrl).then(function (tilemap) {
64287 throw new Error('Unknown Error');
64290 var hasTiles = true;
64292 for (var i = 0; i < tilemap.data.length; i++) {
64293 // 0 means an individual tile in the grid doesn't exist
64294 if (!tilemap.data[i]) {
64298 } // if any tiles are missing at level 20 we restrict maxZoom to 19
64301 esri.zoomExtent[1] = hasTiles ? 22 : 19;
64302 })["catch"](function () {
64307 esri.getMetadata = function (center, tileCoord, callback) {
64308 var tileID = tileCoord.slice(0, 3).join('/');
64309 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
64310 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
64312 var unknown = _t('info_panels.background.unknown');
64316 if (inflight[tileID]) return;
64319 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
64336 metadataLayer = 99;
64339 var url; // build up query using the layer appropriate to the current zoom
64341 if (esri.id === 'EsriWorldImagery') {
64342 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
64343 } else if (esri.id === 'EsriWorldImageryClarity') {
64344 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
64347 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
64349 if (!cache[tileID]) {
64350 cache[tileID] = {};
64353 if (cache[tileID] && cache[tileID].metadata) {
64354 return callback(null, cache[tileID].metadata);
64355 } // accurate metadata is only available >= 13
64358 if (metadataLayer === 99) {
64367 description: unknown,
64368 resolution: unknown,
64371 callback(null, metadata);
64373 inflight[tileID] = true;
64374 d3_json(url).then(function (result) {
64375 delete inflight[tileID];
64378 throw new Error('Unknown Error');
64379 } else if (result.features && result.features.length < 1) {
64380 throw new Error('No Results');
64381 } else if (result.error && result.error.message) {
64382 throw new Error(result.error.message);
64383 } // pass through the discrete capture date from metadata
64386 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
64388 start: captureDate,
64394 source: clean(result.features[0].attributes.NICE_NAME),
64395 description: clean(result.features[0].attributes.NICE_DESC),
64396 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
64397 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
64398 }; // append units - meters
64400 if (isFinite(metadata.resolution)) {
64401 metadata.resolution += ' m';
64404 if (isFinite(metadata.accuracy)) {
64405 metadata.accuracy += ' m';
64408 cache[tileID].metadata = metadata;
64409 if (callback) callback(null, metadata);
64410 })["catch"](function (err) {
64411 delete inflight[tileID];
64412 if (callback) callback(err.message);
64416 function clean(val) {
64417 return String(val).trim() || unknown;
64424 rendererBackgroundSource.None = function () {
64425 var source = rendererBackgroundSource({
64430 source.name = function () {
64431 return _t('background.none');
64434 source.label = function () {
64435 return _t.html('background.none');
64438 source.imageryUsed = function () {
64442 source.area = function () {
64443 return -1; // sources in background pane are sorted by area
64449 rendererBackgroundSource.Custom = function (template) {
64450 var source = rendererBackgroundSource({
64455 source.name = function () {
64456 return _t('background.custom');
64459 source.label = function () {
64460 return _t.html('background.custom');
64463 source.imageryUsed = function () {
64464 // sanitize personal connection tokens - #6801
64465 var cleaned = source.template(); // from query string parameters
64467 if (cleaned.indexOf('?') !== -1) {
64468 var parts = cleaned.split('?', 2);
64469 var qs = utilStringQs(parts[1]);
64470 ['access_token', 'connectId', 'token'].forEach(function (param) {
64472 qs[param] = '{apikey}';
64475 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
64476 } // from wms/wmts api path parameters
64479 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
64480 return 'Custom (' + cleaned + ' )';
64483 source.area = function () {
64484 return -2; // sources in background pane are sorted by area
64490 function rendererTileLayer(context) {
64491 var transformProp = utilPrefixCSSProperty('Transform');
64492 var tiler = utilTiler();
64493 var _tileSize = 256;
64505 function tileSizeAtZoom(d, z) {
64506 var EPSILON = 0.002; // close seams
64508 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
64511 function atZoom(t, distance) {
64512 var power = Math.pow(2, distance);
64513 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
64516 function lookUp(d) {
64517 for (var up = -1; up > -d[2]; up--) {
64518 var tile = atZoom(d, up);
64520 if (_cache[_source.url(tile)] !== false) {
64526 function uniqueBy(a, n) {
64530 for (var i = 0; i < a.length; i++) {
64531 if (seen[a[i][n]] === undefined) {
64533 seen[a[i][n]] = true;
64540 function addSource(d) {
64541 d.push(_source.url(d));
64543 } // Update tiles based on current state of `projection`.
64546 function background(selection) {
64547 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
64551 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
64553 pixelOffset = [0, 0];
64556 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
64557 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
64558 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
64560 } // Derive the tiles onscreen, remove those offscreen and position them.
64561 // Important that this part not depend on `_projection` because it's
64562 // rentered when tiles load/error (see #644).
64565 function render(selection) {
64566 if (!_source) return;
64568 var showDebug = context.getDebug('tile') && !_source.overlay;
64570 if (_source.validZoom(_zoom)) {
64571 tiler.skipNullIsland(!!_source.overlay);
64572 tiler().forEach(function (d) {
64574 if (d[3] === '') return;
64575 if (typeof d[3] !== 'string') return; // Workaround for #2295
64579 if (_cache[d[3]] === false && lookUp(d)) {
64580 requests.push(addSource(lookUp(d)));
64583 requests = uniqueBy(requests, 3).filter(function (r) {
64584 // don't re-request tiles which have failed in the past
64585 return _cache[r[3]] !== false;
64589 function load(d3_event, d) {
64590 _cache[d[3]] = true;
64591 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
64595 function error(d3_event, d) {
64596 _cache[d[3]] = false;
64597 select(this).on('error', null).on('load', null).remove();
64601 function imageTransform(d) {
64602 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
64604 var scale = tileSizeAtZoom(d, _zoom);
64605 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
64608 function tileCenter(d) {
64609 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
64611 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
64614 function debugTransform(d) {
64615 var coord = tileCenter(d);
64616 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
64617 } // Pick a representative tile near the center of the viewport
64618 // (This is useful for sampling the imagery vintage)
64621 var dims = tiler.size();
64622 var mapCenter = [dims[0] / 2, dims[1] / 2];
64623 var minDist = Math.max(dims[0], dims[1]);
64625 requests.forEach(function (d) {
64626 var c = tileCenter(d);
64627 var dist = geoVecLength(c, mapCenter);
64629 if (dist < minDist) {
64634 var image = selection.selectAll('img').data(requests, function (d) {
64637 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
64638 var tile = select(this);
64639 window.setTimeout(function () {
64640 if (tile.classed('tile-removing')) {
64645 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
64647 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
64648 return d === nearCenter;
64650 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
64653 debug.exit().remove();
64656 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
64657 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
64658 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
64659 debug = debug.merge(debugEnter);
64660 debug.style(transformProp, debugTransform);
64661 debug.selectAll('.tile-label-debug-coord').html(function (d) {
64662 return d[2] + ' / ' + d[0] + ' / ' + d[1];
64664 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
64665 var span = select(this);
64666 var center = context.projection.invert(tileCenter(d));
64668 _source.getMetadata(center, d, function (err, result) {
64669 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
64675 background.projection = function (val) {
64676 if (!arguments.length) return _projection;
64681 background.dimensions = function (val) {
64682 if (!arguments.length) return tiler.size();
64687 background.source = function (val) {
64688 if (!arguments.length) return _source;
64690 _tileSize = _source.tileSize;
64692 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
64699 var _imageryIndex = null;
64700 function rendererBackground(context) {
64701 var dispatch$1 = dispatch('change');
64702 var detected = utilDetect();
64703 var baseLayer = rendererTileLayer(context).projection(context.projection);
64704 var _isValid = true;
64705 var _overlayLayers = [];
64706 var _brightness = 1;
64708 var _saturation = 1;
64709 var _sharpness = 1;
64711 function ensureImageryIndex() {
64712 return _mainFileFetcher.get('imagery').then(function (sources) {
64713 if (_imageryIndex) return _imageryIndex;
64717 }; // use which-polygon to support efficient index and querying for imagery
64719 var features = sources.map(function (source) {
64720 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
64721 // Add an extra array nest to each element in `source.polygon`
64722 // so the rings are not treated as a bunch of holes:
64723 // what we have: [ [[outer],[hole],[hole]] ]
64724 // what we want: [ [[outer]],[[outer]],[[outer]] ]
64726 var rings = source.polygon.map(function (ring) {
64735 type: 'MultiPolygon',
64739 _imageryIndex.features[source.id] = feature;
64741 }).filter(Boolean);
64742 _imageryIndex.query = whichPolygon_1({
64743 type: 'FeatureCollection',
64745 }); // Instantiate `rendererBackgroundSource` objects for each source
64747 _imageryIndex.backgrounds = sources.map(function (source) {
64748 if (source.type === 'bing') {
64749 return rendererBackgroundSource.Bing(source, dispatch$1);
64750 } else if (/^EsriWorldImagery/.test(source.id)) {
64751 return rendererBackgroundSource.Esri(source);
64753 return rendererBackgroundSource(source);
64757 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
64760 var template = corePreferences('background-custom-template') || '';
64761 var custom = rendererBackgroundSource.Custom(template);
64763 _imageryIndex.backgrounds.unshift(custom);
64765 return _imageryIndex;
64769 function background(selection) {
64770 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
64771 // check its tilemap to see how high the zoom can go
64773 if (context.map().zoom() > 18) {
64774 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
64775 var center = context.map().center();
64776 currSource.fetchTilemap(center);
64778 } // Is the imagery valid here? - #4827
64781 var sources = background.sources(context.map().extent());
64782 var wasValid = _isValid;
64783 _isValid = !!sources.filter(function (d) {
64784 return d === currSource;
64787 if (wasValid !== _isValid) {
64788 // change in valid status
64789 background.updateImagery();
64792 var baseFilter = '';
64794 if (detected.cssfilters) {
64795 if (_brightness !== 1) {
64796 baseFilter += " brightness(".concat(_brightness, ")");
64799 if (_contrast !== 1) {
64800 baseFilter += " contrast(".concat(_contrast, ")");
64803 if (_saturation !== 1) {
64804 baseFilter += " saturate(".concat(_saturation, ")");
64807 if (_sharpness < 1) {
64809 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
64810 baseFilter += " blur(".concat(blur, "px)");
64814 var base = selection.selectAll('.layer-background').data([0]);
64815 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
64817 if (detected.cssfilters) {
64818 base.style('filter', baseFilter || null);
64820 base.style('opacity', _brightness);
64823 var imagery = base.selectAll('.layer-imagery').data([0]);
64824 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
64825 var maskFilter = '';
64826 var mixBlendMode = '';
64828 if (detected.cssfilters && _sharpness > 1) {
64829 // apply unsharp mask
64830 mixBlendMode = 'overlay';
64831 maskFilter = 'saturate(0) blur(3px) invert(1)';
64832 var contrast = _sharpness - 1;
64833 maskFilter += " contrast(".concat(contrast, ")");
64834 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
64835 maskFilter += " brightness(".concat(brightness, ")");
64838 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
64839 mask.exit().remove();
64840 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);
64841 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
64842 return d.source().name();
64844 overlays.exit().remove();
64845 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
64846 return select(nodes[i]).call(layer);
64850 background.updateImagery = function () {
64851 var currSource = baseLayer.source();
64852 if (context.inIntro() || !currSource) return;
64854 var o = _overlayLayers.filter(function (d) {
64855 return !d.source().isLocatorOverlay() && !d.source().isHidden();
64856 }).map(function (d) {
64857 return d.source().id;
64860 var meters = geoOffsetToMeters(currSource.offset());
64861 var EPSILON = 0.01;
64862 var x = +meters[0].toFixed(2);
64863 var y = +meters[1].toFixed(2);
64864 var hash = utilStringQs(window.location.hash);
64865 var id = currSource.id;
64867 if (id === 'custom') {
64868 id = "custom:".concat(currSource.template());
64872 hash.background = id;
64874 delete hash.background;
64880 delete hash.overlays;
64883 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
64884 hash.offset = "".concat(x, ",").concat(y);
64886 delete hash.offset;
64889 if (!window.mocha) {
64890 window.location.replace('#' + utilQsString(hash, true));
64893 var imageryUsed = [];
64894 var photoOverlaysUsed = [];
64895 var currUsed = currSource.imageryUsed();
64897 if (currUsed && _isValid) {
64898 imageryUsed.push(currUsed);
64901 _overlayLayers.filter(function (d) {
64902 return !d.source().isLocatorOverlay() && !d.source().isHidden();
64903 }).forEach(function (d) {
64904 return imageryUsed.push(d.source().imageryUsed());
64907 var dataLayer = context.layers().layer('data');
64909 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
64910 imageryUsed.push(dataLayer.getSrc());
64913 var photoOverlayLayers = {
64914 streetside: 'Bing Streetside',
64915 mapillary: 'Mapillary Images',
64916 'mapillary-map-features': 'Mapillary Map Features',
64917 'mapillary-signs': 'Mapillary Signs',
64918 openstreetcam: 'OpenStreetCam Images'
64921 for (var layerID in photoOverlayLayers) {
64922 var layer = context.layers().layer(layerID);
64924 if (layer && layer.enabled()) {
64925 photoOverlaysUsed.push(layerID);
64926 imageryUsed.push(photoOverlayLayers[layerID]);
64930 context.history().imageryUsed(imageryUsed);
64931 context.history().photoOverlaysUsed(photoOverlaysUsed);
64934 var _checkedBlocklists;
64936 background.sources = function (extent, zoom, includeCurrent) {
64937 if (!_imageryIndex) return []; // called before init()?
64940 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
64941 return visible[d.id] = true;
64943 var currSource = baseLayer.source();
64944 var osm = context.connection();
64945 var blocklists = osm && osm.imageryBlocklists();
64947 if (blocklists && blocklists !== _checkedBlocklists) {
64948 _imageryIndex.backgrounds.forEach(function (source) {
64949 source.isBlocked = blocklists.some(function (blocklist) {
64950 return blocklist.test(source.template());
64954 _checkedBlocklists = blocklists;
64957 return _imageryIndex.backgrounds.filter(function (source) {
64958 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
64960 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
64962 if (!source.polygon) return true; // always include imagery with worldwide coverage
64964 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
64966 return visible[source.id]; // include imagery visible in given extent
64970 background.dimensions = function (val) {
64972 baseLayer.dimensions(val);
64974 _overlayLayers.forEach(function (layer) {
64975 return layer.dimensions(val);
64979 background.baseLayerSource = function (d) {
64980 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
64982 var osm = context.connection();
64983 if (!osm) return background;
64984 var blocklists = osm.imageryBlocklists();
64985 var template = d.template();
64990 for (var i = 0; i < blocklists.length; i++) {
64991 regex = blocklists[i];
64992 fail = regex.test(template);
64995 } // ensure at least one test was run.
64999 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
65000 fail = regex.test(template);
65003 baseLayer.source(!fail ? d : background.findSource('none'));
65004 dispatch$1.call('change');
65005 background.updateImagery();
65009 background.findSource = function (id) {
65010 if (!id || !_imageryIndex) return null; // called before init()?
65012 return _imageryIndex.backgrounds.find(function (d) {
65013 return d.id && d.id === id;
65017 background.bing = function () {
65018 background.baseLayerSource(background.findSource('Bing'));
65021 background.showsLayer = function (d) {
65022 var currSource = baseLayer.source();
65023 if (!d || !currSource) return false;
65024 return d.id === currSource.id || _overlayLayers.some(function (layer) {
65025 return d.id === layer.source().id;
65029 background.overlayLayerSources = function () {
65030 return _overlayLayers.map(function (layer) {
65031 return layer.source();
65035 background.toggleOverlayLayer = function (d) {
65038 for (var i = 0; i < _overlayLayers.length; i++) {
65039 layer = _overlayLayers[i];
65041 if (layer.source() === d) {
65042 _overlayLayers.splice(i, 1);
65044 dispatch$1.call('change');
65045 background.updateImagery();
65050 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
65052 _overlayLayers.push(layer);
65054 dispatch$1.call('change');
65055 background.updateImagery();
65058 background.nudge = function (d, zoom) {
65059 var currSource = baseLayer.source();
65062 currSource.nudge(d, zoom);
65063 dispatch$1.call('change');
65064 background.updateImagery();
65070 background.offset = function (d) {
65071 var currSource = baseLayer.source();
65073 if (!arguments.length) {
65074 return currSource && currSource.offset() || [0, 0];
65078 currSource.offset(d);
65079 dispatch$1.call('change');
65080 background.updateImagery();
65086 background.brightness = function (d) {
65087 if (!arguments.length) return _brightness;
65089 if (context.mode()) dispatch$1.call('change');
65093 background.contrast = function (d) {
65094 if (!arguments.length) return _contrast;
65096 if (context.mode()) dispatch$1.call('change');
65100 background.saturation = function (d) {
65101 if (!arguments.length) return _saturation;
65103 if (context.mode()) dispatch$1.call('change');
65107 background.sharpness = function (d) {
65108 if (!arguments.length) return _sharpness;
65110 if (context.mode()) dispatch$1.call('change');
65116 background.ensureLoaded = function () {
65117 if (_loadPromise) return _loadPromise;
65119 function parseMapParams(qmap) {
65120 if (!qmap) return false;
65121 var params = qmap.split('/').map(Number);
65122 if (params.length < 3 || params.some(isNaN)) return false;
65123 return geoExtent([params[2], params[1]]); // lon,lat
65126 var hash = utilStringQs(window.location.hash);
65127 var requested = hash.background || hash.layer;
65128 var extent = parseMapParams(hash.map);
65129 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
65130 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
65133 if (!requested && extent) {
65134 best = background.sources(extent).find(function (s) {
65137 } // Decide which background layer to display
65140 if (requested && requested.indexOf('custom:') === 0) {
65141 var template = requested.replace(/^custom:/, '');
65142 var custom = background.findSource('custom');
65143 background.baseLayerSource(custom.template(template));
65144 corePreferences('background-custom-template', template);
65146 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
65149 var locator = imageryIndex.backgrounds.find(function (d) {
65150 return d.overlay && d["default"];
65154 background.toggleOverlayLayer(locator);
65157 var overlays = (hash.overlays || '').split(',');
65158 overlays.forEach(function (overlay) {
65159 overlay = background.findSource(overlay);
65162 background.toggleOverlayLayer(overlay);
65167 var gpx = context.layers().layer('data');
65170 gpx.url(hash.gpx, '.gpx');
65175 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
65176 return !isNaN(n) && n;
65179 if (offset.length === 2) {
65180 background.offset(geoMetersToOffset(offset));
65183 })["catch"](function () {
65188 return utilRebind(background, dispatch$1, 'on');
65191 function rendererFeatures(context) {
65192 var dispatch$1 = dispatch('change', 'redraw');
65193 var features = utilRebind({}, dispatch$1, 'on');
65195 var _deferred = new Set();
65197 var traffic_roads = {
65199 'motorway_link': true,
65201 'trunk_link': true,
65203 'primary_link': true,
65205 'secondary_link': true,
65207 'tertiary_link': true,
65208 'residential': true,
65209 'unclassified': true,
65210 'living_street': true
65212 var service_roads = {
65225 var past_futures = {
65227 'construction': true,
65229 'dismantled': true,
65232 'demolished': true,
65233 'obliterated': true
65235 var _cullFactor = 1;
65241 var _forceVisible = {};
65243 function update() {
65244 if (!window.mocha) {
65245 var hash = utilStringQs(window.location.hash);
65246 var disabled = features.disabled();
65248 if (disabled.length) {
65249 hash.disable_features = disabled.join(',');
65251 delete hash.disable_features;
65254 window.location.replace('#' + utilQsString(hash, true));
65255 corePreferences('disabled-features', disabled.join(','));
65258 _hidden = features.hidden();
65259 dispatch$1.call('change');
65260 dispatch$1.call('redraw');
65263 function defineRule(k, filter, max) {
65264 var isEnabled = true;
65270 enabled: isEnabled,
65271 // whether the user wants it enabled..
65273 currentMax: max || Infinity,
65274 defaultMax: max || Infinity,
65275 enable: function enable() {
65276 this.enabled = true;
65277 this.currentMax = this.defaultMax;
65279 disable: function disable() {
65280 this.enabled = false;
65281 this.currentMax = 0;
65283 hidden: function hidden() {
65284 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
65286 autoHidden: function autoHidden() {
65287 return this.hidden() && this.currentMax > 0;
65292 defineRule('points', function isPoint(tags, geometry) {
65293 return geometry === 'point';
65295 defineRule('traffic_roads', function isTrafficRoad(tags) {
65296 return traffic_roads[tags.highway];
65298 defineRule('service_roads', function isServiceRoad(tags) {
65299 return service_roads[tags.highway];
65301 defineRule('paths', function isPath(tags) {
65302 return paths[tags.highway];
65304 defineRule('buildings', function isBuilding(tags) {
65305 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
65307 defineRule('building_parts', function isBuildingPart(tags) {
65308 return tags['building:part'];
65310 defineRule('indoor', function isIndoor(tags) {
65311 return tags.indoor;
65313 defineRule('landuse', function isLanduse(tags, geometry) {
65314 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
65316 defineRule('boundaries', function isBoundary(tags) {
65317 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);
65319 defineRule('water', function isWater(tags) {
65320 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';
65322 defineRule('rail', function isRail(tags) {
65323 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
65325 defineRule('pistes', function isPiste(tags) {
65326 return tags['piste:type'];
65328 defineRule('aerialways', function isPiste(tags) {
65329 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
65331 defineRule('power', function isPower(tags) {
65332 return !!tags.power;
65333 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
65335 defineRule('past_future', function isPastFuture(tags) {
65336 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
65340 var strings = Object.keys(tags);
65342 for (var i = 0; i < strings.length; i++) {
65343 var s = strings[i];
65345 if (past_futures[s] || past_futures[tags[s]]) {
65351 }); // Lines or areas that don't match another feature filter.
65352 // IMPORTANT: The 'others' feature must be the last one defined,
65353 // so that code in getMatches can skip this test if `hasMatch = true`
65355 defineRule('others', function isOther(tags, geometry) {
65356 return geometry === 'line' || geometry === 'area';
65359 features.features = function () {
65363 features.keys = function () {
65367 features.enabled = function (k) {
65368 if (!arguments.length) {
65369 return _keys.filter(function (k) {
65370 return _rules[k].enabled;
65374 return _rules[k] && _rules[k].enabled;
65377 features.disabled = function (k) {
65378 if (!arguments.length) {
65379 return _keys.filter(function (k) {
65380 return !_rules[k].enabled;
65384 return _rules[k] && !_rules[k].enabled;
65387 features.hidden = function (k) {
65388 if (!arguments.length) {
65389 return _keys.filter(function (k) {
65390 return _rules[k].hidden();
65394 return _rules[k] && _rules[k].hidden();
65397 features.autoHidden = function (k) {
65398 if (!arguments.length) {
65399 return _keys.filter(function (k) {
65400 return _rules[k].autoHidden();
65404 return _rules[k] && _rules[k].autoHidden();
65407 features.enable = function (k) {
65408 if (_rules[k] && !_rules[k].enabled) {
65409 _rules[k].enable();
65415 features.enableAll = function () {
65416 var didEnable = false;
65418 for (var k in _rules) {
65419 if (!_rules[k].enabled) {
65422 _rules[k].enable();
65426 if (didEnable) update();
65429 features.disable = function (k) {
65430 if (_rules[k] && _rules[k].enabled) {
65431 _rules[k].disable();
65437 features.disableAll = function () {
65438 var didDisable = false;
65440 for (var k in _rules) {
65441 if (_rules[k].enabled) {
65444 _rules[k].disable();
65448 if (didDisable) update();
65451 features.toggle = function (k) {
65454 return f.enabled ? f.disable() : f.enable();
65461 features.resetStats = function () {
65462 for (var i = 0; i < _keys.length; i++) {
65463 _rules[_keys[i]].count = 0;
65466 dispatch$1.call('change');
65469 features.gatherStats = function (d, resolver, dimensions) {
65470 var needsRedraw = false;
65471 var types = utilArrayGroupBy(d, 'type');
65472 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
65473 var currHidden, geometry, matches, i, j;
65475 for (i = 0; i < _keys.length; i++) {
65476 _rules[_keys[i]].count = 0;
65477 } // adjust the threshold for point/building culling based on viewport size..
65478 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
65481 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
65483 for (i = 0; i < entities.length; i++) {
65484 geometry = entities[i].geometry(resolver);
65485 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
65487 for (j = 0; j < matches.length; j++) {
65488 _rules[matches[j]].count++;
65492 currHidden = features.hidden();
65494 if (currHidden !== _hidden) {
65495 _hidden = currHidden;
65496 needsRedraw = true;
65497 dispatch$1.call('change');
65500 return needsRedraw;
65503 features.stats = function () {
65504 for (var i = 0; i < _keys.length; i++) {
65505 _stats[_keys[i]] = _rules[_keys[i]].count;
65511 features.clear = function (d) {
65512 for (var i = 0; i < d.length; i++) {
65513 features.clearEntity(d[i]);
65517 features.clearEntity = function (entity) {
65518 delete _cache[osmEntity.key(entity)];
65521 features.reset = function () {
65522 Array.from(_deferred).forEach(function (handle) {
65523 window.cancelIdleCallback(handle);
65525 _deferred["delete"](handle);
65528 }; // only certain relations are worth checking
65531 function relationShouldBeChecked(relation) {
65532 // multipolygon features have `area` geometry and aren't checked here
65533 return relation.tags.type === 'boundary';
65536 features.getMatches = function (entity, resolver, geometry) {
65537 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
65538 var ent = osmEntity.key(entity);
65540 if (!_cache[ent]) {
65544 if (!_cache[ent].matches) {
65546 var hasMatch = false;
65548 for (var i = 0; i < _keys.length; i++) {
65549 if (_keys[i] === 'others') {
65550 if (hasMatch) continue; // If an entity...
65551 // 1. is a way that hasn't matched other 'interesting' feature rules,
65553 if (entity.type === 'way') {
65554 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
65556 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
65557 parents.length > 0 && parents.every(function (parent) {
65558 return parent.tags.type === 'boundary';
65560 // ...then match whatever feature rules the parent relation has matched.
65561 // see #2548, #2887
65564 // For this to work, getMatches must be called on relations before ways.
65566 var pkey = osmEntity.key(parents[0]);
65568 if (_cache[pkey] && _cache[pkey].matches) {
65569 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
65577 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
65578 matches[_keys[i]] = hasMatch = true;
65582 _cache[ent].matches = matches;
65585 return _cache[ent].matches;
65588 features.getParents = function (entity, resolver, geometry) {
65589 if (geometry === 'point') return [];
65590 var ent = osmEntity.key(entity);
65592 if (!_cache[ent]) {
65596 if (!_cache[ent].parents) {
65599 if (geometry === 'vertex') {
65600 parents = resolver.parentWays(entity);
65602 // 'line', 'area', 'relation'
65603 parents = resolver.parentRelations(entity);
65606 _cache[ent].parents = parents;
65609 return _cache[ent].parents;
65612 features.isHiddenPreset = function (preset, geometry) {
65613 if (!_hidden.length) return false;
65614 if (!preset.tags) return false;
65615 var test = preset.setTags({}, geometry);
65617 for (var key in _rules) {
65618 if (_rules[key].filter(test, geometry)) {
65619 if (_hidden.indexOf(key) !== -1) {
65630 features.isHiddenFeature = function (entity, resolver, geometry) {
65631 if (!_hidden.length) return false;
65632 if (!entity.version) return false;
65633 if (_forceVisible[entity.id]) return false;
65634 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
65635 return matches.length && matches.every(function (k) {
65636 return features.hidden(k);
65640 features.isHiddenChild = function (entity, resolver, geometry) {
65641 if (!_hidden.length) return false;
65642 if (!entity.version || geometry === 'point') return false;
65643 if (_forceVisible[entity.id]) return false;
65644 var parents = features.getParents(entity, resolver, geometry);
65645 if (!parents.length) return false;
65647 for (var i = 0; i < parents.length; i++) {
65648 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
65656 features.hasHiddenConnections = function (entity, resolver) {
65657 if (!_hidden.length) return false;
65658 var childNodes, connections;
65660 if (entity.type === 'midpoint') {
65661 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
65664 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
65665 connections = features.getParents(entity, resolver, entity.geometry(resolver));
65666 } // gather ways connected to child nodes..
65669 connections = childNodes.reduce(function (result, e) {
65670 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
65672 return connections.some(function (e) {
65673 return features.isHidden(e, resolver, e.geometry(resolver));
65677 features.isHidden = function (entity, resolver, geometry) {
65678 if (!_hidden.length) return false;
65679 if (!entity.version) return false;
65680 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
65681 return fn(entity, resolver, geometry);
65684 features.filter = function (d, resolver) {
65685 if (!_hidden.length) return d;
65688 for (var i = 0; i < d.length; i++) {
65691 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
65692 result.push(entity);
65699 features.forceVisible = function (entityIDs) {
65700 if (!arguments.length) return Object.keys(_forceVisible);
65701 _forceVisible = {};
65703 for (var i = 0; i < entityIDs.length; i++) {
65704 _forceVisible[entityIDs[i]] = true;
65705 var entity = context.hasEntity(entityIDs[i]);
65707 if (entity && entity.type === 'relation') {
65708 // also show relation members (one level deep)
65709 for (var j in entity.members) {
65710 _forceVisible[entity.members[j].id] = true;
65718 features.init = function () {
65719 var storage = corePreferences('disabled-features');
65722 var storageDisabled = storage.replace(/;/g, ',').split(',');
65723 storageDisabled.forEach(features.disable);
65726 var hash = utilStringQs(window.location.hash);
65728 if (hash.disable_features) {
65729 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
65730 hashDisabled.forEach(features.disable);
65732 }; // warm up the feature matching cache upon merging fetched data
65735 context.history().on('merge.features', function (newEntities) {
65736 if (!newEntities) return;
65737 var handle = window.requestIdleCallback(function () {
65738 var graph = context.graph();
65739 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
65741 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
65743 for (var i = 0; i < entities.length; i++) {
65744 var geometry = entities[i].geometry(graph);
65745 features.getMatches(entities[i], graph, geometry);
65749 _deferred.add(handle);
65755 // - the activeID - nope
65756 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
65757 // - 2 away from the activeID - nope (would create a self intersecting segment)
65758 // - all others on a linear way - yes
65759 // - all others on a closed way - nope (would create a self intersecting polygon)
65762 // 0 = active vertex - no touch/connect
65763 // 1 = passive vertex - yes touch/connect
65764 // 2 = adjacent vertex - yes but pay attention segmenting a line here
65767 function svgPassiveVertex(node, graph, activeID) {
65768 if (!activeID) return 1;
65769 if (activeID === node.id) return 0;
65770 var parents = graph.parentWays(node);
65771 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
65773 for (i = 0; i < parents.length; i++) {
65774 nodes = parents[i].nodes;
65775 isClosed = parents[i].isClosed();
65777 for (j = 0; j < nodes.length; j++) {
65778 // find this vertex, look nearby
65779 if (nodes[j] === node.id) {
65786 // wraparound if needed
65787 max = nodes.length - 1;
65788 if (ix1 < 0) ix1 = max + ix1;
65789 if (ix2 < 0) ix2 = max + ix2;
65790 if (ix3 > max) ix3 = ix3 - max;
65791 if (ix4 > max) ix4 = ix4 - max;
65794 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
65795 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
65796 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
65797 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
65798 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
65805 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
65806 return function (entity) {
65810 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
65811 var coordinates = graph.childNodes(entity).map(function (n) {
65816 if (shouldReverse(entity)) {
65817 coordinates.reverse();
65821 type: 'LineString',
65822 coordinates: coordinates
65823 }, projection.stream(clip({
65824 lineStart: function lineStart() {},
65825 lineEnd: function lineEnd() {
65828 point: function point(x, y) {
65832 var span = geoVecLength(a, b) - offset;
65835 var heading = geoVecAngle(a, b);
65836 var dx = dt * Math.cos(heading);
65837 var dy = dt * Math.sin(heading);
65838 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
65840 var coord = [a, p];
65842 for (span -= dt; span >= 0; span -= dt) {
65843 p = geoVecAdd(p, [dx, dy]);
65847 coord.push(b); // generate svg paths
65852 for (j = 0; j < coord.length; j++) {
65853 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
65862 if (bothDirections(entity)) {
65865 for (j = coord.length - 1; j >= 0; j--) {
65866 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
65886 function svgPath(projection, graph, isArea) {
65887 // Explanation of magic numbers:
65888 // "padding" here allows space for strokes to extend beyond the viewport,
65889 // so that the stroke isn't drawn along the edge of the viewport when
65890 // the shape is clipped.
65892 // When drawing lines, pad viewport by 5px.
65893 // When drawing areas, pad viewport by 65px in each direction to allow
65894 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
65896 var padding = isArea ? 65 : 5;
65897 var viewport = projection.clipExtent();
65898 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
65899 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
65900 var project = projection.stream;
65901 var path = d3_geoPath().projection({
65902 stream: function stream(output) {
65903 return project(clip(output));
65907 var svgpath = function svgpath(entity) {
65908 if (entity.id in cache) {
65909 return cache[entity.id];
65911 return cache[entity.id] = path(entity.asGeoJSON(graph));
65915 svgpath.geojson = function (d) {
65916 if (d.__featurehash__ !== undefined) {
65917 if (d.__featurehash__ in cache) {
65918 return cache[d.__featurehash__];
65920 return cache[d.__featurehash__] = path(d);
65929 function svgPointTransform(projection) {
65930 var svgpoint = function svgpoint(entity) {
65931 // http://jsperf.com/short-array-join
65932 var pt = projection(entity.loc);
65933 return 'translate(' + pt[0] + ',' + pt[1] + ')';
65936 svgpoint.geojson = function (d) {
65937 return svgpoint(d.properties.entity);
65942 function svgRelationMemberTags(graph) {
65943 return function (entity) {
65944 var tags = entity.tags;
65945 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
65946 graph.parentRelations(entity).forEach(function (relation) {
65947 var type = relation.tags.type;
65949 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
65950 tags = Object.assign({}, relation.tags, tags);
65956 function svgSegmentWay(way, graph, activeID) {
65957 // When there is no activeID, we can memoize this expensive computation
65958 if (activeID === undefined) {
65959 return graph["transient"](way, 'waySegments', getWaySegments);
65961 return getWaySegments();
65964 function getWaySegments() {
65965 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
65974 for (var i = 0; i < way.nodes.length; i++) {
65975 node = graph.entity(way.nodes[i]);
65976 type = svgPassiveVertex(node, graph, activeID);
65982 if (start.type !== undefined) {
65983 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
65984 // one adjacent vertex
65985 pushActive(start, end, i);
65986 } else if (start.type === 0 && end.type === 0) {
65987 // both active vertices
65988 pushActive(start, end, i);
65990 pushPassive(start, end, i);
65999 function pushActive(start, end, index) {
66000 features.active.push({
66002 id: way.id + '-' + index + '-nope',
66007 nodes: [start.node, end.node],
66011 type: 'LineString',
66012 coordinates: [start.node.loc, end.node.loc]
66017 function pushPassive(start, end, index) {
66018 features.passive.push({
66020 id: way.id + '-' + index,
66024 nodes: [start.node, end.node],
66028 type: 'LineString',
66029 coordinates: [start.node.loc, end.node.loc]
66036 function svgTagClasses() {
66037 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'];
66038 var statuses = [// nonexistent, might be built
66039 'proposed', 'planned', // under maintentance or between groundbreaking and opening
66040 'construction', // existent but not functional
66041 'disused', // dilapidated to nonexistent
66042 'abandoned', // nonexistent, still may appear in imagery
66043 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
66045 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
66047 var _tags = function _tags(entity) {
66048 return entity.tags;
66051 var tagClasses = function tagClasses(selection) {
66052 selection.each(function tagClassesEach(entity) {
66053 var value = this.className;
66055 if (value.baseVal !== undefined) {
66056 value = value.baseVal;
66059 var t = _tags(entity);
66061 var computed = tagClasses.getClassesString(t, value);
66063 if (computed !== value) {
66064 select(this).attr('class', computed);
66069 tagClasses.getClassesString = function (t, value) {
66070 var primary, status;
66071 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
66073 var overrideGeometry;
66075 if (/\bstroke\b/.test(value)) {
66076 if (!!t.barrier && t.barrier !== 'no') {
66077 overrideGeometry = 'line';
66079 } // preserve base classes (nothing with `tag-`)
66082 var classes = value.trim().split(/\s+/).filter(function (klass) {
66083 return klass.length && !/^tag-/.test(klass);
66084 }).map(function (klass) {
66085 // special overrides for some perimeter strokes
66086 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
66087 }); // pick at most one primary classification tag..
66089 for (i = 0; i < primaries.length; i++) {
66092 if (!v || v === 'no') continue;
66094 if (k === 'piste:type') {
66095 // avoid a ':' in the class name
66097 } else if (k === 'building:part') {
66098 // avoid a ':' in the class name
66099 k = 'building_part';
66104 if (statuses.indexOf(v) !== -1) {
66105 // e.g. `railway=abandoned`
66107 classes.push('tag-' + k);
66109 classes.push('tag-' + k);
66110 classes.push('tag-' + k + '-' + v);
66117 for (i = 0; i < statuses.length; i++) {
66118 for (j = 0; j < primaries.length; j++) {
66119 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
66122 if (!v || v === 'no') continue;
66123 status = statuses[i];
66127 } // add at most one status tag, only if relates to primary tag..
66131 for (i = 0; i < statuses.length; i++) {
66134 if (!v || v === 'no') continue;
66137 // e.g. `railway=rail + abandoned=yes`
66139 } else if (primary && primary === v) {
66140 // e.g. `railway=rail + abandoned=railway`
66142 } else if (!primary && primaries.indexOf(v) !== -1) {
66143 // e.g. `abandoned=railway`
66146 classes.push('tag-' + v);
66147 } // else ignore e.g. `highway=path + abandoned=railway`
66155 classes.push('tag-status');
66156 classes.push('tag-status-' + status);
66157 } // add any secondary tags
66160 for (i = 0; i < secondaries.length; i++) {
66161 k = secondaries[i];
66163 if (!v || v === 'no' || k === primary) continue;
66164 classes.push('tag-' + k);
66165 classes.push('tag-' + k + '-' + v);
66166 } // For highways, look for surface tagging..
66169 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
66170 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
66175 if (k in osmPavedTags) {
66176 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
66179 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
66180 surface = 'semipaved';
66184 classes.push('tag-' + surface);
66185 } // If this is a wikidata-tagged item, add a class for that..
66188 if (t.wikidata || t['brand:wikidata']) {
66189 classes.push('tag-wikidata');
66192 return classes.join(' ').trim();
66195 tagClasses.tags = function (val) {
66196 if (!arguments.length) return _tags;
66204 // Patterns only work in Firefox when set directly on element.
66205 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
66207 // tag - pattern name
66209 // tag - value - pattern name
66211 // tag - value - rules (optional tag-values, pattern name)
66212 // (matches earlier rules first, so fallback should be last entry)
66214 grave_yard: 'cemetery',
66215 fountain: 'water_standing'
66219 religion: 'christian',
66220 pattern: 'cemetery_christian'
66222 religion: 'buddhist',
66223 pattern: 'cemetery_buddhist'
66225 religion: 'muslim',
66226 pattern: 'cemetery_muslim'
66228 religion: 'jewish',
66229 pattern: 'cemetery_jewish'
66231 pattern: 'cemetery'
66233 construction: 'construction',
66234 farmland: 'farmland',
66235 farmyard: 'farmyard',
66237 leaf_type: 'broadleaved',
66238 pattern: 'forest_broadleaved'
66240 leaf_type: 'needleleaved',
66241 pattern: 'forest_needleleaved'
66243 leaf_type: 'leafless',
66244 pattern: 'forest_leafless'
66247 } // same as 'leaf_type:mixed'
66249 grave_yard: 'cemetery',
66252 pattern: 'golf_green'
66256 landfill: 'landfill',
66258 military: 'construction',
66259 orchard: 'orchard',
66261 vineyard: 'vineyard'
66265 grassland: 'grass',
66272 water: 'reservoir',
66273 pattern: 'water_standing'
66279 pattern: 'wetland_marsh'
66282 pattern: 'wetland_swamp'
66285 pattern: 'wetland_bog'
66287 wetland: 'reedbed',
66288 pattern: 'wetland_reedbed'
66293 leaf_type: 'broadleaved',
66294 pattern: 'forest_broadleaved'
66296 leaf_type: 'needleleaved',
66297 pattern: 'forest_needleleaved'
66299 leaf_type: 'leafless',
66300 pattern: 'forest_leafless'
66303 } // same as 'leaf_type:mixed'
66321 function svgTagPattern(tags) {
66322 // Skip pattern filling if this is a building (buildings don't get patterns applied)
66323 if (tags.building && tags.building !== 'no') {
66327 for (var tag in patterns) {
66328 var entityValue = tags[tag];
66329 if (!entityValue) continue;
66331 if (typeof patterns[tag] === 'string') {
66332 // extra short syntax (just tag) - pattern name
66333 return 'pattern-' + patterns[tag];
66335 var values = patterns[tag];
66337 for (var value in values) {
66338 if (entityValue !== value) continue;
66339 var rules = values[value];
66341 if (typeof rules === 'string') {
66342 // short syntax - pattern name
66343 return 'pattern-' + rules;
66344 } // long syntax - rule array
66347 for (var ruleKey in rules) {
66348 var rule = rules[ruleKey];
66351 for (var criterion in rule) {
66352 if (criterion !== 'pattern') {
66353 // reserved for pattern name
66354 // The only rule is a required tag-value pair
66355 var v = tags[criterion];
66357 if (!v || v !== rule[criterion]) {
66365 return 'pattern-' + rule.pattern;
66375 function svgAreas(projection, context) {
66376 function getPatternStyle(tags) {
66377 var imageID = svgTagPattern(tags);
66380 return 'url("#ideditor-' + imageID + '")';
66386 function drawTargets(selection, graph, entities, filter) {
66387 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66388 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
66389 var getPath = svgPath(projection).geojson;
66390 var activeID = context.activeID();
66391 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
66397 entities.forEach(function (way) {
66398 var features = svgSegmentWay(way, graph, activeID);
66399 data.targets.push.apply(data.targets, features.passive);
66400 data.nopes.push.apply(data.nopes, features.active);
66401 }); // Targets allow hover and vertex snapping
66403 var targetData = data.targets.filter(getPath);
66404 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
66405 return filter(d.properties.entity);
66406 }).data(targetData, function key(d) {
66410 targets.exit().remove();
66412 var segmentWasEdited = function segmentWasEdited(d) {
66413 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
66415 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
66419 return d.properties.nodes.some(function (n) {
66420 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
66425 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
66426 return 'way area target target-allowed ' + targetClass + d.id;
66427 }).classed('segment-edited', segmentWasEdited); // NOPE
66429 var nopeData = data.nopes.filter(getPath);
66430 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
66431 return filter(d.properties.entity);
66432 }).data(nopeData, function key(d) {
66436 nopes.exit().remove(); // enter/update
66438 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
66439 return 'way area target target-nope ' + nopeClass + d.id;
66440 }).classed('segment-edited', segmentWasEdited);
66443 function drawAreas(selection, graph, entities, filter) {
66444 var path = svgPath(projection, graph, true);
66447 var base = context.history().base();
66449 for (var i = 0; i < entities.length; i++) {
66450 var entity = entities[i];
66451 if (entity.geometry(graph) !== 'area') continue;
66452 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
66454 if (multipolygon) {
66455 areas[multipolygon.id] = {
66456 entity: multipolygon.mergeTags(entity.tags),
66457 area: Math.abs(entity.area(graph))
66459 } else if (!areas[entity.id]) {
66460 areas[entity.id] = {
66462 area: Math.abs(entity.area(graph))
66467 var fills = Object.values(areas).filter(function hasPath(a) {
66468 return path(a.entity);
66470 fills.sort(function areaSort(a, b) {
66471 return b.area - a.area;
66473 fills = fills.map(function (a) {
66476 var strokes = fills.filter(function (area) {
66477 return area.type === 'way';
66485 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
66486 clipPaths.exit().remove();
66487 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
66488 return 'ideditor-' + entity.id + '-clippath';
66490 clipPathsEnter.append('path');
66491 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
66492 var drawLayer = selection.selectAll('.layer-osm.areas');
66493 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
66495 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
66496 areagroup = areagroup.enter().append('g').attr('class', function (d) {
66497 return 'areagroup area-' + d;
66498 }).merge(areagroup);
66499 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
66500 return data[layer];
66502 paths.exit().remove();
66503 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
66504 var bisect = d3_bisector(function (node) {
66505 return -node.__data__.area(graph);
66508 function sortedByArea(entity) {
66509 if (this._parent.__data__ === 'fill') {
66510 return fillpaths[bisect(fillpaths, -entity.area(graph))];
66514 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
66515 var layer = this.parentNode.__data__;
66516 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
66518 if (layer === 'fill') {
66519 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
66520 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
66522 }).classed('added', function (d) {
66523 return !base.entities[d.id];
66524 }).classed('geometry-edited', function (d) {
66525 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
66526 }).classed('retagged', function (d) {
66527 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66528 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
66530 touchLayer.call(drawTargets, graph, data.stroke, filter);
66536 //[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]
66537 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
66538 //[5] Name ::= NameStartChar (NameChar)*
66539 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
66541 var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
66542 var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
66543 //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(',')
66544 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
66545 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
66547 var S_TAG = 0; //tag name offerring
66549 var S_ATTR = 1; //attr name offerring
66551 var S_ATTR_SPACE = 2; //attr name end and space offer
66553 var S_EQ = 3; //=space?
66555 var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
66557 var S_ATTR_END = 5; //attr value end and no space(quot end)
66559 var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
66561 var S_TAG_CLOSE = 7; //closed el<el />
66563 function XMLReader() {}
66565 XMLReader.prototype = {
66566 parse: function parse(source, defaultNSMap, entityMap) {
66567 var domBuilder = this.domBuilder;
66568 domBuilder.startDocument();
66570 _copy(defaultNSMap, defaultNSMap = {});
66572 _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
66574 domBuilder.endDocument();
66578 function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
66579 function fixedFromCharCode(code) {
66580 // String.prototype.fromCharCode does not supports
66581 // > 2 bytes unicode chars directly
66582 if (code > 0xffff) {
66584 var surrogate1 = 0xd800 + (code >> 10),
66585 surrogate2 = 0xdc00 + (code & 0x3ff);
66586 return String.fromCharCode(surrogate1, surrogate2);
66588 return String.fromCharCode(code);
66592 function entityReplacer(a) {
66593 var k = a.slice(1, -1);
66595 if (k in entityMap) {
66596 return entityMap[k];
66597 } else if (k.charAt(0) === '#') {
66598 return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
66600 errorHandler.error('entity not found:' + a);
66605 function appendText(end) {
66608 var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
66609 locator && position(start);
66610 domBuilder.characters(xt, 0, end - start);
66615 function position(p, m) {
66616 while (p >= lineEnd && (m = linePattern.exec(source))) {
66617 lineStart = m.index;
66618 lineEnd = lineStart + m[0].length;
66619 locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
66622 locator.columnNumber = p - lineStart + 1;
66627 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
66628 var locator = domBuilder.locator;
66629 var parseStack = [{
66630 currentNSMap: defaultNSMapCopy
66637 var tagStart = source.indexOf('<', start);
66639 if (tagStart < 0) {
66640 if (!source.substr(start).match(/^\s*$/)) {
66641 var doc = domBuilder.doc;
66642 var text = doc.createTextNode(source.substr(start));
66643 doc.appendChild(text);
66644 domBuilder.currentElement = text;
66650 if (tagStart > start) {
66651 appendText(tagStart);
66654 switch (source.charAt(tagStart + 1)) {
66656 var end = source.indexOf('>', tagStart + 3);
66657 var tagName = source.substring(tagStart + 2, end);
66658 var config = parseStack.pop();
66661 tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
66663 errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
66664 end = tagStart + 1 + tagName.length;
66665 } else if (tagName.match(/\s</)) {
66666 tagName = tagName.replace(/[\s<].*/, '');
66667 errorHandler.error("end tag name: " + tagName + ' maybe not complete');
66668 end = tagStart + 1 + tagName.length;
66669 } //console.error(parseStack.length,parseStack)
66670 //console.error(config);
66673 var localNSMap = config.localNSMap;
66674 var endMatch = config.tagName == tagName;
66675 var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
66677 if (endIgnoreCaseMach) {
66678 domBuilder.endElement(config.uri, config.localName, tagName);
66681 for (var prefix in localNSMap) {
66682 domBuilder.endPrefixMapping(prefix);
66687 errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
66690 parseStack.push(config);
66699 locator && position(tagStart);
66700 end = parseInstruction(source, tagStart, domBuilder);
66704 // <!doctype,<![CDATA,<!--
66705 locator && position(tagStart);
66706 end = parseDCC(source, tagStart, domBuilder, errorHandler);
66710 locator && position(tagStart);
66711 var el = new ElementAttributes();
66712 var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
66714 var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
66715 var len = el.length;
66717 if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
66720 if (!entityMap.nbsp) {
66721 errorHandler.warning('unclosed xml attribute');
66725 if (locator && len) {
66726 var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
66728 for (var i = 0; i < len; i++) {
66730 position(a.offset);
66731 a.locator = copyLocator(locator, {});
66732 } //}catch(e){console.error('@@@@@'+e)}
66735 domBuilder.locator = locator2;
66737 if (appendElement(el, domBuilder, currentNSMap)) {
66738 parseStack.push(el);
66741 domBuilder.locator = locator;
66743 if (appendElement(el, domBuilder, currentNSMap)) {
66744 parseStack.push(el);
66748 if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
66749 end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
66756 errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
66758 end = -1; //throw e;
66764 //TODO: 这里有可能sax回退,有位置错误风险
66765 appendText(Math.max(tagStart, start) + 1);
66770 function copyLocator(f, t) {
66771 t.lineNumber = f.lineNumber;
66772 t.columnNumber = f.columnNumber;
66776 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
66777 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
66781 function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
66785 var s = S_TAG; //status
66788 var c = source.charAt(p);
66792 if (s === S_ATTR) {
66794 attrName = source.slice(start, p);
66796 } else if (s === S_ATTR_SPACE) {
66799 //fatalError: equal must after attrName or space after attrName
66800 throw new Error('attribute equal must after attrName');
66807 if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
66810 if (s === S_ATTR) {
66811 errorHandler.warning('attribute value must after "="');
66812 attrName = source.slice(start, p);
66816 p = source.indexOf(c, start);
66819 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
66820 el.add(attrName, value, start - 1);
66823 //fatalError: no end quot match
66824 throw new Error('attribute value no end \'' + c + '\' match');
66826 } else if (s == S_ATTR_NOQUOT_VALUE) {
66827 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
66829 el.add(attrName, value, start); //console.dir(el)
66831 errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
66835 //fatalError: no equal before
66836 throw new Error('attribute value must after "="');
66844 el.setTagName(source.slice(start, p));
66852 case S_ATTR_NOQUOT_VALUE:
66859 throw new Error("attribute invalid close char('/')");
66866 //throw new Error('unexpected end of input')
66867 errorHandler.error('unexpected end of input');
66870 el.setTagName(source.slice(start, p));
66878 el.setTagName(source.slice(start, p));
66886 case S_ATTR_NOQUOT_VALUE: //Compatible state
66889 value = source.slice(start, p);
66891 if (value.slice(-1) === '/') {
66893 value = value.slice(0, -1);
66897 if (s === S_ATTR_SPACE) {
66901 if (s == S_ATTR_NOQUOT_VALUE) {
66902 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
66903 el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
66905 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
66906 errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
66909 el.add(value, value, start);
66915 throw new Error('attribute value missed!!');
66916 } // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
66921 /*xml space '\x20' | #x9 | #xD | #xA; */
66931 el.setTagName(source.slice(start, p)); //tagName
66937 attrName = source.slice(start, p);
66941 case S_ATTR_NOQUOT_VALUE:
66942 var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
66943 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
66944 el.add(attrName, value, start);
66949 //case S_TAG_SPACE:
66951 //case S_ATTR_SPACE:
66953 //case S_TAG_CLOSE:
66958 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
66959 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
66961 //case S_TAG:void();break;
66962 //case S_ATTR:void();break;
66963 //case S_ATTR_NOQUOT_VALUE:void();break;
66965 var tagName = el.tagName;
66967 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
66968 errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
66971 el.add(attrName, attrName, start);
66977 errorHandler.warning('attribute space is required"' + attrName + '"!!');
66985 s = S_ATTR_NOQUOT_VALUE;
66990 throw new Error("elements closed character '/' and '>' must be connected to");
66994 } //end outer switch
66995 //console.log('p++',p)
67002 * @return true if has new namespace define
67006 function appendElement(el, domBuilder, currentNSMap) {
67007 var tagName = el.tagName;
67008 var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
67014 var qName = a.qName;
67015 var value = a.value;
67016 var nsp = qName.indexOf(':');
67019 var prefix = a.prefix = qName.slice(0, nsp);
67020 var localName = qName.slice(nsp + 1);
67021 var nsPrefix = prefix === 'xmlns' && localName;
67025 nsPrefix = qName === 'xmlns' && '';
67026 } //can not set prefix,because prefix !== ''
67029 a.localName = localName; //prefix == null for no ns prefix attribute
67031 if (nsPrefix !== false) {
67033 if (localNSMap == null) {
67034 localNSMap = {}; //console.log(currentNSMap,0)
67036 _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
67040 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
67041 a.uri = 'http://www.w3.org/2000/xmlns/';
67042 domBuilder.startPrefixMapping(nsPrefix, value);
67050 var prefix = a.prefix;
67053 //no prefix attribute has no namespace
67054 if (prefix === 'xml') {
67055 a.uri = 'http://www.w3.org/XML/1998/namespace';
67058 if (prefix !== 'xmlns') {
67059 a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
67064 var nsp = tagName.indexOf(':');
67067 prefix = el.prefix = tagName.slice(0, nsp);
67068 localName = el.localName = tagName.slice(nsp + 1);
67070 prefix = null; //important!!
67072 localName = el.localName = tagName;
67073 } //no prefix element has default namespace
67076 var ns = el.uri = currentNSMap[prefix || ''];
67077 domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
67078 //localNSMap = null
67081 domBuilder.endElement(ns, localName, tagName);
67084 for (prefix in localNSMap) {
67085 domBuilder.endPrefixMapping(prefix);
67089 el.currentNSMap = currentNSMap;
67090 el.localNSMap = localNSMap; //parseStack.push(el);
67096 function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
67097 if (/^(?:script|textarea)$/i.test(tagName)) {
67098 var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
67099 var text = source.substring(elStartEnd + 1, elEndStart);
67101 if (/[&<]/.test(text)) {
67102 if (/^script$/i.test(tagName)) {
67103 //if(!/\]\]>/.test(text)){
67104 //lexHandler.startCDATA();
67105 domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
67107 return elEndStart; //}
67108 } //}else{//text area
67111 text = text.replace(/&#?\w+;/g, entityReplacer);
67112 domBuilder.characters(text, 0, text.length);
67113 return elEndStart; //}
67117 return elStartEnd + 1;
67120 function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
67121 //if(tagName in closeMap){
67122 var pos = closeMap[tagName];
67125 //console.log(tagName)
67126 pos = source.lastIndexOf('</' + tagName + '>');
67128 if (pos < elStartEnd) {
67130 pos = source.lastIndexOf('</' + tagName);
67133 closeMap[tagName] = pos;
67136 return pos < elStartEnd; //}
67139 function _copy(source, target) {
67140 for (var n in source) {
67141 target[n] = source[n];
67145 function parseDCC(source, start, domBuilder, errorHandler) {
67146 //sure start with '<!'
67147 var next = source.charAt(start + 2);
67151 if (source.charAt(start + 3) === '-') {
67152 var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
67155 domBuilder.comment(source, start + 4, end - start - 4);
67158 errorHandler.error("Unclosed comment");
67167 if (source.substr(start + 3, 6) == 'CDATA[') {
67168 var end = source.indexOf(']]>', start + 9);
67169 domBuilder.startCDATA();
67170 domBuilder.characters(source, start + 9, end - start - 9);
67171 domBuilder.endCDATA();
67174 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
67177 var matchs = split$1(source, start);
67178 var len = matchs.length;
67180 if (len > 1 && /!doctype/i.test(matchs[0][0])) {
67181 var name = matchs[1][0];
67182 var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
67183 var sysid = len > 4 && matchs[4][0];
67184 var lastMatch = matchs[len - 1];
67185 domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
67186 domBuilder.endDTD();
67187 return lastMatch.index + lastMatch[0].length;
67195 function parseInstruction(source, start, domBuilder) {
67196 var end = source.indexOf('?>', start);
67199 var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
67202 var len = match[0].length;
67203 domBuilder.processingInstruction(match[1], match[2]);
67218 function ElementAttributes(source) {}
67220 ElementAttributes.prototype = {
67221 setTagName: function setTagName(tagName) {
67222 if (!tagNamePattern.test(tagName)) {
67223 throw new Error('invalid tagName:' + tagName);
67226 this.tagName = tagName;
67228 add: function add(qName, value, offset) {
67229 if (!tagNamePattern.test(qName)) {
67230 throw new Error('invalid attribute:' + qName);
67233 this[this.length++] = {
67240 getLocalName: function getLocalName(i) {
67241 return this[i].localName;
67243 getLocator: function getLocator(i) {
67244 return this[i].locator;
67246 getQName: function getQName(i) {
67247 return this[i].qName;
67249 getURI: function getURI(i) {
67250 return this[i].uri;
67252 getValue: function getValue(i) {
67253 return this[i].value;
67254 } // ,getIndex:function(uri, localName)){
67261 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
67262 // getType:function(uri,localName){}
67263 // getType:function(i){},
67267 function _set_proto_(thiz, parent) {
67268 thiz.__proto__ = parent;
67272 if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
67273 _set_proto_ = function _set_proto_(thiz, parent) {
67275 p.prototype = parent;
67278 for (parent in thiz) {
67279 p[parent] = thiz[parent];
67286 function split$1(source, start) {
67289 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
67290 reg.lastIndex = start;
67291 reg.exec(source); //skip <
67293 while (match = reg.exec(source)) {
67295 if (match[1]) return buf;
67299 var XMLReader_1 = XMLReader;
67301 XMLReader: XMLReader_1
67306 * Object DOMException
67307 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
67308 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
67310 function copy$1(src, dest) {
67311 for (var p in src) {
67316 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
67317 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
67321 function _extends(Class, Super) {
67322 var pt = Class.prototype;
67324 if (Object.create) {
67325 var ppt = Object.create(Super.prototype);
67326 pt.__proto__ = ppt;
67329 if (!(pt instanceof Super)) {
67330 var t = function t() {};
67331 t.prototype = Super.prototype;
67334 Class.prototype = pt = t;
67337 if (pt.constructor != Class) {
67338 if (typeof Class != 'function') {
67339 console.error("unknow Class:" + Class);
67342 pt.constructor = Class;
67346 var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
67349 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
67350 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
67351 var TEXT_NODE = NodeType.TEXT_NODE = 3;
67352 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
67353 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
67354 var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
67355 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
67356 var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
67357 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
67358 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
67359 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
67360 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
67362 var ExceptionCode = {};
67363 var ExceptionMessage = {};
67364 var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
67365 var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
67366 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
67367 var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
67368 var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
67369 var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
67370 var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
67371 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
67372 var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
67373 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
67375 var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
67376 var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
67377 var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
67378 var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
67379 var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
67381 function DOMException$2(code, message) {
67382 if (message instanceof Error) {
67383 var error = message;
67386 Error.call(this, ExceptionMessage[code]);
67387 this.message = ExceptionMessage[code];
67388 if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
67392 if (message) this.message = this.message + ": " + message;
67395 DOMException$2.prototype = Error.prototype;
67396 copy$1(ExceptionCode, DOMException$2);
67398 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
67399 * 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.
67400 * The items in the NodeList are accessible via an integral index, starting from 0.
67403 function NodeList() {}
67404 NodeList.prototype = {
67406 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
67412 * 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.
67414 * @param index unsigned long
67415 * Index into the collection.
67417 * The node at the indexth position in the NodeList, or null if that is not a valid index.
67419 item: function item(index) {
67420 return this[index] || null;
67422 toString: function toString(isHTML, nodeFilter) {
67423 for (var buf = [], i = 0; i < this.length; i++) {
67424 serializeToString(this[i], buf, isHTML, nodeFilter);
67427 return buf.join('');
67431 function LiveNodeList(node, refresh) {
67433 this._refresh = refresh;
67435 _updateLiveList(this);
67438 function _updateLiveList(list) {
67439 var inc = list._node._inc || list._node.ownerDocument._inc;
67441 if (list._inc != inc) {
67442 var ls = list._refresh(list._node); //console.log(ls.length)
67445 __set__(list, 'length', ls.length);
67452 LiveNodeList.prototype.item = function (i) {
67453 _updateLiveList(this);
67458 _extends(LiveNodeList, NodeList);
67461 * 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.
67462 * NamedNodeMap objects in the DOM are live.
67463 * used for attributes or DocumentType entities
67467 function NamedNodeMap() {}
67469 function _findNodeIndex(list, node) {
67470 var i = list.length;
67473 if (list[i] === node) {
67479 function _addNamedNode(el, list, newAttr, oldAttr) {
67481 list[_findNodeIndex(list, oldAttr)] = newAttr;
67483 list[list.length++] = newAttr;
67487 newAttr.ownerElement = el;
67488 var doc = el.ownerDocument;
67491 oldAttr && _onRemoveAttribute(doc, el, oldAttr);
67493 _onAddAttribute(doc, el, newAttr);
67498 function _removeNamedNode(el, list, attr) {
67499 //console.log('remove attr:'+attr)
67500 var i = _findNodeIndex(list, attr);
67503 var lastIndex = list.length - 1;
67505 while (i < lastIndex) {
67506 list[i] = list[++i];
67509 list.length = lastIndex;
67512 var doc = el.ownerDocument;
67515 _onRemoveAttribute(doc, el, attr);
67517 attr.ownerElement = null;
67521 throw DOMException$2(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
67525 NamedNodeMap.prototype = {
67527 item: NodeList.prototype.item,
67528 getNamedItem: function getNamedItem(key) {
67529 // if(key.indexOf(':')>0 || key == 'xmlns'){
67533 var i = this.length;
67536 var attr = this[i]; //console.log(attr.nodeName,key)
67538 if (attr.nodeName == key) {
67543 setNamedItem: function setNamedItem(attr) {
67544 var el = attr.ownerElement;
67546 if (el && el != this._ownerElement) {
67547 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
67550 var oldAttr = this.getNamedItem(attr.nodeName);
67552 _addNamedNode(this._ownerElement, this, attr, oldAttr);
67558 setNamedItemNS: function setNamedItemNS(attr) {
67559 // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
67560 var el = attr.ownerElement,
67563 if (el && el != this._ownerElement) {
67564 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
67567 oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
67569 _addNamedNode(this._ownerElement, this, attr, oldAttr);
67575 removeNamedItem: function removeNamedItem(key) {
67576 var attr = this.getNamedItem(key);
67578 _removeNamedNode(this._ownerElement, this, attr);
67582 // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
67584 removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
67585 var attr = this.getNamedItemNS(namespaceURI, localName);
67587 _removeNamedNode(this._ownerElement, this, attr);
67591 getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
67592 var i = this.length;
67595 var node = this[i];
67597 if (node.localName == localName && node.namespaceURI == namespaceURI) {
67606 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
67609 function DOMImplementation(
67612 this._features = {};
67615 for (var feature in features) {
67616 this._features = features[feature];
67620 DOMImplementation.prototype = {
67621 hasFeature: function hasFeature(
67626 var versions = this._features[feature.toLowerCase()];
67628 if (versions && (!version || version in versions)) {
67634 // Introduced in DOM Level 2:
67635 createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
67636 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
67637 var doc = new Document();
67638 doc.implementation = this;
67639 doc.childNodes = new NodeList();
67640 doc.doctype = doctype;
67643 doc.appendChild(doctype);
67646 if (qualifiedName) {
67647 var root = doc.createElementNS(namespaceURI, qualifiedName);
67648 doc.appendChild(root);
67653 // Introduced in DOM Level 2:
67654 createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
67655 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
67656 var node = new DocumentType();
67657 node.name = qualifiedName;
67658 node.nodeName = qualifiedName;
67659 node.publicId = publicId;
67660 node.systemId = systemId; // Introduced in DOM Level 2:
67661 //readonly attribute DOMString internalSubset;
67663 // readonly attribute NamedNodeMap entities;
67664 // readonly attribute NamedNodeMap notations;
67670 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
67677 previousSibling: null,
67682 ownerDocument: null,
67684 namespaceURI: null,
67687 // Modified in DOM Level 2:
67688 insertBefore: function insertBefore(newChild, refChild) {
67690 return _insertBefore(this, newChild, refChild);
67692 replaceChild: function replaceChild(newChild, oldChild) {
67694 this.insertBefore(newChild, oldChild);
67697 this.removeChild(oldChild);
67700 removeChild: function removeChild(oldChild) {
67701 return _removeChild(this, oldChild);
67703 appendChild: function appendChild(newChild) {
67704 return this.insertBefore(newChild, null);
67706 hasChildNodes: function hasChildNodes() {
67707 return this.firstChild != null;
67709 cloneNode: function cloneNode(deep) {
67710 return _cloneNode(this.ownerDocument || this, this, deep);
67712 // Modified in DOM Level 2:
67713 normalize: function normalize() {
67714 var child = this.firstChild;
67717 var next = child.nextSibling;
67719 if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
67720 this.removeChild(next);
67721 child.appendData(next.data);
67728 // Introduced in DOM Level 2:
67729 isSupported: function isSupported(feature, version) {
67730 return this.ownerDocument.implementation.hasFeature(feature, version);
67732 // Introduced in DOM Level 2:
67733 hasAttributes: function hasAttributes() {
67734 return this.attributes.length > 0;
67736 lookupPrefix: function lookupPrefix(namespaceURI) {
67740 var map = el._nsMap; //console.dir(map)
67743 for (var n in map) {
67744 if (map[n] == namespaceURI) {
67750 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
67755 // Introduced in DOM Level 3:
67756 lookupNamespaceURI: function lookupNamespaceURI(prefix) {
67760 var map = el._nsMap; //console.dir(map)
67763 if (prefix in map) {
67764 return map[prefix];
67768 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
67773 // Introduced in DOM Level 3:
67774 isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
67775 var prefix = this.lookupPrefix(namespaceURI);
67776 return prefix == null;
67780 function _xmlEncoder(c) {
67781 return c == '<' && '<' || c == '>' && '>' || c == '&' && '&' || c == '"' && '"' || '&#' + c.charCodeAt() + ';';
67784 copy$1(NodeType, Node);
67785 copy$1(NodeType, Node.prototype);
67787 * @param callback return true for continue,false for break
67788 * @return boolean true: break visit;
67791 function _visitNode(node, callback) {
67792 if (callback(node)) {
67796 if (node = node.firstChild) {
67798 if (_visitNode(node, callback)) {
67801 } while (node = node.nextSibling);
67805 function Document() {}
67807 function _onAddAttribute(doc, el, newAttr) {
67809 var ns = newAttr.namespaceURI;
67811 if (ns == 'http://www.w3.org/2000/xmlns/') {
67813 el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
67817 function _onRemoveAttribute(doc, el, newAttr, remove) {
67819 var ns = newAttr.namespaceURI;
67821 if (ns == 'http://www.w3.org/2000/xmlns/') {
67823 delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
67827 function _onUpdateChild(doc, el, newChild) {
67828 if (doc && doc._inc) {
67829 doc._inc++; //update childNodes
67831 var cs = el.childNodes;
67834 cs[cs.length++] = newChild;
67837 var child = el.firstChild;
67842 child = child.nextSibling;
67853 * writeable properties:
67854 * nodeValue,Attr:value,CharacterData:data
67859 function _removeChild(parentNode, child) {
67860 var previous = child.previousSibling;
67861 var next = child.nextSibling;
67864 previous.nextSibling = next;
67866 parentNode.firstChild = next;
67870 next.previousSibling = previous;
67872 parentNode.lastChild = previous;
67875 _onUpdateChild(parentNode.ownerDocument, parentNode);
67880 * preformance key(refChild == null)
67884 function _insertBefore(parentNode, newChild, nextChild) {
67885 var cp = newChild.parentNode;
67888 cp.removeChild(newChild); //remove and update
67891 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
67892 var newFirst = newChild.firstChild;
67894 if (newFirst == null) {
67898 var newLast = newChild.lastChild;
67900 newFirst = newLast = newChild;
67903 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
67904 newFirst.previousSibling = pre;
67905 newLast.nextSibling = nextChild;
67908 pre.nextSibling = newFirst;
67910 parentNode.firstChild = newFirst;
67913 if (nextChild == null) {
67914 parentNode.lastChild = newLast;
67916 nextChild.previousSibling = newLast;
67920 newFirst.parentNode = parentNode;
67921 } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
67923 _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
67926 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
67927 newChild.firstChild = newChild.lastChild = null;
67933 function _appendSingleChild(parentNode, newChild) {
67934 var cp = newChild.parentNode;
67937 var pre = parentNode.lastChild;
67938 cp.removeChild(newChild); //remove and update
67940 var pre = parentNode.lastChild;
67943 var pre = parentNode.lastChild;
67944 newChild.parentNode = parentNode;
67945 newChild.previousSibling = pre;
67946 newChild.nextSibling = null;
67949 pre.nextSibling = newChild;
67951 parentNode.firstChild = newChild;
67954 parentNode.lastChild = newChild;
67956 _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
67958 return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
67961 Document.prototype = {
67962 //implementation : null,
67963 nodeName: '#document',
67964 nodeType: DOCUMENT_NODE,
67966 documentElement: null,
67968 insertBefore: function insertBefore(newChild, refChild) {
67970 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
67971 var child = newChild.firstChild;
67974 var next = child.nextSibling;
67975 this.insertBefore(child, refChild);
67982 if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
67983 this.documentElement = newChild;
67986 return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
67988 removeChild: function removeChild(oldChild) {
67989 if (this.documentElement == oldChild) {
67990 this.documentElement = null;
67993 return _removeChild(this, oldChild);
67995 // Introduced in DOM Level 2:
67996 importNode: function importNode(importedNode, deep) {
67997 return _importNode(this, importedNode, deep);
67999 // Introduced in DOM Level 2:
68000 getElementById: function getElementById(id) {
68003 _visitNode(this.documentElement, function (node) {
68004 if (node.nodeType == ELEMENT_NODE) {
68005 if (node.getAttribute('id') == id) {
68014 //document factory method:
68015 createElement: function createElement(tagName) {
68016 var node = new Element();
68017 node.ownerDocument = this;
68018 node.nodeName = tagName;
68019 node.tagName = tagName;
68020 node.childNodes = new NodeList();
68021 var attrs = node.attributes = new NamedNodeMap();
68022 attrs._ownerElement = node;
68025 createDocumentFragment: function createDocumentFragment() {
68026 var node = new DocumentFragment();
68027 node.ownerDocument = this;
68028 node.childNodes = new NodeList();
68031 createTextNode: function createTextNode(data) {
68032 var node = new Text();
68033 node.ownerDocument = this;
68034 node.appendData(data);
68037 createComment: function createComment(data) {
68038 var node = new Comment();
68039 node.ownerDocument = this;
68040 node.appendData(data);
68043 createCDATASection: function createCDATASection(data) {
68044 var node = new CDATASection();
68045 node.ownerDocument = this;
68046 node.appendData(data);
68049 createProcessingInstruction: function createProcessingInstruction(target, data) {
68050 var node = new ProcessingInstruction();
68051 node.ownerDocument = this;
68052 node.tagName = node.target = target;
68053 node.nodeValue = node.data = data;
68056 createAttribute: function createAttribute(name) {
68057 var node = new Attr();
68058 node.ownerDocument = this;
68060 node.nodeName = name;
68061 node.localName = name;
68062 node.specified = true;
68065 createEntityReference: function createEntityReference(name) {
68066 var node = new EntityReference();
68067 node.ownerDocument = this;
68068 node.nodeName = name;
68071 // Introduced in DOM Level 2:
68072 createElementNS: function createElementNS(namespaceURI, qualifiedName) {
68073 var node = new Element();
68074 var pl = qualifiedName.split(':');
68075 var attrs = node.attributes = new NamedNodeMap();
68076 node.childNodes = new NodeList();
68077 node.ownerDocument = this;
68078 node.nodeName = qualifiedName;
68079 node.tagName = qualifiedName;
68080 node.namespaceURI = namespaceURI;
68082 if (pl.length == 2) {
68083 node.prefix = pl[0];
68084 node.localName = pl[1];
68086 //el.prefix = null;
68087 node.localName = qualifiedName;
68090 attrs._ownerElement = node;
68093 // Introduced in DOM Level 2:
68094 createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
68095 var node = new Attr();
68096 var pl = qualifiedName.split(':');
68097 node.ownerDocument = this;
68098 node.nodeName = qualifiedName;
68099 node.name = qualifiedName;
68100 node.namespaceURI = namespaceURI;
68101 node.specified = true;
68103 if (pl.length == 2) {
68104 node.prefix = pl[0];
68105 node.localName = pl[1];
68107 //el.prefix = null;
68108 node.localName = qualifiedName;
68115 _extends(Document, Node);
68117 function Element() {
68120 Element.prototype = {
68121 nodeType: ELEMENT_NODE,
68122 hasAttribute: function hasAttribute(name) {
68123 return this.getAttributeNode(name) != null;
68125 getAttribute: function getAttribute(name) {
68126 var attr = this.getAttributeNode(name);
68127 return attr && attr.value || '';
68129 getAttributeNode: function getAttributeNode(name) {
68130 return this.attributes.getNamedItem(name);
68132 setAttribute: function setAttribute(name, value) {
68133 var attr = this.ownerDocument.createAttribute(name);
68134 attr.value = attr.nodeValue = "" + value;
68135 this.setAttributeNode(attr);
68137 removeAttribute: function removeAttribute(name) {
68138 var attr = this.getAttributeNode(name);
68139 attr && this.removeAttributeNode(attr);
68141 //four real opeartion method
68142 appendChild: function appendChild(newChild) {
68143 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
68144 return this.insertBefore(newChild, null);
68146 return _appendSingleChild(this, newChild);
68149 setAttributeNode: function setAttributeNode(newAttr) {
68150 return this.attributes.setNamedItem(newAttr);
68152 setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
68153 return this.attributes.setNamedItemNS(newAttr);
68155 removeAttributeNode: function removeAttributeNode(oldAttr) {
68156 //console.log(this == oldAttr.ownerElement)
68157 return this.attributes.removeNamedItem(oldAttr.nodeName);
68159 //get real attribute name,and remove it by removeAttributeNode
68160 removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
68161 var old = this.getAttributeNodeNS(namespaceURI, localName);
68162 old && this.removeAttributeNode(old);
68164 hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
68165 return this.getAttributeNodeNS(namespaceURI, localName) != null;
68167 getAttributeNS: function getAttributeNS(namespaceURI, localName) {
68168 var attr = this.getAttributeNodeNS(namespaceURI, localName);
68169 return attr && attr.value || '';
68171 setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
68172 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
68173 attr.value = attr.nodeValue = "" + value;
68174 this.setAttributeNode(attr);
68176 getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
68177 return this.attributes.getNamedItemNS(namespaceURI, localName);
68179 getElementsByTagName: function getElementsByTagName(tagName) {
68180 return new LiveNodeList(this, function (base) {
68183 _visitNode(base, function (node) {
68184 if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
68192 getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
68193 return new LiveNodeList(this, function (base) {
68196 _visitNode(base, function (node) {
68197 if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
68206 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
68207 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
68209 _extends(Element, Node);
68212 Attr.prototype.nodeType = ATTRIBUTE_NODE;
68214 _extends(Attr, Node);
68216 function CharacterData() {}
68217 CharacterData.prototype = {
68219 substringData: function substringData(offset, count) {
68220 return this.data.substring(offset, offset + count);
68222 appendData: function appendData(text) {
68223 text = this.data + text;
68224 this.nodeValue = this.data = text;
68225 this.length = text.length;
68227 insertData: function insertData(offset, text) {
68228 this.replaceData(offset, 0, text);
68230 appendChild: function appendChild(newChild) {
68231 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
68233 deleteData: function deleteData(offset, count) {
68234 this.replaceData(offset, count, "");
68236 replaceData: function replaceData(offset, count, text) {
68237 var start = this.data.substring(0, offset);
68238 var end = this.data.substring(offset + count);
68239 text = start + text + end;
68240 this.nodeValue = this.data = text;
68241 this.length = text.length;
68245 _extends(CharacterData, Node);
68250 nodeType: TEXT_NODE,
68251 splitText: function splitText(offset) {
68252 var text = this.data;
68253 var newText = text.substring(offset);
68254 text = text.substring(0, offset);
68255 this.data = this.nodeValue = text;
68256 this.length = text.length;
68257 var newNode = this.ownerDocument.createTextNode(newText);
68259 if (this.parentNode) {
68260 this.parentNode.insertBefore(newNode, this.nextSibling);
68267 _extends(Text, CharacterData);
68269 function Comment() {}
68270 Comment.prototype = {
68271 nodeName: "#comment",
68272 nodeType: COMMENT_NODE
68275 _extends(Comment, CharacterData);
68277 function CDATASection() {}
68278 CDATASection.prototype = {
68279 nodeName: "#cdata-section",
68280 nodeType: CDATA_SECTION_NODE
68283 _extends(CDATASection, CharacterData);
68285 function DocumentType() {}
68286 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
68288 _extends(DocumentType, Node);
68290 function Notation() {}
68291 Notation.prototype.nodeType = NOTATION_NODE;
68293 _extends(Notation, Node);
68295 function Entity() {}
68296 Entity.prototype.nodeType = ENTITY_NODE;
68298 _extends(Entity, Node);
68300 function EntityReference() {}
68301 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
68303 _extends(EntityReference, Node);
68305 function DocumentFragment() {}
68306 DocumentFragment.prototype.nodeName = "#document-fragment";
68307 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
68309 _extends(DocumentFragment, Node);
68311 function ProcessingInstruction() {}
68313 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
68315 _extends(ProcessingInstruction, Node);
68317 function XMLSerializer$1() {}
68319 XMLSerializer$1.prototype.serializeToString = function (node, isHtml, nodeFilter) {
68320 return nodeSerializeToString.call(node, isHtml, nodeFilter);
68323 Node.prototype.toString = nodeSerializeToString;
68325 function nodeSerializeToString(isHtml, nodeFilter) {
68327 var refNode = this.nodeType == 9 ? this.documentElement : this;
68328 var prefix = refNode.prefix;
68329 var uri = refNode.namespaceURI;
68331 if (uri && prefix == null) {
68332 //console.log(prefix)
68333 var prefix = refNode.lookupPrefix(uri);
68335 if (prefix == null) {
68337 var visibleNamespaces = [{
68340 } //{namespace:uri,prefix:''}
68345 serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
68347 return buf.join('');
68350 function needNamespaceDefine(node, isHTML, visibleNamespaces) {
68351 var prefix = node.prefix || '';
68352 var uri = node.namespaceURI;
68354 if (!prefix && !uri) {
68358 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
68362 var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
68365 var ns = visibleNamespaces[i]; // get namespace prefix
68366 //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
68368 if (ns.prefix == prefix) {
68369 return ns.namespace != uri;
68371 } //console.log(isHTML,uri,prefix=='')
68372 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
68375 //node.flag = '11111'
68376 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
68382 function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
68384 node = nodeFilter(node);
68387 if (typeof node == 'string') {
68393 } //buf.sort.apply(attrs, attributeSorter);
68397 switch (node.nodeType) {
68399 if (!visibleNamespaces) visibleNamespaces = [];
68400 var startVisibleNamespaces = visibleNamespaces.length;
68401 var attrs = node.attributes;
68402 var len = attrs.length;
68403 var child = node.firstChild;
68404 var nodeName = node.tagName;
68405 isHTML = htmlns === node.namespaceURI || isHTML;
68406 buf.push('<', nodeName);
68408 for (var i = 0; i < len; i++) {
68409 // add namespaces for attributes
68410 var attr = attrs.item(i);
68412 if (attr.prefix == 'xmlns') {
68413 visibleNamespaces.push({
68414 prefix: attr.localName,
68415 namespace: attr.value
68417 } else if (attr.nodeName == 'xmlns') {
68418 visibleNamespaces.push({
68420 namespace: attr.value
68425 for (var i = 0; i < len; i++) {
68426 var attr = attrs.item(i);
68428 if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
68429 var prefix = attr.prefix || '';
68430 var uri = attr.namespaceURI;
68431 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
68432 buf.push(ns, '="', uri, '"');
68433 visibleNamespaces.push({
68439 serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
68440 } // add namespace for current node
68443 if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
68444 var prefix = node.prefix || '';
68445 var uri = node.namespaceURI;
68446 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
68447 buf.push(ns, '="', uri, '"');
68448 visibleNamespaces.push({
68454 if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
68455 buf.push('>'); //if is cdata child node
68457 if (isHTML && /^script$/i.test(nodeName)) {
68460 buf.push(child.data);
68462 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
68465 child = child.nextSibling;
68469 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
68470 child = child.nextSibling;
68474 buf.push('</', nodeName, '>');
68477 } // remove added visible namespaces
68478 //visibleNamespaces.length = startVisibleNamespaces;
68483 case DOCUMENT_NODE:
68484 case DOCUMENT_FRAGMENT_NODE:
68485 var child = node.firstChild;
68488 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
68489 child = child.nextSibling;
68494 case ATTRIBUTE_NODE:
68495 return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
68498 return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
68500 case CDATA_SECTION_NODE:
68501 return buf.push('<![CDATA[', node.data, ']]>');
68504 return buf.push("<!--", node.data, "-->");
68506 case DOCUMENT_TYPE_NODE:
68507 var pubid = node.publicId;
68508 var sysid = node.systemId;
68509 buf.push('<!DOCTYPE ', node.name);
68512 buf.push(' PUBLIC "', pubid);
68514 if (sysid && sysid != '.') {
68515 buf.push('" "', sysid);
68519 } else if (sysid && sysid != '.') {
68520 buf.push(' SYSTEM "', sysid, '">');
68522 var sub = node.internalSubset;
68525 buf.push(" [", sub, "]");
68533 case PROCESSING_INSTRUCTION_NODE:
68534 return buf.push("<?", node.target, " ", node.data, "?>");
68536 case ENTITY_REFERENCE_NODE:
68537 return buf.push('&', node.nodeName, ';');
68538 //case ENTITY_NODE:
68539 //case NOTATION_NODE:
68542 buf.push('??', node.nodeName);
68546 function _importNode(doc, node, deep) {
68549 switch (node.nodeType) {
68551 node2 = node.cloneNode(false);
68552 node2.ownerDocument = doc;
68553 //var attrs = node2.attributes;
68554 //var len = attrs.length;
68555 //for(var i=0;i<len;i++){
68556 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
68559 case DOCUMENT_FRAGMENT_NODE:
68562 case ATTRIBUTE_NODE:
68565 //case ENTITY_REFERENCE_NODE:
68566 //case PROCESSING_INSTRUCTION_NODE:
68567 ////case TEXT_NODE:
68568 //case CDATA_SECTION_NODE:
68569 //case COMMENT_NODE:
68572 //case DOCUMENT_NODE:
68573 //case DOCUMENT_TYPE_NODE:
68574 //cannot be imported.
68575 //case ENTITY_NODE:
68576 //case NOTATION_NODE:
68577 //can not hit in level3
68582 node2 = node.cloneNode(false); //false
68585 node2.ownerDocument = doc;
68586 node2.parentNode = null;
68589 var child = node.firstChild;
68592 node2.appendChild(_importNode(doc, child, deep));
68593 child = child.nextSibling;
68599 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
68600 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
68603 function _cloneNode(doc, node, deep) {
68604 var node2 = new node.constructor();
68606 for (var n in node) {
68609 if (_typeof(v) != 'object') {
68610 if (v != node2[n]) {
68616 if (node.childNodes) {
68617 node2.childNodes = new NodeList();
68620 node2.ownerDocument = doc;
68622 switch (node2.nodeType) {
68624 var attrs = node.attributes;
68625 var attrs2 = node2.attributes = new NamedNodeMap();
68626 var len = attrs.length;
68627 attrs2._ownerElement = node2;
68629 for (var i = 0; i < len; i++) {
68630 node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
68635 case ATTRIBUTE_NODE:
68640 var child = node.firstChild;
68643 node2.appendChild(_cloneNode(doc, child, deep));
68644 child = child.nextSibling;
68651 function __set__(object, key, value) {
68652 object[key] = value;
68657 if (Object.defineProperty) {
68658 var getTextContent = function getTextContent(node) {
68659 switch (node.nodeType) {
68661 case DOCUMENT_FRAGMENT_NODE:
68663 node = node.firstChild;
68666 if (node.nodeType !== 7 && node.nodeType !== 8) {
68667 buf.push(getTextContent(node));
68670 node = node.nextSibling;
68673 return buf.join('');
68676 return node.nodeValue;
68680 Object.defineProperty(LiveNodeList.prototype, 'length', {
68681 get: function get() {
68682 _updateLiveList(this);
68684 return this.$$length;
68687 Object.defineProperty(Node.prototype, 'textContent', {
68688 get: function get() {
68689 return getTextContent(this);
68691 set: function set(data) {
68692 switch (this.nodeType) {
68694 case DOCUMENT_FRAGMENT_NODE:
68695 while (this.firstChild) {
68696 this.removeChild(this.firstChild);
68699 if (data || String(data)) {
68700 this.appendChild(this.ownerDocument.createTextNode(data));
68709 this.nodeValue = data;
68714 __set__ = function __set__(object, key, value) {
68715 //console.log(value)
68716 object['$$' + key] = value;
68720 } //if(typeof require == 'function'){
68723 var DOMImplementation_1 = DOMImplementation;
68724 var XMLSerializer_1 = XMLSerializer$1; //}
68727 DOMImplementation: DOMImplementation_1,
68728 XMLSerializer: XMLSerializer_1
68731 var domParser = createCommonjsModule(function (module, exports) {
68732 function DOMParser(options) {
68733 this.options = options || {
68738 DOMParser.prototype.parseFromString = function (source, mimeType) {
68739 var options = this.options;
68740 var sax = new XMLReader();
68741 var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
68743 var errorHandler = options.errorHandler;
68744 var locator = options.locator;
68745 var defaultNSMap = options.xmlns || {};
68755 domBuilder.setDocumentLocator(locator);
68758 sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
68759 sax.domBuilder = options.domBuilder || domBuilder;
68761 if (/\/x?html?$/.test(mimeType)) {
68762 entityMap.nbsp = '\xa0';
68763 entityMap.copy = '\xa9';
68764 defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
68767 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
68770 sax.parse(source, defaultNSMap, entityMap);
68772 sax.errorHandler.error("invalid doc source");
68775 return domBuilder.doc;
68778 function buildErrorHandler(errorImpl, domBuilder, locator) {
68780 if (domBuilder instanceof DOMHandler) {
68784 errorImpl = domBuilder;
68787 var errorHandler = {};
68788 var isCallback = errorImpl instanceof Function;
68789 locator = locator || {};
68791 function build(key) {
68792 var fn = errorImpl[key];
68794 if (!fn && isCallback) {
68795 fn = errorImpl.length == 2 ? function (msg) {
68796 errorImpl(key, msg);
68800 errorHandler[key] = fn && function (msg) {
68801 fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
68802 } || function () {};
68807 build('fatalError');
68808 return errorHandler;
68809 } //console.log('#\n\n\n\n\n\n\n####')
68812 * +ContentHandler+ErrorHandler
68813 * +LexicalHandler+EntityResolver2
68814 * -DeclHandler-DTDHandler
68816 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
68817 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
68818 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
68822 function DOMHandler() {
68823 this.cdata = false;
68826 function position(locator, node) {
68827 node.lineNumber = locator.lineNumber;
68828 node.columnNumber = locator.columnNumber;
68831 * @see org.xml.sax.ContentHandler#startDocument
68832 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
68836 DOMHandler.prototype = {
68837 startDocument: function startDocument() {
68838 this.doc = new DOMImplementation().createDocument(null, null, null);
68840 if (this.locator) {
68841 this.doc.documentURI = this.locator.systemId;
68844 startElement: function startElement(namespaceURI, localName, qName, attrs) {
68845 var doc = this.doc;
68846 var el = doc.createElementNS(namespaceURI, qName || localName);
68847 var len = attrs.length;
68848 appendElement(this, el);
68849 this.currentElement = el;
68850 this.locator && position(this.locator, el);
68852 for (var i = 0; i < len; i++) {
68853 var namespaceURI = attrs.getURI(i);
68854 var value = attrs.getValue(i);
68855 var qName = attrs.getQName(i);
68856 var attr = doc.createAttributeNS(namespaceURI, qName);
68857 this.locator && position(attrs.getLocator(i), attr);
68858 attr.value = attr.nodeValue = value;
68859 el.setAttributeNode(attr);
68862 endElement: function endElement(namespaceURI, localName, qName) {
68863 var current = this.currentElement;
68864 var tagName = current.tagName;
68865 this.currentElement = current.parentNode;
68867 startPrefixMapping: function startPrefixMapping(prefix, uri) {},
68868 endPrefixMapping: function endPrefixMapping(prefix) {},
68869 processingInstruction: function processingInstruction(target, data) {
68870 var ins = this.doc.createProcessingInstruction(target, data);
68871 this.locator && position(this.locator, ins);
68872 appendElement(this, ins);
68874 ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
68875 characters: function characters(chars, start, length) {
68876 chars = _toString.apply(this, arguments); //console.log(chars)
68880 var charNode = this.doc.createCDATASection(chars);
68882 var charNode = this.doc.createTextNode(chars);
68885 if (this.currentElement) {
68886 this.currentElement.appendChild(charNode);
68887 } else if (/^\s*$/.test(chars)) {
68888 this.doc.appendChild(charNode); //process xml
68891 this.locator && position(this.locator, charNode);
68894 skippedEntity: function skippedEntity(name) {},
68895 endDocument: function endDocument() {
68896 this.doc.normalize();
68898 setDocumentLocator: function setDocumentLocator(locator) {
68899 if (this.locator = locator) {
68900 // && !('lineNumber' in locator)){
68901 locator.lineNumber = 0;
68905 comment: function comment(chars, start, length) {
68906 chars = _toString.apply(this, arguments);
68907 var comm = this.doc.createComment(chars);
68908 this.locator && position(this.locator, comm);
68909 appendElement(this, comm);
68911 startCDATA: function startCDATA() {
68912 //used in characters() methods
68915 endCDATA: function endCDATA() {
68916 this.cdata = false;
68918 startDTD: function startDTD(name, publicId, systemId) {
68919 var impl = this.doc.implementation;
68921 if (impl && impl.createDocumentType) {
68922 var dt = impl.createDocumentType(name, publicId, systemId);
68923 this.locator && position(this.locator, dt);
68924 appendElement(this, dt);
68929 * @see org.xml.sax.ErrorHandler
68930 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
68932 warning: function warning(error) {
68933 console.warn('[xmldom warning]\t' + error, _locator(this.locator));
68935 error: function error(_error) {
68936 console.error('[xmldom error]\t' + _error, _locator(this.locator));
68938 fatalError: function fatalError(error) {
68939 console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
68944 function _locator(l) {
68946 return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
68950 function _toString(chars, start, length) {
68951 if (typeof chars == 'string') {
68952 return chars.substr(start, length);
68954 //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
68955 if (chars.length >= start + length || start) {
68956 return new java.lang.String(chars, start, length) + '';
68963 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
68964 * used method of org.xml.sax.ext.LexicalHandler:
68965 * #comment(chars, start, length)
68968 * #startDTD(name, publicId, systemId)
68971 * IGNORED method of org.xml.sax.ext.LexicalHandler:
68973 * #startEntity(name)
68977 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
68978 * IGNORED method of org.xml.sax.ext.DeclHandler
68979 * #attributeDecl(eName, aName, type, mode, value)
68980 * #elementDecl(name, model)
68981 * #externalEntityDecl(name, publicId, systemId)
68982 * #internalEntityDecl(name, value)
68983 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
68984 * IGNORED method of org.xml.sax.EntityResolver2
68985 * #resolveEntity(String name,String publicId,String baseURI,String systemId)
68986 * #resolveEntity(publicId, systemId)
68987 * #getExternalSubset(name, baseURI)
68988 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
68989 * IGNORED method of org.xml.sax.DTDHandler
68990 * #notationDecl(name, publicId, systemId) {};
68991 * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
68995 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
68996 DOMHandler.prototype[key] = function () {
69000 /* 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 */
69002 function appendElement(hander, node) {
69003 if (!hander.currentElement) {
69004 hander.doc.appendChild(node);
69006 hander.currentElement.appendChild(node);
69008 } //appendChild and setAttributeNS are preformance key
69009 //if(typeof require == 'function'){
69012 var XMLReader = sax.XMLReader;
69013 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
69014 exports.XMLSerializer = dom.XMLSerializer;
69015 exports.DOMParser = DOMParser; //}
69018 var togeojson = createCommonjsModule(function (module, exports) {
69019 var toGeoJSON = function () {
69021 var removeSpace = /\s*/g,
69022 trimSpace = /^\s*|\s*$/g,
69023 splitSpace = /\s+/; // generate a short, numeric hash of a string
69025 function okhash(x) {
69026 if (!x || !x.length) return 0;
69028 for (var i = 0, h = 0; i < x.length; i++) {
69029 h = (h << 5) - h + x.charCodeAt(i) | 0;
69033 } // all Y children of X
69036 function get(x, y) {
69037 return x.getElementsByTagName(y);
69040 function attr(x, y) {
69041 return x.getAttribute(y);
69044 function attrf(x, y) {
69045 return parseFloat(attr(x, y));
69046 } // one Y child of X, if any, otherwise null
69049 function get1(x, y) {
69051 return n.length ? n[0] : null;
69052 } // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
69055 function norm(el) {
69056 if (el.normalize) {
69061 } // cast array x into numbers
69064 function numarray(x) {
69065 for (var j = 0, o = []; j < x.length; j++) {
69066 o[j] = parseFloat(x[j]);
69070 } // get the content of a text node, if any
69073 function nodeVal(x) {
69078 return x && x.textContent || '';
69079 } // get the contents of multiple text nodes, if present
69082 function getMulti(x, ys) {
69087 for (k = 0; k < ys.length; k++) {
69088 n = get1(x, ys[k]);
69089 if (n) o[ys[k]] = nodeVal(n);
69093 } // add properties of Y to X, overwriting if present in both
69096 function extend(x, y) {
69100 } // get one coordinate from a coordinate array, if any
69103 function coord1(v) {
69104 return numarray(v.replace(removeSpace, '').split(','));
69105 } // get all coordinates from a coordinate array as [[],[]]
69108 function coord(v) {
69109 var coords = v.replace(trimSpace, '').split(splitSpace),
69112 for (var i = 0; i < coords.length; i++) {
69113 o.push(coord1(coords[i]));
69119 function coordPair(x) {
69120 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
69121 ele = get1(x, 'ele'),
69122 // handle namespaced attribute in browser
69123 heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
69124 time = get1(x, 'time'),
69128 e = parseFloat(nodeVal(ele));
69137 time: time ? nodeVal(time) : null,
69138 heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
69140 } // create a new feature collection parent object
69145 type: 'FeatureCollection',
69152 if (typeof XMLSerializer !== 'undefined') {
69153 /* istanbul ignore next */
69154 serializer = new XMLSerializer(); // only require xmldom in a node environment
69155 } else if ( (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && !process.browser) {
69156 serializer = new domParser.XMLSerializer();
69159 function xml2str(str) {
69160 // IE9 will create a new XMLSerializer but it'll crash immediately.
69161 // This line is ignored because we don't run coverage tests in IE9
69163 /* istanbul ignore next */
69164 if (str.xml !== undefined) return str.xml;
69165 return serializer.serializeToString(str);
69169 kml: function kml(doc) {
69171 // styleindex keeps track of hashed styles in order to match features
69174 // stylemapindex keeps track of style maps to expose in properties
69175 styleMapIndex = {},
69176 // atomic geospatial types supported by KML - MultiGeometry is
69177 // handled separately
69178 geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
69179 // all root placemarks in the file
69180 placemarks = get(doc, 'Placemark'),
69181 styles = get(doc, 'Style'),
69182 styleMaps = get(doc, 'StyleMap');
69184 for (var k = 0; k < styles.length; k++) {
69185 var hash = okhash(xml2str(styles[k])).toString(16);
69186 styleIndex['#' + attr(styles[k], 'id')] = hash;
69187 styleByHash[hash] = styles[k];
69190 for (var l = 0; l < styleMaps.length; l++) {
69191 styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
69192 var pairs = get(styleMaps[l], 'Pair');
69195 for (var m = 0; m < pairs.length; m++) {
69196 pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
69199 styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
69202 for (var j = 0; j < placemarks.length; j++) {
69203 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
69206 function kmlColor(v) {
69207 var color, opacity;
69210 if (v.substr(0, 1) === '#') {
69214 if (v.length === 6 || v.length === 3) {
69218 if (v.length === 8) {
69219 opacity = parseInt(v.substr(0, 2), 16) / 255;
69220 color = '#' + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
69223 return [color, isNaN(opacity) ? undefined : opacity];
69226 function gxCoord(v) {
69227 return numarray(v.split(' '));
69230 function gxCoords(root) {
69231 var elems = get(root, 'coord'),
69234 if (elems.length === 0) elems = get(root, 'gx:coord');
69236 for (var i = 0; i < elems.length; i++) {
69237 coords.push(gxCoord(nodeVal(elems[i])));
69240 var timeElems = get(root, 'when');
69242 for (var j = 0; j < timeElems.length; j++) {
69243 times.push(nodeVal(timeElems[j]));
69252 function getGeometry(root) {
69261 if (get1(root, 'MultiGeometry')) {
69262 return getGeometry(get1(root, 'MultiGeometry'));
69265 if (get1(root, 'MultiTrack')) {
69266 return getGeometry(get1(root, 'MultiTrack'));
69269 if (get1(root, 'gx:MultiTrack')) {
69270 return getGeometry(get1(root, 'gx:MultiTrack'));
69273 for (i = 0; i < geotypes.length; i++) {
69274 geomNodes = get(root, geotypes[i]);
69277 for (j = 0; j < geomNodes.length; j++) {
69278 geomNode = geomNodes[j];
69280 if (geotypes[i] === 'Point') {
69283 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
69285 } else if (geotypes[i] === 'LineString') {
69287 type: 'LineString',
69288 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
69290 } else if (geotypes[i] === 'Polygon') {
69291 var rings = get(geomNode, 'LinearRing'),
69294 for (k = 0; k < rings.length; k++) {
69295 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
69300 coordinates: coords
69302 } else if (geotypes[i] === 'Track' || geotypes[i] === 'gx:Track') {
69303 var track = gxCoords(geomNode);
69305 type: 'LineString',
69306 coordinates: track.coords
69308 if (track.times.length) coordTimes.push(track.times);
69316 coordTimes: coordTimes
69320 function getPlacemark(root) {
69321 var geomsAndTimes = getGeometry(root),
69324 name = nodeVal(get1(root, 'name')),
69325 address = nodeVal(get1(root, 'address')),
69326 styleUrl = nodeVal(get1(root, 'styleUrl')),
69327 description = nodeVal(get1(root, 'description')),
69328 timeSpan = get1(root, 'TimeSpan'),
69329 timeStamp = get1(root, 'TimeStamp'),
69330 extendedData = get1(root, 'ExtendedData'),
69331 lineStyle = get1(root, 'LineStyle'),
69332 polyStyle = get1(root, 'PolyStyle'),
69333 visibility = get1(root, 'visibility');
69334 if (!geomsAndTimes.geoms.length) return [];
69335 if (name) properties.name = name;
69336 if (address) properties.address = address;
69339 if (styleUrl[0] !== '#') {
69340 styleUrl = '#' + styleUrl;
69343 properties.styleUrl = styleUrl;
69345 if (styleIndex[styleUrl]) {
69346 properties.styleHash = styleIndex[styleUrl];
69349 if (styleMapIndex[styleUrl]) {
69350 properties.styleMapHash = styleMapIndex[styleUrl];
69351 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
69352 } // Try to populate the lineStyle or polyStyle since we got the style hash
69355 var style = styleByHash[properties.styleHash];
69358 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
69359 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
69363 if (description) properties.description = description;
69366 var begin = nodeVal(get1(timeSpan, 'begin'));
69367 var end = nodeVal(get1(timeSpan, 'end'));
69368 properties.timespan = {
69375 properties.timestamp = nodeVal(get1(timeStamp, 'when'));
69379 var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
69380 color = linestyles[0],
69381 opacity = linestyles[1],
69382 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
69383 if (color) properties.stroke = color;
69384 if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
69385 if (!isNaN(width)) properties['stroke-width'] = width;
69389 var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
69390 pcolor = polystyles[0],
69391 popacity = polystyles[1],
69392 fill = nodeVal(get1(polyStyle, 'fill')),
69393 outline = nodeVal(get1(polyStyle, 'outline'));
69394 if (pcolor) properties.fill = pcolor;
69395 if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
69396 if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
69397 if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
69400 if (extendedData) {
69401 var datas = get(extendedData, 'Data'),
69402 simpleDatas = get(extendedData, 'SimpleData');
69404 for (i = 0; i < datas.length; i++) {
69405 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
69408 for (i = 0; i < simpleDatas.length; i++) {
69409 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
69414 properties.visibility = nodeVal(visibility);
69417 if (geomsAndTimes.coordTimes.length) {
69418 properties.coordTimes = geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
69423 geometry: geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
69424 type: 'GeometryCollection',
69425 geometries: geomsAndTimes.geoms
69427 properties: properties
69429 if (attr(root, 'id')) feature.id = attr(root, 'id');
69435 gpx: function gpx(doc) {
69437 tracks = get(doc, 'trk'),
69438 routes = get(doc, 'rte'),
69439 waypoints = get(doc, 'wpt'),
69440 // a feature collection
69444 for (i = 0; i < tracks.length; i++) {
69445 feature = getTrack(tracks[i]);
69446 if (feature) gj.features.push(feature);
69449 for (i = 0; i < routes.length; i++) {
69450 feature = getRoute(routes[i]);
69451 if (feature) gj.features.push(feature);
69454 for (i = 0; i < waypoints.length; i++) {
69455 gj.features.push(getPoint(waypoints[i]));
69458 function getPoints(node, pointname) {
69459 var pts = get(node, pointname),
69464 if (l < 2) return {}; // Invalid line in GeoJSON
69466 for (var i = 0; i < l; i++) {
69467 var c = coordPair(pts[i]);
69468 line.push(c.coordinates);
69469 if (c.time) times.push(c.time);
69470 if (c.heartRate) heartRates.push(c.heartRate);
69476 heartRates: heartRates
69480 function getTrack(node) {
69481 var segments = get(node, 'trkseg'),
69487 for (var i = 0; i < segments.length; i++) {
69488 line = getPoints(segments[i], 'trkpt');
69491 if (line.line) track.push(line.line);
69492 if (line.times && line.times.length) times.push(line.times);
69493 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
69497 if (track.length === 0) return;
69498 var properties = getProperties(node);
69499 extend(properties, getLineStyle(get1(node, 'extensions')));
69500 if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
69501 if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
69504 properties: properties,
69506 type: track.length === 1 ? 'LineString' : 'MultiLineString',
69507 coordinates: track.length === 1 ? track[0] : track
69512 function getRoute(node) {
69513 var line = getPoints(node, 'rtept');
69514 if (!line.line) return;
69515 var prop = getProperties(node);
69516 extend(prop, getLineStyle(get1(node, 'extensions')));
69521 type: 'LineString',
69522 coordinates: line.line
69528 function getPoint(node) {
69529 var prop = getProperties(node);
69530 extend(prop, getMulti(node, ['sym']));
69536 coordinates: coordPair(node).coordinates
69541 function getLineStyle(extensions) {
69545 var lineStyle = get1(extensions, 'line');
69548 var color = nodeVal(get1(lineStyle, 'color')),
69549 opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
69550 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
69551 if (color) style.stroke = color;
69552 if (!isNaN(opacity)) style['stroke-opacity'] = opacity; // GPX width is in mm, convert to px with 96 px per inch
69554 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
69561 function getProperties(node) {
69562 var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
69563 links = get(node, 'link');
69564 if (links.length) prop.links = [];
69566 for (var i = 0, link; i < links.length; i++) {
69568 href: attr(links[i], 'href')
69570 extend(link, getMulti(links[i], ['text', 'type']));
69571 prop.links.push(link);
69583 module.exports = toGeoJSON;
69586 var _initialized = false;
69587 var _enabled = false;
69591 function svgData(projection, context, dispatch) {
69592 var throttledRedraw = throttle(function () {
69593 dispatch.call('change');
69596 var _showLabels = true;
69597 var detected = utilDetect();
69598 var layer = select(null);
69609 if (_initialized) return; // run once
69614 function over(d3_event) {
69615 d3_event.stopPropagation();
69616 d3_event.preventDefault();
69617 d3_event.dataTransfer.dropEffect = 'copy';
69620 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
69621 d3_event.stopPropagation();
69622 d3_event.preventDefault();
69623 if (!detected.filedrop) return;
69624 drawData.fileList(d3_event.dataTransfer.files);
69625 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
69626 _initialized = true;
69629 function getService() {
69630 if (services.vectorTile && !_vtService) {
69631 _vtService = services.vectorTile;
69633 _vtService.event.on('loadedData', throttledRedraw);
69634 } else if (!services.vectorTile && _vtService) {
69641 function showLayer() {
69643 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
69644 dispatch.call('change');
69648 function hideLayer() {
69649 throttledRedraw.cancel();
69650 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
69653 function layerOn() {
69654 layer.style('display', 'block');
69657 function layerOff() {
69658 layer.selectAll('.viewfield-group').remove();
69659 layer.style('display', 'none');
69660 } // ensure that all geojson features in a collection have IDs
69663 function ensureIDs(gj) {
69664 if (!gj) return null;
69666 if (gj.type === 'FeatureCollection') {
69667 for (var i = 0; i < gj.features.length; i++) {
69668 ensureFeatureID(gj.features[i]);
69671 ensureFeatureID(gj);
69675 } // ensure that each single Feature object has a unique ID
69678 function ensureFeatureID(feature) {
69679 if (!feature) return;
69680 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
69682 } // Prefer an array of Features instead of a FeatureCollection
69685 function getFeatures(gj) {
69686 if (!gj) return [];
69688 if (gj.type === 'FeatureCollection') {
69689 return gj.features;
69695 function featureKey(d) {
69696 return d.__featurehash__;
69699 function isPolygon(d) {
69700 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
69703 function clipPathID(d) {
69704 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
69707 function featureClasses(d) {
69708 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
69711 function drawData(selection) {
69712 var vtService = getService();
69713 var getPath = svgPath(projection).geojson;
69714 var getAreaPath = svgPath(projection, null, true).geojson;
69715 var hasData = drawData.hasData();
69716 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
69717 layer.exit().remove();
69718 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
69719 var surface = context.surface();
69720 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
69723 var geoData, polygonData;
69725 if (_template && vtService) {
69726 // fetch data from vector tile service
69727 var sourceID = _template;
69728 vtService.loadTiles(sourceID, _template, projection);
69729 geoData = vtService.data(sourceID, projection);
69731 geoData = getFeatures(_geojson);
69734 geoData = geoData.filter(getPath);
69735 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
69737 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
69738 clipPaths.exit().remove();
69739 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
69740 clipPathsEnter.append('path');
69741 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
69743 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
69744 datagroups = datagroups.enter().append('g').attr('class', function (d) {
69745 return 'datagroup datagroup-' + d;
69746 }).merge(datagroups); // Draw paths
69753 var paths = datagroups.selectAll('path').data(function (layer) {
69754 return pathData[layer];
69755 }, featureKey); // exit
69757 paths.exit().remove(); // enter/update
69759 paths = paths.enter().append('path').attr('class', function (d) {
69760 var datagroup = this.parentNode.__data__;
69761 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
69762 }).attr('clip-path', function (d) {
69763 var datagroup = this.parentNode.__data__;
69764 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
69765 }).merge(paths).attr('d', function (d) {
69766 var datagroup = this.parentNode.__data__;
69767 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
69770 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
69772 function drawLabels(selection, textClass, data) {
69773 var labelPath = d3_geoPath(projection);
69774 var labelData = data.filter(function (d) {
69775 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
69777 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
69779 labels.exit().remove(); // enter/update
69781 labels = labels.enter().append('text').attr('class', function (d) {
69782 return textClass + ' ' + featureClasses(d);
69783 }).merge(labels).text(function (d) {
69784 return d.properties.desc || d.properties.name;
69785 }).attr('x', function (d) {
69786 var centroid = labelPath.centroid(d);
69787 return centroid[0] + 11;
69788 }).attr('y', function (d) {
69789 var centroid = labelPath.centroid(d);
69790 return centroid[1];
69795 function getExtension(fileName) {
69796 if (!fileName) return;
69797 var re = /\.(gpx|kml|(geo)?json)$/i;
69798 var match = fileName.toLowerCase().match(re);
69799 return match && match.length && match[0];
69802 function xmlToDom(textdata) {
69803 return new DOMParser().parseFromString(textdata, 'text/xml');
69806 drawData.setFile = function (extension, data) {
69813 switch (extension) {
69815 gj = togeojson.gpx(xmlToDom(data));
69819 gj = togeojson.kml(xmlToDom(data));
69824 gj = JSON.parse(data);
69830 if (Object.keys(gj).length) {
69831 _geojson = ensureIDs(gj);
69832 _src = extension + ' data file';
69836 dispatch.call('change');
69840 drawData.showLabels = function (val) {
69841 if (!arguments.length) return _showLabels;
69846 drawData.enabled = function (val) {
69847 if (!arguments.length) return _enabled;
69856 dispatch.call('change');
69860 drawData.hasData = function () {
69861 var gj = _geojson || {};
69862 return !!(_template || Object.keys(gj).length);
69865 drawData.template = function (val, src) {
69866 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
69868 var osm = context.connection();
69871 var blocklists = osm.imageryBlocklists();
69876 for (var i = 0; i < blocklists.length; i++) {
69877 regex = blocklists[i];
69878 fail = regex.test(val);
69881 } // ensure at least one test was run.
69885 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
69886 fail = regex.test(val);
69892 _geojson = null; // strip off the querystring/hash from the template,
69893 // it often includes the access token
69895 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
69896 dispatch.call('change');
69900 drawData.geojson = function (gj, src) {
69901 if (!arguments.length) return _geojson;
69908 if (Object.keys(gj).length) {
69909 _geojson = ensureIDs(gj);
69910 _src = src || 'unknown.geojson';
69913 dispatch.call('change');
69917 drawData.fileList = function (fileList) {
69918 if (!arguments.length) return _fileList;
69920 _fileList = fileList;
69923 if (!fileList || !fileList.length) return this;
69924 var f = fileList[0];
69925 var extension = getExtension(f.name);
69926 var reader = new FileReader();
69928 reader.onload = function () {
69929 return function (e) {
69930 drawData.setFile(extension, e.target.result);
69934 reader.readAsText(f);
69938 drawData.url = function (url, defaultExtension) {
69942 _src = null; // strip off any querystring/hash from the url before checking extension
69944 var testUrl = url.split(/[?#]/)[0];
69945 var extension = getExtension(testUrl) || defaultExtension;
69949 d3_text(url).then(function (data) {
69950 drawData.setFile(extension, data);
69951 })["catch"](function () {
69955 drawData.template(url);
69961 drawData.getSrc = function () {
69965 drawData.fitZoom = function () {
69966 var features = getFeatures(_geojson);
69967 if (!features.length) return;
69968 var map = context.map();
69969 var viewport = map.trimmedExtent().polygon();
69970 var coords = features.reduce(function (coords, feature) {
69971 var geom = feature.geometry;
69972 if (!geom) return coords;
69973 var c = geom.coordinates;
69974 /* eslint-disable no-fallthrough */
69976 switch (geom.type) {
69984 case 'MultiPolygon':
69985 c = utilArrayFlatten(c);
69988 case 'MultiLineString':
69989 c = utilArrayFlatten(c);
69992 /* eslint-enable no-fallthrough */
69995 return utilArrayUnion(coords, c);
69998 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
69999 var extent = geoExtent(d3_geoBounds({
70000 type: 'LineString',
70001 coordinates: coords
70003 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
70013 function svgDebug(projection, context) {
70014 function drawDebug(selection) {
70015 var showTile = context.getDebug('tile');
70016 var showCollision = context.getDebug('collision');
70017 var showImagery = context.getDebug('imagery');
70018 var showTouchTargets = context.getDebug('target');
70019 var showDownloaded = context.getDebug('downloaded');
70020 var debugData = [];
70029 if (showCollision) {
70043 if (showTouchTargets) {
70046 label: 'touchTargets'
70050 if (showDownloaded) {
70053 label: 'downloaded'
70057 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
70058 legend.exit().remove();
70059 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
70060 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
70063 legendItems.exit().remove();
70064 legendItems.enter().append('span').attr('class', function (d) {
70065 return "debug-legend-item ".concat(d["class"]);
70066 }).text(function (d) {
70069 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
70070 layer.exit().remove();
70071 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
70073 var extent = context.map().extent();
70074 _mainFileFetcher.get('imagery').then(function (d) {
70075 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
70076 var features = hits.map(function (d) {
70077 return d.features[d.id];
70079 var imagery = layer.selectAll('path.debug-imagery').data(features);
70080 imagery.exit().remove();
70081 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
70082 })["catch"](function () {
70086 var osm = context.connection();
70087 var dataDownloaded = [];
70089 if (osm && showDownloaded) {
70090 var rtree = osm.caches('get').tile.rtree;
70091 dataDownloaded = rtree.all().map(function (bbox) {
70099 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
70105 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
70106 downloaded.exit().remove();
70107 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
70109 layer.selectAll('path').attr('d', svgPath(projection).geojson);
70110 } // This looks strange because `enabled` methods on other layers are
70111 // chainable getter/setters, and this one is just a getter.
70114 drawDebug.enabled = function () {
70115 if (!arguments.length) {
70116 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
70126 A standalone SVG element that contains only a `defs` sub-element. To be
70127 used once globally, since defs IDs must be unique within a document.
70130 function svgDefs(context) {
70131 var _defsSelection = select(null);
70133 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
70135 function drawDefs(selection) {
70136 _defsSelection = selection.append('defs'); // add markers
70138 _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
70139 // (they can't inherit it from the line they're attached to),
70140 // so we need to manually define markers for each color of tag
70141 // (also, it's slightly nicer if we can control the
70142 // positioning for different tags)
70145 function addSidedMarker(name, color, offset) {
70146 _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);
70149 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
70150 // the water side, so let's color them blue (with a gap) to
70151 // give a stronger indication
70153 addSidedMarker('coastline', '#77dede', 1);
70154 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
70155 // from the line visually suits that
70157 addSidedMarker('barrier', '#ddd', 1);
70158 addSidedMarker('man_made', '#fff', 0);
70160 _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');
70162 _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
70165 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
70166 ['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) {
70167 return 'ideditor-pattern-' + d[0];
70168 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
70170 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
70171 return 'pattern-color-' + d[0];
70173 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
70174 return context.imagePath('pattern/' + d[1] + '.png');
70175 }); // add clip paths
70177 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
70178 return 'ideditor-clip-square-' + d;
70179 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
70181 }).attr('height', function (d) {
70183 }); // add symbol spritesheets
70186 addSprites(_spritesheetIds, true);
70189 function addSprites(ids, overrideColors) {
70190 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
70192 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
70194 spritesheets.enter().append('g').attr('class', function (d) {
70195 return 'spritesheet spritesheet-' + d;
70196 }).each(function (d) {
70197 var url = context.imagePath(d + '.svg');
70198 var node = select(this).node();
70199 svg(url).then(function (svg) {
70200 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
70202 if (overrideColors && d !== 'iD-sprite') {
70203 // allow icon colors to be overridden..
70204 select(node).selectAll('path').attr('fill', 'currentColor');
70206 })["catch"](function () {
70210 spritesheets.exit().remove();
70213 drawDefs.addSprites = addSprites;
70217 var _layerEnabled = false;
70221 function svgKeepRight(projection, context, dispatch) {
70222 var throttledRedraw = throttle(function () {
70223 return dispatch.call('change');
70227 var touchLayer = select(null);
70228 var drawLayer = select(null);
70229 var layerVisible = false;
70231 function markerPath(selection, klass) {
70232 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');
70233 } // Loosely-coupled keepRight service for fetching issues.
70236 function getService() {
70237 if (services.keepRight && !_qaService) {
70238 _qaService = services.keepRight;
70240 _qaService.on('loaded', throttledRedraw);
70241 } else if (!services.keepRight && _qaService) {
70246 } // Show the markers
70249 function editOn() {
70250 if (!layerVisible) {
70251 layerVisible = true;
70252 drawLayer.style('display', 'block');
70254 } // Immediately remove the markers and their touch targets
70257 function editOff() {
70258 if (layerVisible) {
70259 layerVisible = false;
70260 drawLayer.style('display', 'none');
70261 drawLayer.selectAll('.qaItem.keepRight').remove();
70262 touchLayer.selectAll('.qaItem.keepRight').remove();
70264 } // Enable the layer. This shows the markers and transitions them to visible.
70267 function layerOn() {
70269 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
70270 return dispatch.call('change');
70272 } // Disable the layer. This transitions the layer invisible and then hides the markers.
70275 function layerOff() {
70276 throttledRedraw.cancel();
70277 drawLayer.interrupt();
70278 touchLayer.selectAll('.qaItem.keepRight').remove();
70279 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
70281 dispatch.call('change');
70283 } // Update the issue markers
70286 function updateMarkers() {
70287 if (!layerVisible || !_layerEnabled) return;
70288 var service = getService();
70289 var selectedID = context.selectedErrorID();
70290 var data = service ? service.getItems(projection) : [];
70291 var getTransform = svgPointTransform(projection); // Draw markers..
70293 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
70297 markers.exit().remove(); // enter
70299 var markersEnter = markers.enter().append('g').attr('class', function (d) {
70300 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
70302 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
70303 markersEnter.append('path').call(markerPath, 'shadow');
70304 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
70306 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
70307 return d.id === selectedID;
70308 }).attr('transform', getTransform); // Draw targets..
70310 if (touchLayer.empty()) return;
70311 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
70312 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
70316 targets.exit().remove(); // enter/update
70318 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
70319 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
70320 }).attr('transform', getTransform);
70322 function sortY(a, b) {
70323 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];
70325 } // Draw the keepRight layer and schedule loading issues and updating markers.
70328 function drawKeepRight(selection) {
70329 var service = getService();
70330 var surface = context.surface();
70332 if (surface && !surface.empty()) {
70333 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
70336 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
70337 drawLayer.exit().remove();
70338 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
70340 if (_layerEnabled) {
70341 if (service && ~~context.map().zoom() >= minZoom) {
70343 service.loadIssues(projection);
70349 } // Toggles the layer on and off
70352 drawKeepRight.enabled = function (val) {
70353 if (!arguments.length) return _layerEnabled;
70354 _layerEnabled = val;
70356 if (_layerEnabled) {
70361 if (context.selectedErrorID()) {
70362 context.enter(modeBrowse(context));
70366 dispatch.call('change');
70370 drawKeepRight.supported = function () {
70371 return !!getService();
70374 return drawKeepRight;
70377 function svgGeolocate(projection) {
70378 var layer = select(null);
70383 if (svgGeolocate.initialized) return; // run once
70385 svgGeolocate.enabled = false;
70386 svgGeolocate.initialized = true;
70389 function showLayer() {
70390 layer.style('display', 'block');
70393 function hideLayer() {
70394 layer.transition().duration(250).style('opacity', 0);
70397 function layerOn() {
70398 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
70401 function layerOff() {
70402 layer.style('display', 'none');
70405 function transform(d) {
70406 return svgPointTransform(projection)(d);
70409 function accuracy(accuracy, loc) {
70410 // converts accuracy to pixels...
70411 var degreesRadius = geoMetersToLat(accuracy),
70412 tangentLoc = [loc[0], loc[1] + degreesRadius],
70413 projectedTangent = projection(tangentLoc),
70414 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
70416 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
70419 function update() {
70420 var geolocation = {
70421 loc: [_position.coords.longitude, _position.coords.latitude]
70423 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
70424 groups.exit().remove();
70425 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
70426 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');
70427 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');
70428 groups.merge(pointsEnter).attr('transform', transform);
70429 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
70432 function drawLocation(selection) {
70433 var enabled = svgGeolocate.enabled;
70434 layer = selection.selectAll('.layer-geolocate').data([0]);
70435 layer.exit().remove();
70436 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
70437 layerEnter.append('g').attr('class', 'geolocations');
70438 layer = layerEnter.merge(layer);
70447 drawLocation.enabled = function (position, enabled) {
70448 if (!arguments.length) return svgGeolocate.enabled;
70449 _position = position;
70450 svgGeolocate.enabled = enabled;
70452 if (svgGeolocate.enabled) {
70463 return drawLocation;
70466 function svgLabels(projection, context) {
70467 var path = d3_geoPath(projection);
70468 var detected = utilDetect();
70469 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
70471 var _rdrawn = new RBush();
70473 var _rskipped = new RBush();
70475 var _textWidthCache = {};
70476 var _entitybboxes = {}; // Listed from highest to lowest priority
70478 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]];
70480 function shouldSkipIcon(preset) {
70481 var noIcons = ['building', 'landuse', 'natural'];
70482 return noIcons.some(function (s) {
70483 return preset.id.indexOf(s) >= 0;
70487 function get(array, prop) {
70488 return function (d, i) {
70489 return array[i][prop];
70493 function textWidth(text, size, elem) {
70494 var c = _textWidthCache[size];
70495 if (!c) c = _textWidthCache[size] = {};
70500 c[text] = elem.getComputedTextLength();
70503 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
70505 if (str === null) {
70506 return size / 3 * 2 * text.length;
70508 return size / 3 * (2 * text.length + str.length);
70513 function drawLinePaths(selection, entities, filter, classes, labels) {
70514 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
70516 paths.exit().remove(); // enter/update
70518 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
70519 return 'ideditor-labelpath-' + d.id;
70520 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
70523 function drawLineLabels(selection, entities, filter, classes, labels) {
70524 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
70526 texts.exit().remove(); // enter
70528 texts.enter().append('text').attr('class', function (d, i) {
70529 return classes + ' ' + labels[i].classes + ' ' + d.id;
70530 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
70532 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
70533 return '#ideditor-labelpath-' + d.id;
70534 }).text(utilDisplayNameForPath);
70537 function drawPointLabels(selection, entities, filter, classes, labels) {
70538 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
70540 texts.exit().remove(); // enter/update
70542 texts.enter().append('text').attr('class', function (d, i) {
70543 return classes + ' ' + labels[i].classes + ' ' + d.id;
70544 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
70545 textWidth(utilDisplayName(d), labels[i].height, this);
70549 function drawAreaLabels(selection, entities, filter, classes, labels) {
70550 entities = entities.filter(hasText);
70551 labels = labels.filter(hasText);
70552 drawPointLabels(selection, entities, filter, classes, labels);
70554 function hasText(d, i) {
70555 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
70559 function drawAreaIcons(selection, entities, filter, classes, labels) {
70560 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
70562 icons.exit().remove(); // enter/update
70564 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) {
70565 var preset = _mainPresetIndex.match(d, context.graph());
70566 var picon = preset && preset.icon;
70571 var isMaki = /^maki-/.test(picon);
70572 return '#' + picon + (isMaki ? '-15' : '');
70577 function drawCollisionBoxes(selection, rtree, which) {
70578 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
70581 if (context.getDebug('collision')) {
70582 gj = rtree.all().map(function (d) {
70585 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
70590 var boxes = selection.selectAll('.' + which).data(gj); // exit
70592 boxes.exit().remove(); // enter/update
70594 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
70597 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
70598 var wireframe = context.surface().classed('fill-wireframe');
70599 var zoom = geoScaleToZoom(projection.scale());
70600 var labelable = [];
70601 var renderNodeAs = {};
70602 var i, j, k, entity, geometry;
70604 for (i = 0; i < labelStack.length; i++) {
70605 labelable.push([]);
70613 _entitybboxes = {};
70615 for (i = 0; i < entities.length; i++) {
70616 entity = entities[i];
70617 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
70619 for (j = 0; j < toRemove.length; j++) {
70620 _rdrawn.remove(toRemove[j]);
70622 _rskipped.remove(toRemove[j]);
70625 } // Loop through all the entities to do some preprocessing
70628 for (i = 0; i < entities.length; i++) {
70629 entity = entities[i];
70630 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
70632 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
70633 var hasDirections = entity.directions(graph, projection).length;
70636 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
70637 renderNodeAs[entity.id] = 'point';
70638 markerPadding = 20; // extra y for marker height
70640 renderNodeAs[entity.id] = 'vertex';
70644 var coord = projection(entity.loc);
70645 var nodePadding = 10;
70647 minX: coord[0] - nodePadding,
70648 minY: coord[1] - nodePadding - markerPadding,
70649 maxX: coord[0] + nodePadding,
70650 maxY: coord[1] + nodePadding
70652 doInsert(bbox, entity.id + 'P');
70653 } // From here on, treat vertices like points
70656 if (geometry === 'vertex') {
70657 geometry = 'point';
70658 } // Determine which entities are label-able
70661 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
70662 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
70663 if (!icon && !utilDisplayName(entity)) continue;
70665 for (k = 0; k < labelStack.length; k++) {
70666 var matchGeom = labelStack[k][0];
70667 var matchKey = labelStack[k][1];
70668 var matchVal = labelStack[k][2];
70669 var hasVal = entity.tags[matchKey];
70671 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
70672 labelable[k].push(entity);
70687 }; // Try and find a valid label for labellable entities
70689 for (k = 0; k < labelable.length; k++) {
70690 var fontSize = labelStack[k][3];
70692 for (i = 0; i < labelable[k].length; i++) {
70693 entity = labelable[k][i];
70694 geometry = entity.geometry(graph);
70695 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
70696 var name = getName(entity);
70697 var width = name && textWidth(name, fontSize);
70700 if (geometry === 'point' || geometry === 'vertex') {
70701 // no point or vertex labels in wireframe mode
70702 // no vertex labels at low zooms (vertices have no icons)
70703 if (wireframe) continue;
70704 var renderAs = renderNodeAs[entity.id];
70705 if (renderAs === 'vertex' && zoom < 17) continue;
70706 p = getPointLabel(entity, width, fontSize, renderAs);
70707 } else if (geometry === 'line') {
70708 p = getLineLabel(entity, width, fontSize);
70709 } else if (geometry === 'area') {
70710 p = getAreaLabel(entity, width, fontSize);
70714 if (geometry === 'vertex') {
70715 geometry = 'point';
70716 } // treat vertex like point
70719 p.classes = geometry + ' tag-' + labelStack[k][1];
70720 positions[geometry].push(p);
70721 labelled[geometry].push(entity);
70726 function isInterestingVertex(entity) {
70727 var selectedIDs = context.selectedIDs();
70728 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
70729 return selectedIDs.indexOf(parent.id) !== -1;
70733 function getPointLabel(entity, width, height, geometry) {
70734 var y = geometry === 'point' ? -12 : 0;
70735 var pointOffsets = {
70736 ltr: [15, y, 'start'],
70737 rtl: [-15, y, 'end']
70739 var textDirection = _mainLocalizer.textDirection();
70740 var coord = projection(entity.loc);
70741 var textPadding = 2;
70742 var offset = pointOffsets[textDirection];
70746 x: coord[0] + offset[0],
70747 y: coord[1] + offset[1],
70748 textAnchor: offset[2]
70749 }; // insert a collision box for the text label..
70753 if (textDirection === 'rtl') {
70755 minX: p.x - width - textPadding,
70756 minY: p.y - height / 2 - textPadding,
70757 maxX: p.x + textPadding,
70758 maxY: p.y + height / 2 + textPadding
70762 minX: p.x - textPadding,
70763 minY: p.y - height / 2 - textPadding,
70764 maxX: p.x + width + textPadding,
70765 maxY: p.y + height / 2 + textPadding
70769 if (tryInsert([bbox], entity.id, true)) {
70774 function getLineLabel(entity, width, height) {
70775 var viewport = geoExtent(context.projection.clipExtent()).polygon();
70776 var points = graph.childNodes(entity).map(function (node) {
70777 return projection(node.loc);
70779 var length = geoPathLength(points);
70780 if (length < width + 20) return; // % along the line to attempt to place the label
70782 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
70785 for (var i = 0; i < lineOffsets.length; i++) {
70786 var offset = lineOffsets[i];
70787 var middle = offset / 100 * length;
70788 var start = middle - width / 2;
70789 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
70791 var sub = subpath(points, start, start + width);
70793 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
70797 var isReverse = reverse(sub);
70800 sub = sub.reverse();
70804 var boxsize = (height + 2) / 2;
70806 for (var j = 0; j < sub.length - 1; j++) {
70808 var b = sub[j + 1]; // split up the text into small collision boxes
70810 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
70812 for (var box = 0; box < num; box++) {
70813 var p = geoVecInterp(a, b, box / num);
70814 var x0 = p[0] - boxsize - padding;
70815 var y0 = p[1] - boxsize - padding;
70816 var x1 = p[0] + boxsize + padding;
70817 var y1 = p[1] + boxsize + padding;
70819 minX: Math.min(x0, x1),
70820 minY: Math.min(y0, y1),
70821 maxX: Math.max(x0, x1),
70822 maxY: Math.max(y0, y1)
70827 if (tryInsert(bboxes, entity.id, false)) {
70830 'font-size': height + 2,
70831 lineString: lineString(sub),
70832 startOffset: offset + '%'
70837 function reverse(p) {
70838 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
70839 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
70842 function lineString(points) {
70843 return 'M' + points.join('L');
70846 function subpath(points, from, to) {
70848 var start, end, i0, i1;
70850 for (var i = 0; i < points.length - 1; i++) {
70852 var b = points[i + 1];
70853 var current = geoVecLength(a, b);
70856 if (!start && sofar + current >= from) {
70857 portion = (from - sofar) / current;
70858 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
70862 if (!end && sofar + current >= to) {
70863 portion = (to - sofar) / current;
70864 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
70871 var result = points.slice(i0, i1);
70872 result.unshift(start);
70878 function getAreaLabel(entity, width, height) {
70879 var centroid = path.centroid(entity.asGeoJSON(graph, true));
70880 var extent = entity.extent(graph);
70881 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
70882 if (isNaN(centroid[0]) || areaWidth < 20) return;
70883 var preset = _mainPresetIndex.match(entity, context.graph());
70884 var picon = preset && preset.icon;
70890 // icon and label..
70892 addLabel(iconSize + padding);
70902 function addIcon() {
70903 var iconX = centroid[0] - iconSize / 2;
70904 var iconY = centroid[1] - iconSize / 2;
70908 maxX: iconX + iconSize,
70909 maxY: iconY + iconSize
70912 if (tryInsert([bbox], entity.id + 'I', true)) {
70913 p.transform = 'translate(' + iconX + ',' + iconY + ')';
70920 function addLabel(yOffset) {
70921 if (width && areaWidth >= width + 20) {
70922 var labelX = centroid[0];
70923 var labelY = centroid[1] + yOffset;
70925 minX: labelX - width / 2 - padding,
70926 minY: labelY - height / 2 - padding,
70927 maxX: labelX + width / 2 + padding,
70928 maxY: labelY + height / 2 + padding
70931 if (tryInsert([bbox], entity.id, true)) {
70934 p.textAnchor = 'middle';
70942 } // force insert a singular bounding box
70943 // singular box only, no array, id better be unique
70946 function doInsert(bbox, id) {
70948 var oldbox = _entitybboxes[id];
70951 _rdrawn.remove(oldbox);
70954 _entitybboxes[id] = bbox;
70956 _rdrawn.insert(bbox);
70959 function tryInsert(bboxes, id, saveSkipped) {
70960 var skipped = false;
70962 for (var i = 0; i < bboxes.length; i++) {
70963 var bbox = bboxes[i];
70964 bbox.id = id; // Check that label is visible
70966 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
70971 if (_rdrawn.collides(bbox)) {
70977 _entitybboxes[id] = bboxes;
70981 _rskipped.load(bboxes);
70984 _rdrawn.load(bboxes);
70990 var layer = selection.selectAll('.layer-osm.labels');
70991 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
70992 return 'labels-group ' + d;
70994 var halo = layer.selectAll('.labels-group.halo');
70995 var label = layer.selectAll('.labels-group.label');
70996 var debug = layer.selectAll('.labels-group.debug'); // points
70998 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
70999 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
71001 drawLinePaths(layer, labelled.line, filter, '', positions.line);
71002 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
71003 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
71005 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
71006 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
71007 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
71008 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
71010 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
71011 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
71012 layer.call(filterLabels);
71015 function filterLabels(selection) {
71016 var drawLayer = selection.selectAll('.layer-osm.labels');
71017 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
71018 layers.selectAll('.nolabel').classed('nolabel', false);
71019 var mouse = context.map().mouse();
71020 var graph = context.graph();
71021 var selectedIDs = context.selectedIDs();
71023 var pad, bbox; // hide labels near the mouse
71028 minX: mouse[0] - pad,
71029 minY: mouse[1] - pad,
71030 maxX: mouse[0] + pad,
71031 maxY: mouse[1] + pad
71034 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
71038 ids.push.apply(ids, nearMouse);
71039 } // hide labels on selected nodes (they look weird when dragging / haloed)
71042 for (var i = 0; i < selectedIDs.length; i++) {
71043 var entity = graph.hasEntity(selectedIDs[i]);
71045 if (entity && entity.type === 'node') {
71046 ids.push(selectedIDs[i]);
71050 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
71052 var debug = selection.selectAll('.labels-group.debug');
71055 if (context.getDebug('collision')) {
71058 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
71062 var box = debug.selectAll('.debug-mouse').data(gj); // exit
71064 box.exit().remove(); // enter/update
71066 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
71069 var throttleFilterLabels = throttle(filterLabels, 100);
71071 drawLabels.observe = function (selection) {
71072 var listener = function listener() {
71073 throttleFilterLabels(selection);
71076 selection.on('mousemove.hidelabels', listener);
71077 context.on('enter.hidelabels', listener);
71080 drawLabels.off = function (selection) {
71081 throttleFilterLabels.cancel();
71082 selection.on('mousemove.hidelabels', null);
71083 context.on('enter.hidelabels', null);
71089 var _layerEnabled$1 = false;
71093 function svgImproveOSM(projection, context, dispatch) {
71094 var throttledRedraw = throttle(function () {
71095 return dispatch.call('change');
71099 var touchLayer = select(null);
71100 var drawLayer = select(null);
71101 var layerVisible = false;
71103 function markerPath(selection, klass) {
71104 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');
71105 } // Loosely-coupled improveOSM service for fetching issues
71108 function getService() {
71109 if (services.improveOSM && !_qaService$1) {
71110 _qaService$1 = services.improveOSM;
71112 _qaService$1.on('loaded', throttledRedraw);
71113 } else if (!services.improveOSM && _qaService$1) {
71114 _qaService$1 = null;
71117 return _qaService$1;
71118 } // Show the markers
71121 function editOn() {
71122 if (!layerVisible) {
71123 layerVisible = true;
71124 drawLayer.style('display', 'block');
71126 } // Immediately remove the markers and their touch targets
71129 function editOff() {
71130 if (layerVisible) {
71131 layerVisible = false;
71132 drawLayer.style('display', 'none');
71133 drawLayer.selectAll('.qaItem.improveOSM').remove();
71134 touchLayer.selectAll('.qaItem.improveOSM').remove();
71136 } // Enable the layer. This shows the markers and transitions them to visible.
71139 function layerOn() {
71141 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71142 return dispatch.call('change');
71144 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71147 function layerOff() {
71148 throttledRedraw.cancel();
71149 drawLayer.interrupt();
71150 touchLayer.selectAll('.qaItem.improveOSM').remove();
71151 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
71153 dispatch.call('change');
71155 } // Update the issue markers
71158 function updateMarkers() {
71159 if (!layerVisible || !_layerEnabled$1) return;
71160 var service = getService();
71161 var selectedID = context.selectedErrorID();
71162 var data = service ? service.getItems(projection) : [];
71163 var getTransform = svgPointTransform(projection); // Draw markers..
71165 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
71169 markers.exit().remove(); // enter
71171 var markersEnter = markers.enter().append('g').attr('class', function (d) {
71172 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
71174 markersEnter.append('polygon').call(markerPath, 'shadow');
71175 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
71176 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
71177 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
71178 var picon = d.icon;
71183 var isMaki = /^maki-/.test(picon);
71184 return "#".concat(picon).concat(isMaki ? '-11' : '');
71188 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
71189 return d.id === selectedID;
71190 }).attr('transform', getTransform); // Draw targets..
71192 if (touchLayer.empty()) return;
71193 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71194 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
71198 targets.exit().remove(); // enter/update
71200 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
71201 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
71202 }).attr('transform', getTransform);
71204 function sortY(a, b) {
71205 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
71207 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
71210 function drawImproveOSM(selection) {
71211 var service = getService();
71212 var surface = context.surface();
71214 if (surface && !surface.empty()) {
71215 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71218 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
71219 drawLayer.exit().remove();
71220 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
71222 if (_layerEnabled$1) {
71223 if (service && ~~context.map().zoom() >= minZoom) {
71225 service.loadIssues(projection);
71231 } // Toggles the layer on and off
71234 drawImproveOSM.enabled = function (val) {
71235 if (!arguments.length) return _layerEnabled$1;
71236 _layerEnabled$1 = val;
71238 if (_layerEnabled$1) {
71243 if (context.selectedErrorID()) {
71244 context.enter(modeBrowse(context));
71248 dispatch.call('change');
71252 drawImproveOSM.supported = function () {
71253 return !!getService();
71256 return drawImproveOSM;
71259 var _layerEnabled$2 = false;
71263 function svgOsmose(projection, context, dispatch) {
71264 var throttledRedraw = throttle(function () {
71265 return dispatch.call('change');
71269 var touchLayer = select(null);
71270 var drawLayer = select(null);
71271 var layerVisible = false;
71273 function markerPath(selection, klass) {
71274 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');
71275 } // Loosely-coupled osmose service for fetching issues
71278 function getService() {
71279 if (services.osmose && !_qaService$2) {
71280 _qaService$2 = services.osmose;
71282 _qaService$2.on('loaded', throttledRedraw);
71283 } else if (!services.osmose && _qaService$2) {
71284 _qaService$2 = null;
71287 return _qaService$2;
71288 } // Show the markers
71291 function editOn() {
71292 if (!layerVisible) {
71293 layerVisible = true;
71294 drawLayer.style('display', 'block');
71296 } // Immediately remove the markers and their touch targets
71299 function editOff() {
71300 if (layerVisible) {
71301 layerVisible = false;
71302 drawLayer.style('display', 'none');
71303 drawLayer.selectAll('.qaItem.osmose').remove();
71304 touchLayer.selectAll('.qaItem.osmose').remove();
71306 } // Enable the layer. This shows the markers and transitions them to visible.
71309 function layerOn() {
71311 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71312 return dispatch.call('change');
71314 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71317 function layerOff() {
71318 throttledRedraw.cancel();
71319 drawLayer.interrupt();
71320 touchLayer.selectAll('.qaItem.osmose').remove();
71321 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
71323 dispatch.call('change');
71325 } // Update the issue markers
71328 function updateMarkers() {
71329 if (!layerVisible || !_layerEnabled$2) return;
71330 var service = getService();
71331 var selectedID = context.selectedErrorID();
71332 var data = service ? service.getItems(projection) : [];
71333 var getTransform = svgPointTransform(projection); // Draw markers..
71335 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
71339 markers.exit().remove(); // enter
71341 var markersEnter = markers.enter().append('g').attr('class', function (d) {
71342 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
71344 markersEnter.append('polygon').call(markerPath, 'shadow');
71345 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
71346 markersEnter.append('polygon').attr('fill', function (d) {
71347 return service.getColor(d.item);
71348 }).call(markerPath, 'qaItem-fill');
71349 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
71350 var picon = d.icon;
71355 var isMaki = /^maki-/.test(picon);
71356 return "#".concat(picon).concat(isMaki ? '-11' : '');
71360 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
71361 return d.id === selectedID;
71362 }).attr('transform', getTransform); // Draw targets..
71364 if (touchLayer.empty()) return;
71365 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
71366 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
71370 targets.exit().remove(); // enter/update
71372 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
71373 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
71374 }).attr('transform', getTransform);
71376 function sortY(a, b) {
71377 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
71379 } // Draw the Osmose layer and schedule loading issues and updating markers.
71382 function drawOsmose(selection) {
71383 var service = getService();
71384 var surface = context.surface();
71386 if (surface && !surface.empty()) {
71387 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71390 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
71391 drawLayer.exit().remove();
71392 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
71394 if (_layerEnabled$2) {
71395 if (service && ~~context.map().zoom() >= minZoom) {
71397 service.loadIssues(projection);
71403 } // Toggles the layer on and off
71406 drawOsmose.enabled = function (val) {
71407 if (!arguments.length) return _layerEnabled$2;
71408 _layerEnabled$2 = val;
71410 if (_layerEnabled$2) {
71411 // Strings supplied by Osmose fetched before showing layer for first time
71412 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
71413 // Also, If layer is toggled quickly multiple requests are sent
71414 getService().loadStrings().then(layerOn)["catch"](function (err) {
71415 console.log(err); // eslint-disable-line no-console
71420 if (context.selectedErrorID()) {
71421 context.enter(modeBrowse(context));
71425 dispatch.call('change');
71429 drawOsmose.supported = function () {
71430 return !!getService();
71436 function svgStreetside(projection, context, dispatch) {
71437 var throttledRedraw = throttle(function () {
71438 dispatch.call('change');
71442 var minMarkerZoom = 16;
71443 var minViewfieldZoom = 18;
71444 var layer = select(null);
71445 var _viewerYaw = 0;
71446 var _selectedSequence = null;
71455 if (svgStreetside.initialized) return; // run once
71457 svgStreetside.enabled = false;
71458 svgStreetside.initialized = true;
71465 function getService() {
71466 if (services.streetside && !_streetside) {
71467 _streetside = services.streetside;
71469 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
71470 } else if (!services.streetside && _streetside) {
71471 _streetside = null;
71474 return _streetside;
71481 function showLayer() {
71482 var service = getService();
71483 if (!service) return;
71485 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
71486 dispatch.call('change');
71494 function hideLayer() {
71495 throttledRedraw.cancel();
71496 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
71503 function editOn() {
71504 layer.style('display', 'block');
71511 function editOff() {
71512 layer.selectAll('.viewfield-group').remove();
71513 layer.style('display', 'none');
71516 * click() Handles 'bubble' point click event.
71520 function click(d3_event, d) {
71521 var service = getService();
71522 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
71524 if (d.sequenceKey !== _selectedSequence) {
71525 _viewerYaw = 0; // reset
71528 _selectedSequence = d.sequenceKey;
71529 service.ensureViewerLoaded(context).then(function () {
71530 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
71532 context.map().centerEase(d.loc);
71539 function mouseover(d3_event, d) {
71540 var service = getService();
71541 if (service) service.setStyles(context, d);
71548 function mouseout() {
71549 var service = getService();
71550 if (service) service.setStyles(context, null);
71557 function transform(d) {
71558 var t = svgPointTransform(projection)(d);
71559 var rot = d.ca + _viewerYaw;
71562 t += ' rotate(' + Math.floor(rot) + ',0,0)';
71568 function viewerChanged() {
71569 var service = getService();
71570 if (!service) return;
71571 var viewer = service.viewer();
71572 if (!viewer) return; // update viewfield rotation
71574 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
71575 // e.g. during drags or easing.
71577 if (context.map().isTransformed()) return;
71578 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
71581 function filterBubbles(bubbles) {
71582 var fromDate = context.photos().fromDate();
71583 var toDate = context.photos().toDate();
71584 var usernames = context.photos().usernames();
71587 var fromTimestamp = new Date(fromDate).getTime();
71588 bubbles = bubbles.filter(function (bubble) {
71589 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
71594 var toTimestamp = new Date(toDate).getTime();
71595 bubbles = bubbles.filter(function (bubble) {
71596 return new Date(bubble.captured_at).getTime() <= toTimestamp;
71601 bubbles = bubbles.filter(function (bubble) {
71602 return usernames.indexOf(bubble.captured_by) !== -1;
71609 function filterSequences(sequences) {
71610 var fromDate = context.photos().fromDate();
71611 var toDate = context.photos().toDate();
71612 var usernames = context.photos().usernames();
71615 var fromTimestamp = new Date(fromDate).getTime();
71616 sequences = sequences.filter(function (sequences) {
71617 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
71622 var toTimestamp = new Date(toDate).getTime();
71623 sequences = sequences.filter(function (sequences) {
71624 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
71629 sequences = sequences.filter(function (sequences) {
71630 return usernames.indexOf(sequences.properties.captured_by) !== -1;
71641 function update() {
71642 var viewer = context.container().select('.photoviewer');
71643 var selected = viewer.empty() ? undefined : viewer.datum();
71644 var z = ~~context.map().zoom();
71645 var showMarkers = z >= minMarkerZoom;
71646 var showViewfields = z >= minViewfieldZoom;
71647 var service = getService();
71648 var sequences = [];
71651 if (context.photos().showsPanoramic()) {
71652 sequences = service ? service.sequences(projection) : [];
71653 bubbles = service && showMarkers ? service.bubbles(projection) : [];
71654 sequences = filterSequences(sequences);
71655 bubbles = filterBubbles(bubbles);
71658 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
71659 return d.properties.key;
71662 traces.exit().remove(); // enter/update
71664 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
71665 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
71666 // force reenter once bubbles are attached to a sequence
71667 return d.key + (d.sequenceKey ? 'v1' : 'v0');
71670 groups.exit().remove(); // enter
71672 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
71673 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
71675 var markers = groups.merge(groupsEnter).sort(function (a, b) {
71676 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
71677 }).attr('transform', transform).select('.viewfield-scale');
71678 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
71679 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
71680 viewfields.exit().remove(); // viewfields may or may not be drawn...
71681 // but if they are, draw below the circles
71683 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
71685 function viewfieldPath() {
71686 var d = this.parentNode.__data__;
71689 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
71691 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
71697 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
71698 * 'svgStreetside()' is called from index.js
71702 function drawImages(selection) {
71703 var enabled = svgStreetside.enabled;
71704 var service = getService();
71705 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
71706 layer.exit().remove();
71707 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
71708 layerEnter.append('g').attr('class', 'sequences');
71709 layerEnter.append('g').attr('class', 'markers');
71710 layer = layerEnter.merge(layer);
71713 if (service && ~~context.map().zoom() >= minZoom) {
71716 service.loadBubbles(projection);
71723 * drawImages.enabled().
71727 drawImages.enabled = function (_) {
71728 if (!arguments.length) return svgStreetside.enabled;
71729 svgStreetside.enabled = _;
71731 if (svgStreetside.enabled) {
71733 context.photos().on('change.streetside', update);
71736 context.photos().on('change.streetside', null);
71739 dispatch.call('change');
71743 * drawImages.supported().
71747 drawImages.supported = function () {
71748 return !!getService();
71755 function svgMapillaryImages(projection, context, dispatch) {
71756 var throttledRedraw = throttle(function () {
71757 dispatch.call('change');
71761 var minMarkerZoom = 16;
71762 var minViewfieldZoom = 18;
71763 var layer = select(null);
71767 var viewerCompassAngle;
71770 if (svgMapillaryImages.initialized) return; // run once
71772 svgMapillaryImages.enabled = false;
71773 svgMapillaryImages.initialized = true;
71776 function getService() {
71777 if (services.mapillary && !_mapillary) {
71778 _mapillary = services.mapillary;
71780 _mapillary.event.on('loadedImages', throttledRedraw);
71781 } else if (!services.mapillary && _mapillary) {
71788 function showLayer() {
71789 var service = getService();
71790 if (!service) return;
71792 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
71793 dispatch.call('change');
71797 function hideLayer() {
71798 throttledRedraw.cancel();
71799 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
71802 function editOn() {
71803 layer.style('display', 'block');
71806 function editOff() {
71807 layer.selectAll('.viewfield-group').remove();
71808 layer.style('display', 'none');
71811 function click(d3_event, d) {
71812 var service = getService();
71813 if (!service) return;
71814 service.ensureViewerLoaded(context).then(function () {
71815 service.selectImage(context, d.key).showViewer(context);
71817 context.map().centerEase(d.loc);
71820 function mouseover(d) {
71821 var service = getService();
71822 if (service) service.setStyles(context, d);
71825 function mouseout() {
71826 var service = getService();
71827 if (service) service.setStyles(context, null);
71830 function transform(d) {
71831 var t = svgPointTransform(projection)(d);
71833 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
71834 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
71836 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
71842 function filterImages(images) {
71843 var showsPano = context.photos().showsPanoramic();
71844 var showsFlat = context.photos().showsFlat();
71845 var fromDate = context.photos().fromDate();
71846 var toDate = context.photos().toDate();
71847 var usernames = context.photos().usernames();
71849 if (!showsPano || !showsFlat) {
71850 images = images.filter(function (image) {
71851 if (image.pano) return showsPano;
71857 var fromTimestamp = new Date(fromDate).getTime();
71858 images = images.filter(function (image) {
71859 return new Date(image.captured_at).getTime() >= fromTimestamp;
71864 var toTimestamp = new Date(toDate).getTime();
71865 images = images.filter(function (image) {
71866 return new Date(image.captured_at).getTime() <= toTimestamp;
71871 images = images.filter(function (image) {
71872 return usernames.indexOf(image.captured_by) !== -1;
71879 function filterSequences(sequences, service) {
71880 var showsPano = context.photos().showsPanoramic();
71881 var showsFlat = context.photos().showsFlat();
71882 var fromDate = context.photos().fromDate();
71883 var toDate = context.photos().toDate();
71884 var usernames = context.photos().usernames();
71886 if (!showsPano || !showsFlat) {
71887 sequences = sequences.filter(function (sequence) {
71888 if (sequence.properties.hasOwnProperty('pano')) {
71889 if (sequence.properties.pano) return showsPano;
71892 // if the sequence doesn't specify pano or not, search its images
71893 var cProps = sequence.properties.coordinateProperties;
71895 if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
71896 for (var index in cProps.image_keys) {
71897 var imageKey = cProps.image_keys[index];
71898 var image = service.cachedImage(imageKey);
71900 if (image && image.hasOwnProperty('pano')) {
71901 if (image.pano) return showsPano;
71913 var fromTimestamp = new Date(fromDate).getTime();
71914 sequences = sequences.filter(function (sequence) {
71915 return new Date(sequence.properties.captured_at).getTime() >= fromTimestamp;
71920 var toTimestamp = new Date(toDate).getTime();
71921 sequences = sequences.filter(function (sequence) {
71922 return new Date(sequence.properties.captured_at).getTime() <= toTimestamp;
71927 sequences = sequences.filter(function (sequence) {
71928 return usernames.indexOf(sequence.properties.username) !== -1;
71935 function update() {
71936 var z = ~~context.map().zoom();
71937 var showMarkers = z >= minMarkerZoom;
71938 var showViewfields = z >= minViewfieldZoom;
71939 var service = getService();
71940 var sequences = service ? service.sequences(projection) : [];
71941 var images = service && showMarkers ? service.images(projection) : [];
71942 images = filterImages(images);
71943 sequences = filterSequences(sequences, service);
71944 service.filterViewer(context);
71945 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
71946 return d.properties.key;
71949 traces.exit().remove(); // enter/update
71951 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
71952 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
71956 groups.exit().remove(); // enter
71958 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
71959 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
71961 var markers = groups.merge(groupsEnter).sort(function (a, b) {
71962 return b.loc[1] - a.loc[1]; // sort Y
71963 }).attr('transform', transform).select('.viewfield-scale');
71964 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
71965 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
71966 viewfields.exit().remove();
71967 viewfields.enter() // viewfields may or may not be drawn...
71968 .insert('path', 'circle') // but if they are, draw below the circles
71969 .attr('class', 'viewfield').classed('pano', function () {
71970 return this.parentNode.__data__.pano;
71971 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
71973 function viewfieldPath() {
71974 var d = this.parentNode.__data__;
71977 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
71979 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
71984 function drawImages(selection) {
71985 var enabled = svgMapillaryImages.enabled;
71986 var service = getService();
71987 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
71988 layer.exit().remove();
71989 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
71990 layerEnter.append('g').attr('class', 'sequences');
71991 layerEnter.append('g').attr('class', 'markers');
71992 layer = layerEnter.merge(layer);
71995 if (service && ~~context.map().zoom() >= minZoom) {
71998 service.loadImages(projection);
72005 drawImages.enabled = function (_) {
72006 if (!arguments.length) return svgMapillaryImages.enabled;
72007 svgMapillaryImages.enabled = _;
72009 if (svgMapillaryImages.enabled) {
72011 context.photos().on('change.mapillary_images', update);
72014 context.photos().on('change.mapillary_images', null);
72017 dispatch.call('change');
72021 drawImages.supported = function () {
72022 return !!getService();
72029 function svgMapillaryPosition(projection, context) {
72030 var throttledRedraw = throttle(function () {
72035 var minViewfieldZoom = 18;
72036 var layer = select(null);
72040 var viewerCompassAngle;
72043 if (svgMapillaryPosition.initialized) return; // run once
72045 svgMapillaryPosition.initialized = true;
72048 function getService() {
72049 if (services.mapillary && !_mapillary) {
72050 _mapillary = services.mapillary;
72052 _mapillary.event.on('nodeChanged', throttledRedraw);
72054 _mapillary.event.on('bearingChanged', function (e) {
72055 viewerCompassAngle = e;
72056 if (context.map().isTransformed()) return;
72057 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
72059 }).attr('transform', transform);
72061 } else if (!services.mapillary && _mapillary) {
72068 function editOn() {
72069 layer.style('display', 'block');
72072 function editOff() {
72073 layer.selectAll('.viewfield-group').remove();
72074 layer.style('display', 'none');
72077 function transform(d) {
72078 var t = svgPointTransform(projection)(d);
72080 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72081 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72083 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72089 function update() {
72090 var z = ~~context.map().zoom();
72091 var showViewfields = z >= minViewfieldZoom;
72092 var service = getService();
72093 var node = service && service.getActiveImage();
72094 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(node ? [node] : [], function (d) {
72098 groups.exit().remove(); // enter
72100 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
72101 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72103 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
72104 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72105 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72106 viewfields.exit().remove();
72107 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').classed('pano', function () {
72108 return this.parentNode.__data__.pano;
72109 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72111 function viewfieldPath() {
72112 var d = this.parentNode.__data__;
72115 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72117 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72122 function drawImages(selection) {
72123 var service = getService();
72124 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
72125 layer.exit().remove();
72126 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
72127 layerEnter.append('g').attr('class', 'markers');
72128 layer = layerEnter.merge(layer);
72130 if (service && ~~context.map().zoom() >= minZoom) {
72138 drawImages.enabled = function () {
72143 drawImages.supported = function () {
72144 return !!getService();
72151 function svgMapillarySigns(projection, context, dispatch) {
72152 var throttledRedraw = throttle(function () {
72153 dispatch.call('change');
72157 var layer = select(null);
72162 if (svgMapillarySigns.initialized) return; // run once
72164 svgMapillarySigns.enabled = false;
72165 svgMapillarySigns.initialized = true;
72168 function getService() {
72169 if (services.mapillary && !_mapillary) {
72170 _mapillary = services.mapillary;
72172 _mapillary.event.on('loadedSigns', throttledRedraw);
72173 } else if (!services.mapillary && _mapillary) {
72180 function showLayer() {
72181 var service = getService();
72182 if (!service) return;
72183 service.loadSignResources(context);
72187 function hideLayer() {
72188 throttledRedraw.cancel();
72192 function editOn() {
72193 layer.style('display', 'block');
72196 function editOff() {
72197 layer.selectAll('.icon-sign').remove();
72198 layer.style('display', 'none');
72201 function click(d3_event, d) {
72202 var service = getService();
72203 if (!service) return;
72204 context.map().centerEase(d.loc);
72205 var selectedImageKey = service.getSelectedImageKey();
72207 var highlightedDetection; // Pick one of the images the sign was detected in,
72208 // preference given to an image already selected.
72210 d.detections.forEach(function (detection) {
72211 if (!imageKey || selectedImageKey === detection.image_key) {
72212 imageKey = detection.image_key;
72213 highlightedDetection = detection;
72217 if (imageKey === selectedImageKey) {
72218 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
72220 service.ensureViewerLoaded(context).then(function () {
72221 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
72226 function filterData(detectedFeatures) {
72227 var service = getService();
72228 var fromDate = context.photos().fromDate();
72229 var toDate = context.photos().toDate();
72230 var usernames = context.photos().usernames();
72233 var fromTimestamp = new Date(fromDate).getTime();
72234 detectedFeatures = detectedFeatures.filter(function (feature) {
72235 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
72240 var toTimestamp = new Date(toDate).getTime();
72241 detectedFeatures = detectedFeatures.filter(function (feature) {
72242 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
72246 if (usernames && service) {
72247 detectedFeatures = detectedFeatures.filter(function (feature) {
72248 return feature.detections.some(function (detection) {
72249 var imageKey = detection.image_key;
72250 var image = service.cachedImage(imageKey);
72251 return image && usernames.indexOf(image.captured_by) !== -1;
72256 return detectedFeatures;
72259 function update() {
72260 var service = getService();
72261 var data = service ? service.signs(projection) : [];
72262 data = filterData(data);
72263 var selectedImageKey = service.getSelectedImageKey();
72264 var transform = svgPointTransform(projection);
72265 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
72269 signs.exit().remove(); // enter
72271 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
72272 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
72273 return '#' + d.value;
72275 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
72277 signs.merge(enter).attr('transform', transform).classed('currentView', function (d) {
72278 return d.detections.some(function (detection) {
72279 return detection.image_key === selectedImageKey;
72281 }).sort(function (a, b) {
72282 var aSelected = a.detections.some(function (detection) {
72283 return detection.image_key === selectedImageKey;
72285 var bSelected = b.detections.some(function (detection) {
72286 return detection.image_key === selectedImageKey;
72289 if (aSelected === bSelected) {
72290 return b.loc[1] - a.loc[1]; // sort Y
72291 } else if (aSelected) {
72299 function drawSigns(selection) {
72300 var enabled = svgMapillarySigns.enabled;
72301 var service = getService();
72302 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
72303 layer.exit().remove();
72304 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
72307 if (service && ~~context.map().zoom() >= minZoom) {
72310 service.loadSigns(projection);
72311 service.showSignDetections(true);
72315 } else if (service) {
72316 service.showSignDetections(false);
72320 drawSigns.enabled = function (_) {
72321 if (!arguments.length) return svgMapillarySigns.enabled;
72322 svgMapillarySigns.enabled = _;
72324 if (svgMapillarySigns.enabled) {
72326 context.photos().on('change.mapillary_signs', update);
72329 context.photos().on('change.mapillary_signs', null);
72332 dispatch.call('change');
72336 drawSigns.supported = function () {
72337 return !!getService();
72344 function svgMapillaryMapFeatures(projection, context, dispatch) {
72345 var throttledRedraw = throttle(function () {
72346 dispatch.call('change');
72350 var layer = select(null);
72355 if (svgMapillaryMapFeatures.initialized) return; // run once
72357 svgMapillaryMapFeatures.enabled = false;
72358 svgMapillaryMapFeatures.initialized = true;
72361 function getService() {
72362 if (services.mapillary && !_mapillary) {
72363 _mapillary = services.mapillary;
72365 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
72366 } else if (!services.mapillary && _mapillary) {
72373 function showLayer() {
72374 var service = getService();
72375 if (!service) return;
72376 service.loadObjectResources(context);
72380 function hideLayer() {
72381 throttledRedraw.cancel();
72385 function editOn() {
72386 layer.style('display', 'block');
72389 function editOff() {
72390 layer.selectAll('.icon-map-feature').remove();
72391 layer.style('display', 'none');
72394 function click(d3_event, d) {
72395 var service = getService();
72396 if (!service) return;
72397 context.map().centerEase(d.loc);
72398 var selectedImageKey = service.getSelectedImageKey();
72400 var highlightedDetection; // Pick one of the images the map feature was detected in,
72401 // preference given to an image already selected.
72403 d.detections.forEach(function (detection) {
72404 if (!imageKey || selectedImageKey === detection.image_key) {
72405 imageKey = detection.image_key;
72406 highlightedDetection = detection;
72410 if (imageKey === selectedImageKey) {
72411 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
72413 service.ensureViewerLoaded(context).then(function () {
72414 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
72419 function filterData(detectedFeatures) {
72420 var service = getService();
72421 var fromDate = context.photos().fromDate();
72422 var toDate = context.photos().toDate();
72423 var usernames = context.photos().usernames();
72426 var fromTimestamp = new Date(fromDate).getTime();
72427 detectedFeatures = detectedFeatures.filter(function (feature) {
72428 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
72433 var toTimestamp = new Date(toDate).getTime();
72434 detectedFeatures = detectedFeatures.filter(function (feature) {
72435 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
72439 if (usernames && service) {
72440 detectedFeatures = detectedFeatures.filter(function (feature) {
72441 return feature.detections.some(function (detection) {
72442 var imageKey = detection.image_key;
72443 var image = service.cachedImage(imageKey);
72444 return image && usernames.indexOf(image.captured_by) !== -1;
72449 return detectedFeatures;
72452 function update() {
72453 var service = getService();
72454 var data = service ? service.mapFeatures(projection) : [];
72455 data = filterData(data);
72456 var selectedImageKey = service && service.getSelectedImageKey();
72457 var transform = svgPointTransform(projection);
72458 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
72462 mapFeatures.exit().remove(); // enter
72464 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
72465 enter.append('title').text(function (d) {
72466 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
72467 return _t('mapillary_map_features.' + id);
72469 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
72470 if (d.value === 'object--billboard') {
72471 // no billboard icon right now, so use the advertisement icon
72472 return '#object--sign--advertisement';
72475 return '#' + d.value;
72477 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
72479 mapFeatures.merge(enter).attr('transform', transform).classed('currentView', function (d) {
72480 return d.detections.some(function (detection) {
72481 return detection.image_key === selectedImageKey;
72483 }).sort(function (a, b) {
72484 var aSelected = a.detections.some(function (detection) {
72485 return detection.image_key === selectedImageKey;
72487 var bSelected = b.detections.some(function (detection) {
72488 return detection.image_key === selectedImageKey;
72491 if (aSelected === bSelected) {
72492 return b.loc[1] - a.loc[1]; // sort Y
72493 } else if (aSelected) {
72501 function drawMapFeatures(selection) {
72502 var enabled = svgMapillaryMapFeatures.enabled;
72503 var service = getService();
72504 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
72505 layer.exit().remove();
72506 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
72509 if (service && ~~context.map().zoom() >= minZoom) {
72512 service.loadMapFeatures(projection);
72513 service.showFeatureDetections(true);
72517 } else if (service) {
72518 service.showFeatureDetections(false);
72522 drawMapFeatures.enabled = function (_) {
72523 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
72524 svgMapillaryMapFeatures.enabled = _;
72526 if (svgMapillaryMapFeatures.enabled) {
72528 context.photos().on('change.mapillary_map_features', update);
72531 context.photos().on('change.mapillary_map_features', null);
72534 dispatch.call('change');
72538 drawMapFeatures.supported = function () {
72539 return !!getService();
72543 return drawMapFeatures;
72546 function svgOpenstreetcamImages(projection, context, dispatch) {
72547 var throttledRedraw = throttle(function () {
72548 dispatch.call('change');
72552 var minMarkerZoom = 16;
72553 var minViewfieldZoom = 18;
72554 var layer = select(null);
72556 var _openstreetcam;
72559 if (svgOpenstreetcamImages.initialized) return; // run once
72561 svgOpenstreetcamImages.enabled = false;
72562 svgOpenstreetcamImages.initialized = true;
72565 function getService() {
72566 if (services.openstreetcam && !_openstreetcam) {
72567 _openstreetcam = services.openstreetcam;
72569 _openstreetcam.event.on('loadedImages', throttledRedraw);
72570 } else if (!services.openstreetcam && _openstreetcam) {
72571 _openstreetcam = null;
72574 return _openstreetcam;
72577 function showLayer() {
72578 var service = getService();
72579 if (!service) return;
72581 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72582 dispatch.call('change');
72586 function hideLayer() {
72587 throttledRedraw.cancel();
72588 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72591 function editOn() {
72592 layer.style('display', 'block');
72595 function editOff() {
72596 layer.selectAll('.viewfield-group').remove();
72597 layer.style('display', 'none');
72600 function click(d3_event, d) {
72601 var service = getService();
72602 if (!service) return;
72603 service.ensureViewerLoaded(context).then(function () {
72604 service.selectImage(context, d.key).showViewer(context);
72606 context.map().centerEase(d.loc);
72609 function mouseover(d3_event, d) {
72610 var service = getService();
72611 if (service) service.setStyles(context, d);
72614 function mouseout() {
72615 var service = getService();
72616 if (service) service.setStyles(context, null);
72619 function transform(d) {
72620 var t = svgPointTransform(projection)(d);
72623 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72629 function filterImages(images) {
72630 var fromDate = context.photos().fromDate();
72631 var toDate = context.photos().toDate();
72632 var usernames = context.photos().usernames();
72635 var fromTimestamp = new Date(fromDate).getTime();
72636 images = images.filter(function (item) {
72637 return new Date(item.captured_at).getTime() >= fromTimestamp;
72642 var toTimestamp = new Date(toDate).getTime();
72643 images = images.filter(function (item) {
72644 return new Date(item.captured_at).getTime() <= toTimestamp;
72649 images = images.filter(function (item) {
72650 return usernames.indexOf(item.captured_by) !== -1;
72657 function filterSequences(sequences) {
72658 var fromDate = context.photos().fromDate();
72659 var toDate = context.photos().toDate();
72660 var usernames = context.photos().usernames();
72663 var fromTimestamp = new Date(fromDate).getTime();
72664 sequences = sequences.filter(function (image) {
72665 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
72670 var toTimestamp = new Date(toDate).getTime();
72671 sequences = sequences.filter(function (image) {
72672 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
72677 sequences = sequences.filter(function (image) {
72678 return usernames.indexOf(image.properties.captured_by) !== -1;
72685 function update() {
72686 var viewer = context.container().select('.photoviewer');
72687 var selected = viewer.empty() ? undefined : viewer.datum();
72688 var z = ~~context.map().zoom();
72689 var showMarkers = z >= minMarkerZoom;
72690 var showViewfields = z >= minViewfieldZoom;
72691 var service = getService();
72692 var sequences = [];
72695 if (context.photos().showsFlat()) {
72696 sequences = service ? service.sequences(projection) : [];
72697 images = service && showMarkers ? service.images(projection) : [];
72698 sequences = filterSequences(sequences);
72699 images = filterImages(images);
72702 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72703 return d.properties.key;
72706 traces.exit().remove(); // enter/update
72708 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72709 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
72713 groups.exit().remove(); // enter
72715 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72716 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72718 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72719 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
72720 }).attr('transform', transform).select('.viewfield-scale');
72721 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72722 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72723 viewfields.exit().remove();
72724 viewfields.enter() // viewfields may or may not be drawn...
72725 .insert('path', 'circle') // but if they are, draw below the circles
72726 .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');
72729 function drawImages(selection) {
72730 var enabled = svgOpenstreetcamImages.enabled,
72731 service = getService();
72732 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
72733 layer.exit().remove();
72734 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
72735 layerEnter.append('g').attr('class', 'sequences');
72736 layerEnter.append('g').attr('class', 'markers');
72737 layer = layerEnter.merge(layer);
72740 if (service && ~~context.map().zoom() >= minZoom) {
72743 service.loadImages(projection);
72750 drawImages.enabled = function (_) {
72751 if (!arguments.length) return svgOpenstreetcamImages.enabled;
72752 svgOpenstreetcamImages.enabled = _;
72754 if (svgOpenstreetcamImages.enabled) {
72756 context.photos().on('change.openstreetcam_images', update);
72759 context.photos().on('change.openstreetcam_images', null);
72762 dispatch.call('change');
72766 drawImages.supported = function () {
72767 return !!getService();
72774 function svgOsm(projection, context, dispatch) {
72775 var enabled = true;
72777 function drawOsm(selection) {
72778 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
72779 return 'layer-osm ' + d;
72781 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
72782 return 'points-group ' + d;
72786 function showLayer() {
72787 var layer = context.surface().selectAll('.data-layer.osm');
72789 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72790 dispatch.call('change');
72794 function hideLayer() {
72795 var layer = context.surface().selectAll('.data-layer.osm');
72797 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72798 layer.classed('disabled', true);
72799 dispatch.call('change');
72803 drawOsm.enabled = function (val) {
72804 if (!arguments.length) return enabled;
72813 dispatch.call('change');
72820 var _notesEnabled = false;
72824 function svgNotes(projection, context, dispatch$1) {
72826 dispatch$1 = dispatch('change');
72829 var throttledRedraw = throttle(function () {
72830 dispatch$1.call('change');
72834 var touchLayer = select(null);
72835 var drawLayer = select(null);
72836 var _notesVisible = false;
72838 function markerPath(selection, klass) {
72839 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');
72840 } // Loosely-coupled osm service for fetching notes.
72843 function getService() {
72844 if (services.osm && !_osmService) {
72845 _osmService = services.osm;
72847 _osmService.on('loadedNotes', throttledRedraw);
72848 } else if (!services.osm && _osmService) {
72849 _osmService = null;
72852 return _osmService;
72853 } // Show the notes
72856 function editOn() {
72857 if (!_notesVisible) {
72858 _notesVisible = true;
72859 drawLayer.style('display', 'block');
72861 } // Immediately remove the notes and their touch targets
72864 function editOff() {
72865 if (_notesVisible) {
72866 _notesVisible = false;
72867 drawLayer.style('display', 'none');
72868 drawLayer.selectAll('.note').remove();
72869 touchLayer.selectAll('.note').remove();
72871 } // Enable the layer. This shows the notes and transitions them to visible.
72874 function layerOn() {
72876 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72877 dispatch$1.call('change');
72879 } // Disable the layer. This transitions the layer invisible and then hides the notes.
72882 function layerOff() {
72883 throttledRedraw.cancel();
72884 drawLayer.interrupt();
72885 touchLayer.selectAll('.note').remove();
72886 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72888 dispatch$1.call('change');
72890 } // Update the note markers
72893 function updateMarkers() {
72894 if (!_notesVisible || !_notesEnabled) return;
72895 var service = getService();
72896 var selectedID = context.selectedNoteID();
72897 var data = service ? service.notes(projection) : [];
72898 var getTransform = svgPointTransform(projection); // Draw markers..
72900 var notes = drawLayer.selectAll('.note').data(data, function (d) {
72901 return d.status + d.id;
72904 notes.exit().remove(); // enter
72906 var notesEnter = notes.enter().append('g').attr('class', function (d) {
72907 return 'note note-' + d.id + ' ' + d.status;
72908 }).classed('new', function (d) {
72911 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
72912 notesEnter.append('path').call(markerPath, 'shadow');
72913 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');
72914 notesEnter.selectAll('.icon-annotation').data(function (d) {
72916 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
72917 return '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
72920 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
72921 var mode = context.mode();
72922 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
72924 return !isMoving && d.id === selectedID;
72925 }).attr('transform', getTransform); // Draw targets..
72927 if (touchLayer.empty()) return;
72928 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72929 var targets = touchLayer.selectAll('.note').data(data, function (d) {
72933 targets.exit().remove(); // enter/update
72935 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
72936 var newClass = d.id < 0 ? 'new' : '';
72937 return 'note target note-' + d.id + ' ' + fillClass + newClass;
72938 }).attr('transform', getTransform);
72940 function sortY(a, b) {
72941 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72943 } // Draw the notes layer and schedule loading notes and updating markers.
72946 function drawNotes(selection) {
72947 var service = getService();
72948 var surface = context.surface();
72950 if (surface && !surface.empty()) {
72951 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72954 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
72955 drawLayer.exit().remove();
72956 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
72958 if (_notesEnabled) {
72959 if (service && ~~context.map().zoom() >= minZoom) {
72961 service.loadNotes(projection);
72967 } // Toggles the layer on and off
72970 drawNotes.enabled = function (val) {
72971 if (!arguments.length) return _notesEnabled;
72972 _notesEnabled = val;
72974 if (_notesEnabled) {
72979 if (context.selectedNoteID()) {
72980 context.enter(modeBrowse(context));
72984 dispatch$1.call('change');
72991 function svgTouch() {
72992 function drawTouch(selection) {
72993 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
72994 return 'layer-touch ' + d;
73001 function refresh(selection, node) {
73002 var cr = node.getBoundingClientRect();
73003 var prop = [cr.width, cr.height];
73004 selection.property('__dimensions__', prop);
73008 function utilGetDimensions(selection, force) {
73009 if (!selection || selection.empty()) {
73013 var node = selection.node(),
73014 cached = selection.property('__dimensions__');
73015 return !cached || force ? refresh(selection, node) : cached;
73017 function utilSetDimensions(selection, dimensions) {
73018 if (!selection || selection.empty()) {
73022 var node = selection.node();
73024 if (dimensions === null) {
73025 refresh(selection, node);
73029 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
73032 function svgLayers(projection, context) {
73033 var dispatch$1 = dispatch('change');
73034 var svg = select(null);
73037 layer: svgOsm(projection, context, dispatch$1)
73040 layer: svgNotes(projection, context, dispatch$1)
73043 layer: svgData(projection, context, dispatch$1)
73046 layer: svgKeepRight(projection, context, dispatch$1)
73049 layer: svgImproveOSM(projection, context, dispatch$1)
73052 layer: svgOsmose(projection, context, dispatch$1)
73055 layer: svgStreetside(projection, context, dispatch$1)
73058 layer: svgMapillaryImages(projection, context, dispatch$1)
73060 id: 'mapillary-position',
73061 layer: svgMapillaryPosition(projection, context)
73063 id: 'mapillary-map-features',
73064 layer: svgMapillaryMapFeatures(projection, context, dispatch$1)
73066 id: 'mapillary-signs',
73067 layer: svgMapillarySigns(projection, context, dispatch$1)
73069 id: 'openstreetcam',
73070 layer: svgOpenstreetcamImages(projection, context, dispatch$1)
73073 layer: svgDebug(projection, context)
73076 layer: svgGeolocate(projection)
73082 function drawLayers(selection) {
73083 svg = selection.selectAll('.surface').data([0]);
73084 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
73085 var defs = svg.selectAll('.surface-defs').data([0]);
73086 defs.enter().append('defs').attr('class', 'surface-defs');
73087 var groups = svg.selectAll('.data-layer').data(_layers);
73088 groups.exit().remove();
73089 groups.enter().append('g').attr('class', function (d) {
73090 return 'data-layer ' + d.id;
73091 }).merge(groups).each(function (d) {
73092 select(this).call(d.layer);
73096 drawLayers.all = function () {
73100 drawLayers.layer = function (id) {
73101 var obj = _layers.find(function (o) {
73102 return o.id === id;
73105 return obj && obj.layer;
73108 drawLayers.only = function (what) {
73109 var arr = [].concat(what);
73111 var all = _layers.map(function (layer) {
73115 return drawLayers.remove(utilArrayDifference(all, arr));
73118 drawLayers.remove = function (what) {
73119 var arr = [].concat(what);
73120 arr.forEach(function (id) {
73121 _layers = _layers.filter(function (o) {
73122 return o.id !== id;
73125 dispatch$1.call('change');
73129 drawLayers.add = function (what) {
73130 var arr = [].concat(what);
73131 arr.forEach(function (obj) {
73132 if ('id' in obj && 'layer' in obj) {
73136 dispatch$1.call('change');
73140 drawLayers.dimensions = function (val) {
73141 if (!arguments.length) return utilGetDimensions(svg);
73142 utilSetDimensions(svg, val);
73146 return utilRebind(drawLayers, dispatch$1, 'on');
73149 function svgLines(projection, context) {
73150 var detected = utilDetect();
73151 var highway_stack = {
73166 function drawTargets(selection, graph, entities, filter) {
73167 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73168 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
73169 var getPath = svgPath(projection).geojson;
73170 var activeID = context.activeID();
73171 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
73177 entities.forEach(function (way) {
73178 var features = svgSegmentWay(way, graph, activeID);
73179 data.targets.push.apply(data.targets, features.passive);
73180 data.nopes.push.apply(data.nopes, features.active);
73181 }); // Targets allow hover and vertex snapping
73183 var targetData = data.targets.filter(getPath);
73184 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
73185 return filter(d.properties.entity);
73186 }).data(targetData, function key(d) {
73190 targets.exit().remove();
73192 var segmentWasEdited = function segmentWasEdited(d) {
73193 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
73195 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
73199 return d.properties.nodes.some(function (n) {
73200 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
73205 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
73206 return 'way line target target-allowed ' + targetClass + d.id;
73207 }).classed('segment-edited', segmentWasEdited); // NOPE
73209 var nopeData = data.nopes.filter(getPath);
73210 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
73211 return filter(d.properties.entity);
73212 }).data(nopeData, function key(d) {
73216 nopes.exit().remove(); // enter/update
73218 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
73219 return 'way line target target-nope ' + nopeClass + d.id;
73220 }).classed('segment-edited', segmentWasEdited);
73223 function drawLines(selection, graph, entities, filter) {
73224 var base = context.history().base();
73226 function waystack(a, b) {
73227 var selected = context.selectedIDs();
73228 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
73229 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
73231 if (a.tags.highway) {
73232 scoreA -= highway_stack[a.tags.highway];
73235 if (b.tags.highway) {
73236 scoreB -= highway_stack[b.tags.highway];
73239 return scoreA - scoreB;
73242 function drawLineGroup(selection, klass, isSelected) {
73243 // Note: Don't add `.selected` class in draw modes
73244 var mode = context.mode();
73245 var isDrawing = mode && /^draw/.test(mode.id);
73246 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
73247 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
73248 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
73249 // works because osmEntity.key is defined to include the entity v attribute.
73251 lines.enter().append('path').attr('class', function (d) {
73252 var prefix = 'way line'; // if this line isn't styled by its own tags
73254 if (!d.hasInterestingTags()) {
73255 var parentRelations = graph.parentRelations(d);
73256 var parentMultipolygons = parentRelations.filter(function (relation) {
73257 return relation.isMultipolygon();
73258 }); // and if it's a member of at least one multipolygon relation
73260 if (parentMultipolygons.length > 0 && // and only multipolygon relations
73261 parentRelations.length === parentMultipolygons.length) {
73262 // then fudge the classes to style this as an area edge
73263 prefix = 'relation area';
73267 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
73268 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
73269 }).classed('added', function (d) {
73270 return !base.entities[d.id];
73271 }).classed('geometry-edited', function (d) {
73272 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
73273 }).classed('retagged', function (d) {
73274 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
73275 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
73279 function getPathData(isSelected) {
73280 return function () {
73281 var layer = this.parentNode.__data__;
73282 var data = pathdata[layer] || [];
73283 return data.filter(function (d) {
73284 if (isSelected) return context.selectedIDs().indexOf(d.id) !== -1;else return context.selectedIDs().indexOf(d.id) === -1;
73289 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
73290 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
73291 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
73292 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
73293 return groupdata[this.parentNode.__data__] || [];
73294 }, function key(d) {
73295 return [d.id, d.index];
73297 markers.exit().remove();
73298 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
73303 markers.each(function () {
73304 this.parentNode.insertBefore(this, this);
73309 var getPath = svgPath(projection, graph);
73311 var onewaydata = {};
73312 var sideddata = {};
73313 var oldMultiPolygonOuters = {};
73315 for (var i = 0; i < entities.length; i++) {
73316 var entity = entities[i];
73317 var outer = osmOldMultipolygonOuterMember(entity, graph);
73320 ways.push(entity.mergeTags(outer.tags));
73321 oldMultiPolygonOuters[outer.id] = true;
73322 } else if (entity.geometry(graph) === 'line') {
73327 ways = ways.filter(getPath);
73328 var pathdata = utilArrayGroupBy(ways, function (way) {
73329 return way.layer();
73331 Object.keys(pathdata).forEach(function (k) {
73332 var v = pathdata[k];
73333 var onewayArr = v.filter(function (d) {
73334 return d.isOneWay();
73336 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
73337 return entity.tags.oneway === '-1';
73338 }, function bothDirections(entity) {
73339 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
73341 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
73342 var sidedArr = v.filter(function (d) {
73343 return d.isSided();
73345 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
73347 }, function bothDirections() {
73350 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
73352 var covered = selection.selectAll('.layer-osm.covered'); // under areas
73354 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
73356 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
73358 [covered, uncovered].forEach(function (selection) {
73359 var range$1 = selection === covered ? range(-10, 0) : range(0, 11);
73360 var layergroup = selection.selectAll('g.layergroup').data(range$1);
73361 layergroup = layergroup.enter().append('g').attr('class', function (d) {
73362 return 'layergroup layer' + String(d);
73363 }).merge(layergroup);
73364 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
73365 return 'linegroup line-' + d;
73367 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
73368 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
73369 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
73370 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
73371 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
73372 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
73373 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
73374 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
73375 var category = graph.entity(d.id).sidednessIdentifier();
73376 return 'url(#ideditor-sided-marker-' + category + ')';
73378 }); // Draw touch targets..
73380 touchLayer.call(drawTargets, graph, ways, filter);
73386 function svgMidpoints(projection, context) {
73387 var targetRadius = 8;
73389 function drawTargets(selection, graph, entities, filter) {
73390 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73391 var getTransform = svgPointTransform(projection).geojson;
73392 var data = entities.map(function (midpoint) {
73402 coordinates: midpoint.loc
73406 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
73407 return filter(d.properties.entity);
73408 }).data(data, function key(d) {
73412 targets.exit().remove(); // enter/update
73414 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
73415 return 'node midpoint target ' + fillClass + d.id;
73416 }).attr('transform', getTransform);
73419 function drawMidpoints(selection, graph, entities, filter, extent) {
73420 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
73421 var touchLayer = selection.selectAll('.layer-touch.points');
73422 var mode = context.mode();
73424 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
73425 drawLayer.selectAll('.midpoint').remove();
73426 touchLayer.selectAll('.midpoint.target').remove();
73430 var poly = extent.polygon();
73431 var midpoints = {};
73433 for (var i = 0; i < entities.length; i++) {
73434 var entity = entities[i];
73435 if (entity.type !== 'way') continue;
73436 if (!filter(entity)) continue;
73437 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
73438 var nodes = graph.childNodes(entity);
73440 for (var j = 0; j < nodes.length - 1; j++) {
73442 var b = nodes[j + 1];
73443 var id = [a.id, b.id].sort().join('-');
73445 if (midpoints[id]) {
73446 midpoints[id].parents.push(entity);
73447 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
73448 var point = geoVecInterp(a.loc, b.loc, 0.5);
73451 if (extent.intersects(point)) {
73454 for (var k = 0; k < 4; k++) {
73455 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
73457 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
73469 edge: [a.id, b.id],
73477 function midpointFilter(d) {
73478 if (midpoints[d.id]) return true;
73480 for (var i = 0; i < d.parents.length; i++) {
73481 if (filter(d.parents[i])) {
73489 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
73492 groups.exit().remove();
73493 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
73494 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
73495 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
73496 groups = groups.merge(enter).attr('transform', function (d) {
73497 var translate = svgPointTransform(projection);
73498 var a = graph.entity(d.edge[0]);
73499 var b = graph.entity(d.edge[1]);
73500 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
73501 return translate(d) + ' rotate(' + angle + ')';
73502 }).call(svgTagClasses().tags(function (d) {
73503 return d.parents[0].tags;
73504 })); // Propagate data bindings.
73506 groups.select('polygon.shadow');
73507 groups.select('polygon.fill'); // Draw touch targets..
73509 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
73512 return drawMidpoints;
73515 function svgPoints(projection, context) {
73516 function markerPath(selection, klass) {
73517 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');
73520 function sortY(a, b) {
73521 return b.loc[1] - a.loc[1];
73522 } // Avoid exit/enter if we're just moving stuff around.
73523 // The node will get a new version but we only need to run the update selection.
73526 function fastEntityKey(d) {
73527 var mode = context.mode();
73528 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
73529 return isMoving ? d.id : osmEntity.key(d);
73532 function drawTargets(selection, graph, entities, filter) {
73533 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73534 var getTransform = svgPointTransform(projection).geojson;
73535 var activeID = context.activeID();
73537 entities.forEach(function (node) {
73538 if (activeID === node.id) return; // draw no target on the activeID
73547 geometry: node.asGeoJSON()
73550 var targets = selection.selectAll('.point.target').filter(function (d) {
73551 return filter(d.properties.entity);
73552 }).data(data, function key(d) {
73556 targets.exit().remove(); // enter/update
73558 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
73559 return 'node point target ' + fillClass + d.id;
73560 }).attr('transform', getTransform);
73563 function drawPoints(selection, graph, entities, filter) {
73564 var wireframe = context.surface().classed('fill-wireframe');
73565 var zoom = geoScaleToZoom(projection.scale());
73566 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
73568 function renderAsPoint(entity) {
73569 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
73570 } // All points will render as vertices in wireframe mode too..
73573 var points = wireframe ? [] : entities.filter(renderAsPoint);
73574 points.sort(sortY);
73575 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
73576 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
73578 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
73579 groups.exit().remove();
73580 var enter = groups.enter().append('g').attr('class', function (d) {
73581 return 'node point ' + d.id;
73583 enter.append('path').call(markerPath, 'shadow');
73584 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
73585 enter.append('path').call(markerPath, 'stroke');
73586 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
73587 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
73588 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
73589 }).classed('moved', function (d) {
73590 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
73591 }).classed('retagged', function (d) {
73592 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
73593 }).call(svgTagClasses());
73594 groups.select('.shadow'); // propagate bound data
73596 groups.select('.stroke'); // propagate bound data
73598 groups.select('.icon') // propagate bound data
73599 .attr('xlink:href', function (entity) {
73600 var preset = _mainPresetIndex.match(entity, graph);
73601 var picon = preset && preset.icon;
73606 var isMaki = /^maki-/.test(picon);
73607 return '#' + picon + (isMaki ? '-11' : '');
73609 }); // Draw touch targets..
73611 touchLayer.call(drawTargets, graph, points, filter);
73617 function svgTurns(projection, context) {
73618 function icon(turn) {
73619 var u = turn.u ? '-u' : '';
73620 if (turn.no) return '#iD-turn-no' + u;
73621 if (turn.only) return '#iD-turn-only' + u;
73622 return '#iD-turn-yes' + u;
73625 function drawTurns(selection, graph, turns) {
73626 function turnTransform(d) {
73628 var toWay = graph.entity(d.to.way);
73629 var toPoints = graph.childNodes(toWay).map(function (n) {
73631 }).map(projection);
73632 var toLength = geoPathLength(toPoints);
73633 var mid = toLength / 2; // midpoint of destination way
73635 var toNode = graph.entity(d.to.node);
73636 var toVertex = graph.entity(d.to.vertex);
73637 var a = geoAngle(toVertex, toNode, projection);
73638 var o = projection(toVertex.loc);
73639 var r = d.u ? 0 // u-turn: no radius
73640 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
73641 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
73643 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
73646 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
73647 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
73649 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
73653 groups.exit().remove(); // enter
73655 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
73656 return 'turn ' + d.key;
73658 var turnsEnter = groupsEnter.filter(function (d) {
73661 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
73662 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
73663 var uEnter = groupsEnter.filter(function (d) {
73666 uEnter.append('circle').attr('r', '16');
73667 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
73669 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
73670 return d.direct === false ? '0.7' : null;
73671 }).attr('transform', turnTransform);
73672 groups.select('use').attr('xlink:href', icon);
73673 groups.select('rect'); // propagate bound data
73675 groups.select('circle'); // propagate bound data
73676 // Draw touch targets..
73678 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73679 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
73683 groups.exit().remove(); // enter
73685 groupsEnter = groups.enter().append('g').attr('class', function (d) {
73686 return 'turn ' + d.key;
73688 turnsEnter = groupsEnter.filter(function (d) {
73691 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
73692 uEnter = groupsEnter.filter(function (d) {
73695 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
73697 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
73698 groups.select('rect'); // propagate bound data
73700 groups.select('circle'); // propagate bound data
73708 function svgVertices(projection, context) {
73710 // z16-, z17, z18+, w/icon
73711 shadow: [6, 7.5, 7.5, 12],
73712 stroke: [2.5, 3.5, 3.5, 8],
73713 fill: [1, 1.5, 1.5, 1.5]
73716 var _currHoverTarget;
73718 var _currPersistent = {};
73719 var _currHover = {};
73720 var _prevHover = {};
73721 var _currSelected = {};
73722 var _prevSelected = {};
73725 function sortY(a, b) {
73726 return b.loc[1] - a.loc[1];
73727 } // Avoid exit/enter if we're just moving stuff around.
73728 // The node will get a new version but we only need to run the update selection.
73731 function fastEntityKey(d) {
73732 var mode = context.mode();
73733 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
73734 return isMoving ? d.id : osmEntity.key(d);
73737 function draw(selection, graph, vertices, sets, filter) {
73744 var directions = {};
73745 var wireframe = context.surface().classed('fill-wireframe');
73746 var zoom = geoScaleToZoom(projection.scale());
73747 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
73748 var activeID = context.activeID();
73749 var base = context.history().base();
73751 function getIcon(d) {
73752 // always check latest entity, as fastEntityKey avoids enter/exit now
73753 var entity = graph.entity(d.id);
73754 if (entity.id in icons) return icons[entity.id];
73755 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
73756 return icons[entity.id];
73757 } // memoize directions results, return false for empty arrays (for use in filter)
73760 function getDirections(entity) {
73761 if (entity.id in directions) return directions[entity.id];
73762 var angles = entity.directions(graph, projection);
73763 directions[entity.id] = angles.length ? angles : false;
73767 function updateAttributes(selection) {
73768 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
73769 var rads = radiuses[klass];
73770 selection.selectAll('.' + klass).each(function (entity) {
73771 var i = z && getIcon(entity);
73772 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
73774 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
73778 if (klass === 'shadow') {
73779 // remember this value, so we don't need to
73780 _radii[entity.id] = r; // recompute it when we draw the touch targets
73783 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
73788 vertices.sort(sortY);
73789 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
73791 groups.exit().remove(); // enter
73793 var enter = groups.enter().append('g').attr('class', function (d) {
73794 return 'node vertex ' + d.id;
73796 enter.append('circle').attr('class', 'shadow');
73797 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
73799 enter.filter(function (d) {
73800 return d.hasInterestingTags();
73801 }).append('circle').attr('class', 'fill'); // update
73803 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
73804 return d.id in sets.selected;
73805 }).classed('shared', function (d) {
73806 return graph.isShared(d);
73807 }).classed('endpoint', function (d) {
73808 return d.isEndpoint(graph);
73809 }).classed('added', function (d) {
73810 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
73811 }).classed('moved', function (d) {
73812 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
73813 }).classed('retagged', function (d) {
73814 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
73815 }).call(updateAttributes); // Vertices with icons get a `use`.
73817 var iconUse = groups.selectAll('.icon').data(function data(d) {
73818 return zoom >= 17 && getIcon(d) ? [d] : [];
73819 }, fastEntityKey); // exit
73821 iconUse.exit().remove(); // enter
73823 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) {
73824 var picon = getIcon(d);
73825 var isMaki = /^maki-/.test(picon);
73826 return '#' + picon + (isMaki ? '-11' : '');
73827 }); // Vertices with directions get viewfields
73829 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
73830 return zoom >= 18 && getDirections(d) ? [d] : [];
73831 }, fastEntityKey); // exit
73833 dgroups.exit().remove(); // enter/update
73835 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
73836 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
73837 return osmEntity.key(d);
73840 viewfields.exit().remove(); // enter/update
73842 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) {
73843 return 'rotate(' + d + ')';
73847 function drawTargets(selection, graph, entities, filter) {
73848 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73849 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
73850 var getTransform = svgPointTransform(projection).geojson;
73851 var activeID = context.activeID();
73856 entities.forEach(function (node) {
73857 if (activeID === node.id) return; // draw no target on the activeID
73859 var vertexType = svgPassiveVertex(node, graph, activeID);
73861 if (vertexType !== 0) {
73862 // passive or adjacent - allow to connect
73863 data.targets.push({
73870 geometry: node.asGeoJSON()
73875 id: node.id + '-nope',
73881 geometry: node.asGeoJSON()
73884 }); // Targets allow hover and vertex snapping
73886 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
73887 return filter(d.properties.entity);
73888 }).data(data.targets, function key(d) {
73892 targets.exit().remove(); // enter/update
73894 targets.enter().append('circle').attr('r', function (d) {
73895 return _radii[d.id] || radiuses.shadow[3];
73896 }).merge(targets).attr('class', function (d) {
73897 return 'node vertex target target-allowed ' + targetClass + d.id;
73898 }).attr('transform', getTransform); // NOPE
73900 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
73901 return filter(d.properties.entity);
73902 }).data(data.nopes, function key(d) {
73906 nopes.exit().remove(); // enter/update
73908 nopes.enter().append('circle').attr('r', function (d) {
73909 return _radii[d.properties.entity.id] || radiuses.shadow[3];
73910 }).merge(nopes).attr('class', function (d) {
73911 return 'node vertex target target-nope ' + nopeClass + d.id;
73912 }).attr('transform', getTransform);
73913 } // Points can also render as vertices:
73914 // 1. in wireframe mode or
73915 // 2. at higher zooms if they have a direction
73918 function renderAsVertex(entity, graph, wireframe, zoom) {
73919 var geometry = entity.geometry(graph);
73920 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
73923 function isEditedNode(node, base, head) {
73924 var baseNode = base.entities[node.id];
73925 var headNode = head.entities[node.id];
73926 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
73929 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
73933 function addChildVertices(entity) {
73934 // avoid redundant work and infinite recursion of circular relations
73935 if (seenIds[entity.id]) return;
73936 seenIds[entity.id] = true;
73937 var geometry = entity.geometry(graph);
73939 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
73942 if (entity.type === 'way') {
73943 for (i = 0; i < entity.nodes.length; i++) {
73944 var child = graph.hasEntity(entity.nodes[i]);
73947 addChildVertices(child);
73950 } else if (entity.type === 'relation') {
73951 for (i = 0; i < entity.members.length; i++) {
73952 var member = graph.hasEntity(entity.members[i].id);
73955 addChildVertices(member);
73958 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
73959 results[entity.id] = entity;
73964 ids.forEach(function (id) {
73965 var entity = graph.hasEntity(id);
73966 if (!entity) return;
73968 if (entity.type === 'node') {
73969 if (renderAsVertex(entity, graph, wireframe, zoom)) {
73970 results[entity.id] = entity;
73971 graph.parentWays(entity).forEach(function (entity) {
73972 addChildVertices(entity);
73977 addChildVertices(entity);
73983 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
73984 var wireframe = context.surface().classed('fill-wireframe');
73985 var visualDiff = context.surface().classed('highlight-edited');
73986 var zoom = geoScaleToZoom(projection.scale());
73987 var mode = context.mode();
73988 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
73989 var base = context.history().base();
73990 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
73991 var touchLayer = selection.selectAll('.layer-touch.points');
73994 _currPersistent = {};
73996 } // Collect important vertices from the `entities` list..
73997 // (during a partial redraw, it will not contain everything)
74000 for (var i = 0; i < entities.length; i++) {
74001 var entity = entities[i];
74002 var geometry = entity.geometry(graph);
74003 var keep = false; // a point that looks like a vertex..
74005 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
74006 _currPersistent[entity.id] = entity;
74007 keep = true; // a vertex of some importance..
74008 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
74009 _currPersistent[entity.id] = entity;
74011 } // whatever this is, it's not a persistent vertex..
74014 if (!keep && !fullRedraw) {
74015 delete _currPersistent[entity.id];
74017 } // 3 sets of vertices to consider:
74021 persistent: _currPersistent,
74022 // persistent = important vertices (render always)
74023 selected: _currSelected,
74024 // selected + siblings of selected (render always)
74025 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
74028 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
74029 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
74030 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
74032 var filterRendered = function filterRendered(d) {
74033 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
74036 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
74037 // When drawing, render all targets (not just those affected by a partial redraw)
74039 var filterTouch = function filterTouch(d) {
74040 return isMoving ? true : filterRendered(d);
74043 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
74045 function currentVisible(which) {
74046 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
74047 .filter(function (entity) {
74048 return entity && entity.intersects(extent, graph);
74051 } // partial redraw - only update the selected items..
74054 drawVertices.drawSelected = function (selection, graph, extent) {
74055 var wireframe = context.surface().classed('fill-wireframe');
74056 var zoom = geoScaleToZoom(projection.scale());
74057 _prevSelected = _currSelected || {};
74059 if (context.map().isInWideSelection()) {
74060 _currSelected = {};
74061 context.selectedIDs().forEach(function (id) {
74062 var entity = graph.hasEntity(id);
74063 if (!entity) return;
74065 if (entity.type === 'node') {
74066 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74067 _currSelected[entity.id] = entity;
74072 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
74073 } // note that drawVertices will add `_currSelected` automatically if needed..
74076 var filter = function filter(d) {
74077 return d.id in _prevSelected;
74080 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
74081 }; // partial redraw - only update the hovered items..
74084 drawVertices.drawHover = function (selection, graph, target, extent) {
74085 if (target === _currHoverTarget) return; // continue only if something changed
74087 var wireframe = context.surface().classed('fill-wireframe');
74088 var zoom = geoScaleToZoom(projection.scale());
74089 _prevHover = _currHover || {};
74090 _currHoverTarget = target;
74091 var entity = target && target.properties && target.properties.entity;
74094 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
74097 } // note that drawVertices will add `_currHover` automatically if needed..
74100 var filter = function filter(d) {
74101 return d.id in _prevHover;
74104 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
74107 return drawVertices;
74110 function utilBindOnce(target, type, listener, capture) {
74111 var typeOnce = type + '.once';
74114 target.on(typeOnce, null);
74115 listener.apply(this, arguments);
74118 target.on(typeOnce, one, capture);
74122 function defaultFilter$2(d3_event) {
74123 return !d3_event.ctrlKey && !d3_event.button;
74126 function defaultExtent$1() {
74129 if (e instanceof SVGElement) {
74130 e = e.ownerSVGElement || e;
74132 if (e.hasAttribute('viewBox')) {
74133 e = e.viewBox.baseVal;
74134 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
74137 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
74140 return [[0, 0], [e.clientWidth, e.clientHeight]];
74143 function defaultWheelDelta$1(d3_event) {
74144 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
74147 function defaultConstrain$1(transform, extent, translateExtent) {
74148 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
74149 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
74150 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
74151 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
74152 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));
74155 function utilZoomPan() {
74156 var filter = defaultFilter$2,
74157 extent = defaultExtent$1,
74158 constrain = defaultConstrain$1,
74159 wheelDelta = defaultWheelDelta$1,
74160 scaleExtent = [0, Infinity],
74161 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
74162 interpolate = interpolateZoom,
74163 dispatch$1 = dispatch('start', 'zoom', 'end'),
74165 _transform = identity$2,
74168 function zoom(selection) {
74169 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
74170 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
74173 zoom.transform = function (collection, transform, point) {
74174 var selection = collection.selection ? collection.selection() : collection;
74176 if (collection !== selection) {
74177 schedule(collection, transform, point);
74179 selection.interrupt().each(function () {
74180 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
74185 zoom.scaleBy = function (selection, k, p) {
74186 zoom.scaleTo(selection, function () {
74187 var k0 = _transform.k,
74188 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
74193 zoom.scaleTo = function (selection, k, p) {
74194 zoom.transform(selection, function () {
74195 var e = extent.apply(this, arguments),
74197 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
74198 p1 = t0.invert(p0),
74199 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
74200 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
74204 zoom.translateBy = function (selection, x, y) {
74205 zoom.transform(selection, function () {
74206 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);
74210 zoom.translateTo = function (selection, x, y, p) {
74211 zoom.transform(selection, function () {
74212 var e = extent.apply(this, arguments),
74214 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
74215 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);
74219 function scale(transform, k) {
74220 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
74221 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
74224 function translate(transform, p0, p1) {
74225 var x = p0[0] - p1[0] * transform.k,
74226 y = p0[1] - p1[1] * transform.k;
74227 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
74230 function centroid(extent) {
74231 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
74234 function schedule(transition, transform, point) {
74235 transition.on('start.zoom', function () {
74236 gesture(this, arguments).start(null);
74237 }).on('interrupt.zoom end.zoom', function () {
74238 gesture(this, arguments).end(null);
74239 }).tween('zoom', function () {
74242 g = gesture(that, args),
74243 e = extent.apply(that, args),
74244 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
74245 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
74247 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
74248 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
74249 return function (t) {
74250 if (t === 1) t = b; // Avoid rounding error on end.
74254 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
74256 g.zoom(null, null, t);
74261 function gesture(that, args, clean) {
74262 return !clean && _activeGesture || new Gesture(that, args);
74265 function Gesture(that, args) {
74269 this.extent = extent.apply(that, args);
74272 Gesture.prototype = {
74273 start: function start(d3_event) {
74274 if (++this.active === 1) {
74275 _activeGesture = this;
74276 dispatch$1.call('start', this, d3_event);
74281 zoom: function zoom(d3_event, key, transform) {
74282 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
74283 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
74284 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
74285 _transform = transform;
74286 dispatch$1.call('zoom', this, d3_event, key, transform);
74289 end: function end(d3_event) {
74290 if (--this.active === 0) {
74291 _activeGesture = null;
74292 dispatch$1.call('end', this, d3_event);
74299 function wheeled(d3_event) {
74300 if (!filter.apply(this, arguments)) return;
74301 var g = gesture(this, arguments),
74303 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
74304 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
74305 // If there were recent wheel events, reset the wheel idle timeout.
74308 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
74309 g.mouse[1] = t.invert(g.mouse[0] = p);
74312 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
74314 g.mouse = [p, t.invert(p)];
74319 d3_event.preventDefault();
74320 d3_event.stopImmediatePropagation();
74321 g.wheel = setTimeout(wheelidled, _wheelDelay);
74322 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
74324 function wheelidled() {
74330 var _downPointerIDs = new Set();
74332 var _pointerLocGetter;
74334 function pointerdown(d3_event) {
74335 _downPointerIDs.add(d3_event.pointerId);
74337 if (!filter.apply(this, arguments)) return;
74338 var g = gesture(this, arguments, _downPointerIDs.size === 1);
74340 d3_event.stopImmediatePropagation();
74341 _pointerLocGetter = utilFastMouse(this);
74343 var loc = _pointerLocGetter(d3_event);
74345 var p = [loc, _transform.invert(loc), d3_event.pointerId];
74350 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
74360 function pointermove(d3_event) {
74361 if (!_downPointerIDs.has(d3_event.pointerId)) return;
74362 if (!_activeGesture || !_pointerLocGetter) return;
74363 var g = gesture(this, arguments);
74364 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
74365 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
74367 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
74368 // The pointer went up without ending the gesture somehow, e.g.
74369 // a down mouse was moved off the map and released. End it here.
74370 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
74371 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
74376 d3_event.preventDefault();
74377 d3_event.stopImmediatePropagation();
74379 var loc = _pointerLocGetter(d3_event);
74382 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
74386 var p0 = g.pointer0[0],
74387 l0 = g.pointer0[1],
74388 p1 = g.pointer1[0],
74389 l1 = g.pointer1[1],
74390 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
74391 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
74392 t = scale(t, Math.sqrt(dp / dl));
74393 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
74394 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
74395 } else if (g.pointer0) {
74400 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
74403 function pointerup(d3_event) {
74404 if (!_downPointerIDs.has(d3_event.pointerId)) return;
74406 _downPointerIDs["delete"](d3_event.pointerId);
74408 if (!_activeGesture) return;
74409 var g = gesture(this, arguments);
74410 d3_event.stopImmediatePropagation();
74411 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;
74413 if (g.pointer1 && !g.pointer0) {
74414 g.pointer0 = g.pointer1;
74418 if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);else {
74423 zoom.wheelDelta = function (_) {
74424 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
74427 zoom.filter = function (_) {
74428 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
74431 zoom.extent = function (_) {
74432 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
74435 zoom.scaleExtent = function (_) {
74436 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
74439 zoom.translateExtent = function (_) {
74440 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]]];
74443 zoom.constrain = function (_) {
74444 return arguments.length ? (constrain = _, zoom) : constrain;
74447 zoom.interpolate = function (_) {
74448 return arguments.length ? (interpolate = _, zoom) : interpolate;
74451 zoom._transform = function (_) {
74452 return arguments.length ? (_transform = _, zoom) : _transform;
74455 return utilRebind(zoom, dispatch$1, 'on');
74458 // if pointer events are supported. Falls back to default `dblclick` event.
74460 function utilDoubleUp() {
74461 var dispatch$1 = dispatch('doubleUp');
74462 var _maxTimespan = 500; // milliseconds
74464 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
74466 var _pointer; // object representing the pointer that could trigger double up
74469 function pointerIsValidFor(loc) {
74470 // second pointerup must occur within a small timeframe after the first pointerdown
74471 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
74472 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
74475 function pointerdown(d3_event) {
74476 // ignore right-click
74477 if (d3_event.ctrlKey || d3_event.button === 2) return;
74478 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
74479 // events on touch devices
74481 if (_pointer && !pointerIsValidFor(loc)) {
74482 // if this pointer is no longer valid, clear it so another can be started
74483 _pointer = undefined;
74489 startTime: new Date().getTime(),
74491 pointerId: d3_event.pointerId
74495 _pointer.pointerId = d3_event.pointerId;
74499 function pointerup(d3_event) {
74500 // ignore right-click
74501 if (d3_event.ctrlKey || d3_event.button === 2) return;
74502 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
74503 _pointer.upCount += 1;
74505 if (_pointer.upCount === 2) {
74507 var loc = [d3_event.clientX, d3_event.clientY];
74509 if (pointerIsValidFor(loc)) {
74510 var locInThis = utilFastMouse(this)(d3_event);
74511 dispatch$1.call('doubleUp', this, d3_event, locInThis);
74512 } // clear the pointer info in any case
74515 _pointer = undefined;
74519 function doubleUp(selection) {
74520 if ('PointerEvent' in window) {
74521 // dblclick isn't well supported on touch devices so manually use
74522 // pointer events if they're available
74523 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
74525 // fallback to dblclick
74526 selection.on('dblclick.doubleUp', function (d3_event) {
74527 dispatch$1.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
74532 doubleUp.off = function (selection) {
74533 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
74536 return utilRebind(doubleUp, dispatch$1, 'on');
74539 var TILESIZE = 256;
74542 var kMin = geoZoomToScale(minZoom, TILESIZE);
74543 var kMax = geoZoomToScale(maxZoom, TILESIZE);
74545 function clamp(num, min, max) {
74546 return Math.max(min, Math.min(num, max));
74549 function rendererMap(context) {
74550 var dispatch$1 = dispatch('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
74551 var projection = context.projection;
74552 var curtainProjection = context.curtainProjection;
74561 var _selection = select(null);
74563 var supersurface = select(null);
74564 var wrapper = select(null);
74565 var surface = select(null);
74566 var _dimensions = [1, 1];
74567 var _dblClickZoomEnabled = true;
74568 var _redrawEnabled = true;
74570 var _gestureTransformStart;
74572 var _transformStart = projection.transform();
74574 var _transformLast;
74576 var _isTransformed = false;
74579 var _getMouseCoords;
74581 var _lastPointerEvent;
74583 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
74586 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
74588 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
74591 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
74593 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
74594 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
74595 }).on('end.map', function () {
74596 _pointerDown = false;
74599 var _doubleUpHandler = utilDoubleUp();
74601 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
74602 // var pendingRedrawCall;
74603 // function scheduleRedraw() {
74604 // // Only schedule the redraw if one has not already been set.
74605 // if (isRedrawScheduled) return;
74606 // isRedrawScheduled = true;
74607 // var that = this;
74608 // var args = arguments;
74609 // pendingRedrawCall = window.requestIdleCallback(function () {
74610 // // Reset the boolean so future redraws can be set.
74611 // isRedrawScheduled = false;
74612 // redraw.apply(that, args);
74613 // }, { timeout: 1400 });
74617 function cancelPendingRedraw() {
74618 scheduleRedraw.cancel(); // isRedrawScheduled = false;
74619 // window.cancelIdleCallback(pendingRedrawCall);
74622 function map(selection) {
74623 _selection = selection;
74624 context.on('change.map', immediateRedraw);
74625 var osm = context.connection();
74628 osm.on('change.map', immediateRedraw);
74631 function didUndoOrRedo(targetTransform) {
74632 var mode = context.mode().id;
74633 if (mode !== 'browse' && mode !== 'select') return;
74635 if (targetTransform) {
74636 map.transformEase(targetTransform);
74640 context.history().on('merge.map', function () {
74642 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
74643 didUndoOrRedo(fromStack.transform);
74644 }).on('redone.map', function (stack) {
74645 didUndoOrRedo(stack.transform);
74647 context.background().on('change.map', immediateRedraw);
74648 context.features().on('redraw.map', immediateRedraw);
74649 drawLayers.on('change.map', function () {
74650 context.background().updateImagery();
74653 selection.on('wheel.map mousewheel.map', function (d3_event) {
74654 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
74655 d3_event.preventDefault();
74656 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
74658 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
74659 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
74661 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
74662 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
74663 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
74664 _lastPointerEvent = d3_event;
74666 if (d3_event.button === 2) {
74667 d3_event.stopPropagation();
74669 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
74670 _lastPointerEvent = d3_event;
74672 if (resetTransform()) {
74675 }).on(_pointerPrefix + 'move.map', function (d3_event) {
74676 _lastPointerEvent = d3_event;
74677 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
74678 if (map.editableDataEnabled() && !_isTransformed) {
74679 var hover = d3_event.target.__data__;
74680 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
74681 dispatch$1.call('drawn', this, {
74685 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
74686 if (map.editableDataEnabled() && !_isTransformed) {
74687 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
74688 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
74689 dispatch$1.call('drawn', this, {
74694 var detected = utilDetect(); // only WebKit supports gesture events
74696 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
74697 // but we only need to do this on desktop Safari anyway. – #7694
74698 !detected.isMobileWebKit) {
74699 // Desktop Safari sends gesture events for multitouch trackpad pinches.
74700 // We can listen for these and translate them into map zooms.
74701 surface.on('gesturestart.surface', function (d3_event) {
74702 d3_event.preventDefault();
74703 _gestureTransformStart = projection.transform();
74704 }).on('gesturechange.surface', gestureChange);
74705 } // must call after surface init
74710 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
74711 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
74713 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
74714 !select(d3_event.target).classed('fill')) return;
74715 var zoomOut = d3_event.shiftKey;
74716 var t = projection.transform();
74717 var p1 = t.invert(p0);
74718 t = t.scale(zoomOut ? 0.5 : 2);
74719 t.x = p0[0] - p1[0] * t.k;
74720 t.y = p0[1] - p1[1] * t.k;
74721 map.transformEase(t);
74724 context.on('enter.map', function () {
74725 if (!map.editableDataEnabled(true
74726 /* skip zoom check */
74727 )) return; // redraw immediately any objects affected by a change in selectedIDs.
74729 var graph = context.graph();
74730 var selectedAndParents = {};
74731 context.selectedIDs().forEach(function (id) {
74732 var entity = graph.hasEntity(id);
74735 selectedAndParents[entity.id] = entity;
74737 if (entity.type === 'node') {
74738 graph.parentWays(entity).forEach(function (parent) {
74739 selectedAndParents[parent.id] = parent;
74744 var data = Object.values(selectedAndParents);
74746 var filter = function filter(d) {
74747 return d.id in selectedAndParents;
74750 data = context.features().filter(data, graph);
74751 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
74752 dispatch$1.call('drawn', this, {
74754 }); // redraw everything else later
74758 map.dimensions(utilGetDimensions(selection));
74761 function zoomEventFilter(d3_event) {
74762 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
74763 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
74764 // This can happen if a previous `mousedown` occurred without a `mouseup`.
74765 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
74766 // so that d3-zoom won't stop propagation of new `mousedown` events.
74767 if (d3_event.type === 'mousedown') {
74768 var hasOrphan = false;
74769 var listeners = window.__on;
74771 for (var i = 0; i < listeners.length; i++) {
74772 var listener = listeners[i];
74774 if (listener.name === 'zoom' && listener.type === 'mouseup') {
74781 var event = window.CustomEvent;
74784 event = new event('mouseup');
74786 event = window.document.createEvent('Event');
74787 event.initEvent('mouseup', false, false);
74788 } // Event needs to be dispatched with an event.view property.
74791 event.view = window;
74792 window.dispatchEvent(event);
74796 return d3_event.button !== 2; // ignore right clicks
74799 function pxCenter() {
74800 return [_dimensions[0] / 2, _dimensions[1] / 2];
74803 function drawEditable(difference, extent) {
74804 var mode = context.mode();
74805 var graph = context.graph();
74806 var features = context.features();
74807 var all = context.history().intersects(map.extent());
74808 var fullRedraw = false;
74812 var applyFeatureLayerFilters = true;
74814 if (map.isInWideSelection()) {
74816 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
74817 var entity = context.hasEntity(id);
74818 if (entity) data.push(entity);
74821 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
74823 applyFeatureLayerFilters = false;
74824 } else if (difference) {
74825 var complete = difference.complete(map.extent());
74826 data = Object.values(complete).filter(Boolean);
74827 set = new Set(Object.keys(complete));
74829 filter = function filter(d) {
74830 return set.has(d.id);
74833 features.clear(data);
74835 // force a full redraw if gatherStats detects that a feature
74836 // should be auto-hidden (e.g. points or buildings)..
74837 if (features.gatherStats(all, graph, _dimensions)) {
74838 extent = undefined;
74842 data = context.history().intersects(map.extent().intersection(extent));
74843 set = new Set(data.map(function (entity) {
74847 filter = function filter(d) {
74848 return set.has(d.id);
74853 filter = utilFunctor(true);
74857 if (applyFeatureLayerFilters) {
74858 data = features.filter(data, graph);
74860 context.features().resetStats();
74863 if (mode && mode.id === 'select') {
74864 // update selected vertices - the user might have just double-clicked a way,
74865 // creating a new vertex, triggering a partial redraw without a mode change
74866 surface.call(drawVertices.drawSelected, graph, map.extent());
74869 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);
74870 dispatch$1.call('drawn', this, {
74875 map.init = function () {
74876 drawLayers = svgLayers(projection, context);
74877 drawPoints = svgPoints(projection, context);
74878 drawVertices = svgVertices(projection, context);
74879 drawLines = svgLines(projection, context);
74880 drawAreas = svgAreas(projection, context);
74881 drawMidpoints = svgMidpoints(projection, context);
74882 drawLabels = svgLabels(projection, context);
74885 function editOff() {
74886 context.features().resetStats();
74887 surface.selectAll('.layer-osm *').remove();
74888 surface.selectAll('.layer-touch:not(.markers) *').remove();
74892 'select-note': true,
74893 'select-data': true,
74894 'select-error': true
74896 var mode = context.mode();
74898 if (mode && !allowed[mode.id]) {
74899 context.enter(modeBrowse(context));
74902 dispatch$1.call('drawn', this, {
74907 function gestureChange(d3_event) {
74908 // Remap Safari gesture events to wheel events - #5492
74909 // We want these disabled most places, but enabled for zoom/unzoom on map surface
74910 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
74912 e.preventDefault();
74915 // dummy values to ignore in zoomPan
74917 // dummy values to ignore in zoomPan
74918 clientX: e.clientX,
74919 clientY: e.clientY,
74920 screenX: e.screenX,
74921 screenY: e.screenY,
74925 var e2 = new WheelEvent('wheel', props);
74926 e2._scale = e.scale; // preserve the original scale
74928 e2._rotation = e.rotation; // preserve the original rotation
74930 _selection.node().dispatchEvent(e2);
74933 function zoomPan(event, key, transform) {
74934 var source = event && event.sourceEvent || event;
74935 var eventTransform = transform || event && event.transform;
74936 var x = eventTransform.x;
74937 var y = eventTransform.y;
74938 var k = eventTransform.k; // Special handling of 'wheel' events:
74939 // They might be triggered by the user scrolling the mouse wheel,
74940 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
74942 if (source && source.type === 'wheel') {
74943 // assume that the gesture is already handled by pointer events
74944 if (_pointerDown) return;
74945 var detected = utilDetect();
74946 var dX = source.deltaX;
74947 var dY = source.deltaY;
74951 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
74952 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
74953 // We are essentially redoing the calculations that occur here:
74954 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
74955 // See this for more info:
74956 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
74958 if (source.deltaMode === 1
74961 // Convert from lines to pixels, more if the user is scrolling fast.
74962 // (I made up the exp function to roughly match Firefox to what Chrome does)
74963 // These numbers should be floats, because integers are treated as pan gesture below.
74964 var lines = Math.abs(source.deltaY);
74965 var sign = source.deltaY > 0 ? 1 : -1;
74966 dY = sign * clamp(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
74967 350.000244140625 // max
74968 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
74969 // There doesn't seem to be any scroll acceleration.
74970 // This multiplier increases the speed a little bit - #5512
74972 if (detected.os !== 'mac') {
74974 } // recalculate x2,y2,k2
74977 t0 = _isTransformed ? _transformLast : _transformStart;
74978 p0 = _getMouseCoords(source);
74979 p1 = t0.invert(p0);
74980 k2 = t0.k * Math.pow(2, -dY / 500);
74981 k2 = clamp(k2, kMin, kMax);
74982 x2 = p0[0] - p1[0] * k2;
74983 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
74984 // These are fake `wheel` events we made from Safari `gesturechange` events..
74985 } else if (source._scale) {
74986 // recalculate x2,y2,k2
74987 t0 = _gestureTransformStart;
74988 p0 = _getMouseCoords(source);
74989 p1 = t0.invert(p0);
74990 k2 = t0.k * source._scale;
74991 k2 = clamp(k2, kMin, kMax);
74992 x2 = p0[0] - p1[0] * k2;
74993 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
74994 // Pinch zooming via the `wheel` event will always have:
74995 // - `ctrlKey = true`
74996 // - `deltaY` is not round integer pixels (ignore `deltaX`)
74997 } else if (source.ctrlKey && !isInteger(dY)) {
74998 dY *= 6; // slightly scale up whatever the browser gave us
74999 // recalculate x2,y2,k2
75001 t0 = _isTransformed ? _transformLast : _transformStart;
75002 p0 = _getMouseCoords(source);
75003 p1 = t0.invert(p0);
75004 k2 = t0.k * Math.pow(2, -dY / 500);
75005 k2 = clamp(k2, kMin, kMax);
75006 x2 = p0[0] - p1[0] * k2;
75007 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
75008 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
75009 // recalculate x2,y2,k2
75010 t0 = _isTransformed ? _transformLast : _transformStart;
75011 p0 = _getMouseCoords(source);
75012 p1 = t0.invert(p0);
75013 k2 = t0.k * Math.pow(2, -dY / 500);
75014 k2 = clamp(k2, kMin, kMax);
75015 x2 = p0[0] - p1[0] * k2;
75016 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers) - #5492, #5512
75017 // Panning via the `wheel` event will always have:
75018 // - `ctrlKey = false`
75019 // - `deltaX`,`deltaY` are round integer pixels
75020 } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
75021 p1 = projection.translate();
75024 k2 = projection.scale();
75025 k2 = clamp(k2, kMin, kMax);
75026 } // something changed - replace the event transform
75029 if (x2 !== x || y2 !== y || k2 !== k) {
75033 eventTransform = identity$2.translate(x2, y2).scale(k2);
75035 if (_zoomerPanner._transform) {
75036 // utilZoomPan interface
75037 _zoomerPanner._transform(eventTransform);
75039 // d3_zoom interface
75040 _selection.node().__zoom = eventTransform;
75045 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
75046 return; // no change
75049 var withinEditableZoom = map.withinEditableZoom();
75051 if (_lastWithinEditableZoom !== withinEditableZoom) {
75052 if (_lastWithinEditableZoom !== undefined) {
75053 // notify that the map zoomed in or out over the editable zoom threshold
75054 dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
75057 _lastWithinEditableZoom = withinEditableZoom;
75060 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
75061 surface.interrupt();
75062 dispatch$1.call('hitMinZoom', this, map);
75063 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
75065 dispatch$1.call('move', this, map);
75069 projection.transform(eventTransform);
75070 var scale = k / _transformStart.k;
75071 var tX = (x / scale - _transformStart.x) * scale;
75072 var tY = (y / scale - _transformStart.y) * scale;
75074 if (context.inIntro()) {
75075 curtainProjection.transform({
75083 _lastPointerEvent = event;
75086 _isTransformed = true;
75087 _transformLast = eventTransform;
75088 utilSetTransform(supersurface, tX, tY, scale);
75090 dispatch$1.call('move', this, map);
75092 function isInteger(val) {
75093 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
75097 function resetTransform() {
75098 if (!_isTransformed) return false;
75099 utilSetTransform(supersurface, 0, 0);
75100 _isTransformed = false;
75102 if (context.inIntro()) {
75103 curtainProjection.transform(projection.transform());
75109 function redraw(difference, extent) {
75110 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
75111 // It would result in artifacts where differenced entities are redrawn with
75112 // one transform and unchanged entities with another.
75114 if (resetTransform()) {
75115 difference = extent = undefined;
75118 var zoom = map.zoom();
75119 var z = String(~~zoom);
75121 if (surface.attr('data-zoom') !== z) {
75122 surface.attr('data-zoom', z);
75123 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
75126 var lat = map.center()[1];
75127 var lowzoom = linear$2().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
75128 surface.classed('low-zoom', zoom <= lowzoom(lat));
75131 supersurface.call(context.background());
75132 wrapper.call(drawLayers);
75136 if (map.editableDataEnabled() || map.isInWideSelection()) {
75137 context.loadTiles(projection);
75138 drawEditable(difference, extent);
75143 _transformStart = projection.transform();
75147 var immediateRedraw = function immediateRedraw(difference, extent) {
75148 if (!difference && !extent) cancelPendingRedraw();
75149 redraw(difference, extent);
75152 map.lastPointerEvent = function () {
75153 return _lastPointerEvent;
75156 map.mouse = function (d3_event) {
75157 var event = _lastPointerEvent || d3_event;
75162 while (s = event.sourceEvent) {
75166 return _getMouseCoords(event);
75170 }; // returns Lng/Lat
75173 map.mouseCoordinates = function () {
75174 var coord = map.mouse() || pxCenter();
75175 return projection.invert(coord);
75178 map.dblclickZoomEnable = function (val) {
75179 if (!arguments.length) return _dblClickZoomEnabled;
75180 _dblClickZoomEnabled = val;
75184 map.redrawEnable = function (val) {
75185 if (!arguments.length) return _redrawEnabled;
75186 _redrawEnabled = val;
75190 map.isTransformed = function () {
75191 return _isTransformed;
75194 function setTransform(t2, duration, force) {
75195 var t = projection.transform();
75196 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
75199 _selection.transition().duration(duration).on('start', function () {
75201 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
75203 projection.transform(t2);
75204 _transformStart = t2;
75206 _selection.call(_zoomerPanner.transform, _transformStart);
75212 function setCenterZoom(loc2, z2, duration, force) {
75213 var c = map.center();
75214 var z = map.zoom();
75215 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
75216 var proj = geoRawMercator().transform(projection.transform()); // copy projection
75218 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
75220 var t = proj.translate();
75221 var point = proj(loc2);
75222 var center = pxCenter();
75223 t[0] += center[0] - point[0];
75224 t[1] += center[1] - point[1];
75225 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
75228 map.pan = function (delta, duration) {
75229 var t = projection.translate();
75230 var k = projection.scale();
75235 _selection.transition().duration(duration).on('start', function () {
75237 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
75239 projection.translate(t);
75240 _transformStart = projection.transform();
75242 _selection.call(_zoomerPanner.transform, _transformStart);
75244 dispatch$1.call('move', this, map);
75251 map.dimensions = function (val) {
75252 if (!arguments.length) return _dimensions;
75254 drawLayers.dimensions(_dimensions);
75255 context.background().dimensions(_dimensions);
75256 projection.clipExtent([[0, 0], _dimensions]);
75257 _getMouseCoords = utilFastMouse(supersurface.node());
75262 function zoomIn(delta) {
75263 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
75266 function zoomOut(delta) {
75267 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
75270 map.zoomIn = function () {
75274 map.zoomInFurther = function () {
75278 map.canZoomIn = function () {
75279 return map.zoom() < maxZoom;
75282 map.zoomOut = function () {
75286 map.zoomOutFurther = function () {
75290 map.canZoomOut = function () {
75291 return map.zoom() > minZoom;
75294 map.center = function (loc2) {
75295 if (!arguments.length) {
75296 return projection.invert(pxCenter());
75299 if (setCenterZoom(loc2, map.zoom())) {
75300 dispatch$1.call('move', this, map);
75307 map.unobscuredCenterZoomEase = function (loc, zoom) {
75308 var offset = map.unobscuredOffsetPx();
75309 var proj = geoRawMercator().transform(projection.transform()); // copy projection
75310 // use the target zoom to calculate the offset center
75312 proj.scale(geoZoomToScale(zoom, TILESIZE));
75313 var locPx = proj(loc);
75314 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
75315 var offsetLoc = proj.invert(offsetLocPx);
75316 map.centerZoomEase(offsetLoc, zoom);
75319 map.unobscuredOffsetPx = function () {
75320 var openPane = context.container().select('.map-panes .map-pane.shown');
75322 if (!openPane.empty()) {
75323 return [openPane.node().offsetWidth / 2, 0];
75329 map.zoom = function (z2) {
75330 if (!arguments.length) {
75331 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
75334 if (z2 < _minzoom) {
75335 surface.interrupt();
75336 dispatch$1.call('hitMinZoom', this, map);
75337 z2 = context.minEditableZoom();
75340 if (setCenterZoom(map.center(), z2)) {
75341 dispatch$1.call('move', this, map);
75348 map.centerZoom = function (loc2, z2) {
75349 if (setCenterZoom(loc2, z2)) {
75350 dispatch$1.call('move', this, map);
75357 map.zoomTo = function (entity) {
75358 var extent = entity.extent(context.graph());
75359 if (!isFinite(extent.area())) return map;
75360 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
75361 return map.centerZoom(extent.center(), z2);
75364 map.centerEase = function (loc2, duration) {
75365 duration = duration || 250;
75366 setCenterZoom(loc2, map.zoom(), duration);
75370 map.zoomEase = function (z2, duration) {
75371 duration = duration || 250;
75372 setCenterZoom(map.center(), z2, duration, false);
75376 map.centerZoomEase = function (loc2, z2, duration) {
75377 duration = duration || 250;
75378 setCenterZoom(loc2, z2, duration, false);
75382 map.transformEase = function (t2, duration) {
75383 duration = duration || 250;
75384 setTransform(t2, duration, false
75390 map.zoomToEase = function (obj, duration) {
75393 if (Array.isArray(obj)) {
75394 obj.forEach(function (entity) {
75395 var entityExtent = entity.extent(context.graph());
75398 extent = entityExtent;
75400 extent = extent.extend(entityExtent);
75404 extent = obj.extent(context.graph());
75407 if (!isFinite(extent.area())) return map;
75408 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
75409 return map.centerZoomEase(extent.center(), z2, duration);
75412 map.startEase = function () {
75413 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
75419 map.cancelEase = function () {
75420 _selection.interrupt();
75425 map.extent = function (val) {
75426 if (!arguments.length) {
75427 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
75429 var extent = geoExtent(val);
75430 map.centerZoom(extent.center(), map.extentZoom(extent));
75434 map.trimmedExtent = function (val) {
75435 if (!arguments.length) {
75439 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
75441 var extent = geoExtent(val);
75442 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
75446 function calcExtentZoom(extent, dim) {
75447 var tl = projection([extent[0][0], extent[1][1]]);
75448 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
75450 var hFactor = (br[0] - tl[0]) / dim[0];
75451 var vFactor = (br[1] - tl[1]) / dim[1];
75452 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
75453 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
75454 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
75458 map.extentZoom = function (val) {
75459 return calcExtentZoom(geoExtent(val), _dimensions);
75462 map.trimmedExtentZoom = function (val) {
75465 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
75466 return calcExtentZoom(geoExtent(val), trimmed);
75469 map.withinEditableZoom = function () {
75470 return map.zoom() >= context.minEditableZoom();
75473 map.isInWideSelection = function () {
75474 return !map.withinEditableZoom() && context.selectedIDs().length;
75477 map.editableDataEnabled = function (skipZoomCheck) {
75478 var layer = context.layers().layer('osm');
75479 if (!layer || !layer.enabled()) return false;
75480 return skipZoomCheck || map.withinEditableZoom();
75483 map.notesEditable = function () {
75484 var layer = context.layers().layer('notes');
75485 if (!layer || !layer.enabled()) return false;
75486 return map.withinEditableZoom();
75489 map.minzoom = function (val) {
75490 if (!arguments.length) return _minzoom;
75495 map.toggleHighlightEdited = function () {
75496 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
75497 map.pan([0, 0]); // trigger a redraw
75499 dispatch$1.call('changeHighlighting', this);
75502 map.areaFillOptions = ['wireframe', 'partial', 'full'];
75504 map.activeAreaFill = function (val) {
75505 if (!arguments.length) return corePreferences('area-fill') || 'partial';
75506 corePreferences('area-fill', val);
75508 if (val !== 'wireframe') {
75509 corePreferences('area-fill-toggle', val);
75513 map.pan([0, 0]); // trigger a redraw
75515 dispatch$1.call('changeAreaFill', this);
75519 map.toggleWireframe = function () {
75520 var activeFill = map.activeAreaFill();
75522 if (activeFill === 'wireframe') {
75523 activeFill = corePreferences('area-fill-toggle') || 'partial';
75525 activeFill = 'wireframe';
75528 map.activeAreaFill(activeFill);
75531 function updateAreaFill() {
75532 var activeFill = map.activeAreaFill();
75533 map.areaFillOptions.forEach(function (opt) {
75534 surface.classed('fill-' + opt, Boolean(opt === activeFill));
75538 map.layers = function () {
75542 map.doubleUpHandler = function () {
75543 return _doubleUpHandler;
75546 return utilRebind(map, dispatch$1, 'on');
75549 function rendererPhotos(context) {
75550 var dispatch$1 = dispatch('change');
75551 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
75552 var _allPhotoTypes = ['flat', 'panoramic'];
75554 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
75557 var _dateFilters = ['fromDate', 'toDate'];
75565 function photos() {}
75567 function updateStorage() {
75568 if (window.mocha) return;
75569 var hash = utilStringQs(window.location.hash);
75570 var enabled = context.layers().all().filter(function (d) {
75571 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
75572 }).map(function (d) {
75576 if (enabled.length) {
75577 hash.photo_overlay = enabled.join(',');
75579 delete hash.photo_overlay;
75582 window.location.replace('#' + utilQsString(hash, true));
75585 photos.overlayLayerIDs = function () {
75589 photos.allPhotoTypes = function () {
75590 return _allPhotoTypes;
75593 photos.dateFilters = function () {
75594 return _dateFilters;
75597 photos.dateFilterValue = function (val) {
75598 return val === _dateFilters[0] ? _fromDate : _toDate;
75601 photos.setDateFilter = function (type, val, updateUrl) {
75602 // validate the date
75603 var date = val && new Date(val);
75605 if (date && !isNaN(date)) {
75606 val = date.toISOString().substr(0, 10);
75611 if (type === _dateFilters[0]) {
75614 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
75615 _toDate = _fromDate;
75619 if (type === _dateFilters[1]) {
75622 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
75623 _fromDate = _toDate;
75627 dispatch$1.call('change', this);
75632 if (_fromDate || _toDate) {
75633 rangeString = (_fromDate || '') + '_' + (_toDate || '');
75636 setUrlFilterValue('photo_dates', rangeString);
75640 photos.setUsernameFilter = function (val, updateUrl) {
75641 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
75644 val = val.map(function (d) {
75646 }).filter(Boolean);
75654 dispatch$1.call('change', this);
75660 hashString = _usernames.join(',');
75663 setUrlFilterValue('photo_username', hashString);
75667 function setUrlFilterValue(property, val) {
75668 if (!window.mocha) {
75669 var hash = utilStringQs(window.location.hash);
75672 if (hash[property] === val) return;
75673 hash[property] = val;
75675 if (!(property in hash)) return;
75676 delete hash[property];
75679 window.location.replace('#' + utilQsString(hash, true));
75683 function showsLayer(id) {
75684 var layer = context.layers().layer(id);
75685 return layer && layer.supported() && layer.enabled();
75688 photos.shouldFilterByDate = function () {
75689 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
75692 photos.shouldFilterByPhotoType = function () {
75693 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
75696 photos.shouldFilterByUsername = function () {
75697 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
75700 photos.showsPhotoType = function (val) {
75701 if (!photos.shouldFilterByPhotoType()) return true;
75702 return _shownPhotoTypes.indexOf(val) !== -1;
75705 photos.showsFlat = function () {
75706 return photos.showsPhotoType('flat');
75709 photos.showsPanoramic = function () {
75710 return photos.showsPhotoType('panoramic');
75713 photos.fromDate = function () {
75717 photos.toDate = function () {
75721 photos.togglePhotoType = function (val) {
75722 var index = _shownPhotoTypes.indexOf(val);
75724 if (index !== -1) {
75725 _shownPhotoTypes.splice(index, 1);
75727 _shownPhotoTypes.push(val);
75730 dispatch$1.call('change', this);
75734 photos.usernames = function () {
75738 photos.init = function () {
75739 var hash = utilStringQs(window.location.hash);
75741 if (hash.photo_dates) {
75742 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
75743 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
75744 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
75745 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
75748 if (hash.photo_username) {
75749 this.setUsernameFilter(hash.photo_username, false);
75752 if (hash.photo_overlay) {
75753 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
75754 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
75755 hashOverlayIDs.forEach(function (id) {
75756 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
75757 if (layer && !layer.enabled()) layer.enabled(true);
75762 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
75763 var photoIds = hash.photo.replace(/;/g, ',').split(',');
75764 var photoId = photoIds.length && photoIds[0].trim();
75765 var results = /(.*)\/(.*)/g.exec(photoId);
75767 if (results && results.length >= 3) {
75768 var serviceId = results[1];
75769 var photoKey = results[2];
75770 var service = services[serviceId];
75772 if (service && service.ensureViewerLoaded) {
75773 // if we're showing a photo then make sure its layer is enabled too
75774 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
75775 if (layer && !layer.enabled()) layer.enabled(true);
75776 var baselineTime = Date.now();
75777 service.on('loadedImages.rendererPhotos', function () {
75778 // don't open the viewer if too much time has elapsed
75779 if (Date.now() - baselineTime > 45000) {
75780 service.on('loadedImages.rendererPhotos', null);
75784 if (!service.cachedImage(photoKey)) return;
75785 service.on('loadedImages.rendererPhotos', null);
75786 service.ensureViewerLoaded(context).then(function () {
75787 service.selectImage(context, photoKey).showViewer(context);
75794 context.layers().on('change.rendererPhotos', updateStorage);
75797 return utilRebind(photos, dispatch$1, 'on');
75800 function uiAccount(context) {
75801 var osm = context.connection();
75803 function update(selection) {
75806 if (!osm.authenticated()) {
75807 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
75811 osm.userDetails(function (err, details) {
75812 var userLink = selection.select('.userLink'),
75813 logoutLink = selection.select('.logoutLink');
75815 logoutLink.html('');
75816 if (err || !details) return;
75817 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
75819 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
75821 if (details.image_url) {
75822 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
75824 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
75828 userLinkA.append('span').attr('class', 'label').html(details.display_name);
75829 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
75830 d3_event.preventDefault();
75836 return function (selection) {
75837 selection.append('li').attr('class', 'userLink').classed('hide', true);
75838 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
75841 osm.on('change.account', function () {
75849 function uiAttribution(context) {
75850 var _selection = select(null);
75852 function render(selection, data, klass) {
75853 var div = selection.selectAll(".".concat(klass)).data([0]);
75854 div = div.enter().append('div').attr('class', klass).merge(div);
75855 var attributions = div.selectAll('.attribution').data(data, function (d) {
75858 attributions.exit().remove();
75859 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
75860 var attribution = select(nodes[i]);
75862 if (d.terms_html) {
75863 attribution.html(d.terms_html);
75868 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
75871 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
75872 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
75873 "default": d.terms_text || d.id || d.name()
75876 if (d.icon && !d.overlay) {
75877 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
75880 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
75881 }).merge(attributions);
75882 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
75883 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
75884 return notice ? [notice] : [];
75886 copyright.exit().remove();
75887 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
75888 copyright.html(String);
75891 function update() {
75892 var baselayer = context.background().baseLayerSource();
75894 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
75896 var z = context.map().zoom();
75897 var overlays = context.background().overlayLayerSources() || [];
75899 _selection.call(render, overlays.filter(function (s) {
75900 return s.validZoom(z);
75901 }), 'overlay-layer-attribution');
75904 return function (selection) {
75905 _selection = selection;
75906 context.background().on('change.attribution', update);
75907 context.map().on('move.attribution', throttle(update, 400, {
75914 function uiContributors(context) {
75915 var osm = context.connection(),
75916 debouncedUpdate = debounce(function () {
75921 wrap = select(null);
75923 function update() {
75926 entities = context.history().intersects(context.map().extent());
75927 entities.forEach(function (entity) {
75928 if (entity && entity.user) users[entity.user] = true;
75930 var u = Object.keys(users),
75931 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
75932 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
75933 var userList = select(document.createElement('span'));
75934 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
75935 return osm.userURL(d);
75936 }).attr('target', '_blank').html(String);
75938 if (u.length > limit) {
75939 var count = select(document.createElement('span'));
75940 var othersNum = u.length - limit + 1;
75941 count.append('a').attr('target', '_blank').attr('href', function () {
75942 return osm.changesetsURL(context.map().center(), context.map().zoom());
75943 }).html(othersNum);
75944 wrap.append('span').html(_t.html('contributors.truncated_list', {
75946 users: userList.html(),
75947 count: count.html()
75950 wrap.append('span').html(_t.html('contributors.list', {
75951 users: userList.html()
75957 wrap.transition().style('opacity', 0);
75958 } else if (hidden) {
75959 wrap.transition().style('opacity', 1);
75963 return function (selection) {
75967 osm.on('loaded.contributors', debouncedUpdate);
75968 context.map().on('move.contributors', debouncedUpdate);
75972 var _popoverID = 0;
75973 function uiPopover(klass) {
75974 var _id = _popoverID++;
75976 var _anchorSelection = select(null);
75978 var popover = function popover(selection) {
75979 _anchorSelection = selection;
75980 selection.each(setup);
75983 var _animation = utilFunctor(false);
75985 var _placement = utilFunctor('top'); // top, bottom, left, right
75988 var _alignment = utilFunctor('center'); // leading, center, trailing
75991 var _scrollContainer = utilFunctor(select(null));
75995 var _displayType = utilFunctor('');
75997 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
76000 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
76002 popover.displayType = function (val) {
76003 if (arguments.length) {
76004 _displayType = utilFunctor(val);
76007 return _displayType;
76011 popover.hasArrow = function (val) {
76012 if (arguments.length) {
76013 _hasArrow = utilFunctor(val);
76020 popover.placement = function (val) {
76021 if (arguments.length) {
76022 _placement = utilFunctor(val);
76029 popover.alignment = function (val) {
76030 if (arguments.length) {
76031 _alignment = utilFunctor(val);
76038 popover.scrollContainer = function (val) {
76039 if (arguments.length) {
76040 _scrollContainer = utilFunctor(val);
76043 return _scrollContainer;
76047 popover.content = function (val) {
76048 if (arguments.length) {
76056 popover.isShown = function () {
76057 var popoverSelection = _anchorSelection.select('.popover-' + _id);
76059 return !popoverSelection.empty() && popoverSelection.classed('in');
76062 popover.show = function () {
76063 _anchorSelection.each(show);
76066 popover.updateContent = function () {
76067 _anchorSelection.each(updateContent);
76070 popover.hide = function () {
76071 _anchorSelection.each(hide);
76074 popover.toggle = function () {
76075 _anchorSelection.each(toggle);
76078 popover.destroy = function (selection, selector) {
76079 // by default, just destroy the current popover
76080 selector = selector || '.popover-' + _id;
76081 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 () {
76082 return this.getAttribute('data-original-title') || this.getAttribute('title');
76083 }).attr('data-original-title', null).selectAll(selector).remove();
76086 popover.destroyAny = function (selection) {
76087 selection.call(popover.destroy, '.popover');
76091 var anchor = select(this);
76093 var animate = _animation.apply(this, arguments);
76095 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
76096 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
76097 enter.append('div').attr('class', 'popover-arrow');
76098 enter.append('div').attr('class', 'popover-inner');
76099 popoverSelection = enter.merge(popoverSelection);
76102 popoverSelection.classed('fade', true);
76105 var display = _displayType.apply(this, arguments);
76107 if (display === 'hover') {
76108 var _lastNonMouseEnterTime;
76110 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
76111 if (d3_event.pointerType) {
76112 if (d3_event.pointerType !== 'mouse') {
76113 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
76116 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
76117 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
76118 // event for non-mouse interactions right after sending
76119 // the correct type pointerenter event. Workaround by discarding
76120 // any mouse event that occurs immediately after a non-mouse event.
76123 } // don't show if buttons are pressed, e.g. during click and drag of map
76126 if (d3_event.buttons !== 0) return;
76127 show.apply(this, arguments);
76128 }).on(_pointerPrefix + 'leave.popover', function () {
76129 hide.apply(this, arguments);
76130 }) // show on focus too for better keyboard navigation support
76131 .on('focus.popover', function () {
76132 show.apply(this, arguments);
76133 }).on('blur.popover', function () {
76134 hide.apply(this, arguments);
76136 } else if (display === 'clickFocus') {
76137 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
76138 d3_event.preventDefault();
76139 d3_event.stopPropagation();
76140 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
76141 d3_event.preventDefault();
76142 d3_event.stopPropagation();
76143 }).on('click.popover', toggle);
76144 popoverSelection // This attribute lets the popover take focus
76145 .attr('tabindex', 0).on('blur.popover', function () {
76146 anchor.each(function () {
76147 hide.apply(this, arguments);
76154 var anchor = select(this);
76155 var popoverSelection = anchor.selectAll('.popover-' + _id);
76157 if (popoverSelection.empty()) {
76158 // popover was removed somehow, put it back
76159 anchor.call(popover.destroy);
76160 anchor.each(setup);
76161 popoverSelection = anchor.selectAll('.popover-' + _id);
76164 popoverSelection.classed('in', true);
76166 var displayType = _displayType.apply(this, arguments);
76168 if (displayType === 'clickFocus') {
76169 anchor.classed('active', true);
76170 popoverSelection.node().focus();
76173 anchor.each(updateContent);
76176 function updateContent() {
76177 var anchor = select(this);
76180 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
76183 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
76184 // set before the dynamic popover size is calculated by the browser
76186 updatePosition.apply(this, arguments);
76187 updatePosition.apply(this, arguments);
76190 function updatePosition() {
76191 var anchor = select(this);
76192 var popoverSelection = anchor.selectAll('.popover-' + _id);
76194 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
76196 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
76197 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
76198 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
76200 var placement = _placement.apply(this, arguments);
76202 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
76204 var alignment = _alignment.apply(this, arguments);
76206 var alignFactor = 0.5;
76208 if (alignment === 'leading') {
76210 } else if (alignment === 'trailing') {
76214 var anchorFrame = getFrame(anchor.node());
76215 var popoverFrame = getFrame(popoverSelection.node());
76218 switch (placement) {
76221 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
76222 y: anchorFrame.y - popoverFrame.h
76228 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
76229 y: anchorFrame.y + anchorFrame.h
76235 x: anchorFrame.x - popoverFrame.w,
76236 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
76242 x: anchorFrame.x + anchorFrame.w,
76243 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
76249 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
76250 var initialPosX = position.x;
76252 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
76253 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
76254 } else if (position.x < 10) {
76258 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
76260 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
76261 arrow.style('left', ~~arrowPosX + 'px');
76264 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
76266 popoverSelection.style('left', null).style('top', null);
76269 function getFrame(node) {
76270 var positionStyle = select(node).style('position');
76272 if (positionStyle === 'absolute' || positionStyle === 'static') {
76274 x: node.offsetLeft - scrollLeft,
76275 y: node.offsetTop - scrollTop,
76276 w: node.offsetWidth,
76277 h: node.offsetHeight
76283 w: node.offsetWidth,
76284 h: node.offsetHeight
76291 var anchor = select(this);
76293 if (_displayType.apply(this, arguments) === 'clickFocus') {
76294 anchor.classed('active', false);
76297 anchor.selectAll('.popover-' + _id).classed('in', false);
76300 function toggle() {
76301 if (select(this).select('.popover-' + _id).classed('in')) {
76302 hide.apply(this, arguments);
76304 show.apply(this, arguments);
76311 function uiTooltip(klass) {
76312 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
76314 var _title = function _title() {
76315 var title = this.getAttribute('data-original-title');
76320 title = this.getAttribute('title');
76321 this.removeAttribute('title');
76322 this.setAttribute('data-original-title', title);
76328 var _heading = utilFunctor(null);
76330 var _keys = utilFunctor(null);
76332 tooltip.title = function (val) {
76333 if (!arguments.length) return _title;
76334 _title = utilFunctor(val);
76338 tooltip.heading = function (val) {
76339 if (!arguments.length) return _heading;
76340 _heading = utilFunctor(val);
76344 tooltip.keys = function (val) {
76345 if (!arguments.length) return _keys;
76346 _keys = utilFunctor(val);
76350 tooltip.content(function () {
76351 var heading = _heading.apply(this, arguments);
76353 var text = _title.apply(this, arguments);
76355 var keys = _keys.apply(this, arguments);
76357 return function (selection) {
76358 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
76359 headingSelect.exit().remove();
76360 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
76361 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
76362 textSelect.exit().remove();
76363 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
76364 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
76365 keyhintWrap.exit().remove();
76366 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
76367 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
76368 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
76369 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
76377 function uiEditMenu(context) {
76378 var dispatch$1 = dispatch('toggled');
76380 var _menu = select(null);
76382 var _operations = []; // the position the menu should be displayed relative to
76384 var _anchorLoc = [0, 0];
76385 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
76387 var _triggerType = '';
76388 var _vpTopMargin = 85; // viewport top margin
76390 var _vpBottomMargin = 45; // viewport bottom margin
76392 var _vpSideMargin = 35; // viewport side margin
76394 var _menuTop = false;
76398 var _menuWidth; // hardcode these values to make menu positioning easier
76401 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
76403 var _tooltipWidth = 210; // offset the menu slightly from the target location
76405 var _menuSideMargin = 10;
76406 var _tooltips = [];
76408 var editMenu = function editMenu(selection) {
76409 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
76411 var ops = _operations.filter(function (op) {
76412 return !isTouchMenu || !op.mouseOnly;
76415 if (!ops.length) return;
76416 _tooltips = []; // Position the menu above the anchor for stylus and finger input
76417 // since the mapper's hand likely obscures the screen below the anchor
76419 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
76421 var showLabels = isTouchMenu;
76422 var buttonHeight = showLabels ? 32 : 34;
76425 // Get a general idea of the width based on the length of the label
76426 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
76427 return op.title.length;
76433 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
76434 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
76436 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
76439 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
76440 return 'edit-menu-item edit-menu-item-' + d.id;
76441 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
76442 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
76443 // don't let button presses also act as map input - #1869
76444 d3_event.stopPropagation();
76445 }).on('mouseenter.highlight', function (d3_event, d) {
76446 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
76447 utilHighlightEntities(d.relatedEntityIds(), true, context);
76448 }).on('mouseleave.highlight', function (d3_event, d) {
76449 if (!d.relatedEntityIds) return;
76450 utilHighlightEntities(d.relatedEntityIds(), false, context);
76452 buttonsEnter.each(function (d) {
76453 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
76455 _tooltips.push(tooltip);
76457 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
76461 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
76467 buttonsEnter.merge(buttons).classed('disabled', function (d) {
76468 return d.disabled();
76471 var initialScale = context.projection.scale();
76472 context.map().on('move.edit-menu', function () {
76473 if (initialScale !== context.projection.scale()) {
76476 }).on('drawn.edit-menu', function (info) {
76477 if (info.full) updatePosition();
76479 var lastPointerUpType; // `pointerup` is always called before `click`
76481 function pointerup(d3_event) {
76482 lastPointerUpType = d3_event.pointerType;
76485 function click(d3_event, operation) {
76486 d3_event.stopPropagation();
76488 if (operation.relatedEntityIds) {
76489 utilHighlightEntities(operation.relatedEntityIds(), false, context);
76492 if (operation.disabled()) {
76493 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
76494 // there are no tooltips for touch interactions so flash feedback instead
76495 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
76498 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
76499 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
76506 lastPointerUpType = null;
76509 dispatch$1.call('toggled', this, true);
76512 function updatePosition() {
76513 if (!_menu || _menu.empty()) return;
76514 var anchorLoc = context.projection(_anchorLocLonLat);
76515 var viewport = context.surfaceRect();
76517 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
76518 // close the menu if it's gone offscreen
76523 var menuLeft = displayOnLeft(viewport);
76524 var offset = [0, 0];
76525 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
76528 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
76529 // menu is near top viewport edge, shift downward
76530 offset[1] = -anchorLoc[1] + _vpTopMargin;
76532 offset[1] = -_menuHeight;
76535 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
76536 // menu is near bottom viewport edge, shift upwards
76537 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
76543 var origin = geoVecAdd(anchorLoc, offset);
76545 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
76547 var tooltipSide = tooltipPosition(viewport, menuLeft);
76549 _tooltips.forEach(function (tooltip) {
76550 tooltip.placement(tooltipSide);
76553 function displayOnLeft(viewport) {
76554 if (_mainLocalizer.textDirection() === 'ltr') {
76555 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
76556 // right menu would be too close to the right viewport edge, go left
76558 } // prefer right menu
76564 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
76565 // left menu would be too close to the left viewport edge, go right
76567 } // prefer left menu
76574 function tooltipPosition(viewport, menuLeft) {
76575 if (_mainLocalizer.textDirection() === 'ltr') {
76577 // if there's not room for a right-side menu then there definitely
76578 // isn't room for right-side tooltips
76582 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
76583 // right tooltips would be too close to the right viewport edge, go left
76585 } // prefer right tooltips
76595 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
76596 // left tooltips would be too close to the left viewport edge, go right
76598 } // prefer left tooltips
76606 editMenu.close = function () {
76607 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
76612 dispatch$1.call('toggled', this, false);
76615 editMenu.anchorLoc = function (val) {
76616 if (!arguments.length) return _anchorLoc;
76618 _anchorLocLonLat = context.projection.invert(_anchorLoc);
76622 editMenu.triggerType = function (val) {
76623 if (!arguments.length) return _triggerType;
76624 _triggerType = val;
76628 editMenu.operations = function (val) {
76629 if (!arguments.length) return _operations;
76634 return utilRebind(editMenu, dispatch$1, 'on');
76637 function uiFeatureInfo(context) {
76638 function update(selection) {
76639 var features = context.features();
76640 var stats = features.stats();
76642 var hiddenList = features.hidden().map(function (k) {
76645 return _t('inspector.title_count', {
76646 title: _t.html('feature.' + k + '.description'),
76652 }).filter(Boolean);
76653 selection.html('');
76655 if (hiddenList.length) {
76656 var tooltipBehavior = uiTooltip().placement('top').title(function () {
76657 return hiddenList.join('<br/>');
76659 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
76661 })).call(tooltipBehavior).on('click', function (d3_event) {
76662 tooltipBehavior.hide();
76663 d3_event.preventDefault(); // open the Map Data pane
76665 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
76669 selection.classed('hide', !hiddenList.length);
76672 return function (selection) {
76674 context.features().on('change.feature_info', function () {
76680 function uiFlash(context) {
76683 var _duration = 2000;
76684 var _iconName = '#iD-icon-no';
76685 var _iconClass = 'disabled';
76690 _flashTimer.stop();
76693 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
76694 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
76695 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
76697 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
76698 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
76699 iconEnter.append('circle').attr('r', 9);
76700 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
76701 contentEnter.append('div').attr('class', 'flash-text'); // Update
76703 content = content.merge(contentEnter);
76704 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
76705 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
76706 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
76707 _flashTimer = d3_timeout(function () {
76708 _flashTimer = null;
76709 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
76710 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
76715 flash.duration = function (_) {
76716 if (!arguments.length) return _duration;
76721 flash.label = function (_) {
76722 if (!arguments.length) return _label;
76727 flash.iconName = function (_) {
76728 if (!arguments.length) return _iconName;
76733 flash.iconClass = function (_) {
76734 if (!arguments.length) return _iconClass;
76742 function uiFullScreen(context) {
76743 var element = context.container().node(); // var button = d3_select(null);
76745 function getFullScreenFn() {
76746 if (element.requestFullscreen) {
76747 return element.requestFullscreen;
76748 } else if (element.msRequestFullscreen) {
76749 return element.msRequestFullscreen;
76750 } else if (element.mozRequestFullScreen) {
76751 return element.mozRequestFullScreen;
76752 } else if (element.webkitRequestFullscreen) {
76753 return element.webkitRequestFullscreen;
76757 function getExitFullScreenFn() {
76758 if (document.exitFullscreen) {
76759 return document.exitFullscreen;
76760 } else if (document.msExitFullscreen) {
76761 return document.msExitFullscreen;
76762 } else if (document.mozCancelFullScreen) {
76763 return document.mozCancelFullScreen;
76764 } else if (document.webkitExitFullscreen) {
76765 return document.webkitExitFullscreen;
76769 function isFullScreen() {
76770 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
76773 function isSupported() {
76774 return !!getFullScreenFn();
76777 function fullScreen(d3_event) {
76778 d3_event.preventDefault();
76780 if (!isFullScreen()) {
76781 // button.classed('active', true);
76782 getFullScreenFn().apply(element);
76784 // button.classed('active', false);
76785 getExitFullScreenFn().apply(document);
76789 return function () {
76791 if (!isSupported()) return; // button = selection.append('button')
76792 // .attr('title', t('full_screen'))
76793 // .on('click', fullScreen)
76795 // button.append('span')
76796 // .attr('class', 'icon full-screen');
76798 var detected = utilDetect();
76799 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
76800 context.keybinding().on(keys, fullScreen);
76804 function uiGeolocate(context) {
76805 var _geolocationOptions = {
76806 // prioritize speed and power usage over precision
76807 enableHighAccuracy: false,
76808 // don't hang indefinitely getting the location
76809 timeout: 6000 // 6sec
76813 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
76815 var _layer = context.layers().layer('geolocate');
76823 var _button = select(null);
76826 if (context.inIntro()) return;
76828 if (!_layer.enabled() && !_locating.isShown()) {
76829 // This timeout ensures that we still call finish() even if
76830 // the user declines to share their location in Firefox
76831 _timeoutID = setTimeout(error, 10000
76834 context.container().call(_locating); // get the latest position even if we already have one
76836 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
76840 _layer.enabled(null, false);
76842 updateButtonState();
76846 function zoomTo() {
76847 context.enter(modeBrowse(context));
76848 var map = context.map();
76850 _layer.enabled(_position, true);
76852 updateButtonState();
76853 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
76856 function success(geolocation) {
76857 _position = geolocation;
76858 var coords = _position.coords;
76859 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
76866 // use the position from a previous call if we have one
76869 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
76875 function finish() {
76876 _locating.close(); // unblock ui
76880 clearTimeout(_timeoutID);
76883 _timeoutID = undefined;
76886 function updateButtonState() {
76887 _button.classed('active', _layer.enabled());
76890 return function (selection) {
76891 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
76892 _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')]));
76893 context.keybinding().on(_t('geolocate.key'), click);
76897 function uiPanelBackground(context) {
76898 var background = context.background();
76899 var _currSourceName = null;
76900 var _metadata = {};
76901 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
76903 var debouncedRedraw = debounce(redraw, 250);
76905 function redraw(selection) {
76906 var source = background.baseLayerSource();
76907 if (!source) return;
76908 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
76909 var sourceLabel = source.label();
76911 if (_currSourceName !== sourceLabel) {
76912 _currSourceName = sourceLabel;
76916 selection.html('');
76917 var list = selection.append('ul').attr('class', 'background-info');
76918 list.append('li').html(_currSourceName);
76920 _metadataKeys.forEach(function (k) {
76921 // DigitalGlobe vintage is available in raster layers for now.
76922 if (isDG && k === 'vintage') return;
76923 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]);
76926 debouncedGetMetadata(selection);
76927 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
76928 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
76929 d3_event.preventDefault();
76930 context.setDebug('tile', !context.getDebug('tile'));
76931 selection.call(redraw);
76935 var key = source.id + '-vintage';
76936 var sourceVintage = context.background().findSource(key);
76937 var showsVintage = context.background().showsLayer(sourceVintage);
76938 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
76939 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
76940 d3_event.preventDefault();
76941 context.background().toggleOverlayLayer(sourceVintage);
76942 selection.call(redraw);
76944 } // disable if necessary
76947 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
76948 if (source.id !== layerId) {
76949 var key = layerId + '-vintage';
76950 var sourceVintage = context.background().findSource(key);
76952 if (context.background().showsLayer(sourceVintage)) {
76953 context.background().toggleOverlayLayer(sourceVintage);
76959 var debouncedGetMetadata = debounce(getMetadata, 250);
76961 function getMetadata(selection) {
76962 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
76964 if (tile.empty()) return;
76965 var sourceName = _currSourceName;
76966 var d = tile.datum();
76967 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
76968 var center = context.map().center(); // update zoom
76970 _metadata.zoom = String(zoom);
76971 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
76972 if (!d || !d.length >= 3) return;
76973 background.baseLayerSource().getMetadata(center, d, function (err, result) {
76974 if (err || _currSourceName !== sourceName) return; // update vintage
76976 var vintage = result.vintage;
76977 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
76978 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
76980 _metadataKeys.forEach(function (k) {
76981 if (k === 'zoom' || k === 'vintage') return; // done already
76983 var val = result[k];
76984 _metadata[k] = val;
76985 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
76990 var panel = function panel(selection) {
76991 selection.call(redraw);
76992 context.map().on('drawn.info-background', function () {
76993 selection.call(debouncedRedraw);
76994 }).on('move.info-background', function () {
76995 selection.call(debouncedGetMetadata);
76999 panel.off = function () {
77000 context.map().on('drawn.info-background', null).on('move.info-background', null);
77003 panel.id = 'background';
77004 panel.label = _t.html('info_panels.background.title');
77005 panel.key = _t('info_panels.background.key');
77009 function uiPanelHistory(context) {
77012 function displayTimestamp(timestamp) {
77013 if (!timestamp) return _t('info_panels.history.unknown');
77022 var d = new Date(timestamp);
77023 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
77024 return d.toLocaleString(_mainLocalizer.localeCode(), options);
77027 function displayUser(selection, userName) {
77029 selection.append('span').html(_t.html('info_panels.history.unknown'));
77033 selection.append('span').attr('class', 'user-name').html(userName);
77034 var links = selection.append('div').attr('class', 'links');
77037 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
77040 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
77043 function displayChangeset(selection, changeset) {
77045 selection.append('span').html(_t.html('info_panels.history.unknown'));
77049 selection.append('span').attr('class', 'changeset-id').html(changeset);
77050 var links = selection.append('div').attr('class', 'links');
77053 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
77056 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
77057 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
77060 function redraw(selection) {
77061 var selectedNoteID = context.selectedNoteID();
77062 osm = context.connection();
77063 var selected, note, entity;
77065 if (selectedNoteID && osm) {
77067 selected = [_t('note.note') + ' ' + selectedNoteID];
77068 note = osm.getNote(selectedNoteID);
77070 // selected 1..n entities
77071 selected = context.selectedIDs().filter(function (e) {
77072 return context.hasEntity(e);
77075 if (selected.length) {
77076 entity = context.entity(selected[0]);
77080 var singular = selected.length === 1 ? selected[0] : null;
77081 selection.html('');
77082 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
77085 if (!singular) return;
77088 selection.call(redrawEntity, entity);
77090 selection.call(redrawNote, note);
77094 function redrawNote(selection, note) {
77095 if (!note || note.isNew()) {
77096 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
77100 var list = selection.append('ul');
77101 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
77103 if (note.comments.length) {
77104 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
77105 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
77109 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'));
77113 function redrawEntity(selection, entity) {
77114 if (!entity || entity.isNew()) {
77115 selection.append('div').html(_t.html('info_panels.history.no_history'));
77119 var links = selection.append('div').attr('class', 'links');
77122 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');
77125 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');
77126 var list = selection.append('ul');
77127 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
77128 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
77129 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
77130 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
77133 var panel = function panel(selection) {
77134 selection.call(redraw);
77135 context.map().on('drawn.info-history', function () {
77136 selection.call(redraw);
77138 context.on('enter.info-history', function () {
77139 selection.call(redraw);
77143 panel.off = function () {
77144 context.map().on('drawn.info-history', null);
77145 context.on('enter.info-history', null);
77148 panel.id = 'history';
77149 panel.label = _t.html('info_panels.history.title');
77150 panel.key = _t('info_panels.history.key');
77154 var OSM_PRECISION = 7;
77156 * Returns a localized representation of the given length measurement.
77158 * @param {Number} m area in meters
77159 * @param {Boolean} isImperial true for U.S. customary units; false for metric
77162 function displayLength(m, isImperial) {
77163 var d = m * (isImperial ? 3.28084 : 1);
77176 unit = 'kilometers';
77182 return _t('units.' + unit, {
77183 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
77184 maximumSignificantDigits: 4
77189 * Returns a localized representation of the given area measurement.
77191 * @param {Number} m2 area in square meters
77192 * @param {Boolean} isImperial true for U.S. customary units; false for metric
77195 function displayArea(m2, isImperial) {
77196 var locale = _mainLocalizer.localeCode();
77197 var d = m2 * (isImperial ? 10.7639111056 : 1);
77203 if (d >= 6969600) {
77204 // > 0.25mi² show mi²
77206 unit1 = 'square_miles';
77209 unit1 = 'square_feet';
77212 if (d > 4356 && d < 43560000) {
77213 // 0.1 - 1000 acres
77219 // > 0.25km² show km²
77221 unit1 = 'square_kilometers';
77224 unit1 = 'square_meters';
77227 if (d > 1000 && d < 10000000) {
77228 // 0.1 - 1000 hectares
77230 unit2 = 'hectares';
77234 area = _t('units.' + unit1, {
77235 quantity: d1.toLocaleString(locale, {
77236 maximumSignificantDigits: 4
77241 return _t('units.area_pair', {
77243 area2: _t('units.' + unit2, {
77244 quantity: d2.toLocaleString(locale, {
77245 maximumSignificantDigits: 2
77254 function wrap$2(x, min, max) {
77256 return ((x - min) % d + d) % d + min;
77259 function clamp$1(x, min, max) {
77260 return Math.max(min, Math.min(x, max));
77263 function displayCoordinate(deg, pos, neg) {
77264 var locale = _mainLocalizer.localeCode();
77265 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
77266 var sec = (min - Math.floor(min)) * 60;
77267 var displayDegrees = _t('units.arcdegrees', {
77268 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
77270 var displayCoordinate;
77272 if (Math.floor(sec) > 0) {
77273 displayCoordinate = displayDegrees + _t('units.arcminutes', {
77274 quantity: Math.floor(min).toLocaleString(locale)
77275 }) + _t('units.arcseconds', {
77276 quantity: Math.round(sec).toLocaleString(locale)
77278 } else if (Math.floor(min) > 0) {
77279 displayCoordinate = displayDegrees + _t('units.arcminutes', {
77280 quantity: Math.round(min).toLocaleString(locale)
77283 displayCoordinate = _t('units.arcdegrees', {
77284 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
77289 return displayCoordinate;
77291 return _t('units.coordinate', {
77292 coordinate: displayCoordinate,
77293 direction: _t('units.' + (deg > 0 ? pos : neg))
77298 * Returns given coordinate pair in degree-minute-second format.
77300 * @param {Array<Number>} coord longitude and latitude
77304 function dmsCoordinatePair(coord) {
77305 return _t('units.coordinate_pair', {
77306 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
77307 longitude: displayCoordinate(wrap$2(coord[0], -180, 180), 'east', 'west')
77311 * Returns the given coordinate pair in decimal format.
77312 * note: unlocalized to avoid comma ambiguity - see #4765
77314 * @param {Array<Number>} coord longitude and latitude
77317 function decimalCoordinatePair(coord) {
77318 return _t('units.coordinate_pair', {
77319 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
77320 longitude: wrap$2(coord[0], -180, 180).toFixed(OSM_PRECISION)
77324 function uiPanelLocation(context) {
77325 var currLocation = '';
77327 function redraw(selection) {
77328 selection.html('');
77329 var list = selection.append('ul'); // Mouse coordinates
77331 var coord = context.map().mouseCoordinates();
77333 if (coord.some(isNaN)) {
77334 coord = context.map().center();
77337 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
77339 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
77340 debouncedGetLocation(selection, coord);
77343 var debouncedGetLocation = debounce(getLocation, 250);
77345 function getLocation(selection, coord) {
77346 if (!services.geocoder) {
77347 currLocation = _t('info_panels.location.unknown_location');
77348 selection.selectAll('.location-info').html(currLocation);
77350 services.geocoder.reverse(coord, function (err, result) {
77351 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
77352 selection.selectAll('.location-info').html(currLocation);
77357 var panel = function panel(selection) {
77358 selection.call(redraw);
77359 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
77360 selection.call(redraw);
77364 panel.off = function () {
77365 context.surface().on('.info-location', null);
77368 panel.id = 'location';
77369 panel.label = _t.html('info_panels.location.title');
77370 panel.key = _t('info_panels.location.key');
77374 function uiPanelMeasurement(context) {
77375 function radiansToMeters(r) {
77376 // using WGS84 authalic radius (6371007.1809 m)
77377 return r * 6371007.1809;
77380 function steradiansToSqmeters(r) {
77381 // http://gis.stackexchange.com/a/124857/40446
77382 return r / (4 * Math.PI) * 510065621724000;
77385 function toLineString(feature) {
77386 if (feature.type === 'LineString') return feature;
77388 type: 'LineString',
77392 if (feature.type === 'Polygon') {
77393 result.coordinates = feature.coordinates[0];
77394 } else if (feature.type === 'MultiPolygon') {
77395 result.coordinates = feature.coordinates[0][0];
77401 var _isImperial = !_mainLocalizer.usesMetric();
77403 function redraw(selection) {
77404 var graph = context.graph();
77405 var selectedNoteID = context.selectedNoteID();
77406 var osm = services.osm;
77407 var localeCode = _mainLocalizer.localeCode();
77409 var center, location, centroid;
77410 var closed, geometry;
77411 var totalNodeCount,
77416 if (selectedNoteID && osm) {
77418 var note = osm.getNote(selectedNoteID);
77419 heading = _t('note.note') + ' ' + selectedNoteID;
77420 location = note.loc;
77423 // selected 1..n entities
77424 var selectedIDs = context.selectedIDs().filter(function (id) {
77425 return context.hasEntity(id);
77427 var selected = selectedIDs.map(function (id) {
77428 return context.entity(id);
77430 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
77434 if (selected.length) {
77435 var extent = geoExtent();
77437 for (var i in selected) {
77438 var entity = selected[i];
77440 extent._extend(entity.extent(graph));
77442 geometry = entity.geometry(graph);
77444 if (geometry === 'line' || geometry === 'area') {
77445 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
77446 var feature = entity.asGeoJSON(graph);
77447 length += radiansToMeters(d3_geoLength(toLineString(feature))); // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
77449 centroid = d3_geoCentroid(geojsonRewind(Object.assign({}, feature), true));
77452 area += steradiansToSqmeters(entity.area(graph));
77457 if (selected.length > 1) {
77463 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
77464 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
77467 if (selected.length === 1 && selected[0].type === 'node') {
77468 location = selected[0].loc;
77470 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
77473 if (!location && !centroid) {
77474 center = extent.center();
77479 selection.html('');
77482 selection.append('h4').attr('class', 'measurement-heading').html(heading);
77485 var list = selection.append('ul');
77489 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
77492 if (totalNodeCount) {
77493 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
77497 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
77501 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
77504 if (typeof distance === 'number') {
77505 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
77509 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
77510 coordItem.append('span').html(dmsCoordinatePair(location));
77511 coordItem.append('span').html(decimalCoordinatePair(location));
77515 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
77516 coordItem.append('span').html(dmsCoordinatePair(centroid));
77517 coordItem.append('span').html(decimalCoordinatePair(centroid));
77521 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
77522 coordItem.append('span').html(dmsCoordinatePair(center));
77523 coordItem.append('span').html(decimalCoordinatePair(center));
77526 if (length || area || typeof distance === 'number') {
77527 var toggle = _isImperial ? 'imperial' : 'metric';
77528 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
77529 d3_event.preventDefault();
77530 _isImperial = !_isImperial;
77531 selection.call(redraw);
77536 var panel = function panel(selection) {
77537 selection.call(redraw);
77538 context.map().on('drawn.info-measurement', function () {
77539 selection.call(redraw);
77541 context.on('enter.info-measurement', function () {
77542 selection.call(redraw);
77546 panel.off = function () {
77547 context.map().on('drawn.info-measurement', null);
77548 context.on('enter.info-measurement', null);
77551 panel.id = 'measurement';
77552 panel.label = _t.html('info_panels.measurement.title');
77553 panel.key = _t('info_panels.measurement.key');
77557 var uiInfoPanels = {
77558 background: uiPanelBackground,
77559 history: uiPanelHistory,
77560 location: uiPanelLocation,
77561 measurement: uiPanelMeasurement
77564 function uiInfo(context) {
77565 var ids = Object.keys(uiInfoPanels);
77566 var wasActive = ['measurement'];
77568 var active = {}; // create panels
77570 ids.forEach(function (k) {
77572 panels[k] = uiInfoPanels[k](context);
77577 function info(selection) {
77578 function redraw() {
77579 var activeids = ids.filter(function (k) {
77582 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
77585 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
77586 select(this).call(panels[d].off).remove();
77588 var enter = containers.enter().append('div').attr('class', function (d) {
77589 return 'fillD2 panel-container panel-container-' + d;
77591 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
77592 var title = enter.append('div').attr('class', 'panel-title fillD2');
77593 title.append('h3').html(function (d) {
77594 return panels[d].label;
77596 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
77597 d3_event.stopImmediatePropagation();
77598 d3_event.preventDefault();
77600 }).call(svgIcon('#iD-icon-close'));
77601 enter.append('div').attr('class', function (d) {
77602 return 'panel-content panel-content-' + d;
77603 }); // redraw the panels
77605 infoPanels.selectAll('.panel-content').each(function (d) {
77606 select(this).call(panels[d]);
77610 info.toggle = function (which) {
77611 var activeids = ids.filter(function (k) {
77617 active[which] = !active[which];
77619 if (activeids.length === 1 && activeids[0] === which) {
77620 // none active anymore
77621 wasActive = [which];
77624 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
77627 if (activeids.length) {
77628 wasActive = activeids;
77629 activeids.forEach(function (k) {
77633 wasActive.forEach(function (k) {
77642 var infoPanels = selection.selectAll('.info-panels').data([0]);
77643 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
77645 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
77646 d3_event.stopImmediatePropagation();
77647 d3_event.preventDefault();
77650 ids.forEach(function (k) {
77651 var key = _t('info_panels.' + k + '.key', {
77655 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
77656 d3_event.stopImmediatePropagation();
77657 d3_event.preventDefault();
77666 function pointBox(loc, context) {
77667 var rect = context.surfaceRect();
77668 var point = context.curtainProjection(loc);
77670 left: point[0] + rect.left - 40,
77671 top: point[1] + rect.top - 60,
77676 function pad(locOrBox, padding, context) {
77679 if (locOrBox instanceof Array) {
77680 var rect = context.surfaceRect();
77681 var point = context.curtainProjection(locOrBox);
77683 left: point[0] + rect.left,
77684 top: point[1] + rect.top
77691 left: box.left - padding,
77692 top: box.top - padding,
77693 width: (box.width || 0) + 2 * padding,
77694 height: (box.width || 0) + 2 * padding
77697 function icon(name, svgklass, useklass) {
77698 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
77700 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
77701 // label replacements suitable for tutorials and documentation. Optionally supplemented
77702 // with custom `replacements`
77704 function helpHtml(id, replacements) {
77705 // only load these the first time
77706 if (!helpStringReplacements) helpStringReplacements = {
77707 // insert icons corresponding to various UI elements
77708 point_icon: icon('#iD-icon-point', 'inline'),
77709 line_icon: icon('#iD-icon-line', 'inline'),
77710 area_icon: icon('#iD-icon-area', 'inline'),
77711 note_icon: icon('#iD-icon-note', 'inline add-note'),
77712 plus: icon('#iD-icon-plus', 'inline'),
77713 minus: icon('#iD-icon-minus', 'inline'),
77714 layers_icon: icon('#iD-icon-layers', 'inline'),
77715 data_icon: icon('#iD-icon-data', 'inline'),
77716 inspect: icon('#iD-icon-inspect', 'inline'),
77717 help_icon: icon('#iD-icon-help', 'inline'),
77718 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
77719 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
77720 save_icon: icon('#iD-icon-save', 'inline'),
77722 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
77723 continue_icon: icon('#iD-operation-continue', 'inline operation'),
77724 copy_icon: icon('#iD-operation-copy', 'inline operation'),
77725 delete_icon: icon('#iD-operation-delete', 'inline operation'),
77726 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
77727 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
77728 extract_icon: icon('#iD-operation-extract', 'inline operation'),
77729 merge_icon: icon('#iD-operation-merge', 'inline operation'),
77730 move_icon: icon('#iD-operation-move', 'inline operation'),
77731 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
77732 paste_icon: icon('#iD-operation-paste', 'inline operation'),
77733 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
77734 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
77735 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
77736 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
77737 split_icon: icon('#iD-operation-split', 'inline operation'),
77738 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
77739 // interaction icons
77740 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
77741 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
77742 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
77743 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
77744 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
77745 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
77746 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
77747 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
77748 // insert keys; may be localized and platform-dependent
77749 shift: uiCmd.display('⇧'),
77750 alt: uiCmd.display('⌥'),
77751 "return": uiCmd.display('↵'),
77752 esc: _t.html('shortcuts.key.esc'),
77753 space: _t.html('shortcuts.key.space'),
77754 add_note_key: _t.html('modes.add_note.key'),
77755 help_key: _t.html('help.key'),
77756 shortcuts_key: _t.html('shortcuts.toggle.key'),
77757 // reference localized UI labels directly so that they'll always match
77758 save: _t.html('save.title'),
77759 undo: _t.html('undo.title'),
77760 redo: _t.html('redo.title'),
77761 upload: _t.html('commit.save'),
77762 point: _t.html('modes.add_point.title'),
77763 line: _t.html('modes.add_line.title'),
77764 area: _t.html('modes.add_area.title'),
77765 note: _t.html('modes.add_note.label'),
77766 circularize: _t.html('operations.circularize.title'),
77767 "continue": _t.html('operations.continue.title'),
77768 copy: _t.html('operations.copy.title'),
77769 "delete": _t.html('operations.delete.title'),
77770 disconnect: _t.html('operations.disconnect.title'),
77771 downgrade: _t.html('operations.downgrade.title'),
77772 extract: _t.html('operations.extract.title'),
77773 merge: _t.html('operations.merge.title'),
77774 move: _t.html('operations.move.title'),
77775 orthogonalize: _t.html('operations.orthogonalize.title'),
77776 paste: _t.html('operations.paste.title'),
77777 reflect_long: _t.html('operations.reflect.title.long'),
77778 reflect_short: _t.html('operations.reflect.title.short'),
77779 reverse: _t.html('operations.reverse.title'),
77780 rotate: _t.html('operations.rotate.title'),
77781 split: _t.html('operations.split.title'),
77782 straighten: _t.html('operations.straighten.title'),
77783 map_data: _t.html('map_data.title'),
77784 osm_notes: _t.html('map_data.layers.notes.title'),
77785 fields: _t.html('inspector.fields'),
77786 tags: _t.html('inspector.tags'),
77787 relations: _t.html('inspector.relations'),
77788 new_relation: _t.html('inspector.new_relation'),
77789 turn_restrictions: _t.html('presets.fields.restrictions.label'),
77790 background_settings: _t.html('background.description'),
77791 imagery_offset: _t.html('background.fix_misalignment'),
77792 start_the_walkthrough: _t.html('splash.walkthrough'),
77793 help: _t.html('help.title'),
77794 ok: _t.html('intro.ok')
77798 if (replacements) {
77799 reps = Object.assign(replacements, helpStringReplacements);
77801 reps = helpStringReplacements;
77804 return _t.html(id, reps) // use keyboard key styling for shortcuts
77805 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
77808 function slugify(text) {
77809 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
77810 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
77811 .replace(/\-\-+/g, '-') // Replace multiple - with single -
77812 .replace(/^-+/, '') // Trim - from start of text
77813 .replace(/-+$/, ''); // Trim - from end of text
77814 } // console warning for missing walkthrough names
77817 var missingStrings = {};
77819 function checkKey(key, text) {
77821 "default": undefined
77822 }) === undefined) {
77823 if (missingStrings.hasOwnProperty(key)) return; // warn once
77825 missingStrings[key] = text;
77826 var missing = key + ': ' + text;
77827 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
77831 function localize(obj) {
77832 var key; // Assign name if entity has one..
77834 var name = obj.tags && obj.tags.name;
77837 key = 'intro.graph.name.' + slugify(name);
77838 obj.tags.name = _t(key, {
77841 checkKey(key, name);
77842 } // Assign street name if entity has one..
77845 var street = obj.tags && obj.tags['addr:street'];
77848 key = 'intro.graph.name.' + slugify(street);
77849 obj.tags['addr:street'] = _t(key, {
77852 checkKey(key, street); // Add address details common across walkthrough..
77854 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
77855 addrTags.forEach(function (k) {
77856 var key = 'intro.graph.' + k;
77857 var tag = 'addr:' + k;
77858 var val = obj.tags && obj.tags[tag];
77859 var str = _t(key, {
77864 if (str.match(/^<.*>$/) !== null) {
77865 delete obj.tags[tag];
77867 obj.tags[tag] = str;
77874 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
77876 function isMostlySquare(points) {
77877 // note: uses 15 here instead of the 12 from actionOrthogonalize because
77878 // actionOrthogonalize can actually straighten some larger angles as it iterates
77879 var threshold = 15; // degrees within right or straight
77881 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
77883 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
77885 for (var i = 0; i < points.length; i++) {
77886 var a = points[(i - 1 + points.length) % points.length];
77887 var origin = points[i];
77888 var b = points[(i + 1) % points.length];
77889 var dotp = geoVecNormalizedDot(a, b, origin);
77890 var mag = Math.abs(dotp);
77892 if (mag > lowerBound && mag < upperBound) {
77899 function selectMenuItem(context, operation) {
77900 return context.container().select('.edit-menu .edit-menu-item-' + operation);
77902 function transitionTime(point1, point2) {
77903 var distance = geoSphericalDistance(point1, point2);
77904 if (distance === 0) return 0;else if (distance < 80) return 500;else return 1000;
77907 function uiCurtain(containerNode) {
77908 var surface = select(null),
77909 tooltip = select(null),
77910 darkness = select(null);
77912 function curtain(selection) {
77913 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
77914 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
77915 select(window).on('resize.curtain', resize);
77916 tooltip = selection.append('div').attr('class', 'tooltip');
77917 tooltip.append('div').attr('class', 'popover-arrow');
77918 tooltip.append('div').attr('class', 'popover-inner');
77921 function resize() {
77922 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
77923 curtain.cut(darkness.datum());
77927 * Reveal cuts the curtain to highlight the given box,
77928 * and shows a tooltip with instructions next to the box.
77930 * @param {String|ClientRect} [box] box used to cut the curtain
77931 * @param {String} [text] text for a tooltip
77932 * @param {Object} [options]
77933 * @param {string} [options.tooltipClass] optional class to add to the tooltip
77934 * @param {integer} [options.duration] transition time in milliseconds
77935 * @param {string} [options.buttonText] if set, create a button with this text label
77936 * @param {function} [options.buttonCallback] if set, the callback for the button
77937 * @param {function} [options.padding] extra margin in px to put around bbox
77938 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
77942 curtain.reveal = function (box, html, options) {
77943 options = options || {};
77945 if (typeof box === 'string') {
77946 box = select(box).node();
77949 if (box && box.getBoundingClientRect) {
77950 box = copyBox(box.getBoundingClientRect());
77951 var containerRect = containerNode.getBoundingClientRect();
77952 box.top -= containerRect.top;
77953 box.left -= containerRect.left;
77956 if (box && options.padding) {
77957 box.top -= options.padding;
77958 box.left -= options.padding;
77959 box.bottom += options.padding;
77960 box.right += options.padding;
77961 box.height += options.padding * 2;
77962 box.width += options.padding * 2;
77967 if (options.tooltipBox) {
77968 tooltipBox = options.tooltipBox;
77970 if (typeof tooltipBox === 'string') {
77971 tooltipBox = select(tooltipBox).node();
77974 if (tooltipBox && tooltipBox.getBoundingClientRect) {
77975 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77981 if (tooltipBox && html) {
77982 if (html.indexOf('**') !== -1) {
77983 if (html.indexOf('<span') === 0) {
77984 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
77986 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
77987 } // pseudo markdown bold text for the instruction section..
77990 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
77993 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
77995 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
77997 if (options.buttonText && options.buttonCallback) {
77998 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
78001 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
78002 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
78004 if (options.buttonText && options.buttonCallback) {
78005 var button = tooltip.selectAll('.button-section .button.action');
78006 button.on('click', function (d3_event) {
78007 d3_event.preventDefault();
78008 options.buttonCallback();
78012 var tip = copyBox(tooltip.node().getBoundingClientRect()),
78013 w = containerNode.clientWidth,
78014 h = containerNode.clientHeight,
78015 tooltipWidth = 200,
78018 pos; // hack: this will have bottom placement,
78019 // so need to reserve extra space for the tooltip illustration.
78021 if (options.tooltipClass === 'intro-mouse') {
78023 } // trim box dimensions to just the portion that fits in the container..
78026 if (tooltipBox.top + tooltipBox.height > h) {
78027 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
78030 if (tooltipBox.left + tooltipBox.width > w) {
78031 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
78032 } // determine tooltip placement..
78035 if (tooltipBox.top + tooltipBox.height < 100) {
78036 // tooltip below box..
78038 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
78039 } else if (tooltipBox.top > h - 140) {
78040 // tooltip above box..
78042 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
78044 // tooltip to the side of the tooltipBox..
78045 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
78047 if (_mainLocalizer.textDirection() === 'rtl') {
78048 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
78050 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78053 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78056 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
78058 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78061 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78066 if (options.duration !== 0 || !tooltip.classed(side)) {
78067 tooltip.call(uiToggle(true));
78070 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
78071 // (doesn't affect the placement of the popover-arrow)
78075 if (side === 'left' || side === 'right') {
78077 shiftY = 60 - pos[1];
78078 } else if (pos[1] + tip.height > h - 100) {
78079 shiftY = h - pos[1] - tip.height - 100;
78083 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
78085 tooltip.classed('in', false).call(uiToggle(false));
78088 curtain.cut(box, options.duration);
78092 curtain.cut = function (datum, duration) {
78093 darkness.datum(datum).interrupt();
78096 if (duration === 0) {
78097 selection = darkness;
78099 selection = darkness.transition().duration(duration || 600).ease(linear$1);
78102 selection.attr('d', function (d) {
78103 var containerWidth = containerNode.clientWidth;
78104 var containerHeight = containerNode.clientHeight;
78105 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
78106 if (!d) return string;
78107 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';
78111 curtain.remove = function () {
78114 select(window).on('resize.curtain', null);
78115 }; // ClientRects are immutable, so copy them to an object,
78116 // in case we need to trim the height/width.
78119 function copyBox(src) {
78123 bottom: src.bottom,
78133 function uiIntroWelcome(context, reveal) {
78134 var dispatch$1 = dispatch('done');
78136 title: 'intro.welcome.title'
78139 function welcome() {
78140 context.map().centerZoom([-85.63591, 41.94285], 19);
78141 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
78142 buttonText: _t.html('intro.ok'),
78143 buttonCallback: practice
78147 function practice() {
78148 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
78149 buttonText: _t.html('intro.ok'),
78150 buttonCallback: words
78155 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
78156 buttonText: _t.html('intro.ok'),
78157 buttonCallback: chapters
78161 function chapters() {
78162 dispatch$1.call('done');
78163 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
78164 next: _t('intro.navigation.title')
78168 chapter.enter = function () {
78172 chapter.exit = function () {
78173 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
78176 chapter.restart = function () {
78181 return utilRebind(chapter, dispatch$1, 'on');
78184 function uiIntroNavigation(context, reveal) {
78185 var dispatch$1 = dispatch('done');
78187 var hallId = 'n2061';
78188 var townHall = [-85.63591, 41.94285];
78189 var springStreetId = 'w397';
78190 var springStreetEndId = 'n1834';
78191 var springStreet = [-85.63582, 41.94255];
78192 var onewayField = _mainPresetIndex.field('oneway');
78193 var maxspeedField = _mainPresetIndex.field('maxspeed');
78195 title: 'intro.navigation.title'
78198 function timeout(f, t) {
78199 timeouts.push(window.setTimeout(f, t));
78202 function eventCancel(d3_event) {
78203 d3_event.stopPropagation();
78204 d3_event.preventDefault();
78207 function isTownHallSelected() {
78208 var ids = context.selectedIDs();
78209 return ids.length === 1 && ids[0] === hallId;
78212 function dragMap() {
78213 context.enter(modeBrowse(context));
78214 context.history().reset('initial');
78215 var msec = transitionTime(townHall, context.map().center());
78218 reveal(null, null, {
78223 context.map().centerZoomEase(townHall, 19, msec);
78224 timeout(function () {
78225 var centerStart = context.map().center();
78226 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
78227 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
78228 reveal('.surface', dragString);
78229 context.map().on('drawn.intro', function () {
78230 reveal('.surface', dragString, {
78234 context.map().on('move.intro', function () {
78235 var centerNow = context.map().center();
78237 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
78238 context.map().on('move.intro', null);
78239 timeout(function () {
78240 continueTo(zoomMap);
78246 function continueTo(nextStep) {
78247 context.map().on('move.intro drawn.intro', null);
78252 function zoomMap() {
78253 var zoomStart = context.map().zoom();
78254 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
78255 var zoomString = helpHtml('intro.navigation.' + textId);
78256 reveal('.surface', zoomString);
78257 context.map().on('drawn.intro', function () {
78258 reveal('.surface', zoomString, {
78262 context.map().on('move.intro', function () {
78263 if (context.map().zoom() !== zoomStart) {
78264 context.map().on('move.intro', null);
78265 timeout(function () {
78266 continueTo(features);
78271 function continueTo(nextStep) {
78272 context.map().on('move.intro drawn.intro', null);
78277 function features() {
78278 var onClick = function onClick() {
78279 continueTo(pointsLinesAreas);
78282 reveal('.surface', helpHtml('intro.navigation.features'), {
78283 buttonText: _t.html('intro.ok'),
78284 buttonCallback: onClick
78286 context.map().on('drawn.intro', function () {
78287 reveal('.surface', helpHtml('intro.navigation.features'), {
78289 buttonText: _t.html('intro.ok'),
78290 buttonCallback: onClick
78294 function continueTo(nextStep) {
78295 context.map().on('drawn.intro', null);
78300 function pointsLinesAreas() {
78301 var onClick = function onClick() {
78302 continueTo(nodesWays);
78305 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
78306 buttonText: _t.html('intro.ok'),
78307 buttonCallback: onClick
78309 context.map().on('drawn.intro', function () {
78310 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
78312 buttonText: _t.html('intro.ok'),
78313 buttonCallback: onClick
78317 function continueTo(nextStep) {
78318 context.map().on('drawn.intro', null);
78323 function nodesWays() {
78324 var onClick = function onClick() {
78325 continueTo(clickTownHall);
78328 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
78329 buttonText: _t.html('intro.ok'),
78330 buttonCallback: onClick
78332 context.map().on('drawn.intro', function () {
78333 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
78335 buttonText: _t.html('intro.ok'),
78336 buttonCallback: onClick
78340 function continueTo(nextStep) {
78341 context.map().on('drawn.intro', null);
78346 function clickTownHall() {
78347 context.enter(modeBrowse(context));
78348 context.history().reset('initial');
78349 var entity = context.hasEntity(hallId);
78350 if (!entity) return;
78351 reveal(null, null, {
78354 context.map().centerZoomEase(entity.loc, 19, 500);
78355 timeout(function () {
78356 var entity = context.hasEntity(hallId);
78357 if (!entity) return;
78358 var box = pointBox(entity.loc, context);
78359 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
78360 reveal(box, helpHtml('intro.navigation.' + textId));
78361 context.map().on('move.intro drawn.intro', function () {
78362 var entity = context.hasEntity(hallId);
78363 if (!entity) return;
78364 var box = pointBox(entity.loc, context);
78365 reveal(box, helpHtml('intro.navigation.' + textId), {
78369 context.on('enter.intro', function () {
78370 if (isTownHallSelected()) continueTo(selectedTownHall);
78372 }, 550); // after centerZoomEase
78374 context.history().on('change.intro', function () {
78375 if (!context.hasEntity(hallId)) {
78376 continueTo(clickTownHall);
78380 function continueTo(nextStep) {
78381 context.on('enter.intro', null);
78382 context.map().on('move.intro drawn.intro', null);
78383 context.history().on('change.intro', null);
78388 function selectedTownHall() {
78389 if (!isTownHallSelected()) return clickTownHall();
78390 var entity = context.hasEntity(hallId);
78391 if (!entity) return clickTownHall();
78392 var box = pointBox(entity.loc, context);
78394 var onClick = function onClick() {
78395 continueTo(editorTownHall);
78398 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
78399 buttonText: _t.html('intro.ok'),
78400 buttonCallback: onClick
78402 context.map().on('move.intro drawn.intro', function () {
78403 var entity = context.hasEntity(hallId);
78404 if (!entity) return;
78405 var box = pointBox(entity.loc, context);
78406 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
78408 buttonText: _t.html('intro.ok'),
78409 buttonCallback: onClick
78412 context.history().on('change.intro', function () {
78413 if (!context.hasEntity(hallId)) {
78414 continueTo(clickTownHall);
78418 function continueTo(nextStep) {
78419 context.map().on('move.intro drawn.intro', null);
78420 context.history().on('change.intro', null);
78425 function editorTownHall() {
78426 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
78428 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78430 var onClick = function onClick() {
78431 continueTo(presetTownHall);
78434 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
78435 buttonText: _t.html('intro.ok'),
78436 buttonCallback: onClick
78438 context.on('exit.intro', function () {
78439 continueTo(clickTownHall);
78441 context.history().on('change.intro', function () {
78442 if (!context.hasEntity(hallId)) {
78443 continueTo(clickTownHall);
78447 function continueTo(nextStep) {
78448 context.on('exit.intro', null);
78449 context.history().on('change.intro', null);
78450 context.container().select('.inspector-wrap').on('wheel.intro', null);
78455 function presetTownHall() {
78456 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
78458 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
78460 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
78462 var entity = context.entity(context.selectedIDs()[0]);
78463 var preset = _mainPresetIndex.match(entity, context.graph());
78465 var onClick = function onClick() {
78466 continueTo(fieldsTownHall);
78469 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
78470 preset: preset.name()
78472 buttonText: _t.html('intro.ok'),
78473 buttonCallback: onClick
78475 context.on('exit.intro', function () {
78476 continueTo(clickTownHall);
78478 context.history().on('change.intro', function () {
78479 if (!context.hasEntity(hallId)) {
78480 continueTo(clickTownHall);
78484 function continueTo(nextStep) {
78485 context.on('exit.intro', null);
78486 context.history().on('change.intro', null);
78487 context.container().select('.inspector-wrap').on('wheel.intro', null);
78492 function fieldsTownHall() {
78493 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
78495 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
78497 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78499 var onClick = function onClick() {
78500 continueTo(closeTownHall);
78503 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
78504 buttonText: _t.html('intro.ok'),
78505 buttonCallback: onClick
78507 context.on('exit.intro', function () {
78508 continueTo(clickTownHall);
78510 context.history().on('change.intro', function () {
78511 if (!context.hasEntity(hallId)) {
78512 continueTo(clickTownHall);
78516 function continueTo(nextStep) {
78517 context.on('exit.intro', null);
78518 context.history().on('change.intro', null);
78519 context.container().select('.inspector-wrap').on('wheel.intro', null);
78524 function closeTownHall() {
78525 if (!isTownHallSelected()) return clickTownHall();
78526 var selector = '.entity-editor-pane button.close svg use';
78527 var href = select(selector).attr('href') || '#iD-icon-close';
78528 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
78529 button: icon(href, 'inline')
78531 context.on('exit.intro', function () {
78532 continueTo(searchStreet);
78534 context.history().on('change.intro', function () {
78535 // update the close icon in the tooltip if the user edits something.
78536 var selector = '.entity-editor-pane button.close svg use';
78537 var href = select(selector).attr('href') || '#iD-icon-close';
78538 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
78539 button: icon(href, 'inline')
78545 function continueTo(nextStep) {
78546 context.on('exit.intro', null);
78547 context.history().on('change.intro', null);
78552 function searchStreet() {
78553 context.enter(modeBrowse(context));
78554 context.history().reset('initial'); // ensure spring street exists
78556 var msec = transitionTime(springStreet, context.map().center());
78559 reveal(null, null, {
78564 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
78566 timeout(function () {
78567 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
78568 name: _t('intro.graph.name.spring-street')
78570 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
78574 function checkSearchResult() {
78575 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
78577 var firstName = first.select('.entity-name');
78578 var name = _t('intro.graph.name.spring-street');
78580 if (!firstName.empty() && firstName.html() === name) {
78581 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
78586 context.on('exit.intro', function () {
78587 continueTo(selectedStreet);
78589 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
78592 function continueTo(nextStep) {
78593 context.on('exit.intro', null);
78594 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
78599 function selectedStreet() {
78600 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
78601 return searchStreet();
78604 var onClick = function onClick() {
78605 continueTo(editorStreet);
78608 var entity = context.entity(springStreetEndId);
78609 var box = pointBox(entity.loc, context);
78611 reveal(box, helpHtml('intro.navigation.selected_street', {
78612 name: _t('intro.graph.name.spring-street')
78615 buttonText: _t.html('intro.ok'),
78616 buttonCallback: onClick
78618 timeout(function () {
78619 context.map().on('move.intro drawn.intro', function () {
78620 var entity = context.hasEntity(springStreetEndId);
78621 if (!entity) return;
78622 var box = pointBox(entity.loc, context);
78624 reveal(box, helpHtml('intro.navigation.selected_street', {
78625 name: _t('intro.graph.name.spring-street')
78628 buttonText: _t.html('intro.ok'),
78629 buttonCallback: onClick
78632 }, 600); // after reveal.
78634 context.on('enter.intro', function (mode) {
78635 if (!context.hasEntity(springStreetId)) {
78636 return continueTo(searchStreet);
78639 var ids = context.selectedIDs();
78641 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
78642 // keep Spring Street selected..
78643 context.enter(modeSelect(context, [springStreetId]));
78646 context.history().on('change.intro', function () {
78647 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
78648 timeout(function () {
78649 continueTo(searchStreet);
78650 }, 300); // after any transition (e.g. if user deleted intersection)
78654 function continueTo(nextStep) {
78655 context.map().on('move.intro drawn.intro', null);
78656 context.on('enter.intro', null);
78657 context.history().on('change.intro', null);
78662 function editorStreet() {
78663 var selector = '.entity-editor-pane button.close svg use';
78664 var href = select(selector).attr('href') || '#iD-icon-close';
78665 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
78666 button: icon(href, 'inline'),
78667 field1: onewayField.label(),
78668 field2: maxspeedField.label()
78670 context.on('exit.intro', function () {
78673 context.history().on('change.intro', function () {
78674 // update the close icon in the tooltip if the user edits something.
78675 var selector = '.entity-editor-pane button.close svg use';
78676 var href = select(selector).attr('href') || '#iD-icon-close';
78677 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
78678 button: icon(href, 'inline'),
78679 field1: onewayField.label(),
78680 field2: maxspeedField.label()
78686 function continueTo(nextStep) {
78687 context.on('exit.intro', null);
78688 context.history().on('change.intro', null);
78694 dispatch$1.call('done');
78695 reveal('.ideditor', helpHtml('intro.navigation.play', {
78696 next: _t('intro.points.title')
78698 tooltipBox: '.intro-nav-wrap .chapter-point',
78699 buttonText: _t.html('intro.ok'),
78700 buttonCallback: function buttonCallback() {
78701 reveal('.ideditor');
78706 chapter.enter = function () {
78710 chapter.exit = function () {
78711 timeouts.forEach(window.clearTimeout);
78712 context.on('enter.intro exit.intro', null);
78713 context.map().on('move.intro drawn.intro', null);
78714 context.history().on('change.intro', null);
78715 context.container().select('.inspector-wrap').on('wheel.intro', null);
78716 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
78719 chapter.restart = function () {
78724 return utilRebind(chapter, dispatch$1, 'on');
78727 function uiIntroPoint(context, reveal) {
78728 var dispatch$1 = dispatch('done');
78730 var intersection = [-85.63279, 41.94394];
78731 var building = [-85.632422, 41.944045];
78732 var cafePreset = _mainPresetIndex.item('amenity/cafe');
78733 var _pointID = null;
78735 title: 'intro.points.title'
78738 function timeout(f, t) {
78739 timeouts.push(window.setTimeout(f, t));
78742 function eventCancel(d3_event) {
78743 d3_event.stopPropagation();
78744 d3_event.preventDefault();
78747 function addPoint() {
78748 context.enter(modeBrowse(context));
78749 context.history().reset('initial');
78750 var msec = transitionTime(intersection, context.map().center());
78753 reveal(null, null, {
78758 context.map().centerZoomEase(intersection, 19, msec);
78759 timeout(function () {
78760 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
78762 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
78763 context.on('enter.intro', function (mode) {
78764 if (mode.id !== 'add-point') return;
78765 continueTo(placePoint);
78769 function continueTo(nextStep) {
78770 context.on('enter.intro', null);
78775 function placePoint() {
78776 if (context.mode().id !== 'add-point') {
78777 return chapter.restart();
78780 var pointBox = pad(building, 150, context);
78781 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
78782 reveal(pointBox, helpHtml('intro.points.' + textId));
78783 context.map().on('move.intro drawn.intro', function () {
78784 pointBox = pad(building, 150, context);
78785 reveal(pointBox, helpHtml('intro.points.' + textId), {
78789 context.on('enter.intro', function (mode) {
78790 if (mode.id !== 'select') return chapter.restart();
78791 _pointID = context.mode().selectedIDs()[0];
78792 continueTo(searchPreset);
78795 function continueTo(nextStep) {
78796 context.map().on('move.intro drawn.intro', null);
78797 context.on('enter.intro', null);
78802 function searchPreset() {
78803 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78805 } // disallow scrolling
78808 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78809 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
78810 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
78811 preset: cafePreset.name()
78813 context.on('enter.intro', function (mode) {
78814 if (!_pointID || !context.hasEntity(_pointID)) {
78815 return continueTo(addPoint);
78818 var ids = context.selectedIDs();
78820 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
78821 // keep the user's point selected..
78822 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
78824 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78825 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
78826 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
78827 preset: cafePreset.name()
78829 context.history().on('change.intro', null);
78833 function checkPresetSearch() {
78834 var first = context.container().select('.preset-list-item:first-child');
78836 if (first.classed('preset-amenity-cafe')) {
78837 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
78838 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
78839 preset: cafePreset.name()
78843 context.history().on('change.intro', function () {
78844 continueTo(aboutFeatureEditor);
78849 function continueTo(nextStep) {
78850 context.on('enter.intro', null);
78851 context.history().on('change.intro', null);
78852 context.container().select('.inspector-wrap').on('wheel.intro', null);
78853 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78858 function aboutFeatureEditor() {
78859 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78863 timeout(function () {
78864 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
78865 tooltipClass: 'intro-points-describe',
78866 buttonText: _t.html('intro.ok'),
78867 buttonCallback: function buttonCallback() {
78868 continueTo(addName);
78872 context.on('exit.intro', function () {
78873 // if user leaves select mode here, just continue with the tutorial.
78874 continueTo(reselectPoint);
78877 function continueTo(nextStep) {
78878 context.on('exit.intro', null);
78883 function addName() {
78884 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78886 } // reset pane, in case user happened to change it..
78889 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78890 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
78891 timeout(function () {
78892 // It's possible for the user to add a name in a previous step..
78893 // If so, don't tell them to add the name in this step.
78894 // Give them an OK button instead.
78895 var entity = context.entity(_pointID);
78897 if (entity.tags.name) {
78898 var tooltip = reveal('.entity-editor-pane', addNameString, {
78899 tooltipClass: 'intro-points-describe',
78900 buttonText: _t.html('intro.ok'),
78901 buttonCallback: function buttonCallback() {
78902 continueTo(addCloseEditor);
78905 tooltip.select('.instruction').style('display', 'none');
78907 reveal('.entity-editor-pane', addNameString, {
78908 tooltipClass: 'intro-points-describe'
78912 context.history().on('change.intro', function () {
78913 continueTo(addCloseEditor);
78915 context.on('exit.intro', function () {
78916 // if user leaves select mode here, just continue with the tutorial.
78917 continueTo(reselectPoint);
78920 function continueTo(nextStep) {
78921 context.on('exit.intro', null);
78922 context.history().on('change.intro', null);
78927 function addCloseEditor() {
78928 // reset pane, in case user happened to change it..
78929 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78930 var selector = '.entity-editor-pane button.close svg use';
78931 var href = select(selector).attr('href') || '#iD-icon-close';
78932 context.on('exit.intro', function () {
78933 continueTo(reselectPoint);
78935 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
78936 button: icon(href, 'inline')
78939 function continueTo(nextStep) {
78940 context.on('exit.intro', null);
78945 function reselectPoint() {
78946 if (!_pointID) return chapter.restart();
78947 var entity = context.hasEntity(_pointID);
78948 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
78950 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78951 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78952 context.enter(modeBrowse(context));
78953 var msec = transitionTime(entity.loc, context.map().center());
78956 reveal(null, null, {
78961 context.map().centerEase(entity.loc, msec);
78962 timeout(function () {
78963 var box = pointBox(entity.loc, context);
78964 reveal(box, helpHtml('intro.points.reselect'), {
78967 timeout(function () {
78968 context.map().on('move.intro drawn.intro', function () {
78969 var entity = context.hasEntity(_pointID);
78970 if (!entity) return chapter.restart();
78971 var box = pointBox(entity.loc, context);
78972 reveal(box, helpHtml('intro.points.reselect'), {
78976 }, 600); // after reveal..
78978 context.on('enter.intro', function (mode) {
78979 if (mode.id !== 'select') return;
78980 continueTo(updatePoint);
78984 function continueTo(nextStep) {
78985 context.map().on('move.intro drawn.intro', null);
78986 context.on('enter.intro', null);
78991 function updatePoint() {
78992 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78993 return continueTo(reselectPoint);
78994 } // reset pane, in case user happened to untag the point..
78997 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78998 context.on('exit.intro', function () {
78999 continueTo(reselectPoint);
79001 context.history().on('change.intro', function () {
79002 continueTo(updateCloseEditor);
79004 timeout(function () {
79005 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
79006 tooltipClass: 'intro-points-describe'
79010 function continueTo(nextStep) {
79011 context.on('exit.intro', null);
79012 context.history().on('change.intro', null);
79017 function updateCloseEditor() {
79018 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79019 return continueTo(reselectPoint);
79020 } // reset pane, in case user happened to change it..
79023 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79024 context.on('exit.intro', function () {
79025 continueTo(rightClickPoint);
79027 timeout(function () {
79028 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
79029 button: icon('#iD-icon-close', 'inline')
79033 function continueTo(nextStep) {
79034 context.on('exit.intro', null);
79039 function rightClickPoint() {
79040 if (!_pointID) return chapter.restart();
79041 var entity = context.hasEntity(_pointID);
79042 if (!entity) return chapter.restart();
79043 context.enter(modeBrowse(context));
79044 var box = pointBox(entity.loc, context);
79045 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
79046 reveal(box, helpHtml('intro.points.' + textId), {
79049 timeout(function () {
79050 context.map().on('move.intro', function () {
79051 var entity = context.hasEntity(_pointID);
79052 if (!entity) return chapter.restart();
79053 var box = pointBox(entity.loc, context);
79054 reveal(box, helpHtml('intro.points.' + textId), {
79058 }, 600); // after reveal
79060 context.on('enter.intro', function (mode) {
79061 if (mode.id !== 'select') return;
79062 var ids = context.selectedIDs();
79063 if (ids.length !== 1 || ids[0] !== _pointID) return;
79064 timeout(function () {
79065 var node = selectMenuItem(context, 'delete').node();
79067 continueTo(enterDelete);
79068 }, 50); // after menu visible
79071 function continueTo(nextStep) {
79072 context.on('enter.intro', null);
79073 context.map().on('move.intro', null);
79078 function enterDelete() {
79079 if (!_pointID) return chapter.restart();
79080 var entity = context.hasEntity(_pointID);
79081 if (!entity) return chapter.restart();
79082 var node = selectMenuItem(context, 'delete').node();
79085 return continueTo(rightClickPoint);
79088 reveal('.edit-menu', helpHtml('intro.points.delete'), {
79091 timeout(function () {
79092 context.map().on('move.intro', function () {
79093 reveal('.edit-menu', helpHtml('intro.points.delete'), {
79098 }, 300); // after menu visible
79100 context.on('exit.intro', function () {
79101 if (!_pointID) return chapter.restart();
79102 var entity = context.hasEntity(_pointID);
79103 if (entity) return continueTo(rightClickPoint); // point still exists
79105 context.history().on('change.intro', function (changed) {
79106 if (changed.deleted().length) {
79111 function continueTo(nextStep) {
79112 context.map().on('move.intro', null);
79113 context.history().on('change.intro', null);
79114 context.on('exit.intro', null);
79120 context.history().on('change.intro', function () {
79123 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
79125 function continueTo(nextStep) {
79126 context.history().on('change.intro', null);
79132 dispatch$1.call('done');
79133 reveal('.ideditor', helpHtml('intro.points.play', {
79134 next: _t('intro.areas.title')
79136 tooltipBox: '.intro-nav-wrap .chapter-area',
79137 buttonText: _t.html('intro.ok'),
79138 buttonCallback: function buttonCallback() {
79139 reveal('.ideditor');
79144 chapter.enter = function () {
79148 chapter.exit = function () {
79149 timeouts.forEach(window.clearTimeout);
79150 context.on('enter.intro exit.intro', null);
79151 context.map().on('move.intro drawn.intro', null);
79152 context.history().on('change.intro', null);
79153 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79154 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79157 chapter.restart = function () {
79162 return utilRebind(chapter, dispatch$1, 'on');
79165 function uiIntroArea(context, reveal) {
79166 var dispatch$1 = dispatch('done');
79167 var playground = [-85.63552, 41.94159];
79168 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
79169 var nameField = _mainPresetIndex.field('name');
79170 var descriptionField = _mainPresetIndex.field('description');
79176 title: 'intro.areas.title'
79179 function timeout(f, t) {
79180 timeouts.push(window.setTimeout(f, t));
79183 function eventCancel(d3_event) {
79184 d3_event.stopPropagation();
79185 d3_event.preventDefault();
79188 function revealPlayground(center, text, options) {
79189 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
79190 var box = pad(center, padding, context);
79191 reveal(box, text, options);
79194 function addArea() {
79195 context.enter(modeBrowse(context));
79196 context.history().reset('initial');
79198 var msec = transitionTime(playground, context.map().center());
79201 reveal(null, null, {
79206 context.map().centerZoomEase(playground, 19, msec);
79207 timeout(function () {
79208 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
79209 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
79210 context.on('enter.intro', function (mode) {
79211 if (mode.id !== 'add-area') return;
79212 continueTo(startPlayground);
79216 function continueTo(nextStep) {
79217 context.on('enter.intro', null);
79222 function startPlayground() {
79223 if (context.mode().id !== 'add-area') {
79224 return chapter.restart();
79228 context.map().zoomEase(19.5, 500);
79229 timeout(function () {
79230 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
79231 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
79232 revealPlayground(playground, startDrawString, {
79235 timeout(function () {
79236 context.map().on('move.intro drawn.intro', function () {
79237 revealPlayground(playground, startDrawString, {
79241 context.on('enter.intro', function (mode) {
79242 if (mode.id !== 'draw-area') return chapter.restart();
79243 continueTo(continuePlayground);
79245 }, 250); // after reveal
79246 }, 550); // after easing
79248 function continueTo(nextStep) {
79249 context.map().on('move.intro drawn.intro', null);
79250 context.on('enter.intro', null);
79255 function continuePlayground() {
79256 if (context.mode().id !== 'draw-area') {
79257 return chapter.restart();
79261 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
79264 timeout(function () {
79265 context.map().on('move.intro drawn.intro', function () {
79266 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
79270 }, 250); // after reveal
79272 context.on('enter.intro', function (mode) {
79273 if (mode.id === 'draw-area') {
79274 var entity = context.hasEntity(context.selectedIDs()[0]);
79276 if (entity && entity.nodes.length >= 6) {
79277 return continueTo(finishPlayground);
79281 } else if (mode.id === 'select') {
79282 _areaID = context.selectedIDs()[0];
79283 return continueTo(searchPresets);
79285 return chapter.restart();
79289 function continueTo(nextStep) {
79290 context.map().on('move.intro drawn.intro', null);
79291 context.on('enter.intro', null);
79296 function finishPlayground() {
79297 if (context.mode().id !== 'draw-area') {
79298 return chapter.restart();
79302 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
79303 revealPlayground(playground, finishString, {
79306 timeout(function () {
79307 context.map().on('move.intro drawn.intro', function () {
79308 revealPlayground(playground, finishString, {
79312 }, 250); // after reveal
79314 context.on('enter.intro', function (mode) {
79315 if (mode.id === 'draw-area') {
79317 } else if (mode.id === 'select') {
79318 _areaID = context.selectedIDs()[0];
79319 return continueTo(searchPresets);
79321 return chapter.restart();
79325 function continueTo(nextStep) {
79326 context.map().on('move.intro drawn.intro', null);
79327 context.on('enter.intro', null);
79332 function searchPresets() {
79333 if (!_areaID || !context.hasEntity(_areaID)) {
79337 var ids = context.selectedIDs();
79339 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
79340 context.enter(modeSelect(context, [_areaID]));
79341 } // disallow scrolling
79344 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79345 timeout(function () {
79346 // reset pane, in case user somehow happened to change it..
79347 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79348 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79349 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
79350 preset: playgroundPreset.name()
79352 }, 400); // after preset list pane visible..
79354 context.on('enter.intro', function (mode) {
79355 if (!_areaID || !context.hasEntity(_areaID)) {
79356 return continueTo(addArea);
79359 var ids = context.selectedIDs();
79361 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
79362 // keep the user's area selected..
79363 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
79365 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
79367 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79368 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79369 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
79370 preset: playgroundPreset.name()
79372 context.history().on('change.intro', null);
79376 function checkPresetSearch() {
79377 var first = context.container().select('.preset-list-item:first-child');
79379 if (first.classed('preset-leisure-playground')) {
79380 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
79381 preset: playgroundPreset.name()
79385 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79386 context.history().on('change.intro', function () {
79387 continueTo(clickAddField);
79392 function continueTo(nextStep) {
79393 context.container().select('.inspector-wrap').on('wheel.intro', null);
79394 context.on('enter.intro', null);
79395 context.history().on('change.intro', null);
79396 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79401 function clickAddField() {
79402 if (!_areaID || !context.hasEntity(_areaID)) {
79406 var ids = context.selectedIDs();
79408 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
79409 return searchPresets();
79412 if (!context.container().select('.form-field-description').empty()) {
79413 return continueTo(describePlayground);
79414 } // disallow scrolling
79417 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79418 timeout(function () {
79419 // reset pane, in case user somehow happened to change it..
79420 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
79421 // If they did this already, just continue to next step.
79423 var entity = context.entity(_areaID);
79425 if (entity.tags.description) {
79426 return continueTo(play);
79427 } // scroll "Add field" into view
79430 var box = context.container().select('.more-fields').node().getBoundingClientRect();
79432 if (box.top > 300) {
79433 var pane = context.container().select('.entity-editor-pane .inspector-body');
79434 var start = pane.node().scrollTop;
79435 var end = start + (box.top - 300);
79436 pane.transition().duration(250).tween('scroll.inspector', function () {
79438 var i = d3_interpolateNumber(start, end);
79439 return function (t) {
79440 node.scrollTop = i(t);
79445 timeout(function () {
79446 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
79447 name: nameField.label(),
79448 description: descriptionField.label()
79452 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
79453 // Watch for the combobox to appear...
79455 watcher = window.setInterval(function () {
79456 if (!context.container().select('div.combobox').empty()) {
79457 window.clearInterval(watcher);
79458 continueTo(chooseDescriptionField);
79462 }, 300); // after "Add Field" visible
79463 }, 400); // after editor pane visible
79465 context.on('exit.intro', function () {
79466 return continueTo(searchPresets);
79469 function continueTo(nextStep) {
79470 context.container().select('.inspector-wrap').on('wheel.intro', null);
79471 context.container().select('.more-fields .combobox-input').on('click.intro', null);
79472 context.on('exit.intro', null);
79477 function chooseDescriptionField() {
79478 if (!_areaID || !context.hasEntity(_areaID)) {
79482 var ids = context.selectedIDs();
79484 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
79485 return searchPresets();
79488 if (!context.container().select('.form-field-description').empty()) {
79489 return continueTo(describePlayground);
79490 } // Make sure combobox is ready..
79493 if (context.container().select('div.combobox').empty()) {
79494 return continueTo(clickAddField);
79495 } // Watch for the combobox to go away..
79499 watcher = window.setInterval(function () {
79500 if (context.container().select('div.combobox').empty()) {
79501 window.clearInterval(watcher);
79502 timeout(function () {
79503 if (context.container().select('.form-field-description').empty()) {
79504 continueTo(retryChooseDescription);
79506 continueTo(describePlayground);
79508 }, 300); // after description field added.
79511 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
79512 field: descriptionField.label()
79516 context.on('exit.intro', function () {
79517 return continueTo(searchPresets);
79520 function continueTo(nextStep) {
79521 if (watcher) window.clearInterval(watcher);
79522 context.on('exit.intro', null);
79527 function describePlayground() {
79528 if (!_areaID || !context.hasEntity(_areaID)) {
79532 var ids = context.selectedIDs();
79534 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
79535 return searchPresets();
79536 } // reset pane, in case user happened to change it..
79539 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79541 if (context.container().select('.form-field-description').empty()) {
79542 return continueTo(retryChooseDescription);
79545 context.on('exit.intro', function () {
79548 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
79549 button: icon('#iD-icon-close', 'inline')
79554 function continueTo(nextStep) {
79555 context.on('exit.intro', null);
79560 function retryChooseDescription() {
79561 if (!_areaID || !context.hasEntity(_areaID)) {
79565 var ids = context.selectedIDs();
79567 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
79568 return searchPresets();
79569 } // reset pane, in case user happened to change it..
79572 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79573 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
79574 field: descriptionField.label()
79576 buttonText: _t.html('intro.ok'),
79577 buttonCallback: function buttonCallback() {
79578 continueTo(clickAddField);
79581 context.on('exit.intro', function () {
79582 return continueTo(searchPresets);
79585 function continueTo(nextStep) {
79586 context.on('exit.intro', null);
79592 dispatch$1.call('done');
79593 reveal('.ideditor', helpHtml('intro.areas.play', {
79594 next: _t('intro.lines.title')
79596 tooltipBox: '.intro-nav-wrap .chapter-line',
79597 buttonText: _t.html('intro.ok'),
79598 buttonCallback: function buttonCallback() {
79599 reveal('.ideditor');
79604 chapter.enter = function () {
79608 chapter.exit = function () {
79609 timeouts.forEach(window.clearTimeout);
79610 context.on('enter.intro exit.intro', null);
79611 context.map().on('move.intro drawn.intro', null);
79612 context.history().on('change.intro', null);
79613 context.container().select('.inspector-wrap').on('wheel.intro', null);
79614 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79615 context.container().select('.more-fields .combobox-input').on('click.intro', null);
79618 chapter.restart = function () {
79623 return utilRebind(chapter, dispatch$1, 'on');
79626 function uiIntroLine(context, reveal) {
79627 var dispatch$1 = dispatch('done');
79629 var _tulipRoadID = null;
79630 var flowerRoadID = 'w646';
79631 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
79632 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
79633 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
79634 var roadCategory = _mainPresetIndex.item('category-road_minor');
79635 var residentialPreset = _mainPresetIndex.item('highway/residential');
79636 var woodRoadID = 'w525';
79637 var woodRoadEndID = 'n2862';
79638 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
79639 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
79640 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
79641 var washingtonStreetID = 'w522';
79642 var twelfthAvenueID = 'w1';
79643 var eleventhAvenueEndID = 'n3550';
79644 var twelfthAvenueEndID = 'n5';
79645 var _washingtonSegmentID = null;
79646 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
79647 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
79648 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
79649 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
79651 title: 'intro.lines.title'
79654 function timeout(f, t) {
79655 timeouts.push(window.setTimeout(f, t));
79658 function eventCancel(d3_event) {
79659 d3_event.stopPropagation();
79660 d3_event.preventDefault();
79663 function addLine() {
79664 context.enter(modeBrowse(context));
79665 context.history().reset('initial');
79666 var msec = transitionTime(tulipRoadStart, context.map().center());
79669 reveal(null, null, {
79674 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
79675 timeout(function () {
79676 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
79677 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
79678 context.on('enter.intro', function (mode) {
79679 if (mode.id !== 'add-line') return;
79680 continueTo(startLine);
79684 function continueTo(nextStep) {
79685 context.on('enter.intro', null);
79690 function startLine() {
79691 if (context.mode().id !== 'add-line') return chapter.restart();
79692 _tulipRoadID = null;
79693 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
79694 var box = pad(tulipRoadStart, padding, context);
79695 box.height = box.height + 100;
79696 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
79697 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
79698 reveal(box, startLineString);
79699 context.map().on('move.intro drawn.intro', function () {
79700 padding = 70 * Math.pow(2, context.map().zoom() - 18);
79701 box = pad(tulipRoadStart, padding, context);
79702 box.height = box.height + 100;
79703 reveal(box, startLineString, {
79707 context.on('enter.intro', function (mode) {
79708 if (mode.id !== 'draw-line') return chapter.restart();
79709 continueTo(drawLine);
79712 function continueTo(nextStep) {
79713 context.map().on('move.intro drawn.intro', null);
79714 context.on('enter.intro', null);
79719 function drawLine() {
79720 if (context.mode().id !== 'draw-line') return chapter.restart();
79721 _tulipRoadID = context.mode().selectedIDs()[0];
79722 context.map().centerEase(tulipRoadMidpoint, 500);
79723 timeout(function () {
79724 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79725 var box = pad(tulipRoadMidpoint, padding, context);
79726 box.height = box.height * 2;
79727 reveal(box, helpHtml('intro.lines.intersect', {
79728 name: _t('intro.graph.name.flower-street')
79730 context.map().on('move.intro drawn.intro', function () {
79731 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79732 box = pad(tulipRoadMidpoint, padding, context);
79733 box.height = box.height * 2;
79734 reveal(box, helpHtml('intro.lines.intersect', {
79735 name: _t('intro.graph.name.flower-street')
79740 }, 550); // after easing..
79742 context.history().on('change.intro', function () {
79743 if (isLineConnected()) {
79744 continueTo(continueLine);
79747 context.on('enter.intro', function (mode) {
79748 if (mode.id === 'draw-line') {
79750 } else if (mode.id === 'select') {
79751 continueTo(retryIntersect);
79754 return chapter.restart();
79758 function continueTo(nextStep) {
79759 context.map().on('move.intro drawn.intro', null);
79760 context.history().on('change.intro', null);
79761 context.on('enter.intro', null);
79766 function isLineConnected() {
79767 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79769 if (!entity) return false;
79770 var drawNodes = context.graph().childNodes(entity);
79771 return drawNodes.some(function (node) {
79772 return context.graph().parentWays(node).some(function (parent) {
79773 return parent.id === flowerRoadID;
79778 function retryIntersect() {
79779 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79780 var box = pad(tulipRoadIntersection, 80, context);
79781 reveal(box, helpHtml('intro.lines.retry_intersect', {
79782 name: _t('intro.graph.name.flower-street')
79784 timeout(chapter.restart, 3000);
79787 function continueLine() {
79788 if (context.mode().id !== 'draw-line') return chapter.restart();
79790 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79792 if (!entity) return chapter.restart();
79793 context.map().centerEase(tulipRoadIntersection, 500);
79794 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
79795 reveal('.surface', continueLineText);
79796 context.on('enter.intro', function (mode) {
79797 if (mode.id === 'draw-line') return;else if (mode.id === 'select') return continueTo(chooseCategoryRoad);else return chapter.restart();
79800 function continueTo(nextStep) {
79801 context.on('enter.intro', null);
79806 function chooseCategoryRoad() {
79807 if (context.mode().id !== 'select') return chapter.restart();
79808 context.on('exit.intro', function () {
79809 return chapter.restart();
79811 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79812 if (button.empty()) return chapter.restart(); // disallow scrolling
79814 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79815 timeout(function () {
79816 // reset pane, in case user somehow happened to change it..
79817 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79818 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
79819 category: roadCategory.name()
79821 button.on('click.intro', function () {
79822 continueTo(choosePresetResidential);
79824 }, 400); // after editor pane visible
79826 function continueTo(nextStep) {
79827 context.container().select('.inspector-wrap').on('wheel.intro', null);
79828 context.container().select('.preset-list-button').on('click.intro', null);
79829 context.on('exit.intro', null);
79834 function choosePresetResidential() {
79835 if (context.mode().id !== 'select') return chapter.restart();
79836 context.on('exit.intro', function () {
79837 return chapter.restart();
79839 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79840 if (subgrid.empty()) return chapter.restart();
79841 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
79842 continueTo(retryPresetResidential);
79844 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
79845 continueTo(nameRoad);
79847 timeout(function () {
79848 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
79849 preset: residentialPreset.name()
79851 tooltipBox: '.preset-highway-residential .preset-list-button',
79856 function continueTo(nextStep) {
79857 context.container().select('.preset-list-button').on('click.intro', null);
79858 context.on('exit.intro', null);
79861 } // selected wrong road type
79864 function retryPresetResidential() {
79865 if (context.mode().id !== 'select') return chapter.restart();
79866 context.on('exit.intro', function () {
79867 return chapter.restart();
79868 }); // disallow scrolling
79870 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79871 timeout(function () {
79872 var button = context.container().select('.entity-editor-pane .preset-list-button');
79873 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
79874 preset: residentialPreset.name()
79876 button.on('click.intro', function () {
79877 continueTo(chooseCategoryRoad);
79881 function continueTo(nextStep) {
79882 context.container().select('.inspector-wrap').on('wheel.intro', null);
79883 context.container().select('.preset-list-button').on('click.intro', null);
79884 context.on('exit.intro', null);
79889 function nameRoad() {
79890 context.on('exit.intro', function () {
79891 continueTo(didNameRoad);
79893 timeout(function () {
79894 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
79895 button: icon('#iD-icon-close', 'inline')
79897 tooltipClass: 'intro-lines-name_road'
79901 function continueTo(nextStep) {
79902 context.on('exit.intro', null);
79907 function didNameRoad() {
79908 context.history().checkpoint('doneAddLine');
79909 timeout(function () {
79910 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
79911 buttonText: _t.html('intro.ok'),
79912 buttonCallback: function buttonCallback() {
79913 continueTo(updateLine);
79918 function continueTo(nextStep) {
79923 function updateLine() {
79924 context.history().reset('doneAddLine');
79926 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79927 return chapter.restart();
79930 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79933 reveal(null, null, {
79938 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79939 timeout(function () {
79940 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79941 var box = pad(woodRoadDragMidpoint, padding, context);
79943 var advance = function advance() {
79944 continueTo(addNode);
79947 reveal(box, helpHtml('intro.lines.update_line'), {
79948 buttonText: _t.html('intro.ok'),
79949 buttonCallback: advance
79951 context.map().on('move.intro drawn.intro', function () {
79952 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79953 var box = pad(woodRoadDragMidpoint, padding, context);
79954 reveal(box, helpHtml('intro.lines.update_line'), {
79956 buttonText: _t.html('intro.ok'),
79957 buttonCallback: advance
79962 function continueTo(nextStep) {
79963 context.map().on('move.intro drawn.intro', null);
79968 function addNode() {
79969 context.history().reset('doneAddLine');
79971 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79972 return chapter.restart();
79975 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79976 var box = pad(woodRoadAddNode, padding, context);
79977 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79978 reveal(box, addNodeString);
79979 context.map().on('move.intro drawn.intro', function () {
79980 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79981 var box = pad(woodRoadAddNode, padding, context);
79982 reveal(box, addNodeString, {
79986 context.history().on('change.intro', function (changed) {
79987 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79988 return continueTo(updateLine);
79991 if (changed.created().length === 1) {
79992 timeout(function () {
79993 continueTo(startDragEndpoint);
79997 context.on('enter.intro', function (mode) {
79998 if (mode.id !== 'select') {
79999 continueTo(updateLine);
80003 function continueTo(nextStep) {
80004 context.map().on('move.intro drawn.intro', null);
80005 context.history().on('change.intro', null);
80006 context.on('enter.intro', null);
80011 function startDragEndpoint() {
80012 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80013 return continueTo(updateLine);
80016 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80017 var box = pad(woodRoadDragEndpoint, padding, context);
80018 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
80019 reveal(box, startDragString);
80020 context.map().on('move.intro drawn.intro', function () {
80021 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80022 return continueTo(updateLine);
80025 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80026 var box = pad(woodRoadDragEndpoint, padding, context);
80027 reveal(box, startDragString, {
80030 var entity = context.entity(woodRoadEndID);
80032 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
80033 continueTo(finishDragEndpoint);
80037 function continueTo(nextStep) {
80038 context.map().on('move.intro drawn.intro', null);
80043 function finishDragEndpoint() {
80044 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80045 return continueTo(updateLine);
80048 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80049 var box = pad(woodRoadDragEndpoint, padding, context);
80050 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80051 reveal(box, finishDragString);
80052 context.map().on('move.intro drawn.intro', function () {
80053 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80054 return continueTo(updateLine);
80057 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80058 var box = pad(woodRoadDragEndpoint, padding, context);
80059 reveal(box, finishDragString, {
80062 var entity = context.entity(woodRoadEndID);
80064 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
80065 continueTo(startDragEndpoint);
80068 context.on('enter.intro', function () {
80069 continueTo(startDragMidpoint);
80072 function continueTo(nextStep) {
80073 context.map().on('move.intro drawn.intro', null);
80074 context.on('enter.intro', null);
80079 function startDragMidpoint() {
80080 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80081 return continueTo(updateLine);
80084 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
80085 context.enter(modeSelect(context, [woodRoadID]));
80088 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
80089 var box = pad(woodRoadDragMidpoint, padding, context);
80090 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
80091 context.map().on('move.intro drawn.intro', function () {
80092 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80093 return continueTo(updateLine);
80096 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
80097 var box = pad(woodRoadDragMidpoint, padding, context);
80098 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
80102 context.history().on('change.intro', function (changed) {
80103 if (changed.created().length === 1) {
80104 continueTo(continueDragMidpoint);
80107 context.on('enter.intro', function (mode) {
80108 if (mode.id !== 'select') {
80109 // keep Wood Road selected so midpoint triangles are drawn..
80110 context.enter(modeSelect(context, [woodRoadID]));
80114 function continueTo(nextStep) {
80115 context.map().on('move.intro drawn.intro', null);
80116 context.history().on('change.intro', null);
80117 context.on('enter.intro', null);
80122 function continueDragMidpoint() {
80123 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80124 return continueTo(updateLine);
80127 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80128 var box = pad(woodRoadDragEndpoint, padding, context);
80131 var advance = function advance() {
80132 context.history().checkpoint('doneUpdateLine');
80133 continueTo(deleteLines);
80136 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
80137 buttonText: _t.html('intro.ok'),
80138 buttonCallback: advance
80140 context.map().on('move.intro drawn.intro', function () {
80141 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80142 return continueTo(updateLine);
80145 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80146 var box = pad(woodRoadDragEndpoint, padding, context);
80148 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
80150 buttonText: _t.html('intro.ok'),
80151 buttonCallback: advance
80155 function continueTo(nextStep) {
80156 context.map().on('move.intro drawn.intro', null);
80161 function deleteLines() {
80162 context.history().reset('doneUpdateLine');
80163 context.enter(modeBrowse(context));
80165 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80166 return chapter.restart();
80169 var msec = transitionTime(deleteLinesLoc, context.map().center());
80172 reveal(null, null, {
80177 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
80178 timeout(function () {
80179 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80180 var box = pad(deleteLinesLoc, padding, context);
80184 var advance = function advance() {
80185 continueTo(rightClickIntersection);
80188 reveal(box, helpHtml('intro.lines.delete_lines', {
80189 street: _t('intro.graph.name.12th-avenue')
80191 buttonText: _t.html('intro.ok'),
80192 buttonCallback: advance
80194 context.map().on('move.intro drawn.intro', function () {
80195 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80196 var box = pad(deleteLinesLoc, padding, context);
80199 reveal(box, helpHtml('intro.lines.delete_lines', {
80200 street: _t('intro.graph.name.12th-avenue')
80203 buttonText: _t.html('intro.ok'),
80204 buttonCallback: advance
80207 context.history().on('change.intro', function () {
80208 timeout(function () {
80209 continueTo(deleteLines);
80210 }, 500); // after any transition (e.g. if user deleted intersection)
80214 function continueTo(nextStep) {
80215 context.map().on('move.intro drawn.intro', null);
80216 context.history().on('change.intro', null);
80221 function rightClickIntersection() {
80222 context.history().reset('doneUpdateLine');
80223 context.enter(modeBrowse(context));
80224 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
80225 var rightClickString = helpHtml('intro.lines.split_street', {
80226 street1: _t('intro.graph.name.11th-avenue'),
80227 street2: _t('intro.graph.name.washington-street')
80228 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
80229 timeout(function () {
80230 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
80231 var box = pad(eleventhAvenueEnd, padding, context);
80232 reveal(box, rightClickString);
80233 context.map().on('move.intro drawn.intro', function () {
80234 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
80235 var box = pad(eleventhAvenueEnd, padding, context);
80236 reveal(box, rightClickString, {
80240 context.on('enter.intro', function (mode) {
80241 if (mode.id !== 'select') return;
80242 var ids = context.selectedIDs();
80243 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
80244 timeout(function () {
80245 var node = selectMenuItem(context, 'split').node();
80247 continueTo(splitIntersection);
80248 }, 50); // after menu visible
80250 context.history().on('change.intro', function () {
80251 timeout(function () {
80252 continueTo(deleteLines);
80253 }, 300); // after any transition (e.g. if user deleted intersection)
80257 function continueTo(nextStep) {
80258 context.map().on('move.intro drawn.intro', null);
80259 context.on('enter.intro', null);
80260 context.history().on('change.intro', null);
80265 function splitIntersection() {
80266 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80267 return continueTo(deleteLines);
80270 var node = selectMenuItem(context, 'split').node();
80273 return continueTo(rightClickIntersection);
80276 var wasChanged = false;
80277 _washingtonSegmentID = null;
80278 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
80279 street: _t('intro.graph.name.washington-street')
80283 context.map().on('move.intro drawn.intro', function () {
80284 var node = selectMenuItem(context, 'split').node();
80286 if (!wasChanged && !node) {
80287 return continueTo(rightClickIntersection);
80290 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
80291 street: _t('intro.graph.name.washington-street')
80297 context.history().on('change.intro', function (changed) {
80299 timeout(function () {
80300 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
80303 _washingtonSegmentID = changed.created()[0].id;
80304 continueTo(didSplit);
80306 _washingtonSegmentID = null;
80307 continueTo(retrySplit);
80309 }, 300); // after any transition (e.g. if user deleted intersection)
80312 function continueTo(nextStep) {
80313 context.map().on('move.intro drawn.intro', null);
80314 context.history().on('change.intro', null);
80319 function retrySplit() {
80320 context.enter(modeBrowse(context));
80321 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
80323 var advance = function advance() {
80324 continueTo(rightClickIntersection);
80327 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
80328 var box = pad(eleventhAvenueEnd, padding, context);
80329 reveal(box, helpHtml('intro.lines.retry_split'), {
80330 buttonText: _t.html('intro.ok'),
80331 buttonCallback: advance
80333 context.map().on('move.intro drawn.intro', function () {
80334 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
80335 var box = pad(eleventhAvenueEnd, padding, context);
80336 reveal(box, helpHtml('intro.lines.retry_split'), {
80338 buttonText: _t.html('intro.ok'),
80339 buttonCallback: advance
80343 function continueTo(nextStep) {
80344 context.map().on('move.intro drawn.intro', null);
80349 function didSplit() {
80350 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80351 return continueTo(rightClickIntersection);
80354 var ids = context.selectedIDs();
80355 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
80356 var street = _t('intro.graph.name.washington-street');
80357 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80358 var box = pad(twelfthAvenue, padding, context);
80359 box.width = box.width / 2;
80360 reveal(box, helpHtml(string, {
80366 timeout(function () {
80367 context.map().centerZoomEase(twelfthAvenue, 18, 500);
80368 context.map().on('move.intro drawn.intro', function () {
80369 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80370 var box = pad(twelfthAvenue, padding, context);
80371 box.width = box.width / 2;
80372 reveal(box, helpHtml(string, {
80379 }, 600); // after initial reveal and curtain cut
80381 context.on('enter.intro', function () {
80382 var ids = context.selectedIDs();
80384 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
80385 continueTo(multiSelect);
80388 context.history().on('change.intro', function () {
80389 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80390 return continueTo(rightClickIntersection);
80394 function continueTo(nextStep) {
80395 context.map().on('move.intro drawn.intro', null);
80396 context.on('enter.intro', null);
80397 context.history().on('change.intro', null);
80402 function multiSelect() {
80403 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80404 return continueTo(rightClickIntersection);
80407 var ids = context.selectedIDs();
80408 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
80409 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
80411 if (hasWashington && hasTwelfth) {
80412 return continueTo(multiRightClick);
80413 } else if (!hasWashington && !hasTwelfth) {
80414 return continueTo(didSplit);
80417 context.map().centerZoomEase(twelfthAvenue, 18, 500);
80418 timeout(function () {
80419 var selected, other, padding, box;
80421 if (hasWashington) {
80422 selected = _t('intro.graph.name.washington-street');
80423 other = _t('intro.graph.name.12th-avenue');
80424 padding = 60 * Math.pow(2, context.map().zoom() - 18);
80425 box = pad(twelfthAvenueEnd, padding, context);
80428 selected = _t('intro.graph.name.12th-avenue');
80429 other = _t('intro.graph.name.washington-street');
80430 padding = 200 * Math.pow(2, context.map().zoom() - 18);
80431 box = pad(twelfthAvenue, padding, context);
80435 reveal(box, helpHtml('intro.lines.multi_select', {
80436 selected: selected,
80438 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
80439 selected: selected,
80442 context.map().on('move.intro drawn.intro', function () {
80443 if (hasWashington) {
80444 selected = _t('intro.graph.name.washington-street');
80445 other = _t('intro.graph.name.12th-avenue');
80446 padding = 60 * Math.pow(2, context.map().zoom() - 18);
80447 box = pad(twelfthAvenueEnd, padding, context);
80450 selected = _t('intro.graph.name.12th-avenue');
80451 other = _t('intro.graph.name.washington-street');
80452 padding = 200 * Math.pow(2, context.map().zoom() - 18);
80453 box = pad(twelfthAvenue, padding, context);
80457 reveal(box, helpHtml('intro.lines.multi_select', {
80458 selected: selected,
80460 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
80461 selected: selected,
80467 context.on('enter.intro', function () {
80468 continueTo(multiSelect);
80470 context.history().on('change.intro', function () {
80471 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80472 return continueTo(rightClickIntersection);
80477 function continueTo(nextStep) {
80478 context.map().on('move.intro drawn.intro', null);
80479 context.on('enter.intro', null);
80480 context.history().on('change.intro', null);
80485 function multiRightClick() {
80486 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80487 return continueTo(rightClickIntersection);
80490 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80491 var box = pad(twelfthAvenue, padding, context);
80492 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
80493 reveal(box, rightClickString);
80494 context.map().on('move.intro drawn.intro', function () {
80495 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80496 var box = pad(twelfthAvenue, padding, context);
80497 reveal(box, rightClickString, {
80501 context.ui().editMenu().on('toggled.intro', function (open) {
80503 timeout(function () {
80504 var ids = context.selectedIDs();
80506 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
80507 var node = selectMenuItem(context, 'delete').node();
80509 continueTo(multiDelete);
80510 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
80511 return continueTo(multiSelect);
80513 return continueTo(didSplit);
80515 }, 300); // after edit menu visible
80517 context.history().on('change.intro', function () {
80518 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80519 return continueTo(rightClickIntersection);
80523 function continueTo(nextStep) {
80524 context.map().on('move.intro drawn.intro', null);
80525 context.ui().editMenu().on('toggled.intro', null);
80526 context.history().on('change.intro', null);
80531 function multiDelete() {
80532 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80533 return continueTo(rightClickIntersection);
80536 var node = selectMenuItem(context, 'delete').node();
80537 if (!node) return continueTo(multiRightClick);
80538 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
80541 context.map().on('move.intro drawn.intro', function () {
80542 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
80547 context.on('exit.intro', function () {
80548 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
80549 return continueTo(multiSelect); // left select mode but roads still exist
80552 context.history().on('change.intro', function () {
80553 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
80554 continueTo(retryDelete); // changed something but roads still exist
80560 function continueTo(nextStep) {
80561 context.map().on('move.intro drawn.intro', null);
80562 context.on('exit.intro', null);
80563 context.history().on('change.intro', null);
80568 function retryDelete() {
80569 context.enter(modeBrowse(context));
80570 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80571 var box = pad(twelfthAvenue, padding, context);
80572 reveal(box, helpHtml('intro.lines.retry_delete'), {
80573 buttonText: _t.html('intro.ok'),
80574 buttonCallback: function buttonCallback() {
80575 continueTo(multiSelect);
80579 function continueTo(nextStep) {
80585 dispatch$1.call('done');
80586 reveal('.ideditor', helpHtml('intro.lines.play', {
80587 next: _t('intro.buildings.title')
80589 tooltipBox: '.intro-nav-wrap .chapter-building',
80590 buttonText: _t.html('intro.ok'),
80591 buttonCallback: function buttonCallback() {
80592 reveal('.ideditor');
80597 chapter.enter = function () {
80601 chapter.exit = function () {
80602 timeouts.forEach(window.clearTimeout);
80603 select(window).on('pointerdown.intro mousedown.intro', null, true);
80604 context.on('enter.intro exit.intro', null);
80605 context.map().on('move.intro drawn.intro', null);
80606 context.history().on('change.intro', null);
80607 context.container().select('.inspector-wrap').on('wheel.intro', null);
80608 context.container().select('.preset-list-button').on('click.intro', null);
80611 chapter.restart = function () {
80616 return utilRebind(chapter, dispatch$1, 'on');
80619 function uiIntroBuilding(context, reveal) {
80620 var dispatch$1 = dispatch('done');
80621 var house = [-85.62815, 41.95638];
80622 var tank = [-85.62732, 41.95347];
80623 var buildingCatetory = _mainPresetIndex.item('category-building');
80624 var housePreset = _mainPresetIndex.item('building/house');
80625 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
80627 var _houseID = null;
80628 var _tankID = null;
80630 title: 'intro.buildings.title'
80633 function timeout(f, t) {
80634 timeouts.push(window.setTimeout(f, t));
80637 function eventCancel(d3_event) {
80638 d3_event.stopPropagation();
80639 d3_event.preventDefault();
80642 function revealHouse(center, text, options) {
80643 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
80644 var box = pad(center, padding, context);
80645 reveal(box, text, options);
80648 function revealTank(center, text, options) {
80649 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
80650 var box = pad(center, padding, context);
80651 reveal(box, text, options);
80654 function addHouse() {
80655 context.enter(modeBrowse(context));
80656 context.history().reset('initial');
80658 var msec = transitionTime(house, context.map().center());
80661 reveal(null, null, {
80666 context.map().centerZoomEase(house, 19, msec);
80667 timeout(function () {
80668 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
80669 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
80670 context.on('enter.intro', function (mode) {
80671 if (mode.id !== 'add-area') return;
80672 continueTo(startHouse);
80676 function continueTo(nextStep) {
80677 context.on('enter.intro', null);
80682 function startHouse() {
80683 if (context.mode().id !== 'add-area') {
80684 return continueTo(addHouse);
80688 context.map().zoomEase(20, 500);
80689 timeout(function () {
80690 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80691 revealHouse(house, startString);
80692 context.map().on('move.intro drawn.intro', function () {
80693 revealHouse(house, startString, {
80697 context.on('enter.intro', function (mode) {
80698 if (mode.id !== 'draw-area') return chapter.restart();
80699 continueTo(continueHouse);
80701 }, 550); // after easing
80703 function continueTo(nextStep) {
80704 context.map().on('move.intro drawn.intro', null);
80705 context.on('enter.intro', null);
80710 function continueHouse() {
80711 if (context.mode().id !== 'draw-area') {
80712 return continueTo(addHouse);
80716 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
80717 revealHouse(house, continueString);
80718 context.map().on('move.intro drawn.intro', function () {
80719 revealHouse(house, continueString, {
80723 context.on('enter.intro', function (mode) {
80724 if (mode.id === 'draw-area') {
80726 } else if (mode.id === 'select') {
80727 var graph = context.graph();
80728 var way = context.entity(context.selectedIDs()[0]);
80729 var nodes = graph.childNodes(way);
80730 var points = utilArrayUniq(nodes).map(function (n) {
80731 return context.projection(n.loc);
80734 if (isMostlySquare(points)) {
80736 return continueTo(chooseCategoryBuilding);
80738 return continueTo(retryHouse);
80741 return chapter.restart();
80745 function continueTo(nextStep) {
80746 context.map().on('move.intro drawn.intro', null);
80747 context.on('enter.intro', null);
80752 function retryHouse() {
80753 var onClick = function onClick() {
80754 continueTo(addHouse);
80757 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
80758 buttonText: _t.html('intro.ok'),
80759 buttonCallback: onClick
80761 context.map().on('move.intro drawn.intro', function () {
80762 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
80764 buttonText: _t.html('intro.ok'),
80765 buttonCallback: onClick
80769 function continueTo(nextStep) {
80770 context.map().on('move.intro drawn.intro', null);
80775 function chooseCategoryBuilding() {
80776 if (!_houseID || !context.hasEntity(_houseID)) {
80780 var ids = context.selectedIDs();
80782 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80783 context.enter(modeSelect(context, [_houseID]));
80784 } // disallow scrolling
80787 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80788 timeout(function () {
80789 // reset pane, in case user somehow happened to change it..
80790 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80791 var button = context.container().select('.preset-category-building .preset-list-button');
80792 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
80793 category: buildingCatetory.name()
80795 button.on('click.intro', function () {
80796 button.on('click.intro', null);
80797 continueTo(choosePresetHouse);
80799 }, 400); // after preset list pane visible..
80801 context.on('enter.intro', function (mode) {
80802 if (!_houseID || !context.hasEntity(_houseID)) {
80803 return continueTo(addHouse);
80806 var ids = context.selectedIDs();
80808 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80809 return continueTo(chooseCategoryBuilding);
80813 function continueTo(nextStep) {
80814 context.container().select('.inspector-wrap').on('wheel.intro', null);
80815 context.container().select('.preset-list-button').on('click.intro', null);
80816 context.on('enter.intro', null);
80821 function choosePresetHouse() {
80822 if (!_houseID || !context.hasEntity(_houseID)) {
80826 var ids = context.selectedIDs();
80828 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80829 context.enter(modeSelect(context, [_houseID]));
80830 } // disallow scrolling
80833 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80834 timeout(function () {
80835 // reset pane, in case user somehow happened to change it..
80836 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80837 var button = context.container().select('.preset-building-house .preset-list-button');
80838 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
80839 preset: housePreset.name()
80843 button.on('click.intro', function () {
80844 button.on('click.intro', null);
80845 continueTo(closeEditorHouse);
80847 }, 400); // after preset list pane visible..
80849 context.on('enter.intro', function (mode) {
80850 if (!_houseID || !context.hasEntity(_houseID)) {
80851 return continueTo(addHouse);
80854 var ids = context.selectedIDs();
80856 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80857 return continueTo(chooseCategoryBuilding);
80861 function continueTo(nextStep) {
80862 context.container().select('.inspector-wrap').on('wheel.intro', null);
80863 context.container().select('.preset-list-button').on('click.intro', null);
80864 context.on('enter.intro', null);
80869 function closeEditorHouse() {
80870 if (!_houseID || !context.hasEntity(_houseID)) {
80874 var ids = context.selectedIDs();
80876 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80877 context.enter(modeSelect(context, [_houseID]));
80880 context.history().checkpoint('hasHouse');
80881 context.on('exit.intro', function () {
80882 continueTo(rightClickHouse);
80884 timeout(function () {
80885 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
80886 button: icon('#iD-icon-close', 'inline')
80890 function continueTo(nextStep) {
80891 context.on('exit.intro', null);
80896 function rightClickHouse() {
80897 if (!_houseID) return chapter.restart();
80898 context.enter(modeBrowse(context));
80899 context.history().reset('hasHouse');
80900 var zoom = context.map().zoom();
80906 context.map().centerZoomEase(house, zoom, 500);
80907 context.on('enter.intro', function (mode) {
80908 if (mode.id !== 'select') return;
80909 var ids = context.selectedIDs();
80910 if (ids.length !== 1 || ids[0] !== _houseID) return;
80911 timeout(function () {
80912 var node = selectMenuItem(context, 'orthogonalize').node();
80914 continueTo(clickSquare);
80915 }, 50); // after menu visible
80917 context.map().on('move.intro drawn.intro', function () {
80918 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80919 revealHouse(house, rightclickString, {
80923 context.history().on('change.intro', function () {
80924 continueTo(rightClickHouse);
80927 function continueTo(nextStep) {
80928 context.on('enter.intro', null);
80929 context.map().on('move.intro drawn.intro', null);
80930 context.history().on('change.intro', null);
80935 function clickSquare() {
80936 if (!_houseID) return chapter.restart();
80937 var entity = context.hasEntity(_houseID);
80938 if (!entity) return continueTo(rightClickHouse);
80939 var node = selectMenuItem(context, 'orthogonalize').node();
80942 return continueTo(rightClickHouse);
80945 var wasChanged = false;
80946 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
80949 context.on('enter.intro', function (mode) {
80950 if (mode.id === 'browse') {
80951 continueTo(rightClickHouse);
80952 } else if (mode.id === 'move' || mode.id === 'rotate') {
80953 continueTo(retryClickSquare);
80956 context.map().on('move.intro', function () {
80957 var node = selectMenuItem(context, 'orthogonalize').node();
80959 if (!wasChanged && !node) {
80960 return continueTo(rightClickHouse);
80963 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
80968 context.history().on('change.intro', function () {
80970 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
80972 timeout(function () {
80973 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
80976 continueTo(doneSquare);
80978 continueTo(retryClickSquare);
80980 }, 500); // after transitioned actions
80983 function continueTo(nextStep) {
80984 context.on('enter.intro', null);
80985 context.map().on('move.intro', null);
80986 context.history().on('change.intro', null);
80991 function retryClickSquare() {
80992 context.enter(modeBrowse(context));
80993 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
80994 buttonText: _t.html('intro.ok'),
80995 buttonCallback: function buttonCallback() {
80996 continueTo(rightClickHouse);
81000 function continueTo(nextStep) {
81005 function doneSquare() {
81006 context.history().checkpoint('doneSquare');
81007 revealHouse(house, helpHtml('intro.buildings.done_square'), {
81008 buttonText: _t.html('intro.ok'),
81009 buttonCallback: function buttonCallback() {
81010 continueTo(addTank);
81014 function continueTo(nextStep) {
81019 function addTank() {
81020 context.enter(modeBrowse(context));
81021 context.history().reset('doneSquare');
81023 var msec = transitionTime(tank, context.map().center());
81026 reveal(null, null, {
81031 context.map().centerZoomEase(tank, 19.5, msec);
81032 timeout(function () {
81033 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
81034 context.on('enter.intro', function (mode) {
81035 if (mode.id !== 'add-area') return;
81036 continueTo(startTank);
81040 function continueTo(nextStep) {
81041 context.on('enter.intro', null);
81046 function startTank() {
81047 if (context.mode().id !== 'add-area') {
81048 return continueTo(addTank);
81052 timeout(function () {
81053 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81054 revealTank(tank, startString);
81055 context.map().on('move.intro drawn.intro', function () {
81056 revealTank(tank, startString, {
81060 context.on('enter.intro', function (mode) {
81061 if (mode.id !== 'draw-area') return chapter.restart();
81062 continueTo(continueTank);
81064 }, 550); // after easing
81066 function continueTo(nextStep) {
81067 context.map().on('move.intro drawn.intro', null);
81068 context.on('enter.intro', null);
81073 function continueTank() {
81074 if (context.mode().id !== 'draw-area') {
81075 return continueTo(addTank);
81079 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
81080 revealTank(tank, continueString);
81081 context.map().on('move.intro drawn.intro', function () {
81082 revealTank(tank, continueString, {
81086 context.on('enter.intro', function (mode) {
81087 if (mode.id === 'draw-area') {
81089 } else if (mode.id === 'select') {
81090 _tankID = context.selectedIDs()[0];
81091 return continueTo(searchPresetTank);
81093 return continueTo(addTank);
81097 function continueTo(nextStep) {
81098 context.map().on('move.intro drawn.intro', null);
81099 context.on('enter.intro', null);
81104 function searchPresetTank() {
81105 if (!_tankID || !context.hasEntity(_tankID)) {
81109 var ids = context.selectedIDs();
81111 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
81112 context.enter(modeSelect(context, [_tankID]));
81113 } // disallow scrolling
81116 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81117 timeout(function () {
81118 // reset pane, in case user somehow happened to change it..
81119 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81120 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
81121 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
81122 preset: tankPreset.name()
81124 }, 400); // after preset list pane visible..
81126 context.on('enter.intro', function (mode) {
81127 if (!_tankID || !context.hasEntity(_tankID)) {
81128 return continueTo(addTank);
81131 var ids = context.selectedIDs();
81133 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
81134 // keep the user's area selected..
81135 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
81137 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
81139 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81140 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
81141 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
81142 preset: tankPreset.name()
81144 context.history().on('change.intro', null);
81148 function checkPresetSearch() {
81149 var first = context.container().select('.preset-list-item:first-child');
81151 if (first.classed('preset-man_made-storage_tank')) {
81152 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
81153 preset: tankPreset.name()
81157 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
81158 context.history().on('change.intro', function () {
81159 continueTo(closeEditorTank);
81164 function continueTo(nextStep) {
81165 context.container().select('.inspector-wrap').on('wheel.intro', null);
81166 context.on('enter.intro', null);
81167 context.history().on('change.intro', null);
81168 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
81173 function closeEditorTank() {
81174 if (!_tankID || !context.hasEntity(_tankID)) {
81178 var ids = context.selectedIDs();
81180 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
81181 context.enter(modeSelect(context, [_tankID]));
81184 context.history().checkpoint('hasTank');
81185 context.on('exit.intro', function () {
81186 continueTo(rightClickTank);
81188 timeout(function () {
81189 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
81190 button: icon('#iD-icon-close', 'inline')
81194 function continueTo(nextStep) {
81195 context.on('exit.intro', null);
81200 function rightClickTank() {
81201 if (!_tankID) return continueTo(addTank);
81202 context.enter(modeBrowse(context));
81203 context.history().reset('hasTank');
81204 context.map().centerEase(tank, 500);
81205 timeout(function () {
81206 context.on('enter.intro', function (mode) {
81207 if (mode.id !== 'select') return;
81208 var ids = context.selectedIDs();
81209 if (ids.length !== 1 || ids[0] !== _tankID) return;
81210 timeout(function () {
81211 var node = selectMenuItem(context, 'circularize').node();
81213 continueTo(clickCircle);
81214 }, 50); // after menu visible
81216 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
81217 revealTank(tank, rightclickString);
81218 context.map().on('move.intro drawn.intro', function () {
81219 revealTank(tank, rightclickString, {
81223 context.history().on('change.intro', function () {
81224 continueTo(rightClickTank);
81228 function continueTo(nextStep) {
81229 context.on('enter.intro', null);
81230 context.map().on('move.intro drawn.intro', null);
81231 context.history().on('change.intro', null);
81236 function clickCircle() {
81237 if (!_tankID) return chapter.restart();
81238 var entity = context.hasEntity(_tankID);
81239 if (!entity) return continueTo(rightClickTank);
81240 var node = selectMenuItem(context, 'circularize').node();
81243 return continueTo(rightClickTank);
81246 var wasChanged = false;
81247 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
81250 context.on('enter.intro', function (mode) {
81251 if (mode.id === 'browse') {
81252 continueTo(rightClickTank);
81253 } else if (mode.id === 'move' || mode.id === 'rotate') {
81254 continueTo(retryClickCircle);
81257 context.map().on('move.intro', function () {
81258 var node = selectMenuItem(context, 'circularize').node();
81260 if (!wasChanged && !node) {
81261 return continueTo(rightClickTank);
81264 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
81269 context.history().on('change.intro', function () {
81271 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
81273 timeout(function () {
81274 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
81279 continueTo(retryClickCircle);
81281 }, 500); // after transitioned actions
81284 function continueTo(nextStep) {
81285 context.on('enter.intro', null);
81286 context.map().on('move.intro', null);
81287 context.history().on('change.intro', null);
81292 function retryClickCircle() {
81293 context.enter(modeBrowse(context));
81294 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
81295 buttonText: _t.html('intro.ok'),
81296 buttonCallback: function buttonCallback() {
81297 continueTo(rightClickTank);
81301 function continueTo(nextStep) {
81307 dispatch$1.call('done');
81308 reveal('.ideditor', helpHtml('intro.buildings.play', {
81309 next: _t('intro.startediting.title')
81311 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
81312 buttonText: _t.html('intro.ok'),
81313 buttonCallback: function buttonCallback() {
81314 reveal('.ideditor');
81319 chapter.enter = function () {
81323 chapter.exit = function () {
81324 timeouts.forEach(window.clearTimeout);
81325 context.on('enter.intro exit.intro', null);
81326 context.map().on('move.intro drawn.intro', null);
81327 context.history().on('change.intro', null);
81328 context.container().select('.inspector-wrap').on('wheel.intro', null);
81329 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
81330 context.container().select('.more-fields .combobox-input').on('click.intro', null);
81333 chapter.restart = function () {
81338 return utilRebind(chapter, dispatch$1, 'on');
81341 function uiIntroStartEditing(context, reveal) {
81342 var dispatch$1 = dispatch('done', 'startEditing');
81343 var modalSelection = select(null);
81345 title: 'intro.startediting.title'
81348 function showHelp() {
81349 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
81350 buttonText: _t.html('intro.ok'),
81351 buttonCallback: function buttonCallback() {
81357 function shortcuts() {
81358 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
81359 buttonText: _t.html('intro.ok'),
81360 buttonCallback: function buttonCallback() {
81366 function showSave() {
81367 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
81369 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
81370 buttonText: _t.html('intro.ok'),
81371 buttonCallback: function buttonCallback() {
81377 function showStart() {
81378 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
81380 modalSelection = uiModal(context.container());
81381 modalSelection.select('.modal').attr('class', 'modal-splash modal');
81382 modalSelection.selectAll('.close').remove();
81383 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
81384 modalSelection.remove();
81386 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
81387 startbutton.append('h2').html(_t.html('intro.startediting.start'));
81388 dispatch$1.call('startEditing');
81391 chapter.enter = function () {
81395 chapter.exit = function () {
81396 modalSelection.remove();
81397 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
81400 return utilRebind(chapter, dispatch$1, 'on');
81404 welcome: uiIntroWelcome,
81405 navigation: uiIntroNavigation,
81406 point: uiIntroPoint,
81409 building: uiIntroBuilding,
81410 startEditing: uiIntroStartEditing
81412 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
81413 function uiIntro(context) {
81414 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
81415 var _introGraph = {};
81419 function intro(selection) {
81420 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
81421 // create entities for intro graph and localize names
81422 for (var id in dataIntroGraph) {
81423 if (!_introGraph[id]) {
81424 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
81428 selection.call(startIntro);
81429 })["catch"](function () {
81434 function startIntro(selection) {
81435 context.enter(modeBrowse(context)); // Save current map state
81437 var osm = context.connection();
81438 var history = context.history().toJSON();
81439 var hash = window.location.hash;
81440 var center = context.map().center();
81441 var zoom = context.map().zoom();
81442 var background = context.background().baseLayerSource();
81443 var overlays = context.background().overlayLayerSources();
81444 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
81445 var caches = osm && osm.caches();
81446 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
81447 // (this needs to be before `context.inIntro(true)`)
81449 context.ui().sidebar.expand();
81450 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
81452 context.inIntro(true); // Load semi-real data used in intro
81455 osm.toggle(false).reset();
81458 context.history().reset();
81459 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
81460 context.history().checkpoint('initial'); // Setup imagery
81462 var imagery = context.background().findSource(INTRO_IMAGERY);
81465 context.background().baseLayerSource(imagery);
81467 context.background().bing();
81470 overlays.forEach(function (d) {
81471 return context.background().toggleOverlayLayer(d);
81472 }); // Setup data layers (only OSM)
81474 var layers = context.layers();
81475 layers.all().forEach(function (item) {
81476 // if the layer has the function `enabled`
81477 if (typeof item.layer.enabled === 'function') {
81478 item.layer.enabled(item.id === 'osm');
81481 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
81482 var curtain = uiCurtain(context.container().node());
81483 selection.call(curtain); // Store that the user started the walkthrough..
81485 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
81487 var storedProgress = corePreferences('walkthrough_progress') || '';
81488 var progress = storedProgress.split(';').filter(Boolean);
81489 var chapters = chapterFlow.map(function (chapter, i) {
81490 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
81491 buttons.filter(function (d) {
81492 return d.title === s.title;
81493 }).classed('finished', true);
81495 if (i < chapterFlow.length - 1) {
81496 var next = chapterFlow[i + 1];
81497 context.container().select("button.chapter-".concat(next)).classed('next', true);
81498 } // Store walkthrough progress..
81501 progress.push(chapter);
81502 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
81506 chapters[chapters.length - 1].on('startEditing', function () {
81507 // Store walkthrough progress..
81508 progress.push('startEditing');
81509 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
81511 var incomplete = utilArrayDifference(chapterFlow, progress);
81513 if (!incomplete.length) {
81514 corePreferences('walkthrough_completed', 'yes');
81519 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
81520 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
81523 osm.toggle(true).reset().caches(caches);
81526 context.history().reset().merge(Object.values(baseEntities));
81527 context.background().baseLayerSource(background);
81528 overlays.forEach(function (d) {
81529 return context.background().toggleOverlayLayer(d);
81533 context.history().fromJSON(history, false);
81536 context.map().centerZoom(center, zoom);
81537 window.location.replace(hash);
81538 context.inIntro(false);
81540 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
81541 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
81542 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
81543 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
81544 return "chapter chapter-".concat(chapterFlow[i]);
81545 }).on('click', enterChapter);
81546 buttons.append('span').html(function (d) {
81547 return _t.html(d.title);
81549 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
81550 enterChapter(null, chapters[0]);
81552 function enterChapter(d3_event, newChapter) {
81553 if (_currChapter) {
81554 _currChapter.exit();
81557 context.enter(modeBrowse(context));
81558 _currChapter = newChapter;
81560 _currChapter.enter();
81562 buttons.classed('next', false).classed('active', function (d) {
81563 return d.title === _currChapter.title;
81571 function uiIssuesInfo(context) {
81572 var warningsItem = {
81575 iconID: 'iD-icon-alert',
81576 descriptionID: 'issues.warnings_and_errors'
81578 var resolvedItem = {
81581 iconID: 'iD-icon-apply',
81582 descriptionID: 'issues.user_resolved_issues'
81585 function update(selection) {
81586 var shownItems = [];
81587 var liveIssues = context.validator().getIssues({
81588 what: corePreferences('validate-what') || 'edited',
81589 where: corePreferences('validate-where') || 'all'
81592 if (liveIssues.length) {
81593 warningsItem.count = liveIssues.length;
81594 shownItems.push(warningsItem);
81597 if (corePreferences('validate-what') === 'all') {
81598 var resolvedIssues = context.validator().getResolvedIssues();
81600 if (resolvedIssues.length) {
81601 resolvedItem.count = resolvedIssues.length;
81602 shownItems.push(resolvedItem);
81606 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
81609 chips.exit().remove();
81610 var enter = chips.enter().append('a').attr('class', function (d) {
81611 return 'chip ' + d.id + '-count';
81612 }).attr('href', '#').each(function (d) {
81613 var chipSelection = select(this);
81614 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
81615 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
81616 d3_event.preventDefault();
81617 tooltipBehavior.hide(select(this)); // open the Issues pane
81619 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81621 chipSelection.call(svgIcon('#' + d.iconID));
81623 enter.append('span').attr('class', 'count');
81624 enter.merge(chips).selectAll('span.count').html(function (d) {
81625 return d.count.toString();
81629 return function (selection) {
81631 context.validator().on('validated.infobox', function () {
81637 function uiMapInMap(context) {
81638 function mapInMap(selection) {
81639 var backgroundLayer = rendererTileLayer(context);
81640 var overlayLayers = {};
81641 var projection = geoRawMercator();
81642 var dataLayer = svgData(projection, context).showLabels(false);
81643 var debugLayer = svgDebug(projection, context);
81644 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
81645 var wrap = select(null);
81646 var tiles = select(null);
81647 var viewport = select(null);
81648 var _isTransformed = false;
81649 var _isHidden = true;
81650 var _skipEvents = false;
81651 var _gesture = null;
81652 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
81654 var _dMini; // dimensions of minimap
81657 var _cMini; // center pixel of minimap
81660 var _tStart; // transform at start of gesture
81663 var _tCurr; // transform at most recent event
81668 function zoomStarted() {
81669 if (_skipEvents) return;
81670 _tStart = _tCurr = projection.transform();
81674 function zoomed(d3_event) {
81675 if (_skipEvents) return;
81676 var x = d3_event.transform.x;
81677 var y = d3_event.transform.y;
81678 var k = d3_event.transform.k;
81679 var isZooming = k !== _tStart.k;
81680 var isPanning = x !== _tStart.x || y !== _tStart.y;
81682 if (!isZooming && !isPanning) {
81683 return; // no change
81684 } // lock in either zooming or panning, don't allow both in minimap.
81688 _gesture = isZooming ? 'zoom' : 'pan';
81691 var tMini = projection.transform();
81694 if (_gesture === 'zoom') {
81695 scale = k / tMini.k;
81696 tX = (_cMini[0] / scale - _cMini[0]) * scale;
81697 tY = (_cMini[1] / scale - _cMini[1]) * scale;
81705 utilSetTransform(tiles, tX, tY, scale);
81706 utilSetTransform(viewport, 0, 0, scale);
81707 _isTransformed = true;
81708 _tCurr = identity$2.translate(x, y).scale(k);
81709 var zMain = geoScaleToZoom(context.projection.scale());
81710 var zMini = geoScaleToZoom(k);
81711 _zDiff = zMain - zMini;
81715 function zoomEnded() {
81716 if (_skipEvents) return;
81717 if (_gesture !== 'pan') return;
81718 updateProjection();
81720 context.map().center(projection.invert(_cMini)); // recenter main map..
81723 function updateProjection() {
81724 var loc = context.map().center();
81725 var tMain = context.projection.transform();
81726 var zMain = geoScaleToZoom(tMain.k);
81727 var zMini = Math.max(zMain - _zDiff, 0.5);
81728 var kMini = geoZoomToScale(zMini);
81729 projection.translate([tMain.x, tMain.y]).scale(kMini);
81730 var point = projection(loc);
81731 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81732 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81733 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81734 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
81735 _tCurr = projection.transform();
81737 if (_isTransformed) {
81738 utilSetTransform(tiles, 0, 0);
81739 utilSetTransform(viewport, 0, 0);
81740 _isTransformed = false;
81743 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81744 _skipEvents = true;
81745 wrap.call(zoom.transform, _tCurr);
81746 _skipEvents = false;
81749 function redraw() {
81750 clearTimeout(_timeoutID);
81751 if (_isHidden) return;
81752 updateProjection();
81753 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
81755 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
81756 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
81758 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
81759 var background = tiles.selectAll('.map-in-map-background').data([0]);
81760 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
81762 var overlaySources = context.background().overlayLayerSources();
81763 var activeOverlayLayers = [];
81765 for (var i = 0; i < overlaySources.length; i++) {
81766 if (overlaySources[i].validZoom(zMini)) {
81767 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
81768 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
81772 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
81773 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
81774 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
81775 return d.source().name();
81777 overlays.exit().remove();
81778 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
81779 select(this).call(layer);
81781 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
81782 dataLayers.exit().remove();
81783 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
81785 if (_gesture !== 'pan') {
81786 var getPath = d3_geoPath(projection);
81789 coordinates: [context.map().extent().polygon()]
81791 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
81792 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
81793 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
81794 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
81795 return getPath.area(d) < 30;
81800 function queueRedraw() {
81801 clearTimeout(_timeoutID);
81802 _timeoutID = setTimeout(function () {
81807 function toggle(d3_event) {
81808 if (d3_event) d3_event.preventDefault();
81809 _isHidden = !_isHidden;
81810 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
81813 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
81814 selection.selectAll('.map-in-map').style('display', 'none');
81817 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
81823 uiMapInMap.toggle = toggle;
81824 wrap = selection.selectAll('.map-in-map').data([0]);
81825 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..
81827 _dMini = [200, 150]; //utilGetDimensions(wrap);
81829 _cMini = geoVecScale(_dMini, 0.5);
81830 context.map().on('drawn.map-in-map', function (drawn) {
81831 if (drawn.full === true) {
81836 context.keybinding().on(_t('background.minimap.key'), toggle);
81842 function uiNotice(context) {
81843 return function (selection) {
81844 var div = selection.append('div').attr('class', 'notice');
81845 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
81846 context.map().zoomEase(context.minEditableZoom());
81847 }).on('wheel', function (d3_event) {
81848 // let wheel events pass through #4482
81849 var e2 = new WheelEvent(d3_event.type, d3_event);
81850 context.surface().node().dispatchEvent(e2);
81852 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
81854 function disableTooHigh() {
81855 var canEdit = context.map().zoom() >= context.minEditableZoom();
81856 div.style('display', canEdit ? 'none' : 'block');
81859 context.map().on('move.notice', debounce(disableTooHigh, 500));
81864 function uiPhotoviewer(context) {
81865 var dispatch$1 = dispatch('resize');
81867 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81869 function photoviewer(selection) {
81870 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
81871 if (services.streetside) {
81872 services.streetside.hideViewer(context);
81875 if (services.mapillary) {
81876 services.mapillary.hideViewer(context);
81879 if (services.openstreetcam) {
81880 services.openstreetcam.hideViewer(context);
81882 }).append('div').call(svgIcon('#iD-icon-close'));
81884 function preventDefault(d3_event) {
81885 d3_event.preventDefault();
81888 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
81892 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
81895 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
81899 function buildResizeListener(target, eventName, dispatch, options) {
81900 var resizeOnX = !!options.resizeOnX;
81901 var resizeOnY = !!options.resizeOnY;
81902 var minHeight = options.minHeight || 240;
81903 var minWidth = options.minWidth || 320;
81910 function startResize(d3_event) {
81911 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
81912 d3_event.preventDefault();
81913 d3_event.stopPropagation();
81914 var mapSize = context.map().dimensions();
81917 var maxWidth = mapSize[0];
81918 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
81919 target.style('width', newWidth + 'px');
81923 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
81925 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
81926 target.style('height', newHeight + 'px');
81929 dispatch.call(eventName, target, utilGetDimensions(target, true));
81932 function clamp(num, min, max) {
81933 return Math.max(min, Math.min(num, max));
81936 function stopResize(d3_event) {
81937 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
81938 d3_event.preventDefault();
81939 d3_event.stopPropagation(); // remove all the listeners we added
81941 select(window).on('.' + eventName, null);
81944 return function initResize(d3_event) {
81945 d3_event.preventDefault();
81946 d3_event.stopPropagation();
81947 pointerId = d3_event.pointerId || 'mouse';
81948 startX = d3_event.clientX;
81949 startY = d3_event.clientY;
81950 var targetRect = target.node().getBoundingClientRect();
81951 startWidth = targetRect.width;
81952 startHeight = targetRect.height;
81953 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81955 if (_pointerPrefix === 'pointer') {
81956 select(window).on('pointercancel.' + eventName, stopResize, false);
81962 photoviewer.onMapResize = function () {
81963 var photoviewer = context.container().select('.photoviewer');
81964 var content = context.container().select('.main-content');
81965 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
81966 // (-90 preserves space at top and bottom of map used by menus)
81968 var photoDimensions = utilGetDimensions(photoviewer, true);
81970 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
81971 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
81972 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
81973 dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81977 return utilRebind(photoviewer, dispatch$1, 'on');
81980 function uiRestore(context) {
81981 return function (selection) {
81982 if (!context.history().hasRestorableChanges()) return;
81983 var modalSelection = uiModal(selection, true);
81984 modalSelection.select('.modal').attr('class', 'modal fillL');
81985 var introModal = modalSelection.select('.content');
81986 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
81987 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
81988 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
81989 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
81990 context.history().restore();
81991 modalSelection.remove();
81993 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
81994 restore.append('div').html(_t.html('restore.restore'));
81995 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
81996 context.history().clearSaved();
81997 modalSelection.remove();
81999 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
82000 reset.append('div').html(_t.html('restore.reset'));
82001 restore.node().focus();
82005 function uiScale(context) {
82006 var projection = context.projection,
82007 isImperial = !_mainLocalizer.usesMetric(),
82011 function scaleDefs(loc1, loc2) {
82012 var lat = (loc2[1] + loc1[1]) / 2,
82013 conversion = isImperial ? 3.28084 : 1,
82014 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
82026 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
82028 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
82029 } // determine a user-friendly endpoint for the scale
82032 for (i = 0; i < buckets.length; i++) {
82036 scale.dist = Math.floor(dist / val) * val;
82039 scale.dist = +dist.toFixed(2);
82043 dLon = geoMetersToLon(scale.dist / conversion, lat);
82044 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
82045 scale.text = displayLength(scale.dist / conversion, isImperial);
82049 function update(selection) {
82050 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
82051 var dims = context.map().dimensions(),
82052 loc1 = projection.invert([0, dims[1]]),
82053 loc2 = projection.invert([maxLength, dims[1]]),
82054 scale = scaleDefs(loc1, loc2);
82055 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
82056 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
82059 return function (selection) {
82060 function switchUnits() {
82061 isImperial = !isImperial;
82062 selection.call(update);
82065 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
82066 scalegroup.append('path').attr('class', 'scale-path');
82067 selection.append('div').attr('class', 'scale-text');
82068 selection.call(update);
82069 context.map().on('move.scale', function () {
82075 function uiShortcuts(context) {
82076 var detected = utilDetect();
82077 var _activeTab = 0;
82079 var _modalSelection;
82081 var _selection = select(null);
82083 var _dataShortcuts;
82085 function shortcutsModal(_modalSelection) {
82086 _modalSelection.select('.modal').classed('modal-shortcuts', true);
82088 var content = _modalSelection.select('.content');
82090 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
82091 _mainFileFetcher.get('shortcuts').then(function (data) {
82092 _dataShortcuts = data;
82093 content.call(render);
82094 })["catch"](function () {
82099 function render(selection) {
82100 if (!_dataShortcuts) return;
82101 var wrapper = selection.selectAll('.wrapper').data([0]);
82102 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
82103 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
82104 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
82105 wrapper = wrapper.merge(wrapperEnter);
82106 var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
82107 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
82108 d3_event.preventDefault();
82110 var i = _dataShortcuts.indexOf(d);
82115 tabsEnter.append('span').html(function (d) {
82116 return _t.html(d.text);
82119 wrapper.selectAll('.tab').classed('active', function (d, i) {
82120 return i === _activeTab;
82122 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
82123 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
82124 return 'shortcut-tab shortcut-tab-' + d.tab;
82126 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
82128 }).enter().append('table').attr('class', 'shortcut-column');
82129 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
82131 }).enter().append('tr').attr('class', 'shortcut-row');
82132 var sectionRows = rowsEnter.filter(function (d) {
82133 return !d.shortcuts;
82135 sectionRows.append('td');
82136 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
82137 return _t.html(d.text);
82139 var shortcutRows = rowsEnter.filter(function (d) {
82140 return d.shortcuts;
82142 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
82143 var modifierKeys = shortcutKeys.filter(function (d) {
82144 return d.modifiers;
82146 modifierKeys.selectAll('kbd.modifier').data(function (d) {
82147 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
82149 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
82152 return d.modifiers;
82154 }).enter().each(function () {
82155 var selection = select(this);
82156 selection.append('kbd').attr('class', 'modifier').html(function (d) {
82157 return uiCmd.display(d);
82159 selection.append('span').html('+');
82161 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
82162 var arr = d.shortcuts;
82164 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
82166 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
82168 } // replace translations
82171 arr = arr.map(function (s) {
82172 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
82174 return utilArrayUniq(arr).map(function (s) {
82177 separator: d.separator,
82181 }).enter().each(function (d, i, nodes) {
82182 var selection = select(this);
82183 var click = d.shortcut.toLowerCase().match(/(.*).click/);
82185 if (click && click[1]) {
82186 // replace "left_click", "right_click" with mouse icon
82187 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82188 } else if (d.shortcut.toLowerCase() === 'long-press') {
82189 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82190 } else if (d.shortcut.toLowerCase() === 'tap') {
82191 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82193 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
82198 if (i < nodes.length - 1) {
82199 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
82200 } else if (i === nodes.length - 1 && d.suffix) {
82201 selection.append('span').html(d.suffix);
82204 shortcutKeys.filter(function (d) {
82206 }).each(function () {
82207 var selection = select(this);
82208 selection.append('span').html('+');
82209 selection.append('span').attr('class', 'gesture').html(function (d) {
82210 return _t.html(d.gesture);
82213 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
82214 return d.text ? _t.html(d.text) : "\xA0";
82217 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
82218 return i === _activeTab ? 'flex' : 'none';
82222 return function (selection, show) {
82223 _selection = selection;
82226 _modalSelection = uiModal(selection);
82228 _modalSelection.call(shortcutsModal);
82230 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
82231 if (context.container().selectAll('.modal-shortcuts').size()) {
82233 if (_modalSelection) {
82234 _modalSelection.close();
82236 _modalSelection = null;
82239 _modalSelection = uiModal(_selection);
82241 _modalSelection.call(shortcutsModal);
82250 function search(input, dims) {
82251 if (!dims) dims = 'NSEW';
82252 if (typeof input !== 'string') return null;
82253 input = input.toUpperCase();
82254 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
82255 var m = input.match(regex);
82256 if (!m) return null; // no match
82258 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
82262 if (m[1] && m[5]) {
82263 // if matched both..
82264 dim = m[1]; // keep leading
82266 matched = matched.slice(0, -1); // remove trailing dimension from match
82268 dim = m[1] || m[5];
82269 } // if unrecognized dimension
82272 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
82274 var deg = m[2] ? parseFloat(m[2]) : 0;
82275 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82276 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82277 var sign = deg < 0 ? -1 : 1;
82278 if (dim === 'S' || dim === 'W') sign *= -1;
82280 val: (Math.abs(deg) + min + sec) * sign,
82283 remain: input.slice(matched.length)
82287 function pair(input, dims) {
82288 input = input.trim();
82289 var one = search(input, dims);
82290 if (!one) return null;
82291 input = one.remain.trim();
82292 var two = search(input, dims);
82293 if (!two || two.remain) return null;
82296 return swapdim(one.val, two.val, one.dim);
82298 return [one.val, two.val];
82302 function swapdim(a, b, dim) {
82303 if (dim === 'N' || dim === 'S') return [a, b];
82304 if (dim === 'W' || dim === 'E') return [b, a];
82307 function uiFeatureList(context) {
82308 var _geocodeResults;
82310 function featureList(selection) {
82311 var header = selection.append('div').attr('class', 'header fillL');
82312 header.append('h3').html(_t.html('inspector.feature_list'));
82313 var searchWrap = selection.append('div').attr('class', 'search-header');
82314 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
82315 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
82316 var listWrap = selection.append('div').attr('class', 'inspector-body');
82317 var list = listWrap.append('div').attr('class', 'feature-list');
82318 context.on('exit.feature-list', clearSearch);
82319 context.map().on('drawn.feature-list', mapDrawn);
82320 context.keybinding().on(uiCmd('⌘F'), focusSearch);
82322 function focusSearch(d3_event) {
82323 var mode = context.mode() && context.mode().id;
82324 if (mode !== 'browse') return;
82325 d3_event.preventDefault();
82326 search.node().focus();
82329 function keydown(d3_event) {
82330 if (d3_event.keyCode === 27) {
82332 search.node().blur();
82336 function keypress(d3_event) {
82337 var q = search.property('value'),
82338 items = list.selectAll('.feature-list-item');
82340 if (d3_event.keyCode === 13 && // ↩ Return
82341 q.length && items.size()) {
82342 click(items.datum());
82346 function inputevent() {
82347 _geocodeResults = undefined;
82351 function clearSearch() {
82352 search.property('value', '');
82356 function mapDrawn(e) {
82362 function features() {
82364 var graph = context.graph();
82365 var visibleCenter = context.map().extent().center();
82366 var q = search.property('value').toLowerCase();
82367 if (!q) return result;
82368 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82370 if (locationMatch) {
82371 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82375 type: _t('inspector.location'),
82376 name: dmsCoordinatePair([loc[1], loc[0]]),
82379 } // A location search takes priority over an ID search
82382 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82385 var elemType = idMatch[1].charAt(0);
82386 var elemId = idMatch[2];
82388 id: elemType + elemId,
82389 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82390 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82395 var allEntities = graph.entities;
82396 var localResults = [];
82398 for (var id in allEntities) {
82399 var entity = allEntities[id];
82400 if (!entity) continue;
82401 var name = utilDisplayName(entity) || '';
82402 if (name.toLowerCase().indexOf(q) < 0) continue;
82403 var matched = _mainPresetIndex.match(entity, graph);
82404 var type = matched && matched.name() || utilDisplayType(entity.id);
82405 var extent = entity.extent(graph);
82406 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82407 localResults.push({
82410 geometry: entity.geometry(graph),
82415 if (localResults.length > 100) break;
82418 localResults = localResults.sort(function byDistance(a, b) {
82419 return a.distance - b.distance;
82421 result = result.concat(localResults);
82423 (_geocodeResults || []).forEach(function (d) {
82424 if (d.osm_type && d.osm_id) {
82425 // some results may be missing these - #1890
82426 // Make a temporary osmEntity so we can preset match
82427 // and better localize the search result - #4725
82428 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82430 tags[d["class"]] = d.type;
82437 if (d.osm_type === 'way') {
82438 // for ways, add some fake closed nodes
82439 attrs.nodes = ['a', 'a']; // so that geometry area is possible
82442 var tempEntity = osmEntity(attrs);
82443 var tempGraph = coreGraph([tempEntity]);
82444 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82445 var type = matched && matched.name() || utilDisplayType(id);
82448 geometry: tempEntity.geometry(tempGraph),
82450 name: d.display_name,
82451 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82456 if (q.match(/^[0-9]+$/)) {
82457 // if query is just a number, possibly an OSM ID without a prefix
82461 type: _t('inspector.node'),
82467 type: _t('inspector.way'),
82472 geometry: 'relation',
82473 type: _t('inspector.relation'),
82481 function drawList() {
82482 var value = search.property('value');
82483 var results = features();
82484 list.classed('filtered', value.length);
82485 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'));
82486 resultsIndicator.append('span').attr('class', 'entity-name');
82487 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
82489 if (services.geocoder) {
82490 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'));
82493 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
82494 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
82495 list.selectAll('.feature-list-item').data([-1]).remove();
82496 var items = list.selectAll('.feature-list-item').data(results, function (d) {
82499 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
82500 var label = enter.append('div').attr('class', 'label');
82501 label.each(function (d) {
82502 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82504 label.append('span').attr('class', 'entity-type').html(function (d) {
82507 label.append('span').attr('class', 'entity-name').html(function (d) {
82510 enter.style('opacity', 0).transition().style('opacity', 1);
82512 items.exit().remove();
82515 function mouseover(d3_event, d) {
82516 if (d.id === -1) return;
82517 utilHighlightEntities([d.id], true, context);
82520 function mouseout(d3_event, d) {
82521 if (d.id === -1) return;
82522 utilHighlightEntities([d.id], false, context);
82525 function click(d3_event, d) {
82526 d3_event.preventDefault();
82529 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82530 } else if (d.entity) {
82531 utilHighlightEntities([d.id], false, context);
82532 context.enter(modeSelect(context, [d.entity.id]));
82533 context.map().zoomToEase(d.entity);
82535 // download, zoom to, and select the entity with the given ID
82536 context.zoomToEntity(d.id);
82540 function geocoderSearch() {
82541 services.geocoder.search(search.property('value'), function (err, resp) {
82542 _geocodeResults = resp || [];
82548 return featureList;
82551 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
82558 var nativeStartsWith = ''.startsWith;
82559 var min$9 = Math.min;
82561 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('startsWith');
82562 // https://github.com/zloirock/core-js/pull/702
82563 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
82564 var descriptor = getOwnPropertyDescriptor$4(String.prototype, 'startsWith');
82565 return descriptor && !descriptor.writable;
82568 // `String.prototype.startsWith` method
82569 // https://tc39.es/ecma262/#sec-string.prototype.startswith
82570 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
82571 startsWith: function startsWith(searchString /* , position = 0 */) {
82572 var that = String(requireObjectCoercible(this));
82573 notARegexp(searchString);
82574 var index = toLength(min$9(arguments.length > 1 ? arguments[1] : undefined, that.length));
82575 var search = String(searchString);
82576 return nativeStartsWith
82577 ? nativeStartsWith.call(that, search, index)
82578 : that.slice(index, index + search.length) === search;
82582 function uiSectionEntityIssues(context) {
82583 var _entityIDs = [];
82586 var _activeIssueID;
82588 var section = uiSection('entity-issues', context).shouldDisplay(function () {
82589 return _issues.length > 0;
82590 }).label(function () {
82591 return _t('inspector.title_count', {
82592 title: _t.html('issues.list_title'),
82593 count: _issues.length
82595 }).disclosureContent(renderDisclosureContent);
82596 context.validator().on('validated.entity_issues', function () {
82597 // Refresh on validated events
82599 section.reRender();
82600 }).on('focusedIssue.entity_issues', function (issue) {
82601 makeActiveIssue(issue.id);
82604 function reloadIssues() {
82605 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
82606 includeDisabledRules: true
82610 function makeActiveIssue(issueID) {
82611 _activeIssueID = issueID;
82612 section.selection().selectAll('.issue-container').classed('active', function (d) {
82613 return d.id === _activeIssueID;
82617 function renderDisclosureContent(selection) {
82618 selection.classed('grouped-items-area', true);
82619 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82620 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
82624 containers.exit().remove(); // Enter
82626 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
82627 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
82628 return 'issue severity-' + d.severity;
82629 }).on('mouseover.highlight', function (d3_event, d) {
82630 // don't hover-highlight the selected entity
82631 var ids = d.entityIds.filter(function (e) {
82632 return _entityIDs.indexOf(e) === -1;
82634 utilHighlightEntities(ids, true, context);
82635 }).on('mouseout.highlight', function (d3_event, d) {
82636 var ids = d.entityIds.filter(function (e) {
82637 return _entityIDs.indexOf(e) === -1;
82639 utilHighlightEntities(ids, false, context);
82641 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
82642 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
82643 makeActiveIssue(d.id); // expand only the clicked item
82645 var extent = d.extent(context.graph());
82648 var setZoom = Math.max(context.map().zoom(), 19);
82649 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82652 textEnter.each(function (d) {
82653 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82654 select(this).call(svgIcon(iconName, 'issue-icon'));
82656 textEnter.append('span').attr('class', 'issue-message');
82657 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
82658 infoButton.on('click', function (d3_event) {
82659 d3_event.stopPropagation();
82660 d3_event.preventDefault();
82661 this.blur(); // avoid keeping focus on the button - #4641
82663 var container = select(this.parentNode.parentNode.parentNode);
82664 var info = container.selectAll('.issue-info');
82665 var isExpanded = info.classed('expanded');
82668 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
82669 info.classed('expanded', false);
82672 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
82673 info.style('max-height', null);
82677 itemsEnter.append('ul').attr('class', 'issue-fix-list');
82678 containersEnter.append('div').attr('class', 'issue-info').style('max-height', '0').style('opacity', '0').each(function (d) {
82679 if (typeof d.reference === 'function') {
82680 select(this).call(d.reference);
82682 select(this).html(_t.html('inspector.no_documentation_key'));
82686 containers = containers.merge(containersEnter).classed('active', function (d) {
82687 return d.id === _activeIssueID;
82689 containers.selectAll('.issue-message').html(function (d) {
82690 return d.message(context);
82693 var fixLists = containers.selectAll('.issue-fix-list');
82694 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
82695 return d.fixes ? d.fixes(context) : [];
82696 }, function (fix) {
82699 fixes.exit().remove();
82700 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
82701 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
82702 // not all fixes are actionable
82703 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
82704 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82706 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
82707 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
82709 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82710 new Promise(function (resolve, reject) {
82711 d.onClick(context, resolve, reject);
82713 if (d.onClick.length <= 1) {
82714 // if the fix doesn't take any completion parameters then consider it resolved
82717 }).then(function () {
82718 // revalidate whenever the fix has finished running successfully
82719 context.validator().validate();
82721 }).on('mouseover.highlight', function (d3_event, d) {
82722 utilHighlightEntities(d.entityIds, true, context);
82723 }).on('mouseout.highlight', function (d3_event, d) {
82724 utilHighlightEntities(d.entityIds, false, context);
82726 buttons.each(function (d) {
82727 var iconName = d.icon || 'iD-icon-wrench';
82729 if (iconName.startsWith('maki')) {
82733 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
82735 buttons.append('span').attr('class', 'fix-message').html(function (d) {
82738 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
82740 }).attr('disabled', function (d) {
82741 return d.onClick ? null : 'true';
82742 }).attr('title', function (d) {
82743 if (d.disabledReason) {
82744 return d.disabledReason;
82751 section.entityIDs = function (val) {
82752 if (!arguments.length) return _entityIDs;
82754 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82756 _activeIssueID = null;
82766 function uiPresetIcon() {
82771 var _sizeClass = 'medium';
82773 function isSmall() {
82774 return _sizeClass === 'small';
82777 function presetIcon(selection) {
82778 selection.each(render);
82781 function getIcon(p, geom) {
82782 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';
82785 function renderPointBorder(container, drawPoint) {
82786 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
82787 pointBorder.exit().remove();
82788 var pointBorderEnter = pointBorder.enter();
82791 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');
82792 pointBorder = pointBorderEnter.merge(pointBorder);
82795 function renderCircleFill(container, drawVertex) {
82796 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
82797 vertexFill.exit().remove();
82798 var vertexFillEnter = vertexFill.enter();
82802 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);
82803 vertexFill = vertexFillEnter.merge(vertexFill);
82806 function renderSquareFill(container, drawArea, tagClasses) {
82807 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
82808 fill.exit().remove();
82809 var fillEnter = fill.enter();
82810 var d = isSmall() ? 40 : 60;
82814 var c1 = (w - l) / 2;
82816 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));
82817 ['fill', 'stroke'].forEach(function (klass) {
82818 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));
82821 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
82822 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
82826 var rMidpoint = 1.25;
82827 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
82828 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
82832 fill = fillEnter.merge(fill);
82833 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
82834 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
82837 function renderLine(container, drawLine, tagClasses) {
82838 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
82839 line.exit().remove();
82840 var lineEnter = line.enter();
82841 var d = isSmall() ? 40 : 60; // draw the line parametrically
82845 var y = Math.round(d * 0.72);
82846 var l = Math.round(d * 0.6);
82848 var x1 = (w - l) / 2;
82850 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
82851 ['casing', 'stroke'].forEach(function (klass) {
82852 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
82854 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
82855 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
82857 line = lineEnter.merge(line);
82858 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
82859 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
82862 function renderRoute(container, drawRoute, p) {
82863 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
82864 route.exit().remove();
82865 var routeEnter = route.enter();
82866 var d = isSmall() ? 40 : 60; // draw the route parametrically
82870 var y1 = Math.round(d * 0.80);
82871 var y2 = Math.round(d * 0.68);
82872 var l = Math.round(d * 0.6);
82874 var x1 = (w - l) / 2;
82875 var x2 = x1 + l / 3;
82876 var x3 = x2 + l / 3;
82877 var x4 = x3 + l / 3;
82878 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
82879 ['casing', 'stroke'].forEach(function (klass) {
82880 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
82881 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
82882 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
82884 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
82885 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
82887 route = routeEnter.merge(route);
82890 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
82891 var segmentPresetIDs = routeSegments[routeType];
82893 for (var i in segmentPresetIDs) {
82894 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
82895 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
82896 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
82897 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
82900 } // Route icons are drawn with a zigzag annotation underneath:
82904 // This dataset defines the styles that are used to draw the zigzag segments.
82907 var routeSegments = {
82908 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82909 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82910 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82911 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82912 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82913 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82914 hiking: ['highway/path', 'highway/path', 'highway/path'],
82915 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82916 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82917 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82918 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82919 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82920 power: ['power/line', 'power/line', 'power/line'],
82921 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82922 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82923 train: ['railway/rail', 'railway/rail', 'railway/rail'],
82924 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82925 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82928 function render() {
82929 var p = _preset.apply(this, arguments);
82931 var geom = _geometry ? _geometry.apply(this, arguments) : null;
82933 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
82937 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82938 var isFallback = isSmall() && p.isFallback && p.isFallback();
82939 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
82940 var picon = getIcon(p, geom);
82941 var isMaki = picon && /^maki-/.test(picon);
82942 var isTemaki = picon && /^temaki-/.test(picon);
82943 var isFa = picon && /^fa[srb]-/.test(picon);
82944 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
82945 var isCategory = !p.setTags;
82946 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82947 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82948 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82949 var drawArea = picon && geom === 'area' && !isFallback;
82950 var drawRoute = picon && geom === 'route';
82951 var isFramed = drawVertex || drawArea || drawLine || drawRoute;
82952 var tags = !isCategory ? p.setTags({}, geom) : {};
82954 for (var k in tags) {
82955 if (tags[k] === '*') {
82960 var tagClasses = svgTagClasses().getClassesString(tags, '');
82961 var selection = select(this);
82962 var container = selection.selectAll('.preset-icon-container').data([0]);
82963 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
82964 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
82965 renderPointBorder(container, drawPoint);
82966 renderCircleFill(container, drawVertex);
82967 renderSquareFill(container, drawArea, tagClasses);
82968 renderLine(container, drawLine, tagClasses);
82969 renderRoute(container, drawRoute, p);
82970 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
82971 icon.exit().remove();
82972 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
82973 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
82974 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
82975 icon.selectAll('use').attr('href', '#' + picon + (isMaki ? isSmall() && geom === 'point' ? '-11' : '-15' : ''));
82976 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
82977 imageIcon.exit().remove();
82978 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
82979 return container.classed('showing-img', true);
82980 }).on('error', function () {
82981 return container.classed('showing-img', false);
82982 }).merge(imageIcon);
82983 imageIcon.attr('src', imageURL);
82986 presetIcon.preset = function (val) {
82987 if (!arguments.length) return _preset;
82988 _preset = utilFunctor(val);
82992 presetIcon.geometry = function (val) {
82993 if (!arguments.length) return _geometry;
82994 _geometry = utilFunctor(val);
82998 presetIcon.sizeClass = function (val) {
82999 if (!arguments.length) return _sizeClass;
83007 function uiSectionFeatureType(context) {
83008 var dispatch$1 = dispatch('choose');
83009 var _entityIDs = [];
83014 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
83016 function renderDisclosureContent(selection) {
83017 selection.classed('preset-list-item', true);
83018 selection.classed('mixed-types', _presets.length > 1);
83019 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
83020 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
83021 presetButton.append('div').attr('class', 'preset-icon-container');
83022 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83023 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
83024 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
83025 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
83027 if (_tagReference) {
83028 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
83029 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
83032 selection.selectAll('.preset-reset').on('click', function () {
83033 dispatch$1.call('choose', this, _presets);
83034 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
83035 d3_event.preventDefault();
83036 d3_event.stopPropagation();
83038 var geometries = entityGeometries();
83039 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')));
83040 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
83041 var label = selection.select('.label-inner');
83042 var nameparts = label.selectAll('.namepart').data(names, function (d) {
83045 nameparts.exit().remove();
83046 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
83051 section.entityIDs = function (val) {
83052 if (!arguments.length) return _entityIDs;
83057 section.presets = function (val) {
83058 if (!arguments.length) return _presets; // don't reload the same preset
83060 if (!utilArrayIdentical(val, _presets)) {
83063 if (_presets.length === 1) {
83064 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
83071 function entityGeometries() {
83074 for (var i in _entityIDs) {
83075 var geometry = context.graph().geometry(_entityIDs[i]);
83076 if (!counts[geometry]) counts[geometry] = 0;
83077 counts[geometry] += 1;
83080 return Object.keys(counts).sort(function (geom1, geom2) {
83081 return counts[geom2] - counts[geom1];
83085 return utilRebind(section, dispatch$1, 'on');
83088 // It borrows some code from uiHelp
83090 function uiFieldHelp(context, fieldName) {
83091 var fieldHelp = {};
83093 var _inspector = select(null);
83095 var _wrap = select(null);
83097 var _body = select(null);
83099 var fieldHelpKeys = {
83100 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']]]
83102 var fieldHelpHeadings = {};
83103 var replacements = {
83104 distField: _t.html('restriction.controls.distance'),
83105 viaField: _t.html('restriction.controls.via'),
83106 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
83107 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
83108 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
83109 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
83110 allowTurn: icon('#iD-turn-yes', 'inline turn'),
83111 restrictTurn: icon('#iD-turn-no', 'inline turn'),
83112 onlyTurn: icon('#iD-turn-only', 'inline turn')
83113 }; // For each section, squash all the texts into a single markdown document
83115 var docs = fieldHelpKeys[fieldName].map(function (key) {
83116 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83117 var text = key[1].reduce(function (all, part) {
83118 var subkey = helpkey + '.' + part;
83119 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
83121 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
83123 return all + hhh + _t.html(subkey, replacements) + '\n\n';
83127 title: _t.html(helpkey + '.title'),
83128 html: marked_1(text.trim())
83135 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
83139 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
83140 _body.classed('hide', true);
83144 function clickHelp(index) {
83145 var d = docs[index];
83146 var tkeys = fieldHelpKeys[fieldName][index][1];
83148 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
83149 return i === index;
83152 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
83155 content.selectAll('p').attr('class', function (d, i) {
83157 }); // insert special content for certain help sections
83159 if (d.key === 'help.field.restrictions.inspecting') {
83160 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
83161 } else if (d.key === 'help.field.restrictions.modifying') {
83162 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
83166 fieldHelp.button = function (selection) {
83167 if (_body.empty()) return;
83168 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
83170 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
83171 d3_event.stopPropagation();
83172 d3_event.preventDefault();
83174 if (_body.classed('hide')) {
83182 function updatePosition() {
83183 var wrap = _wrap.node();
83185 var inspector = _inspector.node();
83187 var wRect = wrap.getBoundingClientRect();
83188 var iRect = inspector.getBoundingClientRect();
83190 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83193 fieldHelp.body = function (selection) {
83194 // This control expects the field to have a form-field-input-wrap div
83195 _wrap = selection.selectAll('.form-field-input-wrap');
83196 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
83198 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83199 if (_inspector.empty()) return;
83200 _body = _inspector.selectAll('.field-help-body').data([0]);
83202 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
83205 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
83206 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
83207 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
83208 d3_event.stopPropagation();
83209 d3_event.preventDefault();
83211 }).call(svgIcon('#iD-icon-close'));
83212 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
83213 var titles = docs.map(function (d) {
83216 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
83218 }).on('click', function (d3_event, d) {
83219 d3_event.stopPropagation();
83220 d3_event.preventDefault();
83221 clickHelp(titles.indexOf(d));
83223 enter.append('div').attr('class', 'field-help-content');
83224 _body = _body.merge(enter);
83231 function uiFieldCheck(field, context) {
83232 var dispatch$1 = dispatch('change');
83233 var options = field.strings && field.strings.options;
83239 var input = select(null);
83240 var text = select(null);
83241 var label = select(null);
83242 var reverser = select(null);
83246 var _entityIDs = [];
83251 for (var k in options) {
83252 values.push(k === 'undefined' ? undefined : k);
83253 texts.push(field.t.html('options.' + k, {
83254 'default': options[k]
83258 values = [undefined, 'yes'];
83259 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
83261 if (field.type !== 'defaultCheck') {
83263 texts.push(_t.html('inspector.check.no'));
83265 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
83268 function checkImpliedYes() {
83269 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
83270 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83272 if (field.id === 'oneway') {
83273 var entity = context.entity(_entityIDs[0]);
83275 for (var key in entity.tags) {
83276 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
83277 _impliedYes = true;
83278 texts[0] = _t.html('presets.fields.oneway_yes.options.undefined');
83285 function reverserHidden() {
83286 if (!context.container().select('div.inspector-hover').empty()) return true;
83287 return !(_value === 'yes' || _impliedYes && !_value);
83290 function reverserSetText(selection) {
83291 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83292 if (reverserHidden() || !entity) return selection;
83293 var first = entity.first();
83294 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83295 var pseudoDirection = first < last;
83296 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83297 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
83301 var check = function check(selection) {
83303 label = selection.selectAll('.form-field-input-wrap').data([0]);
83304 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
83305 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
83306 enter.append('span').html(texts[0]).attr('class', 'value');
83308 if (field.type === 'onewayCheck') {
83309 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
83312 label = label.merge(enter);
83313 input = label.selectAll('input');
83314 text = label.selectAll('span.value');
83315 input.on('click', function (d3_event) {
83316 d3_event.stopPropagation();
83319 if (Array.isArray(_tags[field.key])) {
83320 if (values.indexOf('yes') !== -1) {
83321 t[field.key] = 'yes';
83323 t[field.key] = values[0];
83326 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83327 } // Don't cycle through `alternating` or `reversible` states - #4970
83328 // (They are supported as translated strings, but should not toggle with clicks)
83331 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83332 t[field.key] = values[0];
83335 dispatch$1.call('change', this, t);
83338 if (field.type === 'onewayCheck') {
83339 reverser = label.selectAll('.reverser');
83340 reverser.call(reverserSetText).on('click', function (d3_event) {
83341 d3_event.preventDefault();
83342 d3_event.stopPropagation();
83343 context.perform(function (graph) {
83344 for (var i in _entityIDs) {
83345 graph = actionReverse(_entityIDs[i])(graph);
83349 }, _t('operations.reverse.annotation.line', {
83351 })); // must manually revalidate since no 'change' event was called
83353 context.validator().validate();
83354 select(this).call(reverserSetText);
83359 check.entityIDs = function (val) {
83360 if (!arguments.length) return _entityIDs;
83365 check.tags = function (tags) {
83368 function isChecked(val) {
83369 return val !== 'no' && val !== '' && val !== undefined && val !== null;
83372 function textFor(val) {
83373 if (val === '') val = undefined;
83374 var index = values.indexOf(val);
83375 return index !== -1 ? texts[index] : '"' + val + '"';
83379 var isMixed = Array.isArray(tags[field.key]);
83380 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83382 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83386 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
83387 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
83388 label.classed('set', !!_value);
83390 if (field.type === 'onewayCheck') {
83391 reverser.classed('hide', reverserHidden()).call(reverserSetText);
83395 check.focus = function () {
83396 input.node().focus();
83399 return utilRebind(check, dispatch$1, 'on');
83402 function uiFieldCombo(field, context) {
83403 var dispatch$1 = dispatch('change');
83405 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
83407 var _isNetwork = field.type === 'networkCombo';
83409 var _isSemi = field.type === 'semiCombo';
83411 var _optstrings = field.strings && field.strings.options;
83413 var _optarray = field.options;
83415 var _snake_case = field.snake_case || field.snake_case === undefined;
83417 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
83419 var _container = select(null);
83421 var _inputWrap = select(null);
83423 var _input = select(null);
83425 var _comboData = [];
83426 var _multiData = [];
83427 var _entityIDs = [];
83433 var _staticPlaceholder; // initialize deprecated tags array
83436 var _dataDeprecated = [];
83437 _mainFileFetcher.get('deprecated').then(function (d) {
83438 _dataDeprecated = d;
83439 })["catch"](function () {
83441 }); // ensure multiCombo field.key ends with a ':'
83443 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
83447 function snake(s) {
83448 return s.replace(/\s+/g, '_');
83451 function unsnake(s) {
83452 return s.replace(/_+/g, ' ');
83455 function clean(s) {
83456 return s.split(';').map(function (s) {
83459 } // returns the tag value for a display value
83460 // (for multiCombo, dval should be the key suffix, not the entire key)
83463 function tagValue(dval) {
83464 dval = clean(dval || '');
83467 var found = _comboData.find(function (o) {
83468 return o.key && clean(o.value) === dval;
83476 if (field.type === 'typeCombo' && !dval) {
83480 return (_snake_case ? snake(dval) : dval) || undefined;
83481 } // returns the display value for a tag value
83482 // (for multiCombo, tval should be the key suffix, not the entire key)
83485 function displayValue(tval) {
83489 var found = _comboData.find(function (o) {
83490 return o.key === tval && o.value;
83494 return found.value;
83498 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83502 return _snake_case ? unsnake(tval) : tval;
83503 } // Compute the difference between arrays of objects by `value` property
83505 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83506 // > [{value:1}, {value:3}]
83510 function objectDifference(a, b) {
83511 return a.filter(function (d1) {
83512 return !b.some(function (d2) {
83513 return !d2.isMixed && d1.value === d2.value;
83518 function initCombo(selection, attachTo) {
83520 selection.attr('readonly', 'readonly');
83521 selection.call(_combobox, attachTo);
83522 setStaticValues(setPlaceholder);
83523 } else if (_optarray) {
83524 selection.call(_combobox, attachTo);
83525 setStaticValues(setPlaceholder);
83526 } else if (services.taginfo) {
83527 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
83528 setTaginfoValues('', setPlaceholder);
83532 function setStaticValues(callback) {
83533 if (!(_optstrings || _optarray)) return;
83536 _comboData = Object.keys(_optstrings).map(function (k) {
83537 var v = field.t('options.' + k, {
83538 'default': _optstrings[k]
83544 display: field.t.html('options.' + k, {
83545 'default': _optstrings[k]
83549 } else if (_optarray) {
83550 _comboData = _optarray.map(function (k) {
83551 var v = _snake_case ? unsnake(k) : k;
83560 _combobox.data(objectDifference(_comboData, _multiData));
83562 if (callback) callback(_comboData);
83565 function setTaginfoValues(q, callback) {
83566 var fn = _isMulti ? 'multikeys' : 'values';
83567 var query = (_isMulti ? field.key : '') + q;
83568 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83570 if (hasCountryPrefix) {
83571 query = _countryCode + ':';
83575 debounce: q !== '',
83580 if (_entityIDs.length) {
83581 params.geometry = context.graph().geometry(_entityIDs[0]);
83584 services.taginfo[fn](params, function (err, data) {
83586 data = data.filter(function (d) {
83587 if (field.type === 'typeCombo' && d.value === 'yes') {
83588 // don't show the fallback value
83590 } // don't show values with very low usage
83593 return !d.count || d.count > 10;
83595 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83597 if (deprecatedValues) {
83598 // don't suggest deprecated tag values
83599 data = data.filter(function (d) {
83600 return deprecatedValues.indexOf(d.value) === -1;
83604 if (hasCountryPrefix) {
83605 data = data.filter(function (d) {
83606 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83608 } // hide the caret if there are no suggestions
83611 _container.classed('empty-combobox', data.length === 0);
83613 _comboData = data.map(function (d) {
83615 if (_isMulti) k = k.replace(field.key, '');
83616 var v = _snake_case ? unsnake(k) : k;
83620 title: _isMulti ? v : d.title
83623 _comboData = objectDifference(_comboData, _multiData);
83624 if (callback) callback(_comboData);
83628 function setPlaceholder(values) {
83629 if (_isMulti || _isSemi) {
83630 _staticPlaceholder = field.placeholder() || _t('inspector.add');
83632 var vals = values.map(function (d) {
83634 }).filter(function (s) {
83635 return s.length < 20;
83637 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
83640 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83643 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83644 _staticPlaceholder += '…';
83649 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
83650 ph = _t('inspector.multiple_values');
83652 ph = _staticPlaceholder;
83655 _container.selectAll('input').attr('placeholder', ph);
83658 function change() {
83662 if (_isMulti || _isSemi) {
83663 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
83665 _container.classed('active', false);
83667 utilGetSetValue(_input, '');
83668 var vals = val.split(';').filter(Boolean);
83669 if (!vals.length) return;
83672 utilArrayUniq(vals).forEach(function (v) {
83673 var key = (field.key || '') + v;
83676 // don't set a multicombo value to 'yes' if it already has a non-'no' value
83677 // e.g. `language:de=main`
83678 var old = _tags[key];
83679 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
83682 key = context.cleanTagKey(key);
83683 field.keys.push(key);
83686 } else if (_isSemi) {
83687 var arr = _multiData.map(function (d) {
83691 arr = arr.concat(vals);
83692 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
83695 window.setTimeout(function () {
83696 _input.node().focus();
83699 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
83701 if (!rawValue && Array.isArray(_tags[field.key])) return;
83702 val = context.cleanTagValue(tagValue(rawValue));
83703 t[field.key] = val || undefined;
83706 dispatch$1.call('change', this, t);
83709 function removeMultikey(d3_event, d) {
83710 d3_event.preventDefault();
83711 d3_event.stopPropagation();
83715 t[d.key] = undefined;
83716 } else if (_isSemi) {
83717 var arr = _multiData.map(function (md) {
83718 return md.key === d.key ? null : md.key;
83719 }).filter(Boolean);
83721 arr = utilArrayUniq(arr);
83722 t[field.key] = arr.length ? arr.join(';') : undefined;
83725 dispatch$1.call('change', this, t);
83728 function combo(selection) {
83729 _container = selection.selectAll('.form-field-input-wrap').data([0]);
83730 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
83731 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
83733 if (_isMulti || _isSemi) {
83734 _container = _container.selectAll('.chiplist').data([0]);
83735 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations field
83736 // to mimic highway exit signs
83738 if (field.key === 'destination') {
83739 listClass += ' full-line-chips';
83742 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
83743 window.setTimeout(function () {
83744 _input.node().focus();
83746 }).merge(_container);
83747 _inputWrap = _container.selectAll('.input-wrap').data([0]);
83748 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
83749 _input = _inputWrap.selectAll('input').data([0]);
83751 _input = _container.selectAll('input').data([0]);
83754 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
83757 var extent = combinedEntityExtent();
83758 var countryCode = extent && iso1A2Code(extent.center());
83759 _countryCode = countryCode && countryCode.toLowerCase();
83762 _input.on('change', change).on('blur', change);
83764 _input.on('keydown.field', function (d3_event) {
83765 switch (d3_event.keyCode) {
83768 _input.node().blur(); // blurring also enters the value
83771 d3_event.stopPropagation();
83776 if (_isMulti || _isSemi) {
83777 _combobox.on('accept', function () {
83778 _input.node().blur();
83780 _input.node().focus();
83783 _input.on('focus', function () {
83784 _container.classed('active', true);
83789 combo.tags = function (tags) {
83792 if (_isMulti || _isSemi) {
83797 // Build _multiData array containing keys already set..
83798 for (var k in tags) {
83799 if (field.key && k.indexOf(field.key) !== 0) continue;
83800 if (!field.key && field.keys.indexOf(k) === -1) continue;
83802 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
83803 var suffix = field.key ? k.substr(field.key.length) : k;
83807 value: displayValue(suffix),
83808 isMixed: Array.isArray(v)
83813 // Set keys for form-field modified (needed for undo and reset buttons)..
83814 field.keys = _multiData.map(function (d) {
83816 }); // limit the input length so it fits after prepending the key prefix
83818 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
83820 maxLength = context.maxCharsForTagKey();
83822 } else if (_isSemi) {
83823 var allValues = [];
83826 if (Array.isArray(tags[field.key])) {
83827 tags[field.key].forEach(function (tagVal) {
83828 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
83829 allValues = allValues.concat(thisVals);
83831 if (!commonValues) {
83832 commonValues = thisVals;
83834 commonValues = commonValues.filter(function (value) {
83835 return thisVals.includes(value);
83839 allValues = utilArrayUniq(allValues).filter(Boolean);
83841 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
83842 commonValues = allValues;
83845 _multiData = allValues.map(function (v) {
83848 value: displayValue(v),
83849 isMixed: !commonValues.includes(v)
83852 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
83854 maxLength = context.maxCharsForTagValue() - currLength;
83856 if (currLength > 0) {
83857 // account for the separator if a new value will be appended to existing
83860 } // a negative maxlength doesn't make sense
83863 maxLength = Math.max(0, maxLength);
83864 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
83865 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
83867 var available = objectDifference(_comboData, _multiData);
83869 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
83870 // translateable _optstrings and they're all currently used,
83871 // or if the field is already at its character limit
83874 var hideAdd = _optstrings && !available.length || maxLength <= 0;
83876 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
83879 var chips = _container.selectAll('.chip').data(_multiData);
83881 chips.exit().remove();
83882 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
83883 enter.append('span');
83885 chips = chips.merge(enter).order().classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
83887 }).attr('title', function (d) {
83888 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
83891 if (allowDragAndDrop) {
83892 registerDragAndDrop(chips);
83895 chips.select('span').html(function (d) {
83898 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
83900 var isMixed = Array.isArray(tags[field.key]);
83901 var mixedValues = isMixed && tags[field.key].map(function (val) {
83902 return displayValue(val);
83903 }).filter(Boolean);
83904 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);
83908 function registerDragAndDrop(selection) {
83909 // allow drag and drop re-ordering of chips
83910 var dragOrigin, targetIndex;
83911 selection.call(d3_drag().on('start', function (d3_event) {
83916 targetIndex = null;
83917 }).on('drag', function (d3_event) {
83918 var x = d3_event.x - dragOrigin.x,
83919 y = d3_event.y - dragOrigin.y;
83920 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
83921 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
83922 var index = selection.nodes().indexOf(this);
83923 select(this).classed('dragging', true);
83924 targetIndex = null;
83925 var targetIndexOffsetTop = null;
83926 var draggedTagWidth = select(this).node().offsetWidth;
83928 if (field.key === 'destination') {
83929 // meaning tags are full width
83930 _container.selectAll('.chip').style('transform', function (d2, index2) {
83931 var node = select(this).node();
83933 if (index === index2) {
83934 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
83935 } else if (index2 > index && d3_event.y > node.offsetTop) {
83936 if (targetIndex === null || index2 > targetIndex) {
83937 targetIndex = index2;
83940 return 'translateY(-100%)'; // move the dragged tag down the order
83941 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
83942 if (targetIndex === null || index2 < targetIndex) {
83943 targetIndex = index2;
83946 return 'translateY(100%)';
83952 _container.selectAll('.chip').each(function (d2, index2) {
83953 var node = select(this).node(); // check the cursor is in the bounding box
83955 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) {
83956 targetIndex = index2;
83957 targetIndexOffsetTop = node.offsetTop;
83959 }).style('transform', function (d2, index2) {
83960 var node = select(this).node();
83962 if (index === index2) {
83963 return 'translate(' + x + 'px, ' + y + 'px)';
83964 } // only translate tags in the same row
83967 if (node.offsetTop === targetIndexOffsetTop) {
83968 if (index2 < index && index2 >= targetIndex) {
83969 return 'translateX(' + draggedTagWidth + 'px)';
83970 } else if (index2 > index && index2 <= targetIndex) {
83971 return 'translateX(-' + draggedTagWidth + 'px)';
83978 }).on('end', function () {
83979 if (!select(this).classed('dragging')) {
83983 var index = selection.nodes().indexOf(this);
83984 select(this).classed('dragging', false);
83986 _container.selectAll('.chip').style('transform', null);
83988 if (typeof targetIndex === 'number') {
83989 var element = _multiData[index];
83991 _multiData.splice(index, 1);
83993 _multiData.splice(targetIndex, 0, element);
83997 if (_multiData.length) {
83998 t[field.key] = _multiData.map(function (element) {
83999 return element.key;
84002 t[field.key] = undefined;
84005 dispatch$1.call('change', this, t);
84008 dragOrigin = undefined;
84009 targetIndex = undefined;
84013 combo.focus = function () {
84014 _input.node().focus();
84017 combo.entityIDs = function (val) {
84018 if (!arguments.length) return _entityIDs;
84023 function combinedEntityExtent() {
84024 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84027 return utilRebind(combo, dispatch$1, 'on');
84030 function uiFieldText(field, context) {
84031 var dispatch$1 = dispatch('change');
84032 var input = select(null);
84033 var outlinkButton = select(null);
84034 var _entityIDs = [];
84038 var _phoneFormats = {};
84040 if (field.type === 'tel') {
84041 _mainFileFetcher.get('phone_formats').then(function (d) {
84043 updatePhonePlaceholder();
84044 })["catch"](function () {
84049 function i(selection) {
84050 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84051 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84052 var isLocked = preset && preset.suggestion && field.id === 'brand';
84053 field.locked(isLocked);
84054 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84055 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84056 input = wrap.selectAll('input').data([0]);
84057 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);
84058 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
84060 if (field.type === 'tel') {
84061 updatePhonePlaceholder();
84062 } else if (field.type === 'number') {
84063 var rtl = _mainLocalizer.textDirection() === 'rtl';
84064 input.attr('type', 'text');
84065 var inc = field.increment;
84066 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
84067 buttons.enter().append('button').attr('class', function (d) {
84068 var which = d > 0 ? 'increment' : 'decrement';
84069 return 'form-field-button ' + which;
84070 }).merge(buttons).on('click', function (d3_event, d) {
84071 d3_event.preventDefault();
84072 var raw_vals = input.node().value || '0';
84073 var vals = raw_vals.split(';');
84074 vals = vals.map(function (v) {
84075 var num = parseFloat(v.trim(), 10);
84076 return isFinite(num) ? clamped(num + d) : v.trim();
84078 input.node().value = vals.join(';');
84081 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84082 input.attr('type', 'text');
84083 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
84084 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
84085 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84087 if (domainResults.length >= 2 && domainResults[1]) {
84088 var domain = domainResults[1];
84089 return _t('icons.view_on', {
84095 }).on('click', function (d3_event) {
84096 d3_event.preventDefault();
84097 var value = validIdentifierValueForLink();
84100 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84101 window.open(url, '_blank');
84103 }).merge(outlinkButton);
84107 function updatePhonePlaceholder() {
84108 if (input.empty() || !Object.keys(_phoneFormats).length) return;
84109 var extent = combinedEntityExtent();
84110 var countryCode = extent && iso1A2Code(extent.center());
84112 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84114 if (format) input.attr('placeholder', format);
84117 function validIdentifierValueForLink() {
84118 if (field.type === 'identifier' && field.pattern) {
84119 var value = utilGetSetValue(input).trim().split(';')[0];
84120 return value && value.match(new RegExp(field.pattern));
84124 } // clamp number to min/max
84127 function clamped(num) {
84128 if (field.minValue !== undefined) {
84129 num = Math.max(num, field.minValue);
84132 if (field.maxValue !== undefined) {
84133 num = Math.min(num, field.maxValue);
84139 function change(onInput) {
84140 return function () {
84142 var val = utilGetSetValue(input);
84143 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
84145 if (!val && Array.isArray(_tags[field.key])) return;
84148 if (field.type === 'number' && val) {
84149 var vals = val.split(';');
84150 vals = vals.map(function (v) {
84151 var num = parseFloat(v.trim(), 10);
84152 return isFinite(num) ? clamped(num) : v.trim();
84154 val = vals.join(';');
84157 utilGetSetValue(input, val);
84160 t[field.key] = val || undefined;
84161 dispatch$1.call('change', this, t, onInput);
84165 i.entityIDs = function (val) {
84166 if (!arguments.length) return _entityIDs;
84171 i.tags = function (tags) {
84173 var isMixed = Array.isArray(tags[field.key]);
84174 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);
84176 if (outlinkButton && !outlinkButton.empty()) {
84177 var disabled = !validIdentifierValueForLink();
84178 outlinkButton.classed('disabled', disabled);
84182 i.focus = function () {
84183 var node = input.node();
84184 if (node) node.focus();
84187 function combinedEntityExtent() {
84188 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84191 return utilRebind(i, dispatch$1, 'on');
84194 function uiFieldAccess(field, context) {
84195 var dispatch$1 = dispatch('change');
84196 var items = select(null);
84200 function access(selection) {
84201 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84202 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84203 var list = wrap.selectAll('ul').data([0]);
84204 list = list.enter().append('ul').attr('class', 'rows').merge(list);
84205 items = list.selectAll('li').data(field.keys); // Enter
84207 var enter = items.enter().append('li').attr('class', function (d) {
84208 return 'labeled-input preset-access-' + d;
84210 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
84211 return 'preset-input-access-' + d;
84212 }).html(function (d) {
84213 return field.t.html('types.' + d);
84215 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
84216 return 'preset-input-access preset-input-access-' + d;
84217 }).call(utilNoAuto).each(function (d) {
84218 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
84221 items = items.merge(enter);
84222 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
84225 function change(d3_event, d) {
84227 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
84229 if (!value && typeof _tags[d] !== 'string') return;
84230 tag[d] = value || undefined;
84231 dispatch$1.call('change', this, tag);
84234 access.options = function (type) {
84235 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84237 if (type !== 'access') {
84238 options.unshift('yes');
84239 options.push('designated');
84241 if (type === 'bicycle') {
84242 options.push('dismount');
84246 return options.map(function (option) {
84248 title: field.t('options.' + option + '.description'),
84254 var placeholdersByHighway = {
84256 foot: 'designated',
84257 motor_vehicle: 'no'
84261 motor_vehicle: 'no',
84267 motor_vehicle: 'no'
84270 motor_vehicle: 'no',
84271 bicycle: 'designated'
84274 motor_vehicle: 'no',
84275 horse: 'designated'
84279 motor_vehicle: 'no',
84285 motor_vehicle: 'yes',
84290 motor_vehicle: 'yes'
84294 motor_vehicle: 'yes',
84300 motor_vehicle: 'yes',
84306 motor_vehicle: 'yes',
84312 motor_vehicle: 'yes',
84318 motor_vehicle: 'yes',
84324 motor_vehicle: 'yes',
84330 motor_vehicle: 'yes',
84335 motor_vehicle: 'yes'
84339 motor_vehicle: 'yes',
84345 motor_vehicle: 'yes',
84351 motor_vehicle: 'yes',
84357 access.tags = function (tags) {
84359 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
84360 return typeof tags[d] === 'string' ? tags[d] : '';
84361 }).classed('mixed', function (d) {
84362 return tags[d] && Array.isArray(tags[d]);
84363 }).attr('title', function (d) {
84364 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84365 }).attr('placeholder', function (d) {
84366 if (tags[d] && Array.isArray(tags[d])) {
84367 return _t('inspector.multiple_values');
84370 if (d === 'access') {
84374 if (tags.access && typeof tags.access === 'string') {
84375 return tags.access;
84378 if (tags.highway) {
84379 if (typeof tags.highway === 'string') {
84380 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
84381 return placeholdersByHighway[tags.highway][d];
84384 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
84385 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84386 }).filter(Boolean);
84388 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
84389 // if all the highway values have the same implied access for this type then use that
84390 return impliedAccesses[0];
84395 return field.placeholder();
84399 access.focus = function () {
84400 items.selectAll('.preset-input-access').node().focus();
84403 return utilRebind(access, dispatch$1, 'on');
84406 function uiFieldAddress(field, context) {
84407 var dispatch$1 = dispatch('change');
84409 var _selection = select(null);
84411 var _wrap = select(null);
84413 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
84415 var _entityIDs = [];
84421 var _addressFormats = [{
84422 format: [['housenumber', 'street'], ['city', 'postcode']]
84424 _mainFileFetcher.get('address_formats').then(function (d) {
84425 _addressFormats = d;
84427 if (!_selection.empty()) {
84428 _selection.call(address);
84430 })["catch"](function () {
84434 function getNearStreets() {
84435 var extent = combinedEntityExtent();
84436 var l = extent.center();
84437 var box = geoExtent(l).padByMeters(200);
84438 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
84439 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
84440 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84442 title: d.tags.name,
84443 value: d.tags.name,
84444 dist: choice.distance
84446 }).sort(function (a, b) {
84447 return a.dist - b.dist;
84449 return utilArrayUniqBy(streets, 'value');
84451 function isAddressable(d) {
84452 return d.tags.highway && d.tags.name && d.type === 'way';
84456 function getNearCities() {
84457 var extent = combinedEntityExtent();
84458 var l = extent.center();
84459 var box = geoExtent(l).padByMeters(200);
84460 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
84462 title: d.tags['addr:city'] || d.tags.name,
84463 value: d.tags['addr:city'] || d.tags.name,
84464 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84466 }).sort(function (a, b) {
84467 return a.dist - b.dist;
84469 return utilArrayUniqBy(cities, 'value');
84471 function isAddressable(d) {
84473 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
84474 if (d.tags.border_type === 'city') return true;
84475 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
84478 if (d.tags['addr:city']) return true;
84483 function getNearValues(key) {
84484 var extent = combinedEntityExtent();
84485 var l = extent.center();
84486 var box = geoExtent(l).padByMeters(200);
84487 var results = context.history().intersects(box).filter(function hasTag(d) {
84488 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
84489 }).map(function (d) {
84491 title: d.tags[key],
84492 value: d.tags[key],
84493 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84495 }).sort(function (a, b) {
84496 return a.dist - b.dist;
84498 return utilArrayUniqBy(results, 'value');
84501 function updateForCountryCode() {
84502 if (!_countryCode) return;
84505 for (var i = 0; i < _addressFormats.length; i++) {
84506 var format = _addressFormats[i];
84508 if (!format.countryCodes) {
84509 addressFormat = format; // choose the default format, keep going
84510 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
84511 addressFormat = format; // choose the country format, stop here
84517 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
84518 var widths = addressFormat.widths || {
84519 housenumber: 1 / 3,
84527 // Normalize widths.
84528 var total = r.reduce(function (sum, key) {
84529 return sum + (widths[key] || 0.5);
84531 return r.map(function (key) {
84534 width: (widths[key] || 0.5) / total
84539 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
84540 return d.toString();
84543 rows.exit().remove();
84544 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
84545 return 'addr-' + d.id;
84546 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
84547 return d.width * 100 + '%';
84550 function addDropdown(d) {
84551 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
84553 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
84554 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
84555 callback(nearValues('addr:' + d.id));
84559 _wrap.selectAll('input').on('blur', change()).on('change', change());
84561 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
84563 if (_tags) updateTags(_tags);
84566 function address(selection) {
84567 _selection = selection;
84568 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84569 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
84570 var extent = combinedEntityExtent();
84575 if (context.inIntro()) {
84576 // localize the address format for the walkthrough
84577 countryCode = _t('intro.graph.countrycode');
84579 var center = extent.center();
84580 countryCode = iso1A2Code(center);
84584 _countryCode = countryCode.toLowerCase();
84585 updateForCountryCode();
84590 function change(onInput) {
84591 return function () {
84594 _wrap.selectAll('input').each(function (subfield) {
84595 var key = field.key + ':' + subfield.id;
84596 var value = this.value;
84597 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
84599 if (Array.isArray(_tags[key]) && !value) return;
84600 tags[key] = value || undefined;
84603 dispatch$1.call('change', this, tags, onInput);
84607 function updatePlaceholder(inputSelection) {
84608 return inputSelection.attr('placeholder', function (subfield) {
84609 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
84610 return _t('inspector.multiple_values');
84613 if (_countryCode) {
84614 var localkey = subfield.id + '!' + _countryCode;
84615 var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
84616 return addrField.t('placeholders.' + tkey);
84621 function updateTags(tags) {
84622 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
84623 var val = tags[field.key + ':' + subfield.id];
84624 return typeof val === 'string' ? val : '';
84625 }).attr('title', function (subfield) {
84626 var val = tags[field.key + ':' + subfield.id];
84627 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
84628 }).classed('mixed', function (subfield) {
84629 return Array.isArray(tags[field.key + ':' + subfield.id]);
84630 }).call(updatePlaceholder);
84633 function combinedEntityExtent() {
84634 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84637 address.entityIDs = function (val) {
84638 if (!arguments.length) return _entityIDs;
84643 address.tags = function (tags) {
84648 address.focus = function () {
84649 var node = _wrap.selectAll('input').node();
84651 if (node) node.focus();
84654 return utilRebind(address, dispatch$1, 'on');
84657 function uiFieldCycleway(field, context) {
84658 var dispatch$1 = dispatch('change');
84659 var items = select(null);
84660 var wrap = select(null);
84664 function cycleway(selection) {
84665 function stripcolon(s) {
84666 return s.replace(':', '');
84669 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84670 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84671 var div = wrap.selectAll('ul').data([0]);
84672 div = div.enter().append('ul').attr('class', 'rows').merge(div);
84673 var keys = ['cycleway:left', 'cycleway:right'];
84674 items = div.selectAll('li').data(keys);
84675 var enter = items.enter().append('li').attr('class', function (d) {
84676 return 'labeled-input preset-cycleway-' + stripcolon(d);
84678 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
84679 return 'preset-input-cycleway-' + stripcolon(d);
84680 }).html(function (d) {
84681 return field.t.html('types.' + d);
84683 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
84684 return 'preset-input-cycleway preset-input-' + stripcolon(d);
84685 }).call(utilNoAuto).each(function (d) {
84686 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
84688 items = items.merge(enter); // Update
84690 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
84693 function change(d3_event, key) {
84694 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
84696 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
84698 if (newValue === 'none' || newValue === '') {
84699 newValue = undefined;
84702 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
84703 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
84705 if (otherValue && Array.isArray(otherValue)) {
84706 // we must always have an explicit value for comparison
84707 otherValue = otherValue[0];
84710 if (otherValue === 'none' || otherValue === '') {
84711 otherValue = undefined;
84714 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
84715 // sides the same way
84717 if (newValue === otherValue) {
84719 cycleway: newValue,
84720 'cycleway:left': undefined,
84721 'cycleway:right': undefined
84724 // Always set both left and right as changing one can affect the other
84726 cycleway: undefined
84728 tag[key] = newValue;
84729 tag[otherKey] = otherValue;
84732 dispatch$1.call('change', this, tag);
84735 cycleway.options = function () {
84736 return Object.keys(field.strings.options).map(function (option) {
84738 title: field.t('options.' + option + '.description'),
84744 cycleway.tags = function (tags) {
84745 _tags = tags; // If cycleway is set, use that instead of individual values
84747 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
84748 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
84749 if (commonValue) return commonValue;
84750 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
84751 }).attr('title', function (d) {
84752 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
84755 if (Array.isArray(tags.cycleway)) {
84756 vals = vals.concat(tags.cycleway);
84759 if (Array.isArray(tags[d])) {
84760 vals = vals.concat(tags[d]);
84763 return vals.filter(Boolean).join('\n');
84767 }).attr('placeholder', function (d) {
84768 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
84769 return _t('inspector.multiple_values');
84772 return field.placeholder();
84773 }).classed('mixed', function (d) {
84774 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
84778 cycleway.focus = function () {
84779 var node = wrap.selectAll('input').node();
84780 if (node) node.focus();
84783 return utilRebind(cycleway, dispatch$1, 'on');
84786 function uiFieldLanes(field, context) {
84787 var dispatch$1 = dispatch('change');
84788 var LANE_WIDTH = 40;
84789 var LANE_HEIGHT = 200;
84790 var _entityIDs = [];
84792 function lanes(selection) {
84793 var lanesData = context.entity(_entityIDs[0]).lanes();
84795 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
84796 selection.call(lanes.off);
84800 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84801 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84802 var surface = wrap.selectAll('.surface').data([0]);
84803 var d = utilGetDimensions(wrap);
84804 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
84805 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
84806 var lanesSelection = surface.selectAll('.lanes').data([0]);
84807 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
84808 lanesSelection.attr('transform', function () {
84809 return 'translate(' + freeSpace / 2 + ', 0)';
84811 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
84812 lane.exit().remove();
84813 var enter = lane.enter().append('g').attr('class', 'lane');
84814 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
84815 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
84816 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
84817 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
84818 lane = lane.merge(enter);
84819 lane.attr('transform', function (d) {
84820 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
84822 lane.select('.forward').style('visibility', function (d) {
84823 return d.direction === 'forward' ? 'visible' : 'hidden';
84825 lane.select('.bothways').style('visibility', function (d) {
84826 return d.direction === 'bothways' ? 'visible' : 'hidden';
84828 lane.select('.backward').style('visibility', function (d) {
84829 return d.direction === 'backward' ? 'visible' : 'hidden';
84833 lanes.entityIDs = function (val) {
84837 lanes.tags = function () {};
84839 lanes.focus = function () {};
84841 lanes.off = function () {};
84843 return utilRebind(lanes, dispatch$1, 'on');
84845 uiFieldLanes.supportsMultiselection = false;
84847 var _languagesArray = [];
84848 function uiFieldLocalized(field, context) {
84849 var dispatch$1 = dispatch('change', 'input');
84850 var wikipedia = services.wikipedia;
84851 var input = select(null);
84852 var localizedInputs = select(null);
84856 var _tags; // A concern here in switching to async data means that _languagesArray will not
84857 // be available the first time through, so things like the fetchers and
84858 // the language() function will not work immediately.
84861 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
84864 var _territoryLanguages = {};
84865 _mainFileFetcher.get('territory_languages').then(function (d) {
84866 _territoryLanguages = d;
84867 })["catch"](function () {
84870 var allSuggestions = _mainPresetIndex.collection.filter(function (p) {
84871 return p.suggestion === true;
84872 }); // reuse these combos
84874 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
84875 var brandCombo = uiCombobox(context, 'localized-brand').canAutocomplete(false).minItems(1);
84877 var _selection = select(null);
84879 var _multilingual = [];
84881 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
84885 var _entityIDs = [];
84887 function loadLanguagesArray(dataLanguages) {
84888 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
84890 var replacements = {
84892 // in OSM, `sr` implies Cyrillic
84893 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
84897 for (var code in dataLanguages) {
84898 if (replacements[code] === false) continue;
84899 var metaCode = code;
84900 if (replacements[code]) metaCode = replacements[code];
84902 _languagesArray.push({
84903 localName: _mainLocalizer.languageName(metaCode, {
84906 nativeName: dataLanguages[metaCode].nativeName,
84908 label: _mainLocalizer.languageName(metaCode)
84913 function calcLocked() {
84914 // only lock the Name field
84915 var isLocked = field.id === 'name' && _entityIDs.length && // lock the field if any feature needs it
84916 _entityIDs.some(function (entityID) {
84917 var entity = context.graph().hasEntity(entityID);
84918 if (!entity) return false;
84920 var original = context.graph().base().entities[_entityIDs[0]];
84922 var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name; // if the name was already edited manually then allow further editing
84924 if (!hasOriginalName) return false; // features linked to Wikidata are likely important and should be protected
84926 if (entity.tags.wikidata) return true; // assume the name has already been confirmed if its source has been researched
84928 if (entity.tags['name:etymology:wikidata']) return true;
84929 var preset = _mainPresetIndex.match(entity, context.graph());
84930 var isSuggestion = preset && preset.suggestion;
84931 var showsBrand = preset && preset.originalFields.filter(function (d) {
84932 return d.id === 'brand';
84933 }).length; // protect standardized brand names
84935 return isSuggestion && !showsBrand;
84938 field.locked(isLocked);
84939 } // update _multilingual, maintaining the existing order
84942 function calcMultilingual(tags) {
84943 var existingLangsOrdered = _multilingual.map(function (item) {
84947 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
84949 for (var k in tags) {
84950 var m = k.match(/^(.*):(.+)$/);
84952 if (m && m[1] === field.key && m[2]) {
84958 if (existingLangs.has(item.lang)) {
84959 // update the value
84960 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
84961 existingLangs["delete"](item.lang);
84963 _multilingual.push(item);
84968 _multilingual = _multilingual.filter(function (item) {
84969 return !item.lang || !existingLangs.has(item.lang);
84973 function localized(selection) {
84974 _selection = selection;
84976 var isLocked = field.locked();
84977 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
84978 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
84979 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
84981 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84982 input = wrap.selectAll('.localized-main').data([0]); // enter/update
84984 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
84986 if (preset && field.id === 'name') {
84987 var pTag = preset.id.split('/', 2);
84988 var pKey = pTag[0];
84989 var pValue = pTag[1];
84991 if (!preset.suggestion) {
84992 // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
84993 // This code attempts to determine if the matched preset is the
84994 // kind of preset that even can benefit from name suggestions..
84995 // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
84996 // - false = churches, parks, hospitals, etc. (things not in the index)
84997 var isFallback = preset.isFallback();
84998 var goodSuggestions = allSuggestions.filter(function (s) {
84999 if (isFallback) return true;
85000 var sTag = s.id.split('/', 2);
85001 var sKey = sTag[0];
85002 var sValue = sTag[1];
85003 return pKey === sKey && (!pValue || pValue === sValue);
85004 }); // Show the suggestions.. If the user picks one, change the tags..
85006 if (allSuggestions.length && goodSuggestions.length) {
85007 input.on('blur.localized', checkBrandOnBlur).call(brandCombo.fetcher(fetchBrandNames(preset, allSuggestions)).on('accept', acceptBrand).on('cancel', cancelBrand));
85012 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
85013 var translateButton = wrap.selectAll('.localized-add').data([0]);
85014 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
85015 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
85017 if (_tags && !_multilingual.length) {
85018 calcMultilingual(_tags);
85021 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
85022 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
85023 localizedInputs.call(renderMultilingual);
85024 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.
85025 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85026 // So compare the current field value against the suggestions one last time.
85028 function checkBrandOnBlur() {
85029 var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85030 if (!latest) return; // deleting the entity blurred the field?
85032 var preset = _mainPresetIndex.match(latest, context.graph());
85033 if (preset && preset.suggestion) return; // already accepted
85035 var name = utilGetSetValue(input).trim();
85036 var matched = allSuggestions.filter(function (s) {
85037 return name === s.name();
85040 if (matched.length === 1) {
85042 suggestion: matched[0]
85049 function acceptBrand(d) {
85050 var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85052 if (!d || !entity) {
85057 var tags = entity.tags;
85058 var geometry = entity.geometry(context.graph());
85059 var removed = preset.unsetTags(tags, geometry);
85061 for (var k in tags) {
85062 tags[k] = removed[k]; // set removed tags to `undefined`
85065 tags = d.suggestion.setTags(tags, geometry);
85066 utilGetSetValue(input, tags.name);
85067 dispatch$1.call('change', this, tags);
85068 } // user hit escape
85071 function cancelBrand() {
85072 var name = utilGetSetValue(input);
85073 dispatch$1.call('change', this, {
85078 function fetchBrandNames(preset, suggestions) {
85079 var pTag = preset.id.split('/', 2);
85080 var pKey = pTag[0];
85081 var pValue = pTag[1];
85082 return function (value, callback) {
85085 if (value && value.length > 2) {
85086 for (var i = 0; i < suggestions.length; i++) {
85087 var s = suggestions[i]; // don't suggest brands from incompatible countries
85089 if (_countryCode && s.countryCodes && s.countryCodes.indexOf(_countryCode) === -1) continue;
85090 var sTag = s.id.split('/', 2);
85091 var sKey = sTag[0];
85092 var sValue = sTag[1];
85093 var subtitle = s.subtitle();
85094 var name = s.name();
85095 if (subtitle) name += ' – ' + subtitle;
85096 var dist = utilEditDistance(value, name.substring(0, value.length));
85097 var matchesPreset = pKey === sKey && (!pValue || pValue === sValue);
85099 if (dist < 1 || matchesPreset && dist < 3) {
85103 display: s.nameLabel() + (subtitle ? ' – ' + s.subtitleLabel() : ''),
85105 dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
85112 results.sort(function (a, b) {
85113 return a.dist - b.dist;
85117 results = results.slice(0, 10);
85122 function addNew(d3_event) {
85123 d3_event.preventDefault();
85124 if (field.locked()) return;
85125 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85127 var langExists = _multilingual.find(function (datum) {
85128 return datum.lang === defaultLang;
85131 var isLangEn = defaultLang.indexOf('en') > -1;
85133 if (isLangEn || langExists) {
85135 langExists = _multilingual.find(function (datum) {
85136 return datum.lang === defaultLang;
85141 // prepend the value so it appears at the top
85142 _multilingual.unshift({
85147 localizedInputs.call(renderMultilingual);
85151 function change(onInput) {
85152 return function (d3_event) {
85153 if (field.locked()) {
85154 d3_event.preventDefault();
85158 var val = utilGetSetValue(select(this));
85159 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
85161 if (!val && Array.isArray(_tags[field.key])) return;
85163 t[field.key] = val || undefined;
85164 dispatch$1.call('change', this, t, onInput);
85169 function key(lang) {
85170 return field.key + ':' + lang;
85173 function changeLang(d3_event, d) {
85174 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
85176 var lang = utilGetSetValue(select(this)).toLowerCase();
85178 var language = _languagesArray.find(function (d) {
85179 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
85182 if (language) lang = language.code;
85184 if (d.lang && d.lang !== lang) {
85185 tags[key(d.lang)] = undefined;
85188 var newKey = lang && context.cleanTagKey(key(lang));
85189 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85191 if (newKey && value) {
85192 tags[newKey] = value;
85193 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85194 tags[newKey] = _wikiTitles[d.lang];
85198 dispatch$1.call('change', this, tags);
85201 function changeValue(d3_event, d) {
85202 if (!d.lang) return;
85203 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
85205 if (!value && Array.isArray(d.value)) return;
85207 t[key(d.lang)] = value;
85209 dispatch$1.call('change', this, t);
85212 function fetchLanguages(value, cb) {
85213 var v = value.toLowerCase(); // show the user's language first
85215 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85217 if (_countryCode && _territoryLanguages[_countryCode]) {
85218 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85221 var langItems = [];
85222 langCodes.forEach(function (code) {
85223 var langItem = _languagesArray.find(function (item) {
85224 return item.code === code;
85227 if (langItem) langItems.push(langItem);
85229 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85230 cb(langItems.filter(function (d) {
85231 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;
85232 }).map(function (d) {
85239 function renderMultilingual(selection) {
85240 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
85243 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
85244 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
85245 var wrap = select(this);
85246 var domId = utilUniqueDomId(index);
85247 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
85248 var text = label.append('span').attr('class', 'label-text');
85249 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
85250 text.append('span').attr('class', 'label-textannotation');
85251 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
85252 if (field.locked()) return;
85253 d3_event.preventDefault();
85255 if (!d.lang || !d.value) {
85256 _multilingual.splice(index, 1);
85258 renderMultilingual(selection);
85260 // remove from entity tags
85262 t[key(d.lang)] = undefined;
85263 dispatch$1.call('change', this, t);
85265 }).call(svgIcon('#iD-operation-delete'));
85266 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);
85267 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
85269 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 () {
85270 select(this).style('max-height', '').style('overflow', 'visible');
85272 entries = entries.merge(entriesEnter);
85274 entries.classed('present', function (d) {
85275 return d.lang && d.value;
85277 utilGetSetValue(entries.select('.localized-lang'), function (d) {
85278 var langItem = _languagesArray.find(function (item) {
85279 return item.code === d.lang;
85282 if (langItem) return langItem.label;
85285 utilGetSetValue(entries.select('.localized-value'), function (d) {
85286 return typeof d.value === 'string' ? d.value : '';
85287 }).attr('title', function (d) {
85288 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
85289 }).attr('placeholder', function (d) {
85290 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
85291 }).classed('mixed', function (d) {
85292 return Array.isArray(d.value);
85296 localized.tags = function (tags) {
85297 _tags = tags; // Fetch translations from wikipedia
85299 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
85301 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
85303 if (wm && wm[0] && wm[1]) {
85304 wikipedia.translations(wm[1], wm[2], function (err, d) {
85305 if (err || !d) return;
85311 var isMixed = Array.isArray(tags[field.key]);
85312 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);
85313 calcMultilingual(tags);
85315 _selection.call(localized);
85318 localized.focus = function () {
85319 input.node().focus();
85322 localized.entityIDs = function (val) {
85323 if (!arguments.length) return _entityIDs;
85325 _multilingual = [];
85330 function loadCountryCode() {
85331 var extent = combinedEntityExtent();
85332 var countryCode = extent && iso1A2Code(extent.center());
85333 _countryCode = countryCode && countryCode.toLowerCase();
85336 function combinedEntityExtent() {
85337 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85340 return utilRebind(localized, dispatch$1, 'on');
85343 function uiFieldMaxspeed(field, context) {
85344 var dispatch$1 = dispatch('change');
85345 var unitInput = select(null);
85346 var input = select(null);
85347 var _entityIDs = [];
85353 var speedCombo = uiCombobox(context, 'maxspeed');
85354 var unitCombo = uiCombobox(context, 'maxspeed-unit').data(['km/h', 'mph'].map(comboValues));
85355 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
85356 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
85358 function maxspeed(selection) {
85359 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85360 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85361 input = wrap.selectAll('input.maxspeed-number').data([0]);
85362 input = input.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
85363 input.on('change', change).on('blur', change);
85364 var loc = combinedEntityExtent().center();
85365 _isImperial = roadSpeedUnit(loc) === 'mph';
85366 unitInput = wrap.selectAll('input.maxspeed-unit').data([0]);
85367 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-unit').call(unitCombo).merge(unitInput);
85368 unitInput.on('blur', changeUnits).on('change', changeUnits);
85370 function changeUnits() {
85371 _isImperial = utilGetSetValue(unitInput) === 'mph';
85372 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
85373 setUnitSuggestions();
85378 function setUnitSuggestions() {
85379 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
85380 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
85383 function comboValues(d) {
85385 value: d.toString(),
85386 title: d.toString()
85390 function change() {
85392 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
85394 if (!value && Array.isArray(_tags[field.key])) return;
85397 tag[field.key] = undefined;
85398 } else if (isNaN(value) || !_isImperial) {
85399 tag[field.key] = context.cleanTagValue(value);
85401 tag[field.key] = context.cleanTagValue(value + ' mph');
85404 dispatch$1.call('change', this, tag);
85407 maxspeed.tags = function (tags) {
85409 var value = tags[field.key];
85410 var isMixed = Array.isArray(value);
85413 if (value && value.indexOf('mph') >= 0) {
85414 value = parseInt(value, 10).toString();
85415 _isImperial = true;
85416 } else if (value) {
85417 _isImperial = false;
85421 setUnitSuggestions();
85422 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);
85425 maxspeed.focus = function () {
85426 input.node().focus();
85429 maxspeed.entityIDs = function (val) {
85433 function combinedEntityExtent() {
85434 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85437 return utilRebind(maxspeed, dispatch$1, 'on');
85440 function uiFieldRadio(field, context) {
85441 var dispatch$1 = dispatch('change');
85442 var placeholder = select(null);
85443 var wrap = select(null);
85444 var labels = select(null);
85445 var radios = select(null);
85446 var radioData = (field.options || field.strings && field.strings.options && Object.keys(field.strings.options) || field.keys).slice(); // shallow copy
85451 var _entityIDs = [];
85453 function selectedKey() {
85454 var node = wrap.selectAll('.form-field-input-radio label.active input');
85455 return !node.empty() && node.datum();
85458 function radio(selection) {
85459 selection.classed('preset-radio', true);
85460 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85461 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
85462 enter.append('span').attr('class', 'placeholder');
85463 wrap = wrap.merge(enter);
85464 placeholder = wrap.selectAll('.placeholder');
85465 labels = wrap.selectAll('label').data(radioData);
85466 enter = labels.enter().append('label');
85467 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
85468 return field.t('options.' + d, {
85471 }).attr('checked', false);
85472 enter.append('span').html(function (d) {
85473 return field.t.html('options.' + d, {
85477 labels = labels.merge(enter);
85478 radios = labels.selectAll('input').on('change', changeRadio);
85481 function structureExtras(selection, tags) {
85482 var selected = selectedKey() || tags.layer !== undefined;
85483 var type = _mainPresetIndex.field(selected);
85484 var layer = _mainPresetIndex.field('layer');
85485 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
85486 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
85487 extrasWrap.exit().remove();
85488 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
85489 var list = extrasWrap.selectAll('ul').data([0]);
85490 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
85493 if (!typeField || typeField.id !== selected) {
85494 typeField = uiField(context, type, _entityIDs, {
85496 }).on('change', changeType);
85499 typeField.tags(tags);
85504 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
85508 typeItem.exit().remove(); // Enter
85510 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
85511 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
85512 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
85514 typeItem = typeItem.merge(typeEnter);
85517 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
85521 if (layer && showLayer) {
85523 layerField = uiField(context, layer, _entityIDs, {
85525 }).on('change', changeLayer);
85528 layerField.tags(tags);
85529 field.keys = utilArrayUnion(field.keys, ['layer']);
85532 field.keys = field.keys.filter(function (k) {
85533 return k !== 'layer';
85537 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
85539 layerItem.exit().remove(); // Enter
85541 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
85542 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
85543 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
85545 layerItem = layerItem.merge(layerEnter);
85548 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
85552 function changeType(t, onInput) {
85553 var key = selectedKey();
85557 if (val !== 'no') {
85558 _oldType[key] = val;
85561 if (field.type === 'structureRadio') {
85562 // remove layer if it should not be set
85563 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
85564 t.layer = undefined;
85565 } // add layer if it should be set
85568 if (t.layer === undefined) {
85569 if (key === 'bridge' && val !== 'no') {
85573 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
85579 dispatch$1.call('change', this, t, onInput);
85582 function changeLayer(t, onInput) {
85583 if (t.layer === '0') {
85584 t.layer = undefined;
85587 dispatch$1.call('change', this, t, onInput);
85590 function changeRadio() {
85595 t[field.key] = undefined;
85598 radios.each(function (d) {
85599 var active = select(this).property('checked');
85600 if (active) activeKey = d;
85603 if (active) t[field.key] = d;
85605 var val = _oldType[activeKey] || 'yes';
85606 t[d] = active ? val : undefined;
85610 if (field.type === 'structureRadio') {
85611 if (activeKey === 'bridge') {
85613 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
85616 t.layer = undefined;
85620 dispatch$1.call('change', this, t);
85623 radio.tags = function (tags) {
85624 radios.property('checked', function (d) {
85626 return tags[field.key] === d;
85629 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
85632 function isMixed(d) {
85634 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
85637 return Array.isArray(tags[d]);
85640 labels.classed('active', function (d) {
85642 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
85645 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
85646 }).classed('mixed', isMixed).attr('title', function (d) {
85647 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
85649 var selection = radios.filter(function () {
85650 return this.checked;
85653 if (selection.empty()) {
85654 placeholder.html(_t.html('inspector.none'));
85656 placeholder.html(selection.attr('value'));
85657 _oldType[selection.datum()] = tags[selection.datum()];
85660 if (field.type === 'structureRadio') {
85661 // For waterways without a tunnel tag, set 'culvert' as
85662 // the _oldType to default to if the user picks 'tunnel'
85663 if (!!tags.waterway && !_oldType.tunnel) {
85664 _oldType.tunnel = 'culvert';
85667 wrap.call(structureExtras, tags);
85671 radio.focus = function () {
85672 radios.node().focus();
85675 radio.entityIDs = function (val) {
85676 if (!arguments.length) return _entityIDs;
85682 radio.isAllowed = function () {
85683 return _entityIDs.length === 1;
85686 return utilRebind(radio, dispatch$1, 'on');
85689 function uiFieldRestrictions(field, context) {
85690 var dispatch$1 = dispatch('change');
85691 var breathe = behaviorBreathe();
85692 corePreferences('turn-restriction-via-way', null); // remove old key
85694 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
85696 var storedDistance = corePreferences('turn-restriction-distance');
85698 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
85700 var _maxDistance = storedDistance ? +storedDistance : 30;
85702 var _initialized = false;
85704 var _parent = select(null); // the entire field
85707 var _container = select(null); // just the map
85722 function restrictions(selection) {
85723 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
85725 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
85726 _graph = context.graph();
85727 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
85728 } // It's possible for there to be no actual intersection here.
85729 // for example, a vertex of two `highway=path`
85730 // In this case, hide the field.
85733 var isOK = _intersection && _intersection.vertices.length && // has vertices
85734 _intersection.vertices // has the vertex that the user selected
85735 .filter(function (vertex) {
85736 return vertex.id === _vertexID;
85737 }).length && _intersection.ways.length > 2 && // has more than 2 ways
85738 _intersection.ways // has more than 1 TO way
85739 .filter(function (way) {
85741 }).length > 1; // Also hide in the case where
85743 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
85745 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
85746 selection.call(restrictions.off);
85750 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85751 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85752 var container = wrap.selectAll('.restriction-container').data([0]); // enter
85754 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
85755 containerEnter.append('div').attr('class', 'restriction-help'); // update
85757 _container = containerEnter.merge(container).call(renderViewer);
85758 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
85760 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
85763 function renderControls(selection) {
85764 var distControl = selection.selectAll('.restriction-distance').data([0]);
85765 distControl.exit().remove();
85766 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
85767 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
85768 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
85769 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
85771 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
85772 var val = select(this).property('value');
85773 _maxDistance = +val;
85774 _intersection = null;
85776 _container.selectAll('.layer-osm .layer-turns *').remove();
85778 corePreferences('turn-restriction-distance', _maxDistance);
85780 _parent.call(restrictions);
85782 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
85783 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
85784 viaControl.exit().remove();
85785 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
85786 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
85787 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
85788 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
85790 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
85791 var val = select(this).property('value');
85794 _container.selectAll('.layer-osm .layer-turns *').remove();
85796 corePreferences('turn-restriction-via-way0', _maxViaWay);
85798 _parent.call(restrictions);
85800 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
85803 function renderViewer(selection) {
85804 if (!_intersection) return;
85805 var vgraph = _intersection.graph;
85806 var filter = utilFunctor(true);
85807 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
85808 // Instead of asking the restriction-container for its dimensions,
85809 // we can ask the .sidebar, which can have its dimensions cached.
85810 // width: calc as sidebar - padding
85811 // height: hardcoded (from `80_app.css`)
85812 // var d = utilGetDimensions(selection);
85814 var sdims = utilGetDimensions(context.container().select('.sidebar'));
85815 var d = [sdims[0] - 50, 370];
85816 var c = geoVecScale(d, 0.5);
85818 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
85820 var extent = geoExtent();
85822 for (var i = 0; i < _intersection.vertices.length; i++) {
85823 extent._extend(_intersection.vertices[i].extent());
85824 } // If this is a large intersection, adjust zoom to fit extent
85827 if (_intersection.vertices.length > 1) {
85828 var padding = 180; // in z22 pixels
85830 var tl = projection([extent[0][0], extent[1][1]]);
85831 var br = projection([extent[1][0], extent[0][1]]);
85832 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
85833 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
85834 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
85835 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
85836 z = z - Math.max(hZoomDiff, vZoomDiff);
85837 projection.scale(geoZoomToScale(z));
85840 var padTop = 35; // reserve top space for hint text
85842 var extentCenter = projection(extent.center());
85843 extentCenter[1] = extentCenter[1] - padTop;
85844 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
85845 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
85846 var drawVertices = svgVertices(projection, context);
85847 var drawLines = svgLines(projection, context);
85848 var drawTurns = svgTurns(projection, context);
85849 var firstTime = selection.selectAll('.surface').empty();
85850 selection.call(drawLayers);
85851 var surface = selection.selectAll('.surface').classed('tr', true);
85854 _initialized = true;
85855 surface.call(breathe);
85856 } // This can happen if we've lowered the detail while a FROM way
85857 // is selected, and that way is no longer part of the intersection.
85860 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
85865 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));
85866 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
85867 surface.selectAll('.selected').classed('selected', false);
85868 surface.selectAll('.related').classed('related', false);
85872 way = vgraph.entity(_fromWayID);
85873 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
85876 document.addEventListener('resizeWindow', function () {
85877 utilSetDimensions(_container, null);
85882 function click(d3_event) {
85883 surface.call(breathe.off).call(breathe);
85884 var datum = d3_event.target.__data__;
85885 var entity = datum && datum.properties && datum.properties.entity;
85891 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
85892 _fromWayID = datum.id;
85895 } else if (datum instanceof osmTurn) {
85896 var actions, extraActions, turns, i;
85897 var restrictionType = osmInferRestriction(vgraph, datum, projection);
85899 if (datum.restrictionID && !datum.direct) {
85901 } else if (datum.restrictionID && !datum.only) {
85904 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
85906 datumOnly.only = true; // but change this property
85908 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
85909 // We will remember them in _oldTurns, and restore them if the user clicks again.
85911 turns = _intersection.turns(_fromWayID, 2);
85915 for (i = 0; i < turns.length; i++) {
85916 var turn = turns[i];
85917 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
85919 if (turn.direct && turn.path[1] === datum.path[1]) {
85920 seen[turns[i].restrictionID] = true;
85921 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
85923 _oldTurns.push(turn);
85925 extraActions.push(actionUnrestrictTurn(turn));
85929 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
85930 } else if (datum.restrictionID) {
85932 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
85933 // This relies on the assumption that the intersection was already split up when we
85934 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
85935 turns = _oldTurns || [];
85938 for (i = 0; i < turns.length; i++) {
85939 if (turns[i].key !== datum.key) {
85940 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
85945 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
85948 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
85951 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
85952 // Refresh it and update the help..
85954 var s = surface.selectAll('.' + datum.key);
85955 datum = s.empty() ? null : s.datum();
85956 updateHints(datum);
85964 function mouseover(d3_event) {
85965 var datum = d3_event.target.__data__;
85966 updateHints(datum);
85969 _lastXPos = _lastXPos || sdims[0];
85971 function redraw(minChange) {
85975 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
85978 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
85979 if (context.hasEntity(_vertexID)) {
85982 _container.call(renderViewer);
85987 function highlightPathsFrom(wayID) {
85988 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
85989 surface.selectAll('.' + wayID).classed('related', true);
85992 var turns = _intersection.turns(wayID, _maxViaWay);
85994 for (var i = 0; i < turns.length; i++) {
85995 var turn = turns[i];
85996 var ids = [turn.to.way];
85997 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
85999 if (turn.only || turns.length === 1) {
86000 if (turn.via.ways) {
86001 ids = ids.concat(turn.via.ways);
86003 } else if (turn.to.way === wayID) {
86007 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
86012 function updateHints(datum) {
86013 var help = _container.selectAll('.restriction-help').html('');
86015 var placeholders = {};
86016 ['from', 'via', 'to'].forEach(function (k) {
86017 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86019 var entity = datum && datum.properties && datum.properties.entity;
86026 way = vgraph.entity(_fromWayID);
86027 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86028 } // Hovering a way
86031 if (datum instanceof osmWay && datum.__from) {
86033 highlightPathsFrom(_fromWayID ? null : way.id);
86034 surface.selectAll('.' + way.id).classed('related', true);
86035 var clickSelect = !_fromWayID || _fromWayID !== way.id;
86036 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
86037 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86038 from: placeholders.from,
86039 fromName: displayName(way.id, vgraph)
86040 })); // Hovering a turn arrow
86041 } else if (datum instanceof osmTurn) {
86042 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86043 var turnType = restrictionType.replace(/^(only|no)\_/, '');
86044 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
86045 var klass, turnText, nextText;
86048 klass = 'restrict';
86049 turnText = _t.html('restriction.help.turn.no_' + turnType, {
86052 nextText = _t.html('restriction.help.turn.only_' + turnType, {
86055 } else if (datum.only) {
86057 turnText = _t.html('restriction.help.turn.only_' + turnType, {
86060 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
86065 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
86068 nextText = _t.html('restriction.help.turn.no_' + turnType, {
86073 help.append('div') // "NO Right Turn (indirect)"
86074 .attr('class', 'qualifier ' + klass).html(turnText);
86075 help.append('div') // "FROM {fromName} TO {toName}"
86076 .html(_t.html('restriction.help.from_name_to_name', {
86077 from: placeholders.from,
86078 fromName: displayName(datum.from.way, vgraph),
86079 to: placeholders.to,
86080 toName: displayName(datum.to.way, vgraph)
86083 if (datum.via.ways && datum.via.ways.length) {
86086 for (var i = 0; i < datum.via.ways.length; i++) {
86087 var prev = names[names.length - 1];
86088 var curr = displayName(datum.via.ways[i], vgraph);
86089 if (!prev || curr !== prev) // collapse identical names
86093 help.append('div') // "VIA {viaNames}"
86094 .html(_t.html('restriction.help.via_names', {
86095 via: placeholders.via,
86096 viaNames: names.join(', ')
86101 help.append('div') // Click for "No Right Turn"
86102 .html(_t.html('restriction.help.toggle', {
86103 turn: nextText.trim()
86107 highlightPathsFrom(null);
86108 var alongIDs = datum.path.slice();
86109 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
86111 highlightPathsFrom(null);
86114 help.append('div') // "FROM {fromName}"
86115 .html(_t.html('restriction.help.from_name', {
86116 from: placeholders.from,
86117 fromName: displayName(_fromWayID, vgraph)
86120 help.append('div') // "Click to select a FROM segment."
86121 .html(_t.html('restriction.help.select_from', {
86122 from: placeholders.from
86129 function displayMaxDistance(maxDist) {
86130 var isImperial = !_mainLocalizer.usesMetric();
86135 // imprecise conversion for prettier display
86145 distance: _t('units.feet', {
86146 quantity: distToFeet
86151 distance: _t('units.meters', {
86157 return _t.html('restriction.controls.distance_up_to', opts);
86160 function displayMaxVia(maxVia) {
86161 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');
86164 function displayName(entityID, graph) {
86165 var entity = graph.entity(entityID);
86166 var name = utilDisplayName(entity) || '';
86167 var matched = _mainPresetIndex.match(entity, graph);
86168 var type = matched && matched.name() || utilDisplayType(entity.id);
86169 return name || type;
86172 restrictions.entityIDs = function (val) {
86173 _intersection = null;
86176 _vertexID = val[0];
86179 restrictions.tags = function () {};
86181 restrictions.focus = function () {};
86183 restrictions.off = function (selection) {
86184 if (!_initialized) return;
86185 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
86186 select(window).on('resize.restrictions', null);
86189 return utilRebind(restrictions, dispatch$1, 'on');
86191 uiFieldRestrictions.supportsMultiselection = false;
86193 function uiFieldTextarea(field, context) {
86194 var dispatch$1 = dispatch('change');
86195 var input = select(null);
86199 function textarea(selection) {
86200 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86201 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86202 input = wrap.selectAll('textarea').data([0]);
86203 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
86206 function change(onInput) {
86207 return function () {
86208 var val = utilGetSetValue(input);
86209 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
86211 if (!val && Array.isArray(_tags[field.key])) return;
86213 t[field.key] = val || undefined;
86214 dispatch$1.call('change', this, t, onInput);
86218 textarea.tags = function (tags) {
86220 var isMixed = Array.isArray(tags[field.key]);
86221 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);
86224 textarea.focus = function () {
86225 input.node().focus();
86228 return utilRebind(textarea, dispatch$1, 'on');
86231 var getOwnPropertyDescriptor$5 = objectGetOwnPropertyDescriptor.f;
86238 var nativeEndsWith = ''.endsWith;
86239 var min$a = Math.min;
86241 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('endsWith');
86242 // https://github.com/zloirock/core-js/pull/702
86243 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
86244 var descriptor = getOwnPropertyDescriptor$5(String.prototype, 'endsWith');
86245 return descriptor && !descriptor.writable;
86248 // `String.prototype.endsWith` method
86249 // https://tc39.es/ecma262/#sec-string.prototype.endswith
86250 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
86251 endsWith: function endsWith(searchString /* , endPosition = @length */) {
86252 var that = String(requireObjectCoercible(this));
86253 notARegexp(searchString);
86254 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
86255 var len = toLength(that.length);
86256 var end = endPosition === undefined ? len : min$a(toLength(endPosition), len);
86257 var search = String(searchString);
86258 return nativeEndsWith
86259 ? nativeEndsWith.call(that, search, end)
86260 : that.slice(end - search.length, end) === search;
86264 function uiFieldWikidata(field, context) {
86265 var wikidata = services.wikidata;
86266 var dispatch$1 = dispatch('change');
86268 var _selection = select(null);
86270 var _searchInput = select(null);
86273 var _wikidataEntity = null;
86275 var _entityIDs = [];
86277 var _wikipediaKey = field.keys && field.keys.find(function (key) {
86278 return key.includes('wikipedia');
86280 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
86282 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
86284 function wiki(selection) {
86285 _selection = selection;
86286 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86287 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86288 var list = wrap.selectAll('ul').data([0]);
86289 list = list.enter().append('ul').attr('class', 'rows').merge(list);
86290 var searchRow = list.selectAll('li.wikidata-search').data([0]);
86291 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
86292 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
86293 var node = select(this).node();
86294 node.setSelectionRange(0, node.value.length);
86295 }).on('blur', function () {
86296 setLabelForEntity();
86297 }).call(combobox.fetcher(fetchWikidataItems));
86298 combobox.on('accept', function (d) {
86303 }).on('cancel', function () {
86304 setLabelForEntity();
86306 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
86307 domain: 'wikidata.org'
86308 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
86309 d3_event.preventDefault();
86310 if (_wikiURL) window.open(_wikiURL, '_blank');
86312 searchRow = searchRow.merge(searchRowEnter);
86313 _searchInput = searchRow.select('input');
86314 var wikidataProperties = ['description', 'identifier'];
86315 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
86317 var enter = items.enter().append('li').attr('class', function (d) {
86318 return 'labeled-input preset-wikidata-' + d;
86320 enter.append('span').attr('class', 'label').html(function (d) {
86321 return _t.html('wikidata.' + d);
86323 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
86324 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
86325 d3_event.preventDefault();
86326 select(this.parentNode).select('input').node().select();
86327 document.execCommand('copy');
86331 function fetchWikidataItems(q, callback) {
86332 if (!q && _hintKey) {
86333 // other tags may be good search terms
86334 for (var i in _entityIDs) {
86335 var entity = context.hasEntity(_entityIDs[i]);
86337 if (entity.tags[_hintKey]) {
86338 q = entity.tags[_hintKey];
86344 wikidata.itemsForSearchQuery(q, function (err, data) {
86347 for (var i in data) {
86348 data[i].value = data[i].label + ' (' + data[i].id + ')';
86349 data[i].title = data[i].description;
86352 if (callback) callback(data);
86356 function change() {
86358 syncTags[field.key] = _qid;
86359 dispatch$1.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
86361 var initGraph = context.graph();
86362 var initEntityIDs = _entityIDs;
86363 wikidata.entityByQID(_qid, function (err, entity) {
86364 if (err) return; // If graph has changed, we can't apply this update.
86366 if (context.graph() !== initGraph) return;
86367 if (!entity.sitelinks) return;
86368 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
86370 ['labels', 'descriptions'].forEach(function (key) {
86371 if (!entity[key]) return;
86372 var valueLangs = Object.keys(entity[key]);
86373 if (valueLangs.length === 0) return;
86374 var valueLang = valueLangs[0];
86376 if (langs.indexOf(valueLang) === -1) {
86377 langs.push(valueLang);
86380 var newWikipediaValue;
86382 if (_wikipediaKey) {
86383 var foundPreferred;
86385 for (var i in langs) {
86386 var lang = langs[i];
86387 var siteID = lang.replace('-', '_') + 'wiki';
86389 if (entity.sitelinks[siteID]) {
86390 foundPreferred = true;
86391 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
86397 if (!foundPreferred) {
86398 // No wikipedia sites available in the user's language or the fallback languages,
86399 // default to any wikipedia sitelink
86400 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
86401 return site.endsWith('wiki');
86404 if (wikiSiteKeys.length === 0) {
86405 // if no wikipedia pages are linked to this wikidata entity, delete that tag
86406 newWikipediaValue = null;
86408 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
86409 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
86410 newWikipediaValue = wikiLang + ':' + wikiTitle;
86415 if (newWikipediaValue) {
86416 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
86419 if (typeof newWikipediaValue === 'undefined') return;
86420 var actions = initEntityIDs.map(function (entityID) {
86421 var entity = context.hasEntity(entityID);
86422 if (!entity) return null;
86423 var currTags = Object.assign({}, entity.tags); // shallow copy
86425 if (newWikipediaValue === null) {
86426 if (!currTags[_wikipediaKey]) return null;
86427 delete currTags[_wikipediaKey];
86429 currTags[_wikipediaKey] = newWikipediaValue;
86432 return actionChangeTags(entityID, currTags);
86433 }).filter(Boolean);
86434 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
86436 context.overwrite(function actionUpdateWikipediaTags(graph) {
86437 actions.forEach(function (action) {
86438 graph = action(graph);
86441 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
86442 // changeTags() is not intended to be called asynchronously
86446 function setLabelForEntity() {
86449 if (_wikidataEntity) {
86450 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
86452 if (label.length === 0) {
86453 label = _wikidataEntity.id.toString();
86457 utilGetSetValue(_searchInput, label);
86460 wiki.tags = function (tags) {
86461 var isMixed = Array.isArray(tags[field.key]);
86463 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
86465 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
86467 if (!/^Q[0-9]*$/.test(_qid)) {
86468 // not a proper QID
86471 } // QID value in correct format
86474 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
86475 wikidata.entityByQID(_qid, function (err, entity) {
86481 _wikidataEntity = entity;
86482 setLabelForEntity();
86483 var description = entityPropertyForDisplay(entity, 'descriptions');
86485 _selection.select('button.wiki-link').classed('disabled', false);
86487 _selection.select('.preset-wikidata-description').style('display', function () {
86488 return description.length > 0 ? 'flex' : 'none';
86489 }).select('input').attr('value', description);
86491 _selection.select('.preset-wikidata-identifier').style('display', function () {
86492 return entity.id ? 'flex' : 'none';
86493 }).select('input').attr('value', entity.id);
86494 }); // not a proper QID
86496 function unrecognized() {
86497 _wikidataEntity = null;
86498 setLabelForEntity();
86500 _selection.select('.preset-wikidata-description').style('display', 'none');
86502 _selection.select('.preset-wikidata-identifier').style('display', 'none');
86504 _selection.select('button.wiki-link').classed('disabled', true);
86506 if (_qid && _qid !== '') {
86507 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
86514 function entityPropertyForDisplay(wikidataEntity, propKey) {
86515 if (!wikidataEntity[propKey]) return '';
86516 var propObj = wikidataEntity[propKey];
86517 var langKeys = Object.keys(propObj);
86518 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
86520 var langs = wikidata.languagesToQuery();
86522 for (var i in langs) {
86523 var lang = langs[i];
86524 var valueObj = propObj[lang];
86525 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
86526 } // default to any available value
86529 return propObj[langKeys[0]].value;
86532 wiki.entityIDs = function (val) {
86533 if (!arguments.length) return _entityIDs;
86538 wiki.focus = function () {
86539 _searchInput.node().focus();
86542 return utilRebind(wiki, dispatch$1, 'on');
86545 function uiFieldWikipedia(field, context) {
86546 var _arguments = arguments;
86547 var dispatch$1 = dispatch('change');
86548 var wikipedia = services.wikipedia;
86549 var wikidata = services.wikidata;
86551 var _langInput = select(null);
86553 var _titleInput = select(null);
86561 var _dataWikipedia = [];
86562 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
86563 _dataWikipedia = d;
86564 if (_tags) updateForTags(_tags);
86565 })["catch"](function () {
86568 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
86569 var v = value.toLowerCase();
86570 callback(_dataWikipedia.filter(function (d) {
86571 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
86572 }).map(function (d) {
86578 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
86582 for (var i in _entityIDs) {
86583 var entity = context.hasEntity(_entityIDs[i]);
86585 if (entity.tags.name) {
86586 value = entity.tags.name;
86592 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
86593 searchfn(language()[2], value, function (query, data) {
86594 callback(data.map(function (d) {
86602 function wiki(selection) {
86603 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86604 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
86605 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
86606 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
86607 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
86608 _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);
86610 _langInput.on('blur', changeLang).on('change', changeLang);
86612 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
86613 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
86614 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
86615 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
86617 _titleInput.on('blur', function () {
86619 }).on('change', function () {
86623 var link = titleContainer.selectAll('.wiki-link').data([0]);
86624 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
86625 domain: 'wikipedia.org'
86626 })).call(svgIcon('#iD-icon-out-link')).merge(link);
86627 link.on('click', function (d3_event) {
86628 d3_event.preventDefault();
86629 if (_wikiURL) window.open(_wikiURL, '_blank');
86633 function defaultLanguageInfo(skipEnglishFallback) {
86634 var langCode = _mainLocalizer.languageCode().toLowerCase();
86636 for (var i in _dataWikipedia) {
86637 var d = _dataWikipedia[i]; // default to the language of iD's current locale
86639 if (d[2] === langCode) return d;
86640 } // fallback to English
86643 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
86646 function language(skipEnglishFallback) {
86647 var value = utilGetSetValue(_langInput).toLowerCase();
86649 for (var i in _dataWikipedia) {
86650 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
86652 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
86653 } // fallback to English
86656 return defaultLanguageInfo(skipEnglishFallback);
86659 function changeLang() {
86660 utilGetSetValue(_langInput, language()[1]);
86664 function change(skipWikidata) {
86665 var value = utilGetSetValue(_titleInput);
86666 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
86668 var langInfo = m && _dataWikipedia.find(function (d) {
86669 return m[1] === d[2];
86675 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
86677 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
86680 var anchor; // try {
86681 // leave this out for now - #6232
86682 // Best-effort `anchordecode:` implementation
86683 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
86686 anchor = decodeURIComponent(m[3]); // }
86688 value += '#' + anchor.replace(/_/g, ' ');
86691 value = value.slice(0, 1).toUpperCase() + value.slice(1);
86692 utilGetSetValue(_langInput, nativeLangName);
86693 utilGetSetValue(_titleInput, value);
86697 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
86699 syncTags.wikipedia = undefined;
86702 dispatch$1.call('change', this, syncTags);
86703 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
86705 var initGraph = context.graph();
86706 var initEntityIDs = _entityIDs;
86707 wikidata.itemsByTitle(language()[2], value, function (err, data) {
86708 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
86710 if (context.graph() !== initGraph) return;
86711 var qids = Object.keys(data);
86712 var value = qids && qids.find(function (id) {
86713 return id.match(/^Q\d+$/);
86715 var actions = initEntityIDs.map(function (entityID) {
86716 var entity = context.entity(entityID).tags;
86717 var currTags = Object.assign({}, entity); // shallow copy
86719 if (currTags.wikidata !== value) {
86720 currTags.wikidata = value;
86721 return actionChangeTags(entityID, currTags);
86725 }).filter(Boolean);
86726 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
86728 context.overwrite(function actionUpdateWikidataTags(graph) {
86729 actions.forEach(function (action) {
86730 graph = action(graph);
86733 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
86734 // changeTags() is not intended to be called asynchronously
86738 wiki.tags = function (tags) {
86740 updateForTags(tags);
86743 function updateForTags(tags) {
86744 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
86745 // optional suffix of `#anchor`
86747 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
86748 var tagLang = m && m[1];
86749 var tagArticleTitle = m && m[2];
86750 var anchor = m && m[3];
86752 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
86753 return tagLang === d[2];
86754 }); // value in correct format
86758 var nativeLangName = tagLangInfo[1];
86759 utilGetSetValue(_langInput, nativeLangName);
86760 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
86764 // Best-effort `anchorencode:` implementation
86765 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
86767 anchor = anchor.replace(/ /g, '_');
86771 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
86773 utilGetSetValue(_titleInput, value);
86775 if (value && value !== '') {
86776 utilGetSetValue(_langInput, '');
86777 var defaultLangInfo = defaultLanguageInfo();
86778 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
86780 var shownOrDefaultLangInfo = language(true
86781 /* skipEnglishFallback */
86783 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
86789 wiki.entityIDs = function (val) {
86790 if (!_arguments.length) return _entityIDs;
86795 wiki.focus = function () {
86796 _titleInput.node().focus();
86799 return utilRebind(wiki, dispatch$1, 'on');
86801 uiFieldWikipedia.supportsMultiselection = false;
86804 access: uiFieldAccess,
86805 address: uiFieldAddress,
86806 check: uiFieldCheck,
86807 combo: uiFieldCombo,
86808 cycleway: uiFieldCycleway,
86809 defaultCheck: uiFieldCheck,
86810 email: uiFieldText,
86811 identifier: uiFieldText,
86812 lanes: uiFieldLanes,
86813 localized: uiFieldLocalized,
86814 maxspeed: uiFieldMaxspeed,
86815 manyCombo: uiFieldCombo,
86816 multiCombo: uiFieldCombo,
86817 networkCombo: uiFieldCombo,
86818 number: uiFieldText,
86819 onewayCheck: uiFieldCheck,
86820 radio: uiFieldRadio,
86821 restrictions: uiFieldRestrictions,
86822 semiCombo: uiFieldCombo,
86823 structureRadio: uiFieldRadio,
86826 textarea: uiFieldTextarea,
86827 typeCombo: uiFieldCombo,
86829 wikidata: uiFieldWikidata,
86830 wikipedia: uiFieldWikipedia
86833 function uiField(context, presetField, entityIDs, options) {
86834 options = Object.assign({
86841 var dispatch$1 = dispatch('change', 'revert');
86842 var field = Object.assign({}, presetField); // shallow copy
86844 field.domId = utilUniqueDomId('form-field-' + field.safeid);
86845 var _show = options.show;
86848 var _locked = false;
86850 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
86852 })).placement('bottom');
86854 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
86856 if (_show && !field.impl) {
86858 } // Creates the field.. This is done lazily,
86859 // once we know that the field will be shown.
86862 function createField() {
86863 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
86864 dispatch$1.call('change', field, t, onInput);
86868 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
86870 if (field.impl.entityIDs) {
86871 field.impl.entityIDs(entityIDs);
86876 function isModified() {
86877 if (!entityIDs || !entityIDs.length) return false;
86878 return entityIDs.some(function (entityID) {
86879 var original = context.graph().base().entities[entityID];
86880 var latest = context.graph().entity(entityID);
86881 return field.keys.some(function (key) {
86882 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
86887 function tagsContainFieldKey() {
86888 return field.keys.some(function (key) {
86889 if (field.type === 'multiCombo') {
86890 for (var tagKey in _tags) {
86891 if (tagKey.indexOf(key) === 0) {
86899 return _tags[key] !== undefined;
86903 function revert(d3_event, d) {
86904 d3_event.stopPropagation();
86905 d3_event.preventDefault();
86906 if (!entityIDs || _locked) return;
86907 dispatch$1.call('revert', d, d.keys);
86910 function remove(d3_event, d) {
86911 d3_event.stopPropagation();
86912 d3_event.preventDefault();
86913 if (_locked) return;
86915 d.keys.forEach(function (key) {
86916 t[key] = undefined;
86918 dispatch$1.call('change', d, t);
86921 field.render = function (selection) {
86922 var container = selection.selectAll('.form-field').data([field]); // Enter
86924 var enter = container.enter().append('div').attr('class', function (d) {
86925 return 'form-field form-field-' + d.safeid;
86926 }).classed('nowrap', !options.wrap);
86928 if (options.wrap) {
86929 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
86932 var textEnter = labelEnter.append('span').attr('class', 'label-text');
86933 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
86936 textEnter.append('span').attr('class', 'label-textannotation');
86938 if (options.remove) {
86939 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
86942 if (options.revert) {
86943 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
86948 container = container.merge(enter);
86949 container.select('.field-label > .remove-icon') // propagate bound data
86950 .on('click', remove);
86951 container.select('.field-label > .modified-icon') // propagate bound data
86952 .on('click', revert);
86953 container.each(function (d) {
86954 var selection = select(this);
86960 var reference, help; // instantiate field help
86962 if (options.wrap && field.type === 'restrictions') {
86963 help = uiFieldHelp(context, 'restrictions');
86964 } // instantiate tag reference
86967 if (options.wrap && options.info) {
86968 var referenceKey = d.key || '';
86970 if (d.type === 'multiCombo') {
86971 // lookup key without the trailing ':'
86972 referenceKey = referenceKey.replace(/:$/, '');
86975 reference = uiTagReference(d.reference || {
86979 if (_state === 'hover') {
86980 reference.showing(false);
86984 selection.call(d.impl); // add field help components
86987 selection.call(help.body).select('.field-label').call(help.button);
86988 } // add tag reference components
86992 selection.call(reference.body).select('.field-label').call(reference.button);
86995 d.impl.tags(_tags);
86997 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
86999 var annotation = container.selectAll('.field-label .label-textannotation');
87000 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
87001 icon.exit().remove();
87002 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
87003 container.call(_locked ? _lockedTip : _lockedTip.destroy);
87006 field.state = function (val) {
87007 if (!arguments.length) return _state;
87012 field.tags = function (val) {
87013 if (!arguments.length) return _tags;
87016 if (tagsContainFieldKey() && !_show) {
87017 // always show a field if it has a value to display
87028 field.locked = function (val) {
87029 if (!arguments.length) return _locked;
87034 field.show = function () {
87041 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
87043 t[field.key] = field["default"];
87044 dispatch$1.call('change', this, t);
87046 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
87049 field.isShown = function () {
87051 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
87052 // A non-allowed field is hidden from the user altogether
87055 field.isAllowed = function () {
87056 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
87057 if (field.geometry && !entityIDs.every(function (entityID) {
87058 return field.matchGeometry(context.graph().geometry(entityID));
87061 if (field.countryCodes || field.notCountryCodes) {
87062 var extent = combinedEntityExtent();
87063 if (!extent) return true;
87064 var center = extent.center();
87065 var countryCode = iso1A2Code(center);
87066 if (!countryCode) return false;
87067 countryCode = countryCode.toLowerCase();
87069 if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
87073 if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
87078 var prerequisiteTag = field.prerequisiteTag;
87080 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
87082 if (!entityIDs.every(function (entityID) {
87083 var entity = context.graph().entity(entityID);
87085 if (prerequisiteTag.key) {
87086 var value = entity.tags[prerequisiteTag.key];
87087 if (!value) return false;
87089 if (prerequisiteTag.valueNot) {
87090 return prerequisiteTag.valueNot !== value;
87093 if (prerequisiteTag.value) {
87094 return prerequisiteTag.value === value;
87096 } else if (prerequisiteTag.keyNot) {
87097 if (entity.tags[prerequisiteTag.keyNot]) return false;
87107 field.focus = function () {
87109 field.impl.focus();
87113 function combinedEntityExtent() {
87114 return entityIDs && entityIDs.length && entityIDs.reduce(function (extent, entityID) {
87115 var entity = context.graph().entity(entityID);
87116 return extent.extend(entity.extent(context.graph()));
87120 return utilRebind(field, dispatch$1, 'on');
87123 function uiFormFields(context) {
87124 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
87125 var _fieldsArr = [];
87126 var _lastPlaceholder = '';
87130 function formFields(selection) {
87131 var allowedFields = _fieldsArr.filter(function (field) {
87132 return field.isAllowed();
87135 var shown = allowedFields.filter(function (field) {
87136 return field.isShown();
87138 var notShown = allowedFields.filter(function (field) {
87139 return !field.isShown();
87141 var container = selection.selectAll('.form-fields-container').data([0]);
87142 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
87143 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
87144 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
87146 fields.exit().remove(); // Enter
87148 var enter = fields.enter().append('div').attr('class', function (d) {
87149 return 'wrap-form-field wrap-form-field-' + d.safeid;
87152 fields = fields.merge(enter);
87153 fields.order().each(function (d) {
87154 select(this).call(d.render);
87157 var moreFields = notShown.map(function (field) {
87158 var title = field.title();
87159 titles.push(title);
87160 var terms = field.terms();
87161 if (field.key) terms.push(field.key);
87162 if (field.keys) terms = terms.concat(field.keys);
87164 display: field.label(),
87171 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
87172 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
87173 more.exit().remove();
87174 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
87175 moreEnter.append('span').html(_t.html('inspector.add_fields'));
87176 more = moreEnter.merge(more);
87177 var input = more.selectAll('.value').data([0]);
87178 input.exit().remove();
87179 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
87180 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
87181 if (!d) return; // user entered something that was not matched
87183 var field = d.field;
87185 selection.call(formFields); // rerender
87188 })); // avoid updating placeholder excessively (triggers style recalc)
87190 if (_lastPlaceholder !== placeholder) {
87191 input.attr('placeholder', placeholder);
87192 _lastPlaceholder = placeholder;
87196 formFields.fieldsArr = function (val) {
87197 if (!arguments.length) return _fieldsArr;
87198 _fieldsArr = val || [];
87202 formFields.state = function (val) {
87203 if (!arguments.length) return _state;
87208 formFields.klass = function (val) {
87209 if (!arguments.length) return _klass;
87217 function uiSectionPresetFields(context) {
87218 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
87219 var dispatch$1 = dispatch('change', 'revert');
87220 var formFields = uiFormFields(context);
87232 function renderDisclosureContent(selection) {
87234 var graph = context.graph();
87235 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
87236 geoms[graph.entity(entityID).geometry(graph)] = true;
87239 var presetsManager = _mainPresetIndex;
87240 var allFields = [];
87241 var allMoreFields = [];
87242 var sharedTotalFields;
87244 _presets.forEach(function (preset) {
87245 var fields = preset.fields();
87246 var moreFields = preset.moreFields();
87247 allFields = utilArrayUnion(allFields, fields);
87248 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
87250 if (!sharedTotalFields) {
87251 sharedTotalFields = utilArrayUnion(fields, moreFields);
87253 sharedTotalFields = sharedTotalFields.filter(function (field) {
87254 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
87259 var sharedFields = allFields.filter(function (field) {
87260 return sharedTotalFields.indexOf(field) !== -1;
87262 var sharedMoreFields = allMoreFields.filter(function (field) {
87263 return sharedTotalFields.indexOf(field) !== -1;
87266 sharedFields.forEach(function (field) {
87267 if (field.matchAllGeometry(geometries)) {
87268 _fieldsArr.push(uiField(context, field, _entityIDs));
87271 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
87273 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
87274 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
87277 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
87278 additionalFields.sort(function (field1, field2) {
87279 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
87281 additionalFields.forEach(function (field) {
87282 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
87283 _fieldsArr.push(uiField(context, field, _entityIDs, {
87289 _fieldsArr.forEach(function (field) {
87290 field.on('change', function (t, onInput) {
87291 dispatch$1.call('change', field, _entityIDs, t, onInput);
87292 }).on('revert', function (keys) {
87293 dispatch$1.call('revert', field, keys);
87298 _fieldsArr.forEach(function (field) {
87299 field.state(_state).tags(_tags);
87302 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
87303 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
87304 // if user presses enter, and combobox is not active, accept edits..
87305 if (d3_event.keyCode === 13 && // ↩ Return
87306 context.container().select('.combobox').empty()) {
87307 context.enter(modeBrowse(context));
87312 section.presets = function (val) {
87313 if (!arguments.length) return _presets;
87315 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
87323 section.state = function (val) {
87324 if (!arguments.length) return _state;
87329 section.tags = function (val) {
87330 if (!arguments.length) return _tags;
87331 _tags = val; // Don't reset _fieldsArr here.
87336 section.entityIDs = function (val) {
87337 if (!arguments.length) return _entityIDs;
87339 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
87347 return utilRebind(section, dispatch$1, 'on');
87350 function uiSectionRawMemberEditor(context) {
87351 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
87352 if (!_entityIDs || _entityIDs.length !== 1) return false;
87353 var entity = context.hasEntity(_entityIDs[0]);
87354 return entity && entity.type === 'relation';
87355 }).label(function () {
87356 var entity = context.hasEntity(_entityIDs[0]);
87357 if (!entity) return '';
87358 var gt = entity.members.length > _maxMembers ? '>' : '';
87359 var count = gt + entity.members.slice(0, _maxMembers).length;
87360 return _t('inspector.title_count', {
87361 title: _t.html('inspector.members'),
87364 }).disclosureContent(renderDisclosureContent);
87365 var taginfo = services.taginfo;
87369 var _maxMembers = 1000;
87371 function downloadMember(d3_event, d) {
87372 d3_event.preventDefault(); // display the loading indicator
87374 select(this.parentNode).classed('tag-reference-loading', true);
87375 context.loadEntity(d.id, function () {
87376 section.reRender();
87380 function zoomToMember(d3_event, d) {
87381 d3_event.preventDefault();
87382 var entity = context.entity(d.id);
87383 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
87385 utilHighlightEntities([d.id], true, context);
87388 function selectMember(d3_event, d) {
87389 d3_event.preventDefault(); // remove the hover-highlight styling
87391 utilHighlightEntities([d.id], false, context);
87392 var entity = context.entity(d.id);
87393 var mapExtent = context.map().extent();
87395 if (!entity.intersects(mapExtent, context.graph())) {
87396 // zoom to the entity if its extent is not visible now
87397 context.map().zoomToEase(entity);
87400 context.enter(modeSelect(context, [d.id]));
87403 function changeRole(d3_event, d) {
87404 var oldRole = d.role;
87405 var newRole = context.cleanRelationRole(select(this).property('value'));
87407 if (oldRole !== newRole) {
87413 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
87416 context.validator().validate();
87420 function deleteMember(d3_event, d) {
87421 // remove the hover-highlight styling
87422 utilHighlightEntities([d.id], false, context);
87423 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
87427 if (!context.hasEntity(d.relation.id)) {
87428 // Removing the last member will also delete the relation.
87429 // If this happens we need to exit the selection mode
87430 context.enter(modeBrowse(context));
87432 // Changing the mode also runs `validate`, but otherwise we need to
87433 // rerun it manually
87434 context.validator().validate();
87438 function renderDisclosureContent(selection) {
87439 var entityID = _entityIDs[0];
87440 var memberships = [];
87441 var entity = context.entity(entityID);
87442 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
87449 member: context.hasEntity(member.id),
87450 domId: utilUniqueDomId(entityID + '-member-' + index)
87453 var list = selection.selectAll('.member-list').data([0]);
87454 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
87455 var items = list.selectAll('li').data(memberships, function (d) {
87456 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
87458 items.exit().each(unbind).remove();
87459 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
87462 itemsEnter.each(function (d) {
87463 var item = select(this);
87464 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
87467 // highlight the member feature in the map while hovering on the list item
87468 item.on('mouseover', function () {
87469 utilHighlightEntities([d.id], true, context);
87470 }).on('mouseout', function () {
87471 utilHighlightEntities([d.id], false, context);
87473 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
87474 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
87475 var matched = _mainPresetIndex.match(d.member, context.graph());
87476 return matched && matched.name() || utilDisplayType(d.member.id);
87478 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
87479 return utilDisplayName(d.member);
87481 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
87482 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
87484 var labelText = label.append('span').attr('class', 'label-text');
87485 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
87488 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
87491 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
87494 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
87495 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
87497 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
87500 wrapEnter.each(bindTypeahead);
87504 items = items.merge(itemsEnter).order();
87505 items.select('input.member-role').property('value', function (d) {
87507 }).on('blur', changeRole).on('change', changeRole);
87508 items.select('button.member-delete').on('click', deleteMember);
87509 var dragOrigin, targetIndex;
87510 items.call(d3_drag().on('start', function (d3_event) {
87515 targetIndex = null;
87516 }).on('drag', function (d3_event) {
87517 var x = d3_event.x - dragOrigin.x,
87518 y = d3_event.y - dragOrigin.y;
87519 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
87520 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
87521 var index = items.nodes().indexOf(this);
87522 select(this).classed('dragging', true);
87523 targetIndex = null;
87524 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
87525 var node = select(this).node();
87527 if (index === index2) {
87528 return 'translate(' + x + 'px, ' + y + 'px)';
87529 } else if (index2 > index && d3_event.y > node.offsetTop) {
87530 if (targetIndex === null || index2 > targetIndex) {
87531 targetIndex = index2;
87534 return 'translateY(-100%)';
87535 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
87536 if (targetIndex === null || index2 < targetIndex) {
87537 targetIndex = index2;
87540 return 'translateY(100%)';
87545 }).on('end', function (d3_event, d) {
87546 if (!select(this).classed('dragging')) return;
87547 var index = items.nodes().indexOf(this);
87548 select(this).classed('dragging', false);
87549 selection.selectAll('li.member-row').style('transform', null);
87551 if (targetIndex !== null) {
87552 // dragged to a new position, reorder
87553 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
87554 context.validator().validate();
87558 function bindTypeahead(d) {
87559 var row = select(this);
87560 var role = row.selectAll('input.member-role');
87561 var origValue = role.property('value');
87563 function sort(value, data) {
87564 var sameletter = [];
87567 for (var i = 0; i < data.length; i++) {
87568 if (data[i].value.substring(0, value.length) === value) {
87569 sameletter.push(data[i]);
87571 other.push(data[i]);
87575 return sameletter.concat(other);
87578 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
87579 // The `geometry` param is used in the `taginfo.js` interface for
87580 // filtering results, as a key into the `tag_members_fractions`
87581 // object. If we don't know the geometry because the member is
87582 // not yet downloaded, it's ok to guess based on type.
87586 geometry = context.graph().geometry(d.member.id);
87587 } else if (d.type === 'relation') {
87588 geometry = 'relation';
87589 } else if (d.type === 'way') {
87592 geometry = 'point';
87595 var rtype = entity.tags.type;
87598 rtype: rtype || '',
87599 geometry: geometry,
87601 }, function (err, data) {
87602 if (!err) callback(sort(role, data));
87604 }).on('cancel', function () {
87605 role.property('value', origValue);
87609 function unbind() {
87610 var row = select(this);
87611 row.selectAll('input.member-role').call(uiCombobox.off, context);
87615 section.entityIDs = function (val) {
87616 if (!arguments.length) return _entityIDs;
87624 function actionDeleteMembers(relationId, memberIndexes) {
87625 return function (graph) {
87626 // Remove the members in descending order so removals won't shift what members
87627 // are at the remaining indexes
87628 memberIndexes.sort(function (a, b) {
87632 for (var i in memberIndexes) {
87633 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
87640 function uiSectionRawMembershipEditor(context) {
87641 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
87642 return _entityIDs && _entityIDs.length;
87643 }).label(function () {
87644 var parents = getSharedParentRelations();
87645 var gt = parents.length > _maxMemberships ? '>' : '';
87646 var count = gt + parents.slice(0, _maxMemberships).length;
87647 return _t('inspector.title_count', {
87648 title: _t.html('inspector.relations'),
87651 }).disclosureContent(renderDisclosureContent);
87652 var taginfo = services.taginfo;
87653 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
87654 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
87655 }).itemsMouseLeave(function (d3_event, d) {
87656 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
87658 var _inChange = false;
87659 var _entityIDs = [];
87663 var _maxMemberships = 1000;
87665 function getSharedParentRelations() {
87668 for (var i = 0; i < _entityIDs.length; i++) {
87669 var entity = context.graph().hasEntity(_entityIDs[i]);
87670 if (!entity) continue;
87673 parents = context.graph().parentRelations(entity);
87675 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
87678 if (!parents.length) break;
87684 function getMemberships() {
87685 var memberships = [];
87686 var relations = getSharedParentRelations().slice(0, _maxMemberships);
87687 var isMultiselect = _entityIDs.length > 1;
87688 var i, relation, membership, index, member, indexedMember;
87690 for (i = 0; i < relations.length; i++) {
87691 relation = relations[i];
87693 relation: relation,
87695 hash: osmEntity.key(relation)
87698 for (index = 0; index < relation.members.length; index++) {
87699 member = relation.members[index];
87701 if (_entityIDs.indexOf(member.id) !== -1) {
87702 indexedMember = Object.assign({}, member, {
87705 membership.members.push(indexedMember);
87706 membership.hash += ',' + index.toString();
87708 if (!isMultiselect) {
87709 // For single selections, list one entry per membership per relation.
87710 // For multiselections, list one entry per relation.
87711 memberships.push(membership);
87713 relation: relation,
87715 hash: osmEntity.key(relation)
87721 if (membership.members.length) memberships.push(membership);
87724 memberships.forEach(function (membership) {
87725 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
87727 membership.members.forEach(function (member) {
87728 if (roles.indexOf(member.role) === -1) roles.push(member.role);
87730 membership.role = roles.length === 1 ? roles[0] : roles;
87732 return memberships;
87735 function selectRelation(d3_event, d) {
87736 d3_event.preventDefault(); // remove the hover-highlight styling
87738 utilHighlightEntities([d.relation.id], false, context);
87739 context.enter(modeSelect(context, [d.relation.id]));
87742 function zoomToRelation(d3_event, d) {
87743 d3_event.preventDefault();
87744 var entity = context.entity(d.relation.id);
87745 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
87747 utilHighlightEntities([d.relation.id], true, context);
87750 function changeRole(d3_event, d) {
87751 if (d === 0) return; // called on newrow (shouldn't happen)
87753 if (_inChange) return; // avoid accidental recursive call #5731
87755 var newRole = context.cleanRelationRole(select(this).property('value'));
87756 if (!newRole.trim() && typeof d.role !== 'string') return;
87757 var membersToUpdate = d.members.filter(function (member) {
87758 return member.role !== newRole;
87761 if (membersToUpdate.length) {
87763 context.perform(function actionChangeMemberRoles(graph) {
87764 membersToUpdate.forEach(function (member) {
87765 var newMember = Object.assign({}, member, {
87768 delete newMember.index;
87769 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
87772 }, _t('operations.change_role.annotation', {
87773 n: membersToUpdate.length
87775 context.validator().validate();
87781 function addMembership(d, role) {
87782 this.blur(); // avoid keeping focus on the button
87784 _showBlank = false;
87786 function actionAddMembers(relationId, ids, role) {
87787 return function (graph) {
87788 for (var i in ids) {
87791 type: graph.entity(ids[i]).type,
87794 graph = actionAddMember(relationId, member)(graph);
87802 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
87803 n: _entityIDs.length
87805 context.validator().validate();
87807 var relation = osmRelation();
87808 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
87810 context.enter(modeSelect(context, [relation.id]).newFeature(true));
87814 function deleteMembership(d3_event, d) {
87815 this.blur(); // avoid keeping focus on the button
87817 if (d === 0) return; // called on newrow (shouldn't happen)
87818 // remove the hover-highlight styling
87820 utilHighlightEntities([d.relation.id], false, context);
87821 var indexes = d.members.map(function (member) {
87822 return member.index;
87824 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
87825 n: _entityIDs.length
87827 context.validator().validate();
87830 function fetchNearbyRelations(q, callback) {
87831 var newRelation = {
87833 value: _t('inspector.new_relation'),
87834 display: _t.html('inspector.new_relation')
87836 var entityID = _entityIDs[0];
87838 var graph = context.graph();
87840 function baseDisplayLabel(entity) {
87841 var matched = _mainPresetIndex.match(entity, graph);
87842 var presetName = matched && matched.name() || _t('inspector.relation');
87843 var entityName = utilDisplayName(entity) || '';
87844 return presetName + ' ' + entityName;
87847 var explicitRelation = q && context.hasEntity(q.toLowerCase());
87849 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
87850 // loaded relation is specified explicitly, only show that
87852 relation: explicitRelation,
87853 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
87856 context.history().intersects(context.map().extent()).forEach(function (entity) {
87857 if (entity.type !== 'relation' || entity.id === entityID) return;
87858 var value = baseDisplayLabel(entity);
87859 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
87865 result.sort(function (a, b) {
87866 return osmRelation.creationOrder(a.relation, b.relation);
87867 }); // Dedupe identical names by appending relation id - see #2891
87869 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
87870 return v.length > 1;
87872 dupeGroups.forEach(function (group) {
87873 group.forEach(function (obj) {
87874 obj.value += ' ' + obj.relation.id;
87879 result.forEach(function (obj) {
87880 obj.title = obj.value;
87882 result.unshift(newRelation);
87886 function renderDisclosureContent(selection) {
87887 var memberships = getMemberships();
87888 var list = selection.selectAll('.member-list').data([0]);
87889 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
87890 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
87893 items.exit().each(unbind).remove(); // Enter
87895 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
87897 itemsEnter.on('mouseover', function (d3_event, d) {
87898 utilHighlightEntities([d.relation.id], true, context);
87899 }).on('mouseout', function (d3_event, d) {
87900 utilHighlightEntities([d.relation.id], false, context);
87902 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
87905 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
87906 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
87907 var matched = _mainPresetIndex.match(d.relation, context.graph());
87908 return matched && matched.name() || _t('inspector.relation');
87910 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
87911 return utilDisplayName(d.relation);
87913 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
87914 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
87915 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
87916 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
87918 }).property('type', 'text').property('value', function (d) {
87919 return typeof d.role === 'string' ? d.role : '';
87920 }).attr('title', function (d) {
87921 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
87922 }).attr('placeholder', function (d) {
87923 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
87924 }).classed('mixed', function (d) {
87925 return Array.isArray(d.role);
87926 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
87929 wrapEnter.each(bindTypeahead);
87932 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
87934 newMembership.exit().remove(); // Enter
87936 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
87937 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
87938 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
87939 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
87940 list.selectAll('.member-row-new').remove();
87942 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
87943 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
87945 newMembership = newMembership.merge(newMembershipEnter);
87946 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
87947 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
87949 var addRow = selection.selectAll('.add-row').data([0]); // enter
87951 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
87952 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
87953 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
87954 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
87955 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
87957 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
87960 addRow = addRow.merge(addRowEnter);
87961 addRow.select('.add-relation').on('click', function () {
87963 section.reRender();
87964 list.selectAll('.member-entity-input').node().focus();
87967 function acceptEntity(d) {
87971 } // remove hover-higlighting
87974 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
87975 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
87976 addMembership(d, role);
87979 function cancelEntity() {
87980 var input = newMembership.selectAll('.member-entity-input');
87981 input.property('value', ''); // remove hover-higlighting
87983 context.surface().selectAll('.highlighted').classed('highlighted', false);
87986 function bindTypeahead(d) {
87987 var row = select(this);
87988 var role = row.selectAll('input.member-role');
87989 var origValue = role.property('value');
87991 function sort(value, data) {
87992 var sameletter = [];
87995 for (var i = 0; i < data.length; i++) {
87996 if (data[i].value.substring(0, value.length) === value) {
87997 sameletter.push(data[i]);
87999 other.push(data[i]);
88003 return sameletter.concat(other);
88006 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88007 var rtype = d.relation.tags.type;
88010 rtype: rtype || '',
88011 geometry: context.graph().geometry(_entityIDs[0]),
88013 }, function (err, data) {
88014 if (!err) callback(sort(role, data));
88016 }).on('cancel', function () {
88017 role.property('value', origValue);
88021 function unbind() {
88022 var row = select(this);
88023 row.selectAll('input.member-role').call(uiCombobox.off, context);
88027 section.entityIDs = function (val) {
88028 if (!arguments.length) return _entityIDs;
88030 _showBlank = false;
88037 function uiSectionSelectionList(context) {
88038 var _selectedIDs = [];
88039 var section = uiSection('selected-features', context).shouldDisplay(function () {
88040 return _selectedIDs.length > 1;
88041 }).label(function () {
88042 return _t('inspector.title_count', {
88043 title: _t.html('inspector.features'),
88044 count: _selectedIDs.length
88046 }).disclosureContent(renderDisclosureContent);
88047 context.history().on('change.selectionList', function (difference) {
88049 section.reRender();
88053 section.entityIDs = function (val) {
88054 if (!arguments.length) return _selectedIDs;
88055 _selectedIDs = val;
88059 function selectEntity(d3_event, entity) {
88060 context.enter(modeSelect(context, [entity.id]));
88063 function deselectEntity(d3_event, entity) {
88064 var selectedIDs = _selectedIDs.slice();
88066 var index = selectedIDs.indexOf(entity.id);
88069 selectedIDs.splice(index, 1);
88070 context.enter(modeSelect(context, selectedIDs));
88074 function renderDisclosureContent(selection) {
88075 var list = selection.selectAll('.feature-list').data([0]);
88076 list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
88078 var entities = _selectedIDs.map(function (id) {
88079 return context.hasEntity(id);
88080 }).filter(Boolean);
88082 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
88083 items.exit().remove(); // Enter
88085 var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
88086 select(this).on('mouseover', function () {
88087 utilHighlightEntities([d.id], true, context);
88088 }).on('mouseout', function () {
88089 utilHighlightEntities([d.id], false, context);
88092 var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
88093 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
88094 label.append('span').attr('class', 'entity-type');
88095 label.append('span').attr('class', 'entity-name');
88096 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
88098 items = items.merge(enter);
88099 items.selectAll('.entity-geom-icon use').attr('href', function () {
88100 var entity = this.parentNode.parentNode.__data__;
88101 return '#iD-icon-' + entity.geometry(context.graph());
88103 items.selectAll('.entity-type').html(function (entity) {
88104 return _mainPresetIndex.match(entity, context.graph()).name();
88106 items.selectAll('.entity-name').html(function (d) {
88107 // fetch latest entity
88108 var entity = context.entity(d.id);
88109 return utilDisplayName(entity);
88116 function uiEntityEditor(context) {
88117 var dispatch$1 = dispatch('choose');
88118 var _state = 'select';
88119 var _coalesceChanges = false;
88120 var _modified = false;
88126 var _activePresets = [];
88132 function entityEditor(selection) {
88133 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
88135 var header = selection.selectAll('.header').data([0]); // Enter
88137 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
88138 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
88139 headerEnter.append('button').attr('class', 'close').on('click', function () {
88140 context.enter(modeBrowse(context));
88141 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
88142 headerEnter.append('h3'); // Update
88144 header = header.merge(headerEnter);
88145 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
88146 header.selectAll('.preset-reset').on('click', function () {
88147 dispatch$1.call('choose', this, _activePresets);
88150 var body = selection.selectAll('.inspector-body').data([0]); // Enter
88152 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
88154 body = body.merge(bodyEnter);
88157 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
88158 dispatch$1.call('choose', this, presets);
88159 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
88162 _sections.forEach(function (section) {
88163 if (section.entityIDs) {
88164 section.entityIDs(_entityIDs);
88167 if (section.presets) {
88168 section.presets(_activePresets);
88171 if (section.tags) {
88172 section.tags(combinedTags);
88175 if (section.state) {
88176 section.state(_state);
88179 body.call(section.render);
88182 context.history().on('change.entity-editor', historyChanged);
88184 function historyChanged(difference) {
88185 if (selection.selectAll('.entity-editor').empty()) return;
88186 if (_state === 'hide') return;
88187 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
88188 if (!significant) return;
88189 _entityIDs = _entityIDs.filter(context.hasEntity);
88190 if (!_entityIDs.length) return;
88191 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
88192 loadActivePresets();
88193 var graph = context.graph();
88194 entityEditor.modified(_base !== graph);
88195 entityEditor(selection);
88197 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
88198 // flash the button to indicate the preset changed
88199 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
88202 } // Tag changes that fire on input can all get coalesced into a single
88203 // history operation when the user leaves the field. #2342
88204 // Use explicit entityIDs in case the selection changes before the event is fired.
88207 function changeTags(entityIDs, changed, onInput) {
88210 for (var i in entityIDs) {
88211 var entityID = entityIDs[i];
88212 var entity = context.entity(entityID);
88213 var tags = Object.assign({}, entity.tags); // shallow copy
88215 for (var k in changed) {
88217 var v = changed[k];
88219 if (v !== undefined || tags.hasOwnProperty(k)) {
88225 tags = utilCleanTags(tags);
88228 if (!fastDeepEqual(entity.tags, tags)) {
88229 actions.push(actionChangeTags(entityID, tags));
88233 if (actions.length) {
88234 var combinedAction = function combinedAction(graph) {
88235 actions.forEach(function (action) {
88236 graph = action(graph);
88241 var annotation = _t('operations.change_tags.annotation');
88243 if (_coalesceChanges) {
88244 context.overwrite(combinedAction, annotation);
88246 context.perform(combinedAction, annotation);
88247 _coalesceChanges = !!onInput;
88249 } // if leaving field (blur event), rerun validation
88253 context.validator().validate();
88257 function revertTags(keys) {
88260 for (var i in _entityIDs) {
88261 var entityID = _entityIDs[i];
88262 var original = context.graph().base().entities[entityID];
88265 for (var j in keys) {
88267 changed[key] = original ? original.tags[key] : undefined;
88270 var entity = context.entity(entityID);
88271 var tags = Object.assign({}, entity.tags); // shallow copy
88273 for (var k in changed) {
88275 var v = changed[k];
88277 if (v !== undefined || tags.hasOwnProperty(k)) {
88282 tags = utilCleanTags(tags);
88284 if (!fastDeepEqual(entity.tags, tags)) {
88285 actions.push(actionChangeTags(entityID, tags));
88289 if (actions.length) {
88290 var combinedAction = function combinedAction(graph) {
88291 actions.forEach(function (action) {
88292 graph = action(graph);
88297 var annotation = _t('operations.change_tags.annotation');
88299 if (_coalesceChanges) {
88300 context.overwrite(combinedAction, annotation);
88302 context.perform(combinedAction, annotation);
88303 _coalesceChanges = false;
88307 context.validator().validate();
88310 entityEditor.modified = function (val) {
88311 if (!arguments.length) return _modified;
88313 return entityEditor;
88316 entityEditor.state = function (val) {
88317 if (!arguments.length) return _state;
88319 return entityEditor;
88322 entityEditor.entityIDs = function (val) {
88323 if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
88324 // could be reselecting after something like dragging a node
88326 _base = context.graph();
88327 _coalesceChanges = false;
88328 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
88331 loadActivePresets(true);
88332 return entityEditor.modified(false);
88335 entityEditor.newFeature = function (val) {
88336 if (!arguments.length) return _newFeature;
88338 return entityEditor;
88341 function loadActivePresets(isForNewSelection) {
88342 var graph = context.graph();
88345 for (var i in _entityIDs) {
88346 var entity = graph.hasEntity(_entityIDs[i]);
88347 if (!entity) return;
88348 var match = _mainPresetIndex.match(entity, graph);
88349 if (!counts[match.id]) counts[match.id] = 0;
88350 counts[match.id] += 1;
88353 var matches = Object.keys(counts).sort(function (p1, p2) {
88354 return counts[p2] - counts[p1];
88355 }).map(function (pID) {
88356 return _mainPresetIndex.item(pID);
88359 if (!isForNewSelection) {
88360 // A "weak" preset doesn't set any tags. (e.g. "Address")
88361 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")
88363 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
88366 entityEditor.presets(matches);
88369 entityEditor.presets = function (val) {
88370 if (!arguments.length) return _activePresets; // don't reload the same preset
88372 if (!utilArrayIdentical(val, _activePresets)) {
88373 _activePresets = val;
88376 return entityEditor;
88379 return utilRebind(entityEditor, dispatch$1, 'on');
88382 function uiPresetList(context) {
88383 var dispatch$1 = dispatch('cancel', 'choose');
88387 var _currentPresets;
88389 var _autofocus = false;
88391 function presetList(selection) {
88392 if (!_entityIDs) return;
88393 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
88394 selection.html('');
88395 var messagewrap = selection.append('div').attr('class', 'header fillL');
88396 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
88397 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
88398 dispatch$1.call('cancel', this);
88399 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
88401 function initialKeydown(d3_event) {
88402 // hack to let delete shortcut work when search is autofocused
88403 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
88404 d3_event.preventDefault();
88405 d3_event.stopPropagation();
88406 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
88407 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
88408 d3_event.preventDefault();
88409 d3_event.stopPropagation();
88411 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
88412 // don't check for delete/undo hack on future keydown events
88413 select(this).on('keydown', keydown);
88414 keydown.call(this, d3_event);
88418 function keydown(d3_event) {
88420 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
88421 search.node().selectionStart === search.property('value').length) {
88422 d3_event.preventDefault();
88423 d3_event.stopPropagation(); // move focus to the first item in the preset list
88425 var buttons = list.selectAll('.preset-list-button');
88426 if (!buttons.empty()) buttons.nodes()[0].focus();
88430 function keypress(d3_event) {
88432 var value = search.property('value');
88434 if (d3_event.keyCode === 13 && // ↩ Return
88436 list.selectAll('.preset-list-item:first-child').each(function (d) {
88437 d.choose.call(this);
88442 function inputevent() {
88443 var value = search.property('value');
88444 list.classed('filtered', value.length);
88445 var extent = combinedEntityExtent();
88446 var results, messageText;
88448 if (value.length && extent) {
88449 var center = extent.center();
88450 var countryCode = iso1A2Code(center);
88451 results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
88452 messageText = _t('inspector.results', {
88453 n: results.collection.length,
88457 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
88458 messageText = _t('inspector.choose');
88461 list.call(drawList, results);
88462 message.html(messageText);
88465 var searchWrap = selection.append('div').attr('class', 'search-header');
88466 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
88467 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);
88470 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
88471 // so try again on the next pass
88473 setTimeout(function () {
88474 search.node().focus();
88478 var listWrap = selection.append('div').attr('class', 'inspector-body');
88479 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
88480 context.features().on('change.preset-list', updateForFeatureHiddenState);
88483 function drawList(list, presets) {
88484 presets = presets.matchAllGeometry(entityGeometries());
88485 var collection = presets.collection.reduce(function (collection, preset) {
88486 if (!preset) return collection;
88488 if (preset.members) {
88489 if (preset.members.collection.filter(function (preset) {
88490 return preset.addable();
88492 collection.push(CategoryItem(preset));
88494 } else if (preset.addable()) {
88495 collection.push(PresetItem(preset));
88500 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
88501 return d.preset.id;
88504 items.exit().remove();
88505 items.enter().append('div').attr('class', function (item) {
88506 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
88507 }).classed('current', function (item) {
88508 return _currentPresets.indexOf(item.preset) !== -1;
88509 }).each(function (item) {
88510 select(this).call(item);
88511 }).style('opacity', 0).transition().style('opacity', 1);
88512 updateForFeatureHiddenState();
88515 function itemKeydown(d3_event) {
88516 // the actively focused item
88517 var item = select(this.closest('.preset-list-item'));
88518 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
88520 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
88521 d3_event.preventDefault();
88522 d3_event.stopPropagation(); // the next item in the list at the same level
88524 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
88526 if (nextItem.empty()) {
88527 // if there is a parent item
88528 if (!parentItem.empty()) {
88529 // the item is the last item of a sublist,
88530 // select the next item at the parent level
88531 nextItem = select(parentItem.node().nextElementSibling);
88532 } // if the focused item is expanded
88534 } else if (select(this).classed('expanded')) {
88535 // select the first subitem instead
88536 nextItem = item.select('.subgrid .preset-list-item:first-child');
88539 if (!nextItem.empty()) {
88540 // focus on the next item
88541 nextItem.select('.preset-list-button').node().focus();
88542 } // arrow up, move focus to the previous, higher item
88544 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
88545 d3_event.preventDefault();
88546 d3_event.stopPropagation(); // the previous item in the list at the same level
88548 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
88550 if (previousItem.empty()) {
88551 // if there is a parent item
88552 if (!parentItem.empty()) {
88553 // the item is the first subitem of a sublist select the parent item
88554 previousItem = parentItem;
88555 } // if the previous item is expanded
88557 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
88558 // select the last subitem of the sublist of the previous item
88559 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
88562 if (!previousItem.empty()) {
88563 // focus on the previous item
88564 previousItem.select('.preset-list-button').node().focus();
88566 // the focus is at the top of the list, move focus back to the search field
88567 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
88568 search.node().focus();
88569 } // arrow left, move focus to the parent item if there is one
88571 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
88572 d3_event.preventDefault();
88573 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
88575 if (!parentItem.empty()) {
88576 parentItem.select('.preset-list-button').node().focus();
88577 } // arrow right, choose this item
88579 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
88580 d3_event.preventDefault();
88581 d3_event.stopPropagation();
88582 item.datum().choose.call(select(this).node());
88586 function CategoryItem(preset) {
88591 function item(selection) {
88592 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
88595 var isExpanded = select(this).classed('expanded');
88596 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
88597 select(this).classed('expanded', !isExpanded);
88598 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
88602 var geometries = entityGeometries();
88603 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) {
88604 // right arrow, expand the focused item
88605 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
88606 d3_event.preventDefault();
88607 d3_event.stopPropagation(); // if the item isn't expanded
88609 if (!select(this).classed('expanded')) {
88610 // toggle expansion (expand the item)
88611 click.call(this, d3_event);
88612 } // left arrow, collapse the focused item
88614 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
88615 d3_event.preventDefault();
88616 d3_event.stopPropagation(); // if the item is expanded
88618 if (select(this).classed('expanded')) {
88619 // toggle expansion (collapse the item)
88620 click.call(this, d3_event);
88623 itemKeydown.call(this, d3_event);
88626 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
88627 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
88628 return preset.nameLabel() + '…';
88630 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
88631 box.append('div').attr('class', 'arrow');
88632 sublist = box.append('div').attr('class', 'preset-list fillL3');
88635 item.choose = function () {
88636 if (!box || !sublist) return;
88640 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
88643 var members = preset.members.matchAllGeometry(entityGeometries());
88644 sublist.call(drawList, members);
88645 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
88649 item.preset = preset;
88653 function PresetItem(preset) {
88654 function item(selection) {
88655 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
88656 var geometries = entityGeometries();
88657 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);
88658 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
88659 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
88660 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
88663 wrap.call(item.reference.button);
88664 selection.call(item.reference.body);
88667 item.choose = function () {
88668 if (select(this).classed('disabled')) return;
88670 if (!context.inIntro()) {
88671 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
88674 context.perform(function (graph) {
88675 for (var i in _entityIDs) {
88676 var entityID = _entityIDs[i];
88677 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
88678 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
88682 }, _t('operations.change_tags.annotation'));
88683 context.validator().validate(); // rerun validation
88685 dispatch$1.call('choose', this, preset);
88688 item.help = function (d3_event) {
88689 d3_event.stopPropagation();
88690 item.reference.toggle();
88693 item.preset = preset;
88694 item.reference = uiTagReference(preset.reference());
88698 function updateForFeatureHiddenState() {
88699 if (!_entityIDs.every(context.hasEntity)) return;
88700 var geometries = entityGeometries();
88701 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
88703 button.call(uiTooltip().destroyAny);
88704 button.each(function (item, index) {
88705 var hiddenPresetFeaturesId;
88707 for (var i in geometries) {
88708 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
88709 if (hiddenPresetFeaturesId) break;
88712 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
88713 select(this).classed('disabled', isHiddenPreset);
88715 if (isHiddenPreset) {
88716 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
88717 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
88718 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
88719 })).placement(index < 2 ? 'bottom' : 'top'));
88724 presetList.autofocus = function (val) {
88725 if (!arguments.length) return _autofocus;
88730 presetList.entityIDs = function (val) {
88731 if (!arguments.length) return _entityIDs;
88734 if (_entityIDs && _entityIDs.length) {
88735 var presets = _entityIDs.map(function (entityID) {
88736 return _mainPresetIndex.match(context.entity(entityID), context.graph());
88739 presetList.presets(presets);
88745 presetList.presets = function (val) {
88746 if (!arguments.length) return _currentPresets;
88747 _currentPresets = val;
88751 function entityGeometries() {
88754 for (var i in _entityIDs) {
88755 var entityID = _entityIDs[i];
88756 var entity = context.entity(entityID);
88757 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
88759 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
88760 geometry = 'point';
88763 if (!counts[geometry]) counts[geometry] = 0;
88764 counts[geometry] += 1;
88767 return Object.keys(counts).sort(function (geom1, geom2) {
88768 return counts[geom2] - counts[geom1];
88772 function combinedEntityExtent() {
88773 return _entityIDs.reduce(function (extent, entityID) {
88774 var entity = context.graph().entity(entityID);
88775 return extent.extend(entity.extent(context.graph()));
88779 return utilRebind(presetList, dispatch$1, 'on');
88782 function uiInspector(context) {
88783 var presetList = uiPresetList(context);
88784 var entityEditor = uiEntityEditor(context);
88785 var wrap = select(null),
88786 presetPane = select(null),
88787 editorPane = select(null);
88788 var _state = 'select';
88792 var _newFeature = false;
88794 function inspector(selection) {
88795 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
88796 inspector.setPreset();
88798 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
88799 wrap = selection.selectAll('.panewrap').data([0]);
88800 var enter = wrap.enter().append('div').attr('class', 'panewrap');
88801 enter.append('div').attr('class', 'preset-list-pane pane');
88802 enter.append('div').attr('class', 'entity-editor-pane pane');
88803 wrap = wrap.merge(enter);
88804 presetPane = wrap.selectAll('.preset-list-pane');
88805 editorPane = wrap.selectAll('.entity-editor-pane');
88807 function shouldDefaultToPresetList() {
88808 // always show the inspector on hover
88809 if (_state !== 'select') return false; // can only change preset on single selection
88811 if (_entityIDs.length !== 1) return false;
88812 var entityID = _entityIDs[0];
88813 var entity = context.hasEntity(entityID);
88814 if (!entity) return false; // default to inspector if there are already tags
88816 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
88818 if (_newFeature) return true; // all existing features except vertices should default to inspector
88820 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
88822 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
88824 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
88826 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
88831 if (shouldDefaultToPresetList()) {
88832 wrap.style('right', '-100%');
88833 editorPane.classed('hide', true);
88834 presetPane.classed('hide', false).call(presetList);
88836 wrap.style('right', '0%');
88837 presetPane.classed('hide', true);
88838 editorPane.classed('hide', false).call(entityEditor);
88841 var footer = selection.selectAll('.footer').data([0]);
88842 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
88843 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
88846 inspector.showList = function (presets) {
88847 presetPane.classed('hide', false);
88848 wrap.transition().styleTween('right', function () {
88849 return interpolate('0%', '-100%');
88850 }).on('end', function () {
88851 editorPane.classed('hide', true);
88855 presetList.presets(presets);
88858 presetPane.call(presetList.autofocus(true));
88861 inspector.setPreset = function (preset) {
88862 // upon setting multipolygon, go to the area preset list instead of the editor
88863 if (preset && preset.id === 'type/multipolygon') {
88864 presetPane.call(presetList.autofocus(true));
88866 editorPane.classed('hide', false);
88867 wrap.transition().styleTween('right', function () {
88868 return interpolate('-100%', '0%');
88869 }).on('end', function () {
88870 presetPane.classed('hide', true);
88874 entityEditor.presets([preset]);
88877 editorPane.call(entityEditor);
88881 inspector.state = function (val) {
88882 if (!arguments.length) return _state;
88884 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
88886 context.container().selectAll('.field-help-body').remove();
88890 inspector.entityIDs = function (val) {
88891 if (!arguments.length) return _entityIDs;
88896 inspector.newFeature = function (val) {
88897 if (!arguments.length) return _newFeature;
88905 function uiSidebar(context) {
88906 var inspector = uiInspector(context);
88907 var dataEditor = uiDataEditor(context);
88908 var noteEditor = uiNoteEditor(context);
88909 var improveOsmEditor = uiImproveOsmEditor(context);
88910 var keepRightEditor = uiKeepRightEditor(context);
88911 var osmoseEditor = uiOsmoseEditor(context);
88915 var _wasData = false;
88916 var _wasNote = false;
88917 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
88919 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
88921 function sidebar(selection) {
88922 var container = context.container();
88923 var minWidth = 240;
88925 var containerWidth;
88926 var dragOffset; // Set the initial width constraints
88928 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
88929 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
88930 var downPointerId, lastClientX, containerLocGetter;
88932 function pointerdown(d3_event) {
88933 if (downPointerId) return;
88934 if ('button' in d3_event && d3_event.button !== 0) return;
88935 downPointerId = d3_event.pointerId || 'mouse';
88936 lastClientX = d3_event.clientX;
88937 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
88939 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
88940 sidebarWidth = selection.node().getBoundingClientRect().width;
88941 containerWidth = container.node().getBoundingClientRect().width;
88942 var widthPct = sidebarWidth / containerWidth * 100;
88943 selection.style('width', widthPct + '%') // lock in current width
88944 .style('max-width', '85%'); // but allow larger widths
88946 resizer.classed('dragging', true);
88947 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
88948 // disable page scrolling while resizing on touch input
88949 d3_event.preventDefault();
88952 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
88955 function pointermove(d3_event) {
88956 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
88957 d3_event.preventDefault();
88958 var dx = d3_event.clientX - lastClientX;
88959 lastClientX = d3_event.clientX;
88960 var isRTL = _mainLocalizer.textDirection() === 'rtl';
88961 var scaleX = isRTL ? 0 : 1;
88962 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
88963 var x = containerLocGetter(d3_event)[0] - dragOffset;
88964 sidebarWidth = isRTL ? containerWidth - x : x;
88965 var isCollapsed = selection.classed('collapsed');
88966 var shouldCollapse = sidebarWidth < minWidth;
88967 selection.classed('collapsed', shouldCollapse);
88969 if (shouldCollapse) {
88970 if (!isCollapsed) {
88971 selection.style(xMarginProperty, '-400px').style('width', '400px');
88972 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
88975 var widthPct = sidebarWidth / containerWidth * 100;
88976 selection.style(xMarginProperty, null).style('width', widthPct + '%');
88979 context.ui().onResize([-sidebarWidth * scaleX, 0]);
88981 context.ui().onResize([-dx * scaleX, 0]);
88986 function pointerup(d3_event) {
88987 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
88988 downPointerId = null;
88989 resizer.classed('dragging', false);
88990 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
88993 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
88994 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
88996 var hoverModeSelect = function hoverModeSelect(targets) {
88997 context.container().selectAll('.feature-list-item button').classed('hover', false);
88999 if (context.selectedIDs().length > 1 && targets && targets.length) {
89000 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
89001 return targets.indexOf(node) !== -1;
89004 if (!elements.empty()) {
89005 elements.classed('hover', true);
89010 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
89012 function hover(targets) {
89013 var datum = targets && targets.length && targets[0];
89015 if (datum && datum.__featurehash__) {
89016 // hovering on data
89018 sidebar.show(dataEditor.datum(datum));
89019 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89020 } else if (datum instanceof osmNote) {
89021 if (context.mode().id === 'drag-note') return;
89023 var osm = services.osm;
89026 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
89029 sidebar.show(noteEditor.note(datum));
89030 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89031 } else if (datum instanceof QAItem) {
89033 var errService = services[datum.service];
89036 // marker may contain stale data - get latest
89037 datum = errService.getError(datum.id);
89038 } // Currently only three possible services
89043 if (datum.service === 'keepRight') {
89044 errEditor = keepRightEditor;
89045 } else if (datum.service === 'osmose') {
89046 errEditor = osmoseEditor;
89048 errEditor = improveOsmEditor;
89051 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
89052 return d.id === datum.id;
89054 sidebar.show(errEditor.error(datum));
89055 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89056 } else if (!_current && datum instanceof osmEntity) {
89057 featureListWrap.classed('inspector-hidden', true);
89058 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
89060 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
89061 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
89062 inspectorWrap.call(inspector);
89064 } else if (!_current) {
89065 featureListWrap.classed('inspector-hidden', false);
89066 inspectorWrap.classed('inspector-hidden', true);
89067 inspector.state('hide');
89068 } else if (_wasData || _wasNote || _wasQaItem) {
89071 _wasQaItem = false;
89072 context.container().selectAll('.note').classed('hover', false);
89073 context.container().selectAll('.qaItem').classed('hover', false);
89078 sidebar.hover = throttle(hover, 200);
89080 sidebar.intersects = function (extent) {
89081 var rect = selection.node().getBoundingClientRect();
89082 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
89085 sidebar.select = function (ids, newFeature) {
89088 if (ids && ids.length) {
89089 var entity = ids.length === 1 && context.entity(ids[0]);
89091 if (entity && newFeature && selection.classed('collapsed')) {
89092 // uncollapse the sidebar
89093 var extent = entity.extent(context.graph());
89094 sidebar.expand(sidebar.intersects(extent));
89097 featureListWrap.classed('inspector-hidden', true);
89098 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
89099 // themselves may have changed
89101 inspector.state('select').entityIDs(ids).newFeature(newFeature);
89102 inspectorWrap.call(inspector);
89104 inspector.state('hide');
89108 sidebar.showPresetList = function () {
89109 inspector.showList();
89112 sidebar.show = function (component, element) {
89113 featureListWrap.classed('inspector-hidden', true);
89114 inspectorWrap.classed('inspector-hidden', true);
89115 if (_current) _current.remove();
89116 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
89119 sidebar.hide = function () {
89120 featureListWrap.classed('inspector-hidden', false);
89121 inspectorWrap.classed('inspector-hidden', true);
89122 if (_current) _current.remove();
89126 sidebar.expand = function (moveMap) {
89127 if (selection.classed('collapsed')) {
89128 sidebar.toggle(moveMap);
89132 sidebar.collapse = function (moveMap) {
89133 if (!selection.classed('collapsed')) {
89134 sidebar.toggle(moveMap);
89138 sidebar.toggle = function (moveMap) {
89139 // Don't allow sidebar to toggle when the user is in the walkthrough.
89140 if (context.inIntro()) return;
89141 var isCollapsed = selection.classed('collapsed');
89142 var isCollapsing = !isCollapsed;
89143 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89144 var scaleX = isRTL ? 0 : 1;
89145 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89146 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
89148 selection.style('width', sidebarWidth + 'px');
89149 var startMargin, endMargin, lastMargin;
89151 if (isCollapsing) {
89152 startMargin = lastMargin = 0;
89153 endMargin = -sidebarWidth;
89155 startMargin = lastMargin = -sidebarWidth;
89159 if (!isCollapsing) {
89160 // unhide the sidebar's content before it transitions onscreen
89161 selection.classed('collapsed', isCollapsing);
89164 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
89165 var i = d3_interpolateNumber(startMargin, endMargin);
89166 return function (t) {
89167 var dx = lastMargin - Math.round(i(t));
89168 lastMargin = lastMargin - dx;
89169 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
89171 }).on('end', function () {
89172 if (isCollapsing) {
89173 // hide the sidebar's content after it transitions offscreen
89174 selection.classed('collapsed', isCollapsing);
89175 } // switch back from px to %
89178 if (!isCollapsing) {
89179 var containerWidth = container.node().getBoundingClientRect().width;
89180 var widthPct = sidebarWidth / containerWidth * 100;
89181 selection.style(xMarginProperty, null).style('width', widthPct + '%');
89184 }; // toggle the sidebar collapse when double-clicking the resizer
89187 resizer.on('dblclick', function (d3_event) {
89188 d3_event.preventDefault();
89190 if (d3_event.sourceEvent) {
89191 d3_event.sourceEvent.preventDefault();
89195 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
89197 context.map().on('crossEditableZoom.sidebar', function (within) {
89198 if (!within && !selection.select('.inspector-hover').empty()) {
89204 sidebar.showPresetList = function () {};
89206 sidebar.hover = function () {};
89208 sidebar.hover.cancel = function () {};
89210 sidebar.intersects = function () {};
89212 sidebar.select = function () {};
89214 sidebar.show = function () {};
89216 sidebar.hide = function () {};
89218 sidebar.expand = function () {};
89220 sidebar.collapse = function () {};
89222 sidebar.toggle = function () {};
89227 function uiSourceSwitch(context) {
89230 function click(d3_event) {
89231 d3_event.preventDefault();
89232 var osm = context.connection();
89234 if (context.inIntro()) return;
89235 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
89236 var isLive = select(this).classed('live');
89238 context.enter(modeBrowse(context));
89239 context.history().clearSaved(); // remove saved history
89241 context.flush(); // remove stored data
89243 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
89244 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
89247 var sourceSwitch = function sourceSwitch(selection) {
89248 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
89251 sourceSwitch.keys = function (_) {
89252 if (!arguments.length) return keys;
89254 return sourceSwitch;
89257 return sourceSwitch;
89260 function uiSpinner(context) {
89261 var osm = context.connection();
89262 return function (selection) {
89263 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
89266 osm.on('loading.spinner', function () {
89267 img.transition().style('opacity', 1);
89268 }).on('loaded.spinner', function () {
89269 img.transition().style('opacity', 0);
89275 function uiSplash(context) {
89276 return function (selection) {
89277 // Exception - if there are restorable changes, skip this splash screen.
89278 // This is because we currently only support one `uiModal` at a time
89279 // and we need to show them `uiRestore`` instead of this one.
89280 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
89282 var updateMessage = '';
89283 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
89284 var showSplash = !corePreferences('sawSplash');
89286 if (sawPrivacyVersion !== context.privacyVersion) {
89287 updateMessage = _t('splash.privacy_update');
89291 if (!showSplash) return;
89292 corePreferences('sawSplash', true);
89293 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
89295 _mainFileFetcher.get('intro_graph');
89296 var modalSelection = uiModal(selection);
89297 modalSelection.select('.modal').attr('class', 'modal-splash modal');
89298 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
89299 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
89300 var modalSection = introModal.append('div').attr('class', 'modal-section');
89301 modalSection.append('p').html(_t.html('splash.text', {
89302 version: context.version,
89303 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
89304 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
89306 modalSection.append('p').html(_t.html('splash.privacy', {
89307 updateMessage: updateMessage,
89308 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
89310 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
89311 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
89312 context.container().call(uiIntro(context));
89313 modalSelection.close();
89315 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
89316 walkthrough.append('div').html(_t.html('splash.walkthrough'));
89317 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
89318 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
89319 startEditing.append('div').html(_t.html('splash.start'));
89320 modalSelection.select('button.close').attr('class', 'hide');
89324 function uiStatus(context) {
89325 var osm = context.connection();
89326 return function (selection) {
89329 function update(err, apiStatus) {
89330 selection.html('');
89333 if (apiStatus === 'connectionSwitched') {
89334 // if the connection was just switched, we can't rely on
89335 // the status (we're getting the status of the previous api)
89337 } else if (apiStatus === 'rateLimited') {
89338 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) {
89339 d3_event.preventDefault();
89340 osm.authenticate();
89343 // don't allow retrying too rapidly
89344 var throttledRetry = throttle(function () {
89345 // try loading the visible tiles
89346 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
89348 osm.reloadApiStatus();
89349 }, 2000); // eslint-disable-next-line no-warning-comments
89350 // TODO: nice messages for different error types
89353 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
89354 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
89355 d3_event.preventDefault();
89359 } else if (apiStatus === 'readonly') {
89360 selection.html(_t.html('osm_api_status.message.readonly'));
89361 } else if (apiStatus === 'offline') {
89362 selection.html(_t.html('osm_api_status.message.offline'));
89365 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
89368 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
89370 window.setInterval(function () {
89371 osm.reloadApiStatus();
89372 }, 90000); // load the initial status in case no OSM data was loaded yet
89374 osm.reloadApiStatus();
89378 function modeDrawArea(context, wayID, startGraph, button) {
89383 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
89384 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
89386 mode.wayID = wayID;
89388 mode.enter = function () {
89389 context.install(behavior);
89392 mode.exit = function () {
89393 context.uninstall(behavior);
89396 mode.selectedIDs = function () {
89400 mode.activeID = function () {
89401 return behavior && behavior.activeID() || [];
89407 function modeAddArea(context, mode) {
89408 mode.id = 'add-area';
89409 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
89410 var defaultTags = {
89413 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
89415 function actionClose(wayId) {
89416 return function (graph) {
89417 return graph.replace(graph.entity(wayId).close());
89421 function start(loc) {
89422 var startGraph = context.graph();
89423 var node = osmNode({
89429 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
89430 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
89433 function startFromWay(loc, edge) {
89434 var startGraph = context.graph();
89435 var node = osmNode({
89441 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
89445 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
89448 function startFromNode(node) {
89449 var startGraph = context.graph();
89453 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
89454 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
89457 mode.enter = function () {
89458 context.install(behavior);
89461 mode.exit = function () {
89462 context.uninstall(behavior);
89468 function modeAddLine(context, mode) {
89469 mode.id = 'add-line';
89470 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
89471 var defaultTags = {};
89472 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
89474 function start(loc) {
89475 var startGraph = context.graph();
89476 var node = osmNode({
89482 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
89483 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
89486 function startFromWay(loc, edge) {
89487 var startGraph = context.graph();
89488 var node = osmNode({
89494 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
89498 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
89501 function startFromNode(node) {
89502 var startGraph = context.graph();
89506 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
89507 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
89510 mode.enter = function () {
89511 context.install(behavior);
89514 mode.exit = function () {
89515 context.uninstall(behavior);
89521 function modeAddPoint(context, mode) {
89522 mode.id = 'add-point';
89523 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
89524 var defaultTags = {};
89525 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
89527 function add(loc) {
89528 var node = osmNode({
89532 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
89533 enterSelectMode(node);
89536 function addWay(loc, edge) {
89537 var node = osmNode({
89540 context.perform(actionAddMidpoint({
89543 }, node), _t('operations.add.annotation.vertex'));
89544 enterSelectMode(node);
89547 function enterSelectMode(node) {
89548 context.enter(modeSelect(context, [node.id]).newFeature(true));
89551 function addNode(node) {
89552 if (Object.keys(defaultTags).length === 0) {
89553 enterSelectMode(node);
89557 var tags = Object.assign({}, node.tags); // shallow copy
89559 for (var key in defaultTags) {
89560 tags[key] = defaultTags[key];
89563 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
89564 enterSelectMode(node);
89567 function cancel() {
89568 context.enter(modeBrowse(context));
89571 mode.enter = function () {
89572 context.install(behavior);
89575 mode.exit = function () {
89576 context.uninstall(behavior);
89582 function modeAddNote(context) {
89586 description: _t.html('modes.add_note.description'),
89587 key: _t('modes.add_note.key')
89589 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
89591 function add(loc) {
89592 var osm = services.osm;
89594 var note = osmNote({
89599 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
89601 context.map().pan([0, 0]);
89602 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
89605 function cancel() {
89606 context.enter(modeBrowse(context));
89609 mode.enter = function () {
89610 context.install(behavior);
89613 mode.exit = function () {
89614 context.uninstall(behavior);
89620 function uiConflicts(context) {
89621 var dispatch$1 = dispatch('cancel', 'save');
89622 var keybinding = utilKeybinding('conflicts');
89628 var _shownConflictIndex;
89630 function keybindingOn() {
89631 select(document).call(keybinding.on('⎋', cancel, true));
89634 function keybindingOff() {
89635 select(document).call(keybinding.unbind);
89638 function tryAgain() {
89640 dispatch$1.call('save');
89643 function cancel() {
89645 dispatch$1.call('cancel');
89648 function conflicts(selection) {
89650 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
89651 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
89652 headerEnter.append('h3').html(_t.html('save.conflict.header'));
89653 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
89654 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
89656 var detected = utilDetect();
89657 var changeset = new osmChangeset();
89658 delete changeset.id; // Export without changeset_id
89660 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
89661 var blob = new Blob([data], {
89662 type: 'text/xml;charset=utf-8;'
89664 var fileName = 'changes.osc';
89665 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
89667 if (detected.download) {
89668 // All except IE11 and Edge
89669 linkEnter // download the data as a file
89670 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
89673 linkEnter // open data uri in a new tab
89674 .attr('target', '_blank').on('click.download', function () {
89675 navigator.msSaveBlob(blob, fileName);
89679 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
89680 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
89681 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
89682 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
89683 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
89684 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
89687 function showConflict(selection, index) {
89688 index = utilWrap(index, _conflictList.length);
89689 _shownConflictIndex = index;
89690 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
89692 if (index === _conflictList.length - 1) {
89693 window.setTimeout(function () {
89694 parent.select('.conflicts-button').attr('disabled', null);
89695 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
89699 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
89700 conflict.exit().remove();
89701 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
89702 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
89704 total: _conflictList.length
89706 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
89708 }).on('click', function (d3_event, d) {
89709 d3_event.preventDefault();
89710 zoomToEntity(d.id);
89712 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
89713 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
89714 return d.details || [];
89715 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
89718 details.append('div').attr('class', 'conflict-choices').call(addChoices);
89719 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
89720 return _t.html('save.conflict.' + d);
89721 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
89722 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
89723 }).on('click', function (d3_event, d) {
89724 d3_event.preventDefault();
89725 var container = parent.selectAll('.conflict-container');
89726 var sign = d === 'previous' ? -1 : 1;
89727 container.selectAll('.conflict').remove();
89728 container.call(showConflict, index + sign);
89732 function addChoices(selection) {
89733 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
89734 return d.choices || [];
89737 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
89738 var labelEnter = choicesEnter.append('label');
89739 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
89741 }).on('change', function (d3_event, d) {
89742 var ul = this.parentNode.parentNode.parentNode;
89743 ul.__data__.chosen = d.id;
89744 choose(d3_event, ul, d);
89746 labelEnter.append('span').html(function (d) {
89750 choicesEnter.merge(choices).each(function (d) {
89751 var ul = this.parentNode;
89753 if (ul.__data__.chosen === d.id) {
89754 choose(null, ul, d);
89759 function choose(d3_event, ul, datum) {
89760 if (d3_event) d3_event.preventDefault();
89761 select(ul).selectAll('li').classed('active', function (d) {
89762 return d === datum;
89763 }).selectAll('input').property('checked', function (d) {
89764 return d === datum;
89766 var extent = geoExtent();
89768 entity = context.graph().hasEntity(datum.id);
89769 if (entity) extent._extend(entity.extent(context.graph()));
89771 entity = context.graph().hasEntity(datum.id);
89772 if (entity) extent._extend(entity.extent(context.graph()));
89773 zoomToEntity(datum.id, extent);
89776 function zoomToEntity(id, extent) {
89777 context.surface().selectAll('.hover').classed('hover', false);
89778 var entity = context.graph().hasEntity(id);
89782 context.map().trimmedExtent(extent);
89784 context.map().zoomToEase(entity);
89787 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
89789 } // The conflict list should be an array of objects like:
89792 // name: entityName(local),
89793 // details: merge.conflicts(),
89796 // choice(id, keepMine, forceLocal),
89797 // choice(id, keepTheirs, forceRemote)
89802 conflicts.conflictList = function (_) {
89803 if (!arguments.length) return _conflictList;
89808 conflicts.origChanges = function (_) {
89809 if (!arguments.length) return _origChanges;
89814 conflicts.shownEntityIds = function () {
89815 if (_conflictList && typeof _shownConflictIndex === 'number') {
89816 return [_conflictList[_shownConflictIndex].id];
89822 return utilRebind(conflicts, dispatch$1, 'on');
89825 function uiConfirm(selection) {
89826 var modalSelection = uiModal(selection);
89827 modalSelection.select('.modal').classed('modal-alert', true);
89828 var section = modalSelection.select('.content');
89829 section.append('div').attr('class', 'modal-section header');
89830 section.append('div').attr('class', 'modal-section message-text');
89831 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
89833 modalSelection.okButton = function () {
89834 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
89835 modalSelection.remove();
89836 }).html(_t.html('confirm.okay')).node().focus();
89837 return modalSelection;
89840 return modalSelection;
89843 function uiChangesetEditor(context) {
89844 var dispatch$1 = dispatch('change');
89845 var formFields = uiFormFields(context);
89846 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
89854 function changesetEditor(selection) {
89858 function render(selection) {
89859 var initial = false;
89863 var presets = _mainPresetIndex;
89864 _fieldsArr = [uiField(context, presets.field('comment'), null, {
89867 }), uiField(context, presets.field('source'), null, {
89870 }), uiField(context, presets.field('hashtags'), null, {
89875 _fieldsArr.forEach(function (field) {
89876 field.on('change', function (t, onInput) {
89877 dispatch$1.call('change', field, undefined, t, onInput);
89882 _fieldsArr.forEach(function (field) {
89886 selection.call(formFields.fieldsArr(_fieldsArr));
89889 var commentField = selection.select('.form-field-comment textarea');
89890 var commentNode = commentField.node();
89893 commentNode.focus();
89894 commentNode.select();
89895 } // trigger a 'blur' event so that comment field can be cleaned
89896 // and checked for hashtags, even if retrieved from localstorage
89899 utilTriggerEvent(commentField, 'blur');
89900 var osm = context.connection();
89903 osm.userChangesets(function (err, changesets) {
89905 var comments = changesets.map(function (changeset) {
89906 var comment = changeset.tags.comment;
89911 }).filter(Boolean);
89912 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
89915 } // Add warning if comment mentions Google
89918 var hasGoogle = _tags.comment.match(/google/i);
89920 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
89921 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
89922 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
89923 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'));
89924 commentEnter.transition().duration(200).style('opacity', 1);
89927 changesetEditor.tags = function (_) {
89928 if (!arguments.length) return _tags;
89929 _tags = _; // Don't reset _fieldsArr here.
89931 return changesetEditor;
89934 changesetEditor.changesetID = function (_) {
89935 if (!arguments.length) return _changesetID;
89936 if (_changesetID === _) return changesetEditor;
89939 return changesetEditor;
89942 return utilRebind(changesetEditor, dispatch$1, 'on');
89945 function uiSectionChanges(context) {
89946 var detected = utilDetect();
89947 var _discardTags = {};
89948 _mainFileFetcher.get('discarded').then(function (d) {
89950 })["catch"](function () {
89953 var section = uiSection('changes-list', context).label(function () {
89954 var history = context.history();
89955 var summary = history.difference().summary();
89956 return _t('inspector.title_count', {
89957 title: _t.html('commit.changes'),
89958 count: summary.length
89960 }).disclosureContent(renderDisclosureContent);
89962 function renderDisclosureContent(selection) {
89963 var history = context.history();
89964 var summary = history.difference().summary();
89965 var container = selection.selectAll('.commit-section').data([0]);
89966 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
89967 containerEnter.append('ul').attr('class', 'changeset-list');
89968 container = containerEnter.merge(container);
89969 var items = container.select('ul').selectAll('li').data(summary);
89970 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
89971 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
89972 buttons.each(function (d) {
89973 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
89975 buttons.append('span').attr('class', 'change-type').html(function (d) {
89976 return _t.html('commit.' + d.changeType) + ' ';
89978 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
89979 var matched = _mainPresetIndex.match(d.entity, d.graph);
89980 return matched && matched.name() || utilDisplayType(d.entity.id);
89982 buttons.append('span').attr('class', 'entity-name').html(function (d) {
89983 var name = utilDisplayName(d.entity) || '',
89990 return string += ' ' + name;
89992 items = itemsEnter.merge(items); // Download changeset link
89994 var changeset = new osmChangeset().update({
89997 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
89998 delete changeset.id; // Export without chnageset_id
90000 var data = JXON.stringify(changeset.osmChangeJXON(changes));
90001 var blob = new Blob([data], {
90002 type: 'text/xml;charset=utf-8;'
90004 var fileName = 'changes.osc';
90005 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
90007 if (detected.download) {
90008 // All except IE11 and Edge
90009 linkEnter // download the data as a file
90010 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90013 linkEnter // open data uri in a new tab
90014 .attr('target', '_blank').on('click.download', function () {
90015 navigator.msSaveBlob(blob, fileName);
90019 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
90021 function mouseover(d) {
90023 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
90027 function mouseout() {
90028 context.surface().selectAll('.hover').classed('hover', false);
90031 function click(d3_event, change) {
90032 if (change.changeType !== 'deleted') {
90033 var entity = change.entity;
90034 context.map().zoomToEase(entity);
90035 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90043 function uiCommitWarnings(context) {
90044 function commitWarnings(selection) {
90045 var issuesBySeverity = context.validator().getIssuesBySeverity({
90048 includeDisabledRules: true
90051 for (var severity in issuesBySeverity) {
90052 var issues = issuesBySeverity[severity];
90053 var section = severity + '-section';
90054 var issueItem = severity + '-item';
90055 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
90056 container.exit().remove();
90057 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
90058 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
90059 containerEnter.append('ul').attr('class', 'changeset-list');
90060 container = containerEnter.merge(container);
90061 var items = container.select('ul').selectAll('li').data(issues, function (d) {
90064 items.exit().remove();
90065 var itemsEnter = items.enter().append('li').attr('class', issueItem);
90066 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
90068 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
90070 }).on('mouseout', function () {
90071 context.surface().selectAll('.hover').classed('hover', false);
90072 }).on('click', function (d3_event, d) {
90073 context.validator().focusIssue(d);
90075 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
90076 buttons.append('strong').attr('class', 'issue-message');
90077 buttons.filter(function (d) {
90079 }).call(uiTooltip().title(function (d) {
90081 }).placement('top'));
90082 items = itemsEnter.merge(items);
90083 items.selectAll('.issue-message').html(function (d) {
90084 return d.message(context);
90089 return commitWarnings;
90092 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
90093 // from https://stackoverflow.com/a/25575009
90095 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
90096 function uiCommit(context) {
90097 var dispatch$1 = dispatch('cancel');
90103 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
90104 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
90105 var commitChanges = uiSectionChanges(context);
90106 var commitWarnings = uiCommitWarnings(context);
90108 function commit(selection) {
90109 _selection = selection; // Initialize changeset if one does not exist yet.
90111 if (!context.changeset) initChangeset();
90112 loadDerivedChangesetTags();
90113 selection.call(render);
90116 function initChangeset() {
90117 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
90118 var commentDate = +corePreferences('commentDate') || 0;
90119 var currDate = Date.now();
90120 var cutoff = 2 * 86400 * 1000; // 2 days
90122 if (commentDate > currDate || currDate - commentDate > cutoff) {
90123 corePreferences('comment', null);
90124 corePreferences('hashtags', null);
90125 corePreferences('source', null);
90126 } // load in explicitly-set values, if any
90129 if (context.defaultChangesetComment()) {
90130 corePreferences('comment', context.defaultChangesetComment());
90131 corePreferences('commentDate', Date.now());
90134 if (context.defaultChangesetSource()) {
90135 corePreferences('source', context.defaultChangesetSource());
90136 corePreferences('commentDate', Date.now());
90139 if (context.defaultChangesetHashtags()) {
90140 corePreferences('hashtags', context.defaultChangesetHashtags());
90141 corePreferences('commentDate', Date.now());
90144 var detected = utilDetect();
90146 comment: corePreferences('comment') || '',
90147 created_by: context.cleanTagValue('iD ' + context.version),
90148 host: context.cleanTagValue(detected.host),
90149 locale: context.cleanTagValue(_mainLocalizer.localeCode())
90150 }; // call findHashtags initially - this will remove stored
90151 // hashtags if any hashtags are found in the comment - #4304
90153 findHashtags(tags, true);
90154 var hashtags = corePreferences('hashtags');
90157 tags.hashtags = hashtags;
90160 var source = corePreferences('source');
90163 tags.source = source;
90166 var photoOverlaysUsed = context.history().photoOverlaysUsed();
90168 if (photoOverlaysUsed.length) {
90169 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
90171 if (sources.indexOf('streetlevel imagery') === -1) {
90172 sources.push('streetlevel imagery');
90173 } // add the photo overlays used during editing as sources
90176 photoOverlaysUsed.forEach(function (photoOverlay) {
90177 if (sources.indexOf(photoOverlay) === -1) {
90178 sources.push(photoOverlay);
90181 tags.source = context.cleanTagValue(sources.join(';'));
90184 context.changeset = new osmChangeset({
90187 } // Calculates read-only metadata tags based on the user's editing session and applies
90188 // them to the changeset.
90191 function loadDerivedChangesetTags() {
90192 var osm = context.connection();
90194 var tags = Object.assign({}, context.changeset.tags); // shallow copy
90195 // assign tags for imagery used
90197 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
90198 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
90200 var osmClosed = osm.getClosedIDs();
90203 if (osmClosed.length) {
90204 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
90207 if (services.keepRight) {
90208 var krClosed = services.keepRight.getClosedIDs();
90210 if (krClosed.length) {
90211 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
90215 if (services.improveOSM) {
90216 var iOsmClosed = services.improveOSM.getClosedCounts();
90218 for (itemType in iOsmClosed) {
90219 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
90223 if (services.osmose) {
90224 var osmoseClosed = services.osmose.getClosedCounts();
90226 for (itemType in osmoseClosed) {
90227 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
90229 } // remove existing issue counts
90232 for (var key in tags) {
90233 if (key.match(/(^warnings:)|(^resolved:)/)) {
90238 function addIssueCounts(issues, prefix) {
90239 var issuesByType = utilArrayGroupBy(issues, 'type');
90241 for (var issueType in issuesByType) {
90242 var issuesOfType = issuesByType[issueType];
90244 if (issuesOfType[0].subtype) {
90245 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
90247 for (var issueSubtype in issuesBySubtype) {
90248 var issuesOfSubtype = issuesBySubtype[issueSubtype];
90249 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
90252 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
90255 } // add counts of warnings generated by the user's edits
90258 var warnings = context.validator().getIssuesBySeverity({
90261 includeIgnored: true,
90262 includeDisabledRules: true
90264 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
90266 var resolvedIssues = context.validator().getResolvedIssues();
90267 addIssueCounts(resolvedIssues, 'resolved');
90268 context.changeset = context.changeset.update({
90273 function render(selection) {
90274 var osm = context.connection();
90276 var header = selection.selectAll('.header').data([0]);
90277 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
90278 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
90279 headerTitle.append('button').attr('class', 'close').on('click', function () {
90280 dispatch$1.call('cancel', this);
90281 }).call(svgIcon('#iD-icon-close'));
90282 var body = selection.selectAll('.body').data([0]);
90283 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
90285 var changesetSection = body.selectAll('.changeset-editor').data([0]);
90286 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
90287 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
90289 body.call(commitWarnings); // Upload Explanation
90291 var saveSection = body.selectAll('.save-section').data([0]);
90292 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
90293 var prose = saveSection.selectAll('.commit-info').data([0]);
90295 if (prose.enter().size()) {
90296 // first time, make sure to update user details in prose
90297 _userDetails = null;
90300 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()
90301 // if needed, because it can trigger a style recalculation
90303 osm.userDetails(function (err, user) {
90305 if (_userDetails === user) return; // no change
90307 _userDetails = user;
90308 var userLink = select(document.createElement('div'));
90310 if (user.image_url) {
90311 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
90314 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
90315 prose.html(_t.html('commit.upload_explanation_with_user', {
90316 user: userLink.html()
90318 }); // Request Review
90320 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
90322 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
90323 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
90324 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
90325 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
90326 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
90328 requestReview = requestReview.merge(requestReviewEnter);
90329 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
90331 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
90333 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
90334 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
90335 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
90336 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
90337 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
90339 buttonSection = buttonSection.merge(buttonEnter);
90340 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
90341 dispatch$1.call('cancel', this);
90343 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
90344 if (!select(this).classed('disabled')) {
90345 this.blur(); // avoid keeping focus on the button - #4641
90347 for (var key in context.changeset.tags) {
90348 // remove any empty keys before upload
90349 if (!key) delete context.changeset.tags[key];
90352 context.uploader().save(context.changeset);
90354 }); // remove any existing tooltip
90356 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
90358 if (uploadBlockerTooltipText) {
90359 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
90360 } // Raw Tag Editor
90363 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
90364 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
90365 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
90367 var changesSection = body.selectAll('.commit-changes-section').data([0]);
90368 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
90370 changesSection.call(commitChanges.render);
90372 function toggleRequestReview() {
90373 var rr = requestReviewInput.property('checked');
90375 review_requested: rr ? 'yes' : undefined
90377 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
90382 function getUploadBlockerMessage() {
90383 var errors = context.validator().getIssuesBySeverity({
90388 if (errors.length) {
90389 return _t('commit.outstanding_errors_message', {
90390 count: errors.length
90393 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
90395 if (!hasChangesetComment) {
90396 return _t('commit.comment_needed_message');
90403 function changeTags(_, changed, onInput) {
90404 if (changed.hasOwnProperty('comment')) {
90405 if (changed.comment === undefined) {
90406 changed.comment = '';
90410 corePreferences('comment', changed.comment);
90411 corePreferences('commentDate', Date.now());
90415 if (changed.hasOwnProperty('source')) {
90416 if (changed.source === undefined) {
90417 corePreferences('source', null);
90418 } else if (!onInput) {
90419 corePreferences('source', changed.source);
90420 corePreferences('commentDate', Date.now());
90422 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
90425 updateChangeset(changed, onInput);
90428 _selection.call(render);
90432 function findHashtags(tags, commentOnly) {
90433 var detectedHashtags = commentHashtags();
90435 if (detectedHashtags.length) {
90436 // always remove stored hashtags if there are hashtags in the comment - #4304
90437 corePreferences('hashtags', null);
90440 if (!detectedHashtags.length || !commentOnly) {
90441 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
90444 var allLowerCase = new Set();
90445 return detectedHashtags.filter(function (hashtag) {
90446 // Compare tags as lowercase strings, but keep original case tags
90447 var lowerCase = hashtag.toLowerCase();
90449 if (!allLowerCase.has(lowerCase)) {
90450 allLowerCase.add(lowerCase);
90455 }); // Extract hashtags from `comment`
90457 function commentHashtags() {
90458 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
90459 .match(hashtagRegex);
90460 return matches || [];
90461 } // Extract and clean hashtags from `hashtags`
90464 function hashtagHashtags() {
90465 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
90466 if (s[0] !== '#') {
90471 var matched = s.match(hashtagRegex);
90472 return matched && matched[0];
90473 }).filter(Boolean); // exclude falsy
90475 return matches || [];
90479 function isReviewRequested(tags) {
90480 var rr = tags.review_requested;
90481 if (rr === undefined) return false;
90482 rr = rr.trim().toLowerCase();
90483 return !(rr === '' || rr === 'no');
90486 function updateChangeset(changed, onInput) {
90487 var tags = Object.assign({}, context.changeset.tags); // shallow copy
90489 Object.keys(changed).forEach(function (k) {
90490 var v = changed[k];
90491 k = context.cleanTagKey(k);
90492 if (readOnlyTags.indexOf(k) !== -1) return;
90494 if (v === undefined) {
90496 } else if (onInput) {
90499 tags[k] = context.cleanTagValue(v);
90504 // when changing the comment, override hashtags with any found in comment.
90505 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
90506 var arr = findHashtags(tags, commentOnly);
90509 tags.hashtags = context.cleanTagValue(arr.join(';'));
90510 corePreferences('hashtags', tags.hashtags);
90512 delete tags.hashtags;
90513 corePreferences('hashtags', null);
90515 } // always update userdetails, just in case user reauthenticates as someone else
90518 if (_userDetails && _userDetails.changesets_count !== undefined) {
90519 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
90521 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
90523 if (changesetsCount <= 100) {
90525 s = corePreferences('walkthrough_completed');
90528 tags['ideditor:walkthrough_completed'] = s;
90531 s = corePreferences('walkthrough_progress');
90534 tags['ideditor:walkthrough_progress'] = s;
90537 s = corePreferences('walkthrough_started');
90540 tags['ideditor:walkthrough_started'] = s;
90544 delete tags.changesets_count;
90547 if (!fastDeepEqual(context.changeset.tags, tags)) {
90548 context.changeset = context.changeset.update({
90554 commit.reset = function () {
90555 context.changeset = null;
90558 return utilRebind(commit, dispatch$1, 'on');
90561 var globalIsFinite = global_1.isFinite;
90563 // `Number.isFinite` method
90564 // https://tc39.es/ecma262/#sec-number.isfinite
90565 var numberIsFinite = Number.isFinite || function isFinite(it) {
90566 return typeof it == 'number' && globalIsFinite(it);
90569 // `Number.isFinite` method
90570 // https://tc39.es/ecma262/#sec-number.isfinite
90571 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
90573 var RADIUS = 6378137;
90574 var FLATTENING = 1 / 298.257223563;
90575 var POLAR_RADIUS$1 = 6356752.3142;
90578 FLATTENING: FLATTENING,
90579 POLAR_RADIUS: POLAR_RADIUS$1
90582 var geometry_1 = geometry;
90583 var ring = ringArea;
90585 function geometry(_) {
90591 return polygonArea(_.coordinates);
90593 case 'MultiPolygon':
90594 for (i = 0; i < _.coordinates.length; i++) {
90595 area += polygonArea(_.coordinates[i]);
90603 case 'MultiLineString':
90606 case 'GeometryCollection':
90607 for (i = 0; i < _.geometries.length; i++) {
90608 area += geometry(_.geometries[i]);
90615 function polygonArea(coords) {
90618 if (coords && coords.length > 0) {
90619 area += Math.abs(ringArea(coords[0]));
90621 for (var i = 1; i < coords.length; i++) {
90622 area -= Math.abs(ringArea(coords[i]));
90629 * Calculate the approximate area of the polygon were it projected onto
90630 * the earth. Note that this area will be positive if ring is oriented
90631 * clockwise, otherwise it will be negative.
90634 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
90635 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
90636 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
90639 * {float} The approximate signed geodesic area of the polygon in square
90644 function ringArea(coords) {
90653 coordsLength = coords.length;
90655 if (coordsLength > 2) {
90656 for (i = 0; i < coordsLength; i++) {
90657 if (i === coordsLength - 2) {
90659 lowerIndex = coordsLength - 2;
90660 middleIndex = coordsLength - 1;
90662 } else if (i === coordsLength - 1) {
90664 lowerIndex = coordsLength - 1;
90670 middleIndex = i + 1;
90671 upperIndex = i + 2;
90674 p1 = coords[lowerIndex];
90675 p2 = coords[middleIndex];
90676 p3 = coords[upperIndex];
90677 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
90680 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
90687 return _ * Math.PI / 180;
90690 var geojsonArea = {
90691 geometry: geometry_1,
90695 var validateCenter_1 = function validateCenter(center) {
90696 var validCenterLengths = [2, 3];
90698 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
90699 throw new Error("ERROR! Center has to be an array of length two or three");
90702 var _center = _slicedToArray(center, 2),
90706 if (typeof lng !== "number" || typeof lat !== "number") {
90707 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
90710 if (lng > 180 || lng < -180) {
90711 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
90714 if (lat > 90 || lat < -90) {
90715 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
90719 var validateCenter = {
90720 validateCenter: validateCenter_1
90723 var validateRadius_1 = function validateRadius(radius) {
90724 if (typeof radius !== "number") {
90725 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
90729 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
90733 var validateRadius = {
90734 validateRadius: validateRadius_1
90737 var validateNumberOfEdges_1 = function validateNumberOfEdges(numberOfEdges) {
90738 if (typeof numberOfEdges !== "number") {
90739 var ARGUMENT_TYPE = Array.isArray(numberOfEdges) ? "array" : _typeof(numberOfEdges);
90740 throw new Error("ERROR! Number of edges has to be a number but was: ".concat(ARGUMENT_TYPE));
90743 if (numberOfEdges < 3) {
90744 throw new Error("ERROR! Number of edges has to be at least 3 but was: ".concat(numberOfEdges));
90748 var validateNumberOfEdges = {
90749 validateNumberOfEdges: validateNumberOfEdges_1
90752 var validateEarthRadius_1 = function validateEarthRadius(earthRadius) {
90753 if (typeof earthRadius !== "number") {
90754 var ARGUMENT_TYPE = Array.isArray(earthRadius) ? "array" : _typeof(earthRadius);
90755 throw new Error("ERROR! Earth radius has to be a number but was: ".concat(ARGUMENT_TYPE));
90758 if (earthRadius <= 0) {
90759 throw new Error("ERROR! Earth radius has to be a positive number but was: ".concat(earthRadius));
90763 var validateEarthRadius = {
90764 validateEarthRadius: validateEarthRadius_1
90767 var validateBearing_1 = function validateBearing(bearing) {
90768 if (typeof bearing !== "number") {
90769 var ARGUMENT_TYPE = Array.isArray(bearing) ? "array" : _typeof(bearing);
90770 throw new Error("ERROR! Bearing has to be a number but was: ".concat(ARGUMENT_TYPE));
90774 var validateBearing = {
90775 validateBearing: validateBearing_1
90778 var validateCenter$1 = validateCenter.validateCenter;
90779 var validateRadius$1 = validateRadius.validateRadius;
90780 var validateNumberOfEdges$1 = validateNumberOfEdges.validateNumberOfEdges;
90781 var validateEarthRadius$1 = validateEarthRadius.validateEarthRadius;
90782 var validateBearing$1 = validateBearing.validateBearing;
90784 function validateInput(_ref) {
90785 var center = _ref.center,
90786 radius = _ref.radius,
90787 numberOfEdges = _ref.numberOfEdges,
90788 earthRadius = _ref.earthRadius,
90789 bearing = _ref.bearing;
90790 validateCenter$1(center);
90791 validateRadius$1(radius);
90792 validateNumberOfEdges$1(numberOfEdges);
90793 validateEarthRadius$1(earthRadius);
90794 validateBearing$1(bearing);
90797 var validateCenter_1$1 = validateCenter$1;
90798 var validateRadius_1$1 = validateRadius$1;
90799 var validateNumberOfEdges_1$1 = validateNumberOfEdges$1;
90800 var validateEarthRadius_1$1 = validateEarthRadius$1;
90801 var validateBearing_1$1 = validateBearing$1;
90802 var validateInput_1 = validateInput;
90803 var inputValidation = {
90804 validateCenter: validateCenter_1$1,
90805 validateRadius: validateRadius_1$1,
90806 validateNumberOfEdges: validateNumberOfEdges_1$1,
90807 validateEarthRadius: validateEarthRadius_1$1,
90808 validateBearing: validateBearing_1$1,
90809 validateInput: validateInput_1
90812 var validateInput$1 = inputValidation.validateInput;
90813 var defaultEarthRadius = 6378137; // equatorial Earth radius
90815 function toRadians(angleInDegrees) {
90816 return angleInDegrees * Math.PI / 180;
90819 function toDegrees(angleInRadians) {
90820 return angleInRadians * 180 / Math.PI;
90823 function offset(c1, distance, earthRadius, bearing) {
90824 var lat1 = toRadians(c1[1]);
90825 var lon1 = toRadians(c1[0]);
90826 var dByR = distance / earthRadius;
90827 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
90828 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
90829 return [toDegrees(lon), toDegrees(lat)];
90832 var circleToPolygon = function circleToPolygon(center, radius, options) {
90833 var n = getNumberOfEdges(options);
90834 var earthRadius = getEarthRadius(options);
90835 var bearing = getBearing(options); // validateInput() throws error on invalid input and do nothing on valid input
90841 earthRadius: earthRadius,
90844 var start = toRadians(bearing);
90845 var coordinates = [];
90847 for (var i = 0; i < n; ++i) {
90848 coordinates.push(offset(center, radius, earthRadius, start + 2 * Math.PI * -i / n));
90851 coordinates.push(coordinates[0]);
90854 coordinates: [coordinates]
90858 function getNumberOfEdges(options) {
90859 if (options === undefined) {
90861 } else if (isObjectNotArray(options)) {
90862 var numberOfEdges = options.numberOfEdges;
90863 return numberOfEdges === undefined ? 32 : numberOfEdges;
90869 function getEarthRadius(options) {
90870 if (options === undefined) {
90871 return defaultEarthRadius;
90872 } else if (isObjectNotArray(options)) {
90873 var earthRadius = options.earthRadius;
90874 return earthRadius === undefined ? defaultEarthRadius : earthRadius;
90877 return defaultEarthRadius;
90880 function getBearing(options) {
90881 if (options === undefined) {
90883 } else if (isObjectNotArray(options)) {
90884 var bearing = options.bearing;
90885 return bearing === undefined ? 0 : bearing;
90891 function isObjectNotArray(argument) {
90892 return _typeof(argument) === "object" && !Array.isArray(argument);
90895 // `Number.EPSILON` constant
90896 // https://tc39.es/ecma262/#sec-number.epsilon
90897 _export({ target: 'Number', stat: true }, {
90898 EPSILON: Math.pow(2, -52)
90903 * Fast Splay tree for Node and browser
90905 * @author Alexander Milevski <info@w8r.name>
90912 function Node(key, data) {
90922 /* follows "An implementation of top-down splaying"
90923 * by D. Sleator <sleator@cs.cmu.edu> March 1992
90927 function DEFAULT_COMPARE$1(a, b) {
90928 return a > b ? 1 : a < b ? -1 : 0;
90931 * Simple top down splay, not requiring i to be in the tree t.
90935 function splay(i, t, comparator) {
90936 var N = new Node$1(null, null);
90941 var cmp = comparator(i, t.key); //if (i < t.key) {
90944 if (t.left === null) break; //if (i < t.left.key) {
90946 if (comparator(i, t.left.key) < 0) {
90953 if (t.left === null) break;
90960 t = t.left; //} else if (i > t.key) {
90961 } else if (cmp > 0) {
90962 if (t.right === null) break; //if (i > t.right.key) {
90964 if (comparator(i, t.right.key) > 0) {
90971 if (t.right === null) break;
90991 function insert(i, data, t, comparator) {
90992 var node = new Node$1(i, data);
90995 node.left = node.right = null;
90999 t = splay(i, t, comparator);
91000 var cmp = comparator(i, t.key);
91003 node.left = t.left;
91006 } else if (cmp >= 0) {
91007 node.right = t.right;
91015 function split$2(key, v, comparator) {
91020 v = splay(key, v, comparator);
91021 var cmp = comparator(v.key, key);
91026 } else if (cmp < 0) {
91043 function merge$4(left, right, comparator) {
91044 if (right === null) return left;
91045 if (left === null) return right;
91046 right = splay(left.key, right, comparator);
91051 * Prints level of the tree
91055 function printRow(root, prefix, isTail, out, printNode) {
91057 out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n");
91058 var indent = prefix + (isTail ? ' ' : '│ ');
91059 if (root.left) printRow(root.left, indent, false, out, printNode);
91060 if (root.right) printRow(root.right, indent, true, out, printNode);
91067 function Tree(comparator) {
91068 if (comparator === void 0) {
91069 comparator = DEFAULT_COMPARE$1;
91074 this._comparator = comparator;
91077 * Inserts a key, allows duplicates
91081 Tree.prototype.insert = function (key, data) {
91083 return this._root = insert(key, data, this._root, this._comparator);
91086 * Adds a key, if it is not present in the tree
91090 Tree.prototype.add = function (key, data) {
91091 var node = new Node$1(key, data);
91093 if (this._root === null) {
91094 node.left = node.right = null;
91099 var comparator = this._comparator;
91100 var t = splay(key, this._root, comparator);
91101 var cmp = comparator(key, t.key);
91102 if (cmp === 0) this._root = t;else {
91104 node.left = t.left;
91107 } else if (cmp > 0) {
91108 node.right = t.right;
91120 * @return {Node|null}
91124 Tree.prototype.remove = function (key) {
91125 this._root = this._remove(key, this._root, this._comparator);
91128 * Deletes i from the tree if it's there
91132 Tree.prototype._remove = function (i, t, comparator) {
91134 if (t === null) return null;
91135 t = splay(i, t, comparator);
91136 var cmp = comparator(i, t.key);
91140 if (t.left === null) {
91143 x = splay(i, t.left, comparator);
91152 /* It wasn't there */
91155 * Removes and returns the node with smallest key
91159 Tree.prototype.pop = function () {
91160 var node = this._root;
91163 while (node.left) {
91167 this._root = splay(node.key, this._root, this._comparator);
91168 this._root = this._remove(node.key, this._root, this._comparator);
91178 * Find without splaying
91182 Tree.prototype.findStatic = function (key) {
91183 var current = this._root;
91184 var compare = this._comparator;
91187 var cmp = compare(key, current.key);
91188 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
91194 Tree.prototype.find = function (key) {
91196 this._root = splay(key, this._root, this._comparator);
91197 if (this._comparator(key, this._root.key) !== 0) return null;
91203 Tree.prototype.contains = function (key) {
91204 var current = this._root;
91205 var compare = this._comparator;
91208 var cmp = compare(key, current.key);
91209 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
91215 Tree.prototype.forEach = function (visitor, ctx) {
91216 var current = this._root;
91218 /* Initialize stack s */
91223 if (current !== null) {
91225 current = current.left;
91227 if (Q.length !== 0) {
91229 visitor.call(ctx, current);
91230 current = current.right;
91231 } else done = true;
91238 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
91242 Tree.prototype.range = function (low, high, fn, ctx) {
91244 var compare = this._comparator;
91245 var node = this._root;
91248 while (Q.length !== 0 || node) {
91254 cmp = compare(node.key, high);
91258 } else if (compare(node.key, low) >= 0) {
91259 if (fn.call(ctx, node)) return this; // stop if smth is returned
91269 * Returns array of keys
91273 Tree.prototype.keys = function () {
91275 this.forEach(function (_a) {
91277 return keys.push(key);
91282 * Returns array of all the data in the nodes
91286 Tree.prototype.values = function () {
91288 this.forEach(function (_a) {
91289 var data = _a.data;
91290 return values.push(data);
91295 Tree.prototype.min = function () {
91296 if (this._root) return this.minNode(this._root).key;
91300 Tree.prototype.max = function () {
91301 if (this._root) return this.maxNode(this._root).key;
91305 Tree.prototype.minNode = function (t) {
91306 if (t === void 0) {
91310 if (t) while (t.left) {
91316 Tree.prototype.maxNode = function (t) {
91317 if (t === void 0) {
91321 if (t) while (t.right) {
91327 * Returns node at given index
91331 Tree.prototype.at = function (index) {
91332 var current = this._root;
91340 current = current.left;
91342 if (Q.length > 0) {
91344 if (i === index) return current;
91346 current = current.right;
91347 } else done = true;
91354 Tree.prototype.next = function (d) {
91355 var root = this._root;
91356 var successor = null;
91359 successor = d.right;
91361 while (successor.left) {
91362 successor = successor.left;
91368 var comparator = this._comparator;
91371 var cmp = comparator(d.key, root.key);
91372 if (cmp === 0) break;else if (cmp < 0) {
91375 } else root = root.right;
91381 Tree.prototype.prev = function (d) {
91382 var root = this._root;
91383 var predecessor = null;
91385 if (d.left !== null) {
91386 predecessor = d.left;
91388 while (predecessor.right) {
91389 predecessor = predecessor.right;
91392 return predecessor;
91395 var comparator = this._comparator;
91398 var cmp = comparator(d.key, root.key);
91399 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
91400 predecessor = root;
91405 return predecessor;
91408 Tree.prototype.clear = function () {
91414 Tree.prototype.toList = function () {
91415 return toList(this._root);
91418 * Bulk-load items. Both array have to be same size
91422 Tree.prototype.load = function (keys, values, presort) {
91423 if (values === void 0) {
91427 if (presort === void 0) {
91431 var size = keys.length;
91432 var comparator = this._comparator; // sort if needed
91434 if (presort) sort$1(keys, values, 0, size - 1, comparator);
91436 if (this._root === null) {
91438 this._root = loadRecursive$1(keys, values, 0, size);
91441 // that re-builds the whole tree from two in-order traversals
91442 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
91443 size = this._size + size;
91444 this._root = sortedListToBST({
91452 Tree.prototype.isEmpty = function () {
91453 return this._root === null;
91456 Object.defineProperty(Tree.prototype, "size", {
91457 get: function get() {
91463 Object.defineProperty(Tree.prototype, "root", {
91464 get: function get() {
91471 Tree.prototype.toString = function (printNode) {
91472 if (printNode === void 0) {
91473 printNode = function printNode(n) {
91474 return String(n.key);
91479 printRow(this._root, '', true, function (v) {
91480 return out.push(v);
91482 return out.join('');
91485 Tree.prototype.update = function (key, newKey, newData) {
91486 var comparator = this._comparator;
91488 var _a = split$2(key, this._root, comparator),
91492 if (comparator(key, newKey) < 0) {
91493 right = insert(newKey, newData, right, comparator);
91495 left = insert(newKey, newData, left, comparator);
91498 this._root = merge$4(left, right, comparator);
91501 Tree.prototype.split = function (key) {
91502 return split$2(key, this._root, this._comparator);
91508 function loadRecursive$1(keys, values, start, end) {
91509 var size = end - start;
91512 var middle = start + Math.floor(size / 2);
91513 var key = keys[middle];
91514 var data = values[middle];
91515 var node = new Node$1(key, data);
91516 node.left = loadRecursive$1(keys, values, start, middle);
91517 node.right = loadRecursive$1(keys, values, middle + 1, end);
91524 function createList(keys, values) {
91525 var head = new Node$1(null, null);
91528 for (var i = 0; i < keys.length; i++) {
91529 p = p.next = new Node$1(keys[i], values[i]);
91536 function toList(root) {
91537 var current = root;
91540 var head = new Node$1(null, null);
91546 current = current.left;
91548 if (Q.length > 0) {
91549 current = p = p.next = Q.pop();
91550 current = current.right;
91551 } else done = true;
91555 p.next = null; // that'll work even if the tree was empty
91560 function sortedListToBST(list, start, end) {
91561 var size = end - start;
91564 var middle = start + Math.floor(size / 2);
91565 var left = sortedListToBST(list, start, middle);
91566 var root = list.head;
91568 list.head = list.head.next;
91569 root.right = sortedListToBST(list, middle + 1, end);
91576 function mergeLists(l1, l2, compare) {
91577 var head = new Node$1(null, null); // dummy
91583 while (p1 !== null && p2 !== null) {
91584 if (compare(p1.key, p2.key) < 0) {
91597 } else if (p2 !== null) {
91604 function sort$1(keys, values, left, right, compare) {
91605 if (left >= right) return;
91606 var pivot = keys[left + right >> 1];
91613 } while (compare(keys[i], pivot) < 0);
91617 } while (compare(keys[j], pivot) > 0);
91624 values[i] = values[j];
91628 sort$1(keys, values, left, j, compare);
91629 sort$1(keys, values, j + 1, right, compare);
91632 function _classCallCheck$1(instance, Constructor) {
91633 if (!(instance instanceof Constructor)) {
91634 throw new TypeError("Cannot call a class as a function");
91638 function _defineProperties$1(target, props) {
91639 for (var i = 0; i < props.length; i++) {
91640 var descriptor = props[i];
91641 descriptor.enumerable = descriptor.enumerable || false;
91642 descriptor.configurable = true;
91643 if ("value" in descriptor) descriptor.writable = true;
91644 Object.defineProperty(target, descriptor.key, descriptor);
91648 function _createClass$1(Constructor, protoProps, staticProps) {
91649 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
91650 if (staticProps) _defineProperties$1(Constructor, staticProps);
91651 return Constructor;
91654 * A bounding box has the format:
91656 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
91661 var isInBbox = function isInBbox(bbox, point) {
91662 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
91664 /* Returns either null, or a bbox (aka an ordered pair of points)
91665 * If there is only one point of overlap, a bbox with identical points
91666 * will be returned */
91669 var getBboxOverlap = function getBboxOverlap(b1, b2) {
91670 // check if the bboxes overlap at all
91671 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
91673 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
91674 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
91676 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
91677 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
91690 /* Javascript doesn't do integer math. Everything is
91691 * floating point with percision Number.EPSILON.
91693 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
91697 var epsilon$2 = Number.EPSILON; // IE Polyfill
91699 if (epsilon$2 === undefined) epsilon$2 = Math.pow(2, -52);
91700 var EPSILON_SQ = epsilon$2 * epsilon$2;
91701 /* FLP comparator */
91703 var cmp = function cmp(a, b) {
91704 // check if they're both 0
91705 if (-epsilon$2 < a && a < epsilon$2) {
91706 if (-epsilon$2 < b && b < epsilon$2) {
91709 } // check if they're flp equal
91714 if (ab * ab < EPSILON_SQ * a * b) {
91716 } // normal comparison
91719 return a < b ? -1 : 1;
91722 * This class rounds incoming values sufficiently so that
91723 * floating points problems are, for the most part, avoided.
91725 * Incoming points are have their x & y values tested against
91726 * all previously seen x & y values. If either is 'too close'
91727 * to a previously seen value, it's value is 'snapped' to the
91728 * previously seen value.
91730 * All points should be rounded by this class before being
91731 * stored in any data structures in the rest of this algorithm.
91735 var PtRounder = /*#__PURE__*/function () {
91736 function PtRounder() {
91737 _classCallCheck$1(this, PtRounder);
91742 _createClass$1(PtRounder, [{
91744 value: function reset() {
91745 this.xRounder = new CoordRounder();
91746 this.yRounder = new CoordRounder();
91750 value: function round(x, y) {
91752 x: this.xRounder.round(x),
91753 y: this.yRounder.round(y)
91761 var CoordRounder = /*#__PURE__*/function () {
91762 function CoordRounder() {
91763 _classCallCheck$1(this, CoordRounder);
91765 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
91768 } // Note: this can rounds input values backwards or forwards.
91769 // You might ask, why not restrict this to just rounding
91770 // forwards? Wouldn't that allow left endpoints to always
91771 // remain left endpoints during splitting (never change to
91772 // right). No - it wouldn't, because we snap intersections
91773 // to endpoints (to establish independence from the segment
91774 // angle for t-intersections).
91777 _createClass$1(CoordRounder, [{
91779 value: function round(coord) {
91780 var node = this.tree.add(coord);
91781 var prevNode = this.tree.prev(node);
91783 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
91784 this.tree.remove(coord);
91785 return prevNode.key;
91788 var nextNode = this.tree.next(node);
91790 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
91791 this.tree.remove(coord);
91792 return nextNode.key;
91799 return CoordRounder;
91800 }(); // singleton available by import
91803 var rounder = new PtRounder();
91804 /* Cross Product of two vectors with first point at origin */
91806 var crossProduct$1 = function crossProduct(a, b) {
91807 return a.x * b.y - a.y * b.x;
91809 /* Dot Product of two vectors with first point at origin */
91812 var dotProduct$1 = function dotProduct(a, b) {
91813 return a.x * b.x + a.y * b.y;
91815 /* Comparator for two vectors with same starting point */
91818 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
91820 x: endPt1.x - basePt.x,
91821 y: endPt1.y - basePt.y
91824 x: endPt2.x - basePt.x,
91825 y: endPt2.y - basePt.y
91827 var kross = crossProduct$1(v1, v2);
91828 return cmp(kross, 0);
91831 var length = function length(v) {
91832 return Math.sqrt(dotProduct$1(v, v));
91834 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
91837 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
91839 x: pBase.x - pShared.x,
91840 y: pBase.y - pShared.y
91843 x: pAngle.x - pShared.x,
91844 y: pAngle.y - pShared.y
91846 return crossProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
91848 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
91851 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
91853 x: pBase.x - pShared.x,
91854 y: pBase.y - pShared.y
91857 x: pAngle.x - pShared.x,
91858 y: pAngle.y - pShared.y
91860 return dotProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
91862 /* Get the x coordinate where the given line (defined by a point and vector)
91863 * crosses the horizontal line with the given y coordiante.
91864 * In the case of parrallel lines (including overlapping ones) returns null. */
91867 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
91868 if (v.y === 0) return null;
91870 x: pt.x + v.x / v.y * (y - pt.y),
91874 /* Get the y coordinate where the given line (defined by a point and vector)
91875 * crosses the vertical line with the given x coordiante.
91876 * In the case of parrallel lines (including overlapping ones) returns null. */
91879 var verticalIntersection = function verticalIntersection(pt, v, x) {
91880 if (v.x === 0) return null;
91883 y: pt.y + v.y / v.x * (x - pt.x)
91886 /* Get the intersection of two lines, each defined by a base point and a vector.
91887 * In the case of parrallel lines (including overlapping ones) returns null. */
91890 var intersection$1 = function intersection(pt1, v1, pt2, v2) {
91891 // take some shortcuts for vertical and horizontal lines
91892 // this also ensures we don't calculate an intersection and then discover
91893 // it's actually outside the bounding box of the line
91894 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
91895 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
91896 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
91897 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
91898 // This algorithm is based on Schneider and Eberly.
91899 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
91901 var kross = crossProduct$1(v1, v2);
91902 if (kross == 0) return null;
91907 var d1 = crossProduct$1(ve, v1) / kross;
91908 var d2 = crossProduct$1(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
91910 var x1 = pt1.x + d2 * v1.x,
91911 x2 = pt2.x + d1 * v2.x;
91912 var y1 = pt1.y + d2 * v1.y,
91913 y2 = pt2.y + d1 * v2.y;
91914 var x = (x1 + x2) / 2;
91915 var y = (y1 + y2) / 2;
91922 var SweepEvent$1 = /*#__PURE__*/function () {
91923 _createClass$1(SweepEvent, null, [{
91925 // for ordering sweep events in the sweep event queue
91926 value: function compare(a, b) {
91927 // favor event with a point that the sweep line hits first
91928 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
91929 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
91931 if (a.point !== b.point) a.link(b); // favor right events over left
91933 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
91934 // ordering of this case is the same as for their segments
91936 return Segment.compare(a.segment, b.segment);
91937 } // for ordering points in sweep line order
91940 key: "comparePoints",
91941 value: function comparePoints(aPt, bPt) {
91942 if (aPt.x < bPt.x) return -1;
91943 if (aPt.x > bPt.x) return 1;
91944 if (aPt.y < bPt.y) return -1;
91945 if (aPt.y > bPt.y) return 1;
91947 } // Warning: 'point' input will be modified and re-used (for performance)
91951 function SweepEvent(point, isLeft) {
91952 _classCallCheck$1(this, SweepEvent);
91954 if (point.events === undefined) point.events = [this];else point.events.push(this);
91955 this.point = point;
91956 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
91959 _createClass$1(SweepEvent, [{
91961 value: function link(other) {
91962 if (other.point === this.point) {
91963 throw new Error('Tried to link already linked events');
91966 var otherEvents = other.point.events;
91968 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
91969 var evt = otherEvents[i];
91970 this.point.events.push(evt);
91971 evt.point = this.point;
91974 this.checkForConsuming();
91976 /* Do a pass over our linked events and check to see if any pair
91977 * of segments match, and should be consumed. */
91980 key: "checkForConsuming",
91981 value: function checkForConsuming() {
91982 // FIXME: The loops in this method run O(n^2) => no good.
91983 // Maintain little ordered sweep event trees?
91984 // Can we maintaining an ordering that avoids the need
91985 // for the re-sorting with getLeftmostComparator in geom-out?
91986 // Compare each pair of events to see if other events also match
91987 var numEvents = this.point.events.length;
91989 for (var i = 0; i < numEvents; i++) {
91990 var evt1 = this.point.events[i];
91991 if (evt1.segment.consumedBy !== undefined) continue;
91993 for (var j = i + 1; j < numEvents; j++) {
91994 var evt2 = this.point.events[j];
91995 if (evt2.consumedBy !== undefined) continue;
91996 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
91997 evt1.segment.consume(evt2.segment);
92002 key: "getAvailableLinkedEvents",
92003 value: function getAvailableLinkedEvents() {
92004 // point.events is always of length 2 or greater
92007 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
92008 var evt = this.point.events[i];
92010 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
92018 * Returns a comparator function for sorting linked events that will
92019 * favor the event that will give us the smallest left-side angle.
92020 * All ring construction starts as low as possible heading to the right,
92021 * so by always turning left as sharp as possible we'll get polygons
92022 * without uncessary loops & holes.
92024 * The comparator function has a compute cache such that it avoids
92025 * re-computing already-computed values.
92029 key: "getLeftmostComparator",
92030 value: function getLeftmostComparator(baseEvent) {
92033 var cache = new Map();
92035 var fillCache = function fillCache(linkedEvent) {
92036 var nextEvent = linkedEvent.otherSE;
92037 cache.set(linkedEvent, {
92038 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
92039 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
92043 return function (a, b) {
92044 if (!cache.has(a)) fillCache(a);
92045 if (!cache.has(b)) fillCache(b);
92047 var _cache$get = cache.get(a),
92048 asine = _cache$get.sine,
92049 acosine = _cache$get.cosine;
92051 var _cache$get2 = cache.get(b),
92052 bsine = _cache$get2.sine,
92053 bcosine = _cache$get2.cosine; // both on or above x-axis
92056 if (asine >= 0 && bsine >= 0) {
92057 if (acosine < bcosine) return 1;
92058 if (acosine > bcosine) return -1;
92060 } // both below x-axis
92063 if (asine < 0 && bsine < 0) {
92064 if (acosine < bcosine) return -1;
92065 if (acosine > bcosine) return 1;
92067 } // one above x-axis, one below
92070 if (bsine < asine) return -1;
92071 if (bsine > asine) return 1;
92078 }(); // segments and sweep events when all else is identical
92083 var Segment = /*#__PURE__*/function () {
92084 _createClass$1(Segment, null, [{
92087 /* This compare() function is for ordering segments in the sweep
92088 * line tree, and does so according to the following criteria:
92090 * Consider the vertical line that lies an infinestimal step to the
92091 * right of the right-more of the two left endpoints of the input
92092 * segments. Imagine slowly moving a point up from negative infinity
92093 * in the increasing y direction. Which of the two segments will that
92094 * point intersect first? That segment comes 'before' the other one.
92096 * If neither segment would be intersected by such a line, (if one
92097 * or more of the segments are vertical) then the line to be considered
92098 * is directly on the right-more of the two left inputs.
92100 value: function compare(a, b) {
92101 var alx = a.leftSE.point.x;
92102 var blx = b.leftSE.point.x;
92103 var arx = a.rightSE.point.x;
92104 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
92106 if (brx < alx) return 1;
92107 if (arx < blx) return -1;
92108 var aly = a.leftSE.point.y;
92109 var bly = b.leftSE.point.y;
92110 var ary = a.rightSE.point.y;
92111 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
92114 // are the two segments in the same horizontal plane?
92115 if (bly < aly && bly < ary) return 1;
92116 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
92118 var aCmpBLeft = a.comparePoint(b.leftSE.point);
92119 if (aCmpBLeft < 0) return 1;
92120 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
92122 var bCmpARight = b.comparePoint(a.rightSE.point);
92123 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
92124 // left endpoint to be first (arbitrary?)
92127 } // is left endpoint of segment A the right-more?
92131 if (aly < bly && aly < bry) return -1;
92132 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
92134 var bCmpALeft = b.comparePoint(a.leftSE.point);
92135 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
92137 var aCmpBRight = a.comparePoint(b.rightSE.point);
92138 if (aCmpBRight < 0) return 1;
92139 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
92140 // left endpoint to be first (arbitrary?)
92143 } // if we get here, the two left endpoints are in the same
92144 // vertical plane, ie alx === blx
92145 // consider the lower left-endpoint to come first
92148 if (aly < bly) return -1;
92149 if (aly > bly) return 1; // left endpoints are identical
92150 // check for colinearity by using the left-more right endpoint
92151 // is the A right endpoint more left-more?
92154 var _bCmpARight = b.comparePoint(a.rightSE.point);
92156 if (_bCmpARight !== 0) return _bCmpARight;
92157 } // is the B right endpoint more left-more?
92161 var _aCmpBRight = a.comparePoint(b.rightSE.point);
92163 if (_aCmpBRight < 0) return 1;
92164 if (_aCmpBRight > 0) return -1;
92168 // are these two [almost] vertical segments with opposite orientation?
92169 // if so, the one with the lower right endpoint comes first
92170 var ay = ary - aly;
92171 var ax = arx - alx;
92172 var by = bry - bly;
92173 var bx = brx - blx;
92174 if (ay > ax && by < bx) return 1;
92175 if (ay < ax && by > bx) return -1;
92176 } // we have colinear segments with matching orientation
92177 // consider the one with more left-more right endpoint to be first
92180 if (arx > brx) return 1;
92181 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
92182 // vertical plane, ie arx === brx
92183 // consider the lower right-endpoint to come first
92185 if (ary < bry) return -1;
92186 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
92187 // fall back on creation order as consistent tie-breaker
92189 if (a.id < b.id) return -1;
92190 if (a.id > b.id) return 1; // identical segment, ie a === b
92194 /* Warning: a reference to ringWindings input will be stored,
92195 * and possibly will be later modified */
92199 function Segment(leftSE, rightSE, rings, windings) {
92200 _classCallCheck$1(this, Segment);
92202 this.id = ++segmentId;
92203 this.leftSE = leftSE;
92204 leftSE.segment = this;
92205 leftSE.otherSE = rightSE;
92206 this.rightSE = rightSE;
92207 rightSE.segment = this;
92208 rightSE.otherSE = leftSE;
92209 this.rings = rings;
92210 this.windings = windings; // left unset for performance, set later in algorithm
92211 // this.ringOut, this.consumedBy, this.prev
92214 _createClass$1(Segment, [{
92215 key: "replaceRightSE",
92217 /* When a segment is split, the rightSE is replaced with a new sweep event */
92218 value: function replaceRightSE(newRightSE) {
92219 this.rightSE = newRightSE;
92220 this.rightSE.segment = this;
92221 this.rightSE.otherSE = this.leftSE;
92222 this.leftSE.otherSE = this.rightSE;
92226 value: function bbox() {
92227 var y1 = this.leftSE.point.y;
92228 var y2 = this.rightSE.point.y;
92231 x: this.leftSE.point.x,
92232 y: y1 < y2 ? y1 : y2
92235 x: this.rightSE.point.x,
92236 y: y1 > y2 ? y1 : y2
92240 /* A vector from the left point to the right */
92244 value: function vector() {
92246 x: this.rightSE.point.x - this.leftSE.point.x,
92247 y: this.rightSE.point.y - this.leftSE.point.y
92251 key: "isAnEndpoint",
92252 value: function isAnEndpoint(pt) {
92253 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;
92255 /* Compare this segment with a point.
92257 * A point P is considered to be colinear to a segment if there
92258 * exists a distance D such that if we travel along the segment
92259 * from one * endpoint towards the other a distance D, we find
92260 * ourselves at point P.
92262 * Return value indicates:
92264 * 1: point lies above the segment (to the left of vertical)
92265 * 0: point is colinear to segment
92266 * -1: point lies below the segment (to the right of vertical)
92270 key: "comparePoint",
92271 value: function comparePoint(point) {
92272 if (this.isAnEndpoint(point)) return 0;
92273 var lPt = this.leftSE.point;
92274 var rPt = this.rightSE.point;
92275 var v = this.vector(); // Exactly vertical segments.
92277 if (lPt.x === rPt.x) {
92278 if (point.x === lPt.x) return 0;
92279 return point.x < lPt.x ? 1 : -1;
92280 } // Nearly vertical segments with an intersection.
92281 // Check to see where a point on the line with matching Y coordinate is.
92284 var yDist = (point.y - lPt.y) / v.y;
92285 var xFromYDist = lPt.x + yDist * v.x;
92286 if (point.x === xFromYDist) return 0; // General case.
92287 // Check to see where a point on the line with matching X coordinate is.
92289 var xDist = (point.x - lPt.x) / v.x;
92290 var yFromXDist = lPt.y + xDist * v.y;
92291 if (point.y === yFromXDist) return 0;
92292 return point.y < yFromXDist ? -1 : 1;
92295 * Given another segment, returns the first non-trivial intersection
92296 * between the two segments (in terms of sweep line ordering), if it exists.
92298 * A 'non-trivial' intersection is one that will cause one or both of the
92299 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
92301 * * endpoint of segA with endpoint of segB --> trivial
92302 * * endpoint of segA with point along segB --> non-trivial
92303 * * endpoint of segB with point along segA --> non-trivial
92304 * * point along segA with point along segB --> non-trivial
92306 * If no non-trivial intersection exists, return null
92307 * Else, return null.
92311 key: "getIntersection",
92312 value: function getIntersection(other) {
92313 // If bboxes don't overlap, there can't be any intersections
92314 var tBbox = this.bbox();
92315 var oBbox = other.bbox();
92316 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
92317 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
92318 // This will 'snap' intersections to endpoints if possible, and will
92319 // handle cases of colinearity.
92321 var tlp = this.leftSE.point;
92322 var trp = this.rightSE.point;
92323 var olp = other.leftSE.point;
92324 var orp = other.rightSE.point; // does each endpoint touch the other segment?
92325 // note that we restrict the 'touching' definition to only allow segments
92326 // to touch endpoints that lie forward from where we are in the sweep line pass
92328 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
92329 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
92330 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
92331 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
92333 if (touchesThisLSE && touchesOtherLSE) {
92334 // these two cases are for colinear segments with matching left
92335 // endpoints, and one segment being longer than the other
92336 if (touchesThisRSE && !touchesOtherRSE) return trp;
92337 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
92338 // or just on their left endpoint (one trivial intersection
92341 } // does this left endpoint matches (other doesn't)
92344 if (touchesThisLSE) {
92345 // check for segments that just intersect on opposing endpoints
92346 if (touchesOtherRSE) {
92347 if (tlp.x === orp.x && tlp.y === orp.y) return null;
92348 } // t-intersection on left endpoint
92352 } // does other left endpoint matches (this doesn't)
92355 if (touchesOtherLSE) {
92356 // check for segments that just intersect on opposing endpoints
92357 if (touchesThisRSE) {
92358 if (trp.x === olp.x && trp.y === olp.y) return null;
92359 } // t-intersection on left endpoint
92363 } // trivial intersection on right endpoints
92366 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
92368 if (touchesThisRSE) return trp;
92369 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
92370 // infinite lines laid over the segments
92372 var pt = intersection$1(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
92373 // they would have an endpoint intersection and that case was already handled above
92375 if (pt === null) return null; // is the intersection found between the lines not on the segments?
92377 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
92379 return rounder.round(pt.x, pt.y);
92382 * Split the given segment into multiple segments on the given points.
92383 * * Each existing segment will retain its leftSE and a new rightSE will be
92384 * generated for it.
92385 * * A new segment will be generated which will adopt the original segment's
92386 * rightSE, and a new leftSE will be generated for it.
92387 * * If there are more than two points given to split on, new segments
92388 * in the middle will be generated with new leftSE and rightSE's.
92389 * * An array of the newly generated SweepEvents will be returned.
92391 * Warning: input array of points is modified
92396 value: function split(point) {
92397 var newEvents = [];
92398 var alreadyLinked = point.events !== undefined;
92399 var newLeftSE = new SweepEvent$1(point, true);
92400 var newRightSE = new SweepEvent$1(point, false);
92401 var oldRightSE = this.rightSE;
92402 this.replaceRightSE(newRightSE);
92403 newEvents.push(newRightSE);
92404 newEvents.push(newLeftSE);
92405 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
92406 // sometimes one of the resulting new segments is vertical, in which
92407 // case its left and right events may need to be swapped
92409 if (SweepEvent$1.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
92410 newSeg.swapEvents();
92413 if (SweepEvent$1.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
92415 } // in the point we just used to create new sweep events with was already
92416 // linked to other events, we need to check if either of the affected
92417 // segments should be consumed
92420 if (alreadyLinked) {
92421 newLeftSE.checkForConsuming();
92422 newRightSE.checkForConsuming();
92427 /* Swap which event is left and right */
92431 value: function swapEvents() {
92432 var tmpEvt = this.rightSE;
92433 this.rightSE = this.leftSE;
92434 this.leftSE = tmpEvt;
92435 this.leftSE.isLeft = true;
92436 this.rightSE.isLeft = false;
92438 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
92439 this.windings[i] *= -1;
92442 /* Consume another segment. We take their rings under our wing
92443 * and mark them as consumed. Use for perfectly overlapping segments */
92447 value: function consume(other) {
92448 var consumer = this;
92449 var consumee = other;
92451 while (consumer.consumedBy) {
92452 consumer = consumer.consumedBy;
92455 while (consumee.consumedBy) {
92456 consumee = consumee.consumedBy;
92459 var cmp = Segment.compare(consumer, consumee);
92460 if (cmp === 0) return; // already consumed
92461 // the winner of the consumption is the earlier segment
92462 // according to sweep line ordering
92465 var tmp = consumer;
92466 consumer = consumee;
92468 } // make sure a segment doesn't consume it's prev
92471 if (consumer.prev === consumee) {
92472 var _tmp = consumer;
92473 consumer = consumee;
92477 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
92478 var ring = consumee.rings[i];
92479 var winding = consumee.windings[i];
92480 var index = consumer.rings.indexOf(ring);
92482 if (index === -1) {
92483 consumer.rings.push(ring);
92484 consumer.windings.push(winding);
92485 } else consumer.windings[index] += winding;
92488 consumee.rings = null;
92489 consumee.windings = null;
92490 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
92492 consumee.leftSE.consumedBy = consumer.leftSE;
92493 consumee.rightSE.consumedBy = consumer.rightSE;
92495 /* The first segment previous segment chain that is in the result */
92498 key: "prevInResult",
92499 value: function prevInResult() {
92500 if (this._prevInResult !== undefined) return this._prevInResult;
92501 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
92502 return this._prevInResult;
92505 key: "beforeState",
92506 value: function beforeState() {
92507 if (this._beforeState !== undefined) return this._beforeState;
92508 if (!this.prev) this._beforeState = {
92513 var seg = this.prev.consumedBy || this.prev;
92514 this._beforeState = seg.afterState();
92516 return this._beforeState;
92520 value: function afterState() {
92521 if (this._afterState !== undefined) return this._afterState;
92522 var beforeState = this.beforeState();
92523 this._afterState = {
92524 rings: beforeState.rings.slice(0),
92525 windings: beforeState.windings.slice(0),
92528 var ringsAfter = this._afterState.rings;
92529 var windingsAfter = this._afterState.windings;
92530 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
92532 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
92533 var ring = this.rings[i];
92534 var winding = this.windings[i];
92535 var index = ringsAfter.indexOf(ring);
92537 if (index === -1) {
92538 ringsAfter.push(ring);
92539 windingsAfter.push(winding);
92540 } else windingsAfter[index] += winding;
92541 } // calcualte polysAfter
92544 var polysAfter = [];
92545 var polysExclude = [];
92547 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
92548 if (windingsAfter[_i] === 0) continue; // non-zero rule
92550 var _ring = ringsAfter[_i];
92551 var poly = _ring.poly;
92552 if (polysExclude.indexOf(poly) !== -1) continue;
92553 if (_ring.isExterior) polysAfter.push(poly);else {
92554 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
92556 var _index = polysAfter.indexOf(_ring.poly);
92558 if (_index !== -1) polysAfter.splice(_index, 1);
92560 } // calculate multiPolysAfter
92563 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
92564 var mp = polysAfter[_i2].multiPoly;
92565 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
92568 return this._afterState;
92570 /* Is this segment part of the final result? */
92574 value: function isInResult() {
92575 // if we've been consumed, we're not in the result
92576 if (this.consumedBy) return false;
92577 if (this._isInResult !== undefined) return this._isInResult;
92578 var mpsBefore = this.beforeState().multiPolys;
92579 var mpsAfter = this.afterState().multiPolys;
92581 switch (operation.type) {
92584 // UNION - included iff:
92585 // * On one side of us there is 0 poly interiors AND
92586 // * On the other side there is 1 or more.
92587 var noBefores = mpsBefore.length === 0;
92588 var noAfters = mpsAfter.length === 0;
92589 this._isInResult = noBefores !== noAfters;
92593 case 'intersection':
92595 // INTERSECTION - included iff:
92596 // * on one side of us all multipolys are rep. with poly interiors AND
92597 // * on the other side of us, not all multipolys are repsented
92598 // with poly interiors
92602 if (mpsBefore.length < mpsAfter.length) {
92603 least = mpsBefore.length;
92604 most = mpsAfter.length;
92606 least = mpsAfter.length;
92607 most = mpsBefore.length;
92610 this._isInResult = most === operation.numMultiPolys && least < most;
92616 // XOR - included iff:
92617 // * the difference between the number of multipolys represented
92618 // with poly interiors on our two sides is an odd number
92619 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
92620 this._isInResult = diff % 2 === 1;
92626 // DIFFERENCE included iff:
92627 // * on exactly one side, we have just the subject
92628 var isJustSubject = function isJustSubject(mps) {
92629 return mps.length === 1 && mps[0].isSubject;
92632 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
92637 throw new Error("Unrecognized operation type found ".concat(operation.type));
92640 return this._isInResult;
92644 value: function fromRing(pt1, pt2, ring) {
92645 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
92647 var cmpPts = SweepEvent$1.comparePoints(pt1, pt2);
92653 } else if (cmpPts > 0) {
92657 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
92659 var leftSE = new SweepEvent$1(leftPt, true);
92660 var rightSE = new SweepEvent$1(rightPt, false);
92661 return new Segment(leftSE, rightSE, [ring], [winding]);
92668 var RingIn = /*#__PURE__*/function () {
92669 function RingIn(geomRing, poly, isExterior) {
92670 _classCallCheck$1(this, RingIn);
92672 if (!Array.isArray(geomRing) || geomRing.length === 0) {
92673 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
92677 this.isExterior = isExterior;
92678 this.segments = [];
92680 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
92681 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
92684 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
92695 var prevPoint = firstPoint;
92697 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
92698 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
92699 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
92702 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
92704 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
92705 this.segments.push(Segment.fromRing(prevPoint, point, this));
92706 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
92707 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
92708 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
92709 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
92711 } // add segment from last to first if last is not the same as first
92714 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
92715 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
92719 _createClass$1(RingIn, [{
92720 key: "getSweepEvents",
92721 value: function getSweepEvents() {
92722 var sweepEvents = [];
92724 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
92725 var segment = this.segments[i];
92726 sweepEvents.push(segment.leftSE);
92727 sweepEvents.push(segment.rightSE);
92730 return sweepEvents;
92737 var PolyIn = /*#__PURE__*/function () {
92738 function PolyIn(geomPoly, multiPoly) {
92739 _classCallCheck$1(this, PolyIn);
92741 if (!Array.isArray(geomPoly)) {
92742 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
92745 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
92749 x: this.exteriorRing.bbox.ll.x,
92750 y: this.exteriorRing.bbox.ll.y
92753 x: this.exteriorRing.bbox.ur.x,
92754 y: this.exteriorRing.bbox.ur.y
92757 this.interiorRings = [];
92759 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
92760 var ring = new RingIn(geomPoly[i], this, false);
92761 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
92762 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
92763 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
92764 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
92765 this.interiorRings.push(ring);
92768 this.multiPoly = multiPoly;
92771 _createClass$1(PolyIn, [{
92772 key: "getSweepEvents",
92773 value: function getSweepEvents() {
92774 var sweepEvents = this.exteriorRing.getSweepEvents();
92776 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
92777 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
92779 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
92780 sweepEvents.push(ringSweepEvents[j]);
92784 return sweepEvents;
92791 var MultiPolyIn = /*#__PURE__*/function () {
92792 function MultiPolyIn(geom, isSubject) {
92793 _classCallCheck$1(this, MultiPolyIn);
92795 if (!Array.isArray(geom)) {
92796 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
92800 // if the input looks like a polygon, convert it to a multipolygon
92801 if (typeof geom[0][0][0] === 'number') geom = [geom];
92802 } catch (ex) {// The input is either malformed or has empty arrays.
92803 // In either case, it will be handled later on.
92809 x: Number.POSITIVE_INFINITY,
92810 y: Number.POSITIVE_INFINITY
92813 x: Number.NEGATIVE_INFINITY,
92814 y: Number.NEGATIVE_INFINITY
92818 for (var i = 0, iMax = geom.length; i < iMax; i++) {
92819 var poly = new PolyIn(geom[i], this);
92820 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
92821 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
92822 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
92823 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
92824 this.polys.push(poly);
92827 this.isSubject = isSubject;
92830 _createClass$1(MultiPolyIn, [{
92831 key: "getSweepEvents",
92832 value: function getSweepEvents() {
92833 var sweepEvents = [];
92835 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
92836 var polySweepEvents = this.polys[i].getSweepEvents();
92838 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
92839 sweepEvents.push(polySweepEvents[j]);
92843 return sweepEvents;
92847 return MultiPolyIn;
92850 var RingOut = /*#__PURE__*/function () {
92851 _createClass$1(RingOut, null, [{
92854 /* Given the segments from the sweep line pass, compute & return a series
92855 * of closed rings from all the segments marked to be part of the result */
92856 value: function factory(allSegments) {
92859 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
92860 var segment = allSegments[i];
92861 if (!segment.isInResult() || segment.ringOut) continue;
92862 var prevEvent = null;
92863 var event = segment.leftSE;
92864 var nextEvent = segment.rightSE;
92865 var events = [event];
92866 var startingPoint = event.point;
92867 var intersectionLEs = [];
92868 /* Walk the chain of linked events to form a closed ring */
92873 events.push(event);
92874 /* Is the ring complete? */
92876 if (event.point === startingPoint) break;
92879 var availableLEs = event.getAvailableLinkedEvents();
92880 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
92881 * part of the algorithm malfunctioned... please file a bug report. */
92883 if (availableLEs.length === 0) {
92884 var firstPt = events[0].point;
92885 var lastPt = events[events.length - 1].point;
92886 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, "]."));
92888 /* Only one way to go, so cotinue on the path */
92891 if (availableLEs.length === 1) {
92892 nextEvent = availableLEs[0].otherSE;
92895 /* We must have an intersection. Check for a completed loop */
92898 var indexLE = null;
92900 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
92901 if (intersectionLEs[j].point === event.point) {
92906 /* Found a completed loop. Cut that off and make a ring */
92909 if (indexLE !== null) {
92910 var intersectionLE = intersectionLEs.splice(indexLE)[0];
92911 var ringEvents = events.splice(intersectionLE.index);
92912 ringEvents.unshift(ringEvents[0].otherSE);
92913 ringsOut.push(new RingOut(ringEvents.reverse()));
92916 /* register the intersection */
92919 intersectionLEs.push({
92920 index: events.length,
92923 /* Choose the left-most option to continue the walk */
92925 var comparator = event.getLeftmostComparator(prevEvent);
92926 nextEvent = availableLEs.sort(comparator)[0].otherSE;
92931 ringsOut.push(new RingOut(events));
92938 function RingOut(events) {
92939 _classCallCheck$1(this, RingOut);
92941 this.events = events;
92943 for (var i = 0, iMax = events.length; i < iMax; i++) {
92944 events[i].segment.ringOut = this;
92950 _createClass$1(RingOut, [{
92952 value: function getGeom() {
92953 // Remove superfluous points (ie extra points along a straight line),
92954 var prevPt = this.events[0].point;
92955 var points = [prevPt];
92957 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
92958 var _pt = this.events[i].point;
92959 var _nextPt = this.events[i + 1].point;
92960 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
92963 } // ring was all (within rounding error of angle calc) colinear points
92966 if (points.length === 1) return null; // check if the starting point is necessary
92968 var pt = points[0];
92969 var nextPt = points[1];
92970 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
92971 points.push(points[0]);
92972 var step = this.isExteriorRing() ? 1 : -1;
92973 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
92974 var iEnd = this.isExteriorRing() ? points.length : -1;
92975 var orderedPoints = [];
92977 for (var _i = iStart; _i != iEnd; _i += step) {
92978 orderedPoints.push([points[_i].x, points[_i].y]);
92981 return orderedPoints;
92984 key: "isExteriorRing",
92985 value: function isExteriorRing() {
92986 if (this._isExteriorRing === undefined) {
92987 var enclosing = this.enclosingRing();
92988 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
92991 return this._isExteriorRing;
92994 key: "enclosingRing",
92995 value: function enclosingRing() {
92996 if (this._enclosingRing === undefined) {
92997 this._enclosingRing = this._calcEnclosingRing();
93000 return this._enclosingRing;
93002 /* Returns the ring that encloses this one, if any */
93005 key: "_calcEnclosingRing",
93006 value: function _calcEnclosingRing() {
93007 // start with the ealier sweep line event so that the prevSeg
93008 // chain doesn't lead us inside of a loop of ours
93009 var leftMostEvt = this.events[0];
93011 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
93012 var evt = this.events[i];
93013 if (SweepEvent$1.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
93016 var prevSeg = leftMostEvt.segment.prevInResult();
93017 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93020 // no segment found, thus no ring can enclose us
93021 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
93022 // segment must loop back around and enclose us
93024 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
93025 // segment must either loop around us or the ring of the prev prev
93026 // seg, which would make us and the ring of the prev peers
93028 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
93029 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
93030 return prevSeg.ringOut;
93031 } else return prevSeg.ringOut.enclosingRing();
93032 } // two segments are from the same ring, so this was a penisula
93033 // of that ring. iterate downward, keep searching
93036 prevSeg = prevPrevSeg.prevInResult();
93037 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93045 var PolyOut = /*#__PURE__*/function () {
93046 function PolyOut(exteriorRing) {
93047 _classCallCheck$1(this, PolyOut);
93049 this.exteriorRing = exteriorRing;
93050 exteriorRing.poly = this;
93051 this.interiorRings = [];
93054 _createClass$1(PolyOut, [{
93055 key: "addInterior",
93056 value: function addInterior(ring) {
93057 this.interiorRings.push(ring);
93062 value: function getGeom() {
93063 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
93065 if (geom[0] === null) return null;
93067 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93068 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
93070 if (ringGeom === null) continue;
93071 geom.push(ringGeom);
93081 var MultiPolyOut = /*#__PURE__*/function () {
93082 function MultiPolyOut(rings) {
93083 _classCallCheck$1(this, MultiPolyOut);
93085 this.rings = rings;
93086 this.polys = this._composePolys(rings);
93089 _createClass$1(MultiPolyOut, [{
93091 value: function getGeom() {
93094 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93095 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
93097 if (polyGeom === null) continue;
93098 geom.push(polyGeom);
93104 key: "_composePolys",
93105 value: function _composePolys(rings) {
93108 for (var i = 0, iMax = rings.length; i < iMax; i++) {
93109 var ring = rings[i];
93110 if (ring.poly) continue;
93111 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
93112 var enclosingRing = ring.enclosingRing();
93113 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
93114 enclosingRing.poly.addInterior(ring);
93122 return MultiPolyOut;
93125 * NOTE: We must be careful not to change any segments while
93126 * they are in the SplayTree. AFAIK, there's no way to tell
93127 * the tree to rebalance itself - thus before splitting
93128 * a segment that's in the tree, we remove it from the tree,
93129 * do the split, then re-insert it. (Even though splitting a
93130 * segment *shouldn't* change its correct position in the
93131 * sweep line tree, the reality is because of rounding errors,
93132 * it sometimes does.)
93136 var SweepLine = /*#__PURE__*/function () {
93137 function SweepLine(queue) {
93138 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
93140 _classCallCheck$1(this, SweepLine);
93142 this.queue = queue;
93143 this.tree = new Tree(comparator);
93144 this.segments = [];
93147 _createClass$1(SweepLine, [{
93149 value: function process(event) {
93150 var segment = event.segment;
93151 var newEvents = []; // if we've already been consumed by another segment,
93152 // clean up our body parts and get out
93154 if (event.consumedBy) {
93155 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
93159 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
93160 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.');
93161 var prevNode = node;
93162 var nextNode = node;
93163 var prevSeg = undefined;
93164 var nextSeg = undefined; // skip consumed segments still in tree
93166 while (prevSeg === undefined) {
93167 prevNode = this.tree.prev(prevNode);
93168 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
93169 } // skip consumed segments still in tree
93172 while (nextSeg === undefined) {
93173 nextNode = this.tree.next(nextNode);
93174 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
93177 if (event.isLeft) {
93178 // Check for intersections against the previous segment in the sweep line
93179 var prevMySplitter = null;
93182 var prevInter = prevSeg.getIntersection(segment);
93184 if (prevInter !== null) {
93185 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
93187 if (!prevSeg.isAnEndpoint(prevInter)) {
93188 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
93190 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
93191 newEvents.push(newEventsFromSplit[i]);
93195 } // Check for intersections against the next segment in the sweep line
93198 var nextMySplitter = null;
93201 var nextInter = nextSeg.getIntersection(segment);
93203 if (nextInter !== null) {
93204 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
93206 if (!nextSeg.isAnEndpoint(nextInter)) {
93207 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
93209 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
93210 newEvents.push(_newEventsFromSplit[_i]);
93214 } // For simplicity, even if we find more than one intersection we only
93215 // spilt on the 'earliest' (sweep-line style) of the intersections.
93216 // The other intersection will be handled in a future process().
93219 if (prevMySplitter !== null || nextMySplitter !== null) {
93220 var mySplitter = null;
93221 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
93222 var cmpSplitters = SweepEvent$1.comparePoints(prevMySplitter, nextMySplitter);
93223 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
93224 } // Rounding errors can cause changes in ordering,
93225 // so remove afected segments and right sweep events before splitting
93227 this.queue.remove(segment.rightSE);
93228 newEvents.push(segment.rightSE);
93230 var _newEventsFromSplit2 = segment.split(mySplitter);
93232 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
93233 newEvents.push(_newEventsFromSplit2[_i2]);
93237 if (newEvents.length > 0) {
93238 // We found some intersections, so re-do the current event to
93239 // make sure sweep line ordering is totally consistent for later
93240 // use with the segment 'prev' pointers
93241 this.tree.remove(segment);
93242 newEvents.push(event);
93244 // done with left event
93245 this.segments.push(segment);
93246 segment.prev = prevSeg;
93250 // since we're about to be removed from the sweep line, check for
93251 // intersections between our previous and next segments
93252 if (prevSeg && nextSeg) {
93253 var inter = prevSeg.getIntersection(nextSeg);
93255 if (inter !== null) {
93256 if (!prevSeg.isAnEndpoint(inter)) {
93257 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
93259 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
93260 newEvents.push(_newEventsFromSplit3[_i3]);
93264 if (!nextSeg.isAnEndpoint(inter)) {
93265 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
93267 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
93268 newEvents.push(_newEventsFromSplit4[_i4]);
93274 this.tree.remove(segment);
93279 /* Safely split a segment that is currently in the datastructures
93280 * IE - a segment other than the one that is currently being processed. */
93283 key: "_splitSafely",
93284 value: function _splitSafely(seg, pt) {
93285 // Rounding errors can cause changes in ordering,
93286 // so remove afected segments and right sweep events before splitting
93287 // removeNode() doesn't work, so have re-find the seg
93288 // https://github.com/w8r/splay-tree/pull/5
93289 this.tree.remove(seg);
93290 var rightSE = seg.rightSE;
93291 this.queue.remove(rightSE);
93292 var newEvents = seg.split(pt);
93293 newEvents.push(rightSE); // splitting can trigger consumption
93295 if (seg.consumedBy === undefined) this.tree.insert(seg);
93303 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
93304 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
93306 var Operation = /*#__PURE__*/function () {
93307 function Operation() {
93308 _classCallCheck$1(this, Operation);
93311 _createClass$1(Operation, [{
93313 value: function run(type, geom, moreGeoms) {
93314 operation.type = type;
93316 /* Convert inputs to MultiPoly objects */
93318 var multipolys = [new MultiPolyIn(geom, true)];
93320 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
93321 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
93324 operation.numMultiPolys = multipolys.length;
93325 /* BBox optimization for difference operation
93326 * If the bbox of a multipolygon that's part of the clipping doesn't
93327 * intersect the bbox of the subject at all, we can just drop that
93330 if (operation.type === 'difference') {
93331 // in place removal
93332 var subject = multipolys[0];
93335 while (_i < multipolys.length) {
93336 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
93339 /* BBox optimization for intersection operation
93340 * If we can find any pair of multipolygons whose bbox does not overlap,
93341 * then the result will be empty. */
93344 if (operation.type === 'intersection') {
93345 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
93346 // it could be optimized to O(n * ln(n))
93347 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
93348 var mpA = multipolys[_i2];
93350 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
93351 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
93355 /* Put segment endpoints in a priority queue */
93358 var queue = new Tree(SweepEvent$1.compare);
93360 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
93361 var sweepEvents = multipolys[_i3].getSweepEvents();
93363 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
93364 queue.insert(sweepEvents[_j]);
93366 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
93367 // prevents an infinite loop, an otherwise common manifestation of bugs
93368 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
93372 /* Pass the sweep line over those endpoints */
93375 var sweepLine = new SweepLine(queue);
93376 var prevQueueSize = queue.size;
93377 var node = queue.pop();
93380 var evt = node.key;
93382 if (queue.size === prevQueueSize) {
93383 // prevents an infinite loop, an otherwise common manifestation of bugs
93384 var seg = evt.segment;
93385 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.');
93388 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
93389 // prevents an infinite loop, an otherwise common manifestation of bugs
93390 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
93393 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
93394 // prevents an infinite loop, an otherwise common manifestation of bugs
93395 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
93398 var newEvents = sweepLine.process(evt);
93400 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
93401 var _evt = newEvents[_i4];
93402 if (_evt.consumedBy === undefined) queue.insert(_evt);
93405 prevQueueSize = queue.size;
93406 node = queue.pop();
93407 } // free some memory we don't need anymore
93411 /* Collect and compile segments we're keeping into a multipolygon */
93413 var ringsOut = RingOut.factory(sweepLine.segments);
93414 var result = new MultiPolyOut(ringsOut);
93415 return result.getGeom();
93420 }(); // singleton available by import
93423 var operation = new Operation();
93425 var union$1 = function union(geom) {
93426 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
93427 moreGeoms[_key - 1] = arguments[_key];
93430 return operation.run('union', geom, moreGeoms);
93433 var intersection$1$1 = function intersection(geom) {
93434 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
93435 moreGeoms[_key2 - 1] = arguments[_key2];
93438 return operation.run('intersection', geom, moreGeoms);
93441 var xor = function xor(geom) {
93442 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
93443 moreGeoms[_key3 - 1] = arguments[_key3];
93446 return operation.run('xor', geom, moreGeoms);
93449 var difference = function difference(subjectGeom) {
93450 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
93451 clippingGeoms[_key4 - 1] = arguments[_key4];
93454 return operation.run('difference', subjectGeom, clippingGeoms);
93459 intersection: intersection$1$1,
93461 difference: difference
93464 var geojsonPrecision = createCommonjsModule(function (module) {
93466 function parse(t, coordinatePrecision, extrasPrecision) {
93467 function point(p) {
93468 return p.map(function (e, index) {
93470 return 1 * e.toFixed(coordinatePrecision);
93472 return 1 * e.toFixed(extrasPrecision);
93477 function multi(l) {
93478 return l.map(point);
93482 return p.map(multi);
93485 function multiPoly(m) {
93486 return m.map(poly);
93489 function geometry(obj) {
93494 switch (obj.type) {
93496 obj.coordinates = point(obj.coordinates);
93501 obj.coordinates = multi(obj.coordinates);
93505 case "MultiLineString":
93506 obj.coordinates = poly(obj.coordinates);
93509 case "MultiPolygon":
93510 obj.coordinates = multiPoly(obj.coordinates);
93513 case "GeometryCollection":
93514 obj.geometries = obj.geometries.map(geometry);
93522 function feature(obj) {
93523 obj.geometry = geometry(obj.geometry);
93527 function featureCollection(f) {
93528 f.features = f.features.map(feature);
93532 function geometryCollection(g) {
93533 g.geometries = g.geometries.map(geometry);
93545 case "GeometryCollection":
93546 return geometryCollection(t);
93548 case "FeatureCollection":
93549 return featureCollection(t);
93555 case "MultiPolygon":
93556 case "MultiLineString":
93557 return geometry(t);
93564 module.exports = parse;
93565 module.exports.parse = parse;
93569 function isObject$4(obj) {
93570 return _typeof(obj) === 'object' && obj !== null;
93573 function forEach(obj, cb) {
93574 if (Array.isArray(obj)) {
93576 } else if (isObject$4(obj)) {
93577 Object.keys(obj).forEach(function (key) {
93578 var val = obj[key];
93584 function getTreeDepth(obj) {
93587 if (Array.isArray(obj) || isObject$4(obj)) {
93588 forEach(obj, function (val) {
93589 if (Array.isArray(val) || isObject$4(val)) {
93590 var tmpDepth = getTreeDepth(val);
93592 if (tmpDepth > depth) {
93603 function stringify(obj, options) {
93604 options = options || {};
93605 var indent = JSON.stringify([1], null, get$5(options, 'indent', 2)).slice(2, -3);
93606 var addMargin = get$5(options, 'margins', false);
93607 var addArrayMargin = get$5(options, 'arrayMargins', false);
93608 var addObjectMargin = get$5(options, 'objectMargins', false);
93609 var maxLength = indent === '' ? Infinity : get$5(options, 'maxLength', 80);
93610 var maxNesting = get$5(options, 'maxNesting', Infinity);
93611 return function _stringify(obj, currentIndent, reserved) {
93612 if (obj && typeof obj.toJSON === 'function') {
93613 obj = obj.toJSON();
93616 var string = JSON.stringify(obj);
93618 if (string === undefined) {
93622 var length = maxLength - currentIndent.length - reserved;
93623 var treeDepth = getTreeDepth(obj);
93625 if (treeDepth <= maxNesting && string.length <= length) {
93626 var prettified = prettify(string, {
93627 addMargin: addMargin,
93628 addArrayMargin: addArrayMargin,
93629 addObjectMargin: addObjectMargin
93632 if (prettified.length <= length) {
93637 if (isObject$4(obj)) {
93638 var nextIndent = currentIndent + indent;
93642 var comma = function comma(array, index) {
93643 return index === array.length - 1 ? 0 : 1;
93646 if (Array.isArray(obj)) {
93647 for (var index = 0; index < obj.length; index++) {
93648 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
93653 Object.keys(obj).forEach(function (key, index, array) {
93654 var keyPart = JSON.stringify(key) + ': ';
93656 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
93658 if (value !== undefined) {
93659 items.push(keyPart + value);
93665 if (items.length > 0) {
93666 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
93672 } // Note: This regex matches even invalid JSON strings, but since we’re
93673 // working on the output of `JSON.stringify` we know that only valid strings
93674 // are present (unless the user supplied a weird `options.indent` but in
93675 // that case we don’t care since the output would be invalid anyway).
93678 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
93680 function prettify(string, options) {
93681 options = options || {};
93691 if (options.addMargin || options.addObjectMargin) {
93692 tokens['{'] = '{ ';
93693 tokens['}'] = ' }';
93696 if (options.addMargin || options.addArrayMargin) {
93697 tokens['['] = '[ ';
93698 tokens[']'] = ' ]';
93701 return string.replace(stringOrChar, function (match, string) {
93702 return string ? match : tokens[match];
93706 function get$5(options, name, defaultValue) {
93707 return name in options ? options[name] : defaultValue;
93710 var jsonStringifyPrettyCompact = stringify;
93712 var _default$2 = /*#__PURE__*/function () {
93715 // `fc` Optional FeatureCollection of known features
93717 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
93718 // Each feature must have a filename-like `id`, for example: `something.geojson`
93721 // "type": "FeatureCollection"
93724 // "type": "Feature",
93725 // "id": "philly_metro.geojson",
93726 // "properties": { … },
93727 // "geometry": { … }
93731 function _default(fc) {
93734 _classCallCheck(this, _default);
93736 // The _cache retains resolved features, so if you ask for the same thing multiple times
93737 // we don't repeat the expensive resolving/clipping operations.
93739 // Each feature has a stable identifier that is used as the cache key.
93740 // The identifiers look like:
93741 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
93742 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
93743 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
93744 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
93745 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
93746 // When strict mode = false, return `null` for invalid locations or locationSets.
93748 this._strict = true; // process input FeatureCollection
93750 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
93751 fc.features.forEach(function (feature) {
93752 feature.properties = feature.properties || {};
93753 var props = feature.properties; // get `id` from either `id` or `properties`
93755 var id = feature.id || props.id;
93756 if (!id || !/^\S+\.geojson$/i.test(id)) return; // ensure `id` exists and is lowercase
93758 id = id.toLowerCase();
93760 props.id = id; // ensure `area` property exists
93763 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
93765 props.area = Number(area.toFixed(2));
93768 _this._cache[id] = feature;
93770 } // Replace CountryCoder world geometry to be a polygon covering the world.
93773 var world = _cloneDeep(feature$1('Q2'));
93777 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
93780 world.properties.id = 'Q2';
93781 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
93783 this._cache.Q2 = world;
93784 } // validateLocation
93785 // `location` The location to validate
93787 // Pass a `location` value to validate
93789 // Returns a result like:
93791 // type: 'point', 'geojson', or 'countrycoder'
93792 // location: the queried location
93793 // id: the stable identifier for the feature
93795 // or `null` if the location is invalid
93799 _createClass(_default, [{
93800 key: "validateLocation",
93801 value: function validateLocation(location) {
93802 if (Array.isArray(location)) {
93803 // a [lon,lat] coordinate pair?
93804 if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) && location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90) {
93805 var id = '[' + location.toString() + ']';
93808 location: location,
93812 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
93813 // a .geojson filename?
93814 var _id = location.toLowerCase();
93816 if (this._cache[_id]) {
93819 location: location,
93823 } else if (typeof location === 'string' || typeof location === 'number') {
93824 // a country-coder value?
93825 var feature = feature$1(location);
93828 // Use wikidata QID as the identifier, since that seems to be the one
93829 // property that everything in CountryCoder is guaranteed to have.
93830 var _id2 = feature.properties.wikidata;
93832 type: 'countrycoder',
93833 location: location,
93839 if (this._strict) {
93840 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
93844 } // resolveLocation
93845 // `location` The location to resolve
93847 // Pass a `location` value to resolve
93849 // Returns a result like:
93851 // type: 'point', 'geojson', or 'countrycoder'
93852 // location: the queried location
93853 // id: a stable identifier for the feature
93854 // feature: the resolved GeoJSON feature
93856 // or `null` if the location is invalid
93860 key: "resolveLocation",
93861 value: function resolveLocation(location) {
93862 var valid = this.validateLocation(location);
93863 if (!valid) return null;
93864 var id = valid.id; // return a result from cache if we can
93866 if (this._cache[id]) {
93867 return Object.assign(valid, {
93868 feature: this._cache[id]
93870 } // a [lon,lat] coordinate pair?
93873 if (valid.type === 'point') {
93874 var RADIUS = 25000; // meters
93878 var area = Math.PI * RADIUS * RADIUS / 1e6; // m² to km²
93880 var feature = this._cache[id] = geojsonPrecision({
93885 area: Number(area.toFixed(2))
93887 geometry: circleToPolygon(location, RADIUS, EDGES)
93889 return Object.assign(valid, {
93891 }); // a .geojson filename?
93892 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
93893 var _feature = _cloneDeep(feature$1(id));
93895 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
93896 // CountryCoder includes higher level features which are made up of members.
93897 // These features don't have their own geometry, but CountryCoder provides an
93898 // `aggregateFeature` method to combine these members into a MultiPolygon.
93899 // BUT, when we try to actually work with these aggregated MultiPolygons,
93900 // Turf/JSTS gets crashy because of topography bugs.
93901 // SO, we'll aggregate the features ourselves by unioning them together.
93902 // This approach also has the benefit of removing all the internal boaders and
93903 // simplifying the regional polygons a lot.
93905 if (Array.isArray(props.members)) {
93906 var seed = _feature.geometry ? _feature : null;
93907 var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
93908 _feature.geometry = aggregate.geometry;
93909 } // ensure `area` property exists
93913 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
93916 props.area = Number(_area.toFixed(2));
93917 } // ensure `id` property exists
93922 this._cache[id] = _feature;
93923 return Object.assign(valid, {
93928 if (this._strict) {
93929 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
93933 } // validateLocationSet
93934 // `locationSet` the locationSet to validate
93936 // Pass a locationSet Object to validate like:
93938 // include: [ Array of locations ],
93939 // exclude: [ Array of locations ]
93942 // Returns a result like:
93944 // type: 'locationset'
93945 // locationSet: the queried locationSet
93946 // id: the stable identifier for the feature
93948 // or `null` if the locationSet is invalid
93952 key: "validateLocationSet",
93953 value: function validateLocationSet(locationSet) {
93954 locationSet = locationSet || {};
93955 var validator = this.validateLocation.bind(this);
93956 var include = (locationSet.include || []).map(validator).filter(Boolean);
93957 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
93959 if (!include.length) {
93960 if (this._strict) {
93961 throw new Error("validateLocationSet: LocationSet includes nothing.");
93963 // non-strict mode, replace an empty locationSet with one that includes "the world"
93964 locationSet.include = ['Q2'];
93966 type: 'countrycoder',
93971 } // generate stable identifier
93974 include.sort(_sortLocations);
93975 var id = '+[' + include.map(function (d) {
93977 }).join(',') + ']';
93979 if (exclude.length) {
93980 exclude.sort(_sortLocations);
93981 id += '-[' + exclude.map(function (d) {
93983 }).join(',') + ']';
93987 type: 'locationset',
93988 locationSet: locationSet,
93991 } // resolveLocationSet
93992 // `locationSet` the locationSet to resolve
93994 // Pass a locationSet Object to validate like:
93996 // include: [ Array of locations ],
93997 // exclude: [ Array of locations ]
94000 // Returns a result like:
94002 // type: 'locationset'
94003 // locationSet: the queried locationSet
94004 // id: the stable identifier for the feature
94005 // feature: the resolved GeoJSON feature
94007 // or `null` if the locationSet is invalid
94011 key: "resolveLocationSet",
94012 value: function resolveLocationSet(locationSet) {
94013 locationSet = locationSet || {};
94014 var valid = this.validateLocationSet(locationSet);
94015 if (!valid) return null;
94016 var id = valid.id; // return a result from cache if we can
94018 if (this._cache[id]) {
94019 return Object.assign(valid, {
94020 feature: this._cache[id]
94024 var resolver = this.resolveLocation.bind(this);
94025 var include = (locationSet.include || []).map(resolver).filter(Boolean);
94026 var exclude = (locationSet.exclude || []).map(resolver).filter(Boolean); // return quickly if it's a single included location..
94028 if (include.length === 1 && exclude.length === 0) {
94029 return Object.assign(valid, {
94030 feature: include[0].feature
94032 } // calculate unions
94035 var includeGeoJSON = include.map(function (d) {
94037 }).reduce(_locationReducer.bind(this), null);
94038 var excludeGeoJSON = exclude.map(function (d) {
94040 }).reduce(_locationReducer.bind(this), null); // calculate difference, update `area` and return result
94042 var resultGeoJSON = excludeGeoJSON ? _clip(includeGeoJSON, excludeGeoJSON, 'DIFFERENCE') : includeGeoJSON;
94043 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
94045 resultGeoJSON.id = id;
94046 resultGeoJSON.properties = {
94048 area: Number(area.toFixed(2))
94050 this._cache[id] = resultGeoJSON;
94051 return Object.assign(valid, {
94052 feature: resultGeoJSON
94059 value: function strict(val) {
94060 if (val === undefined) {
94062 return this._strict;
94065 this._strict = val;
94069 // convenience method to access the internal cache
94073 value: function cache() {
94074 return this._cache;
94076 // convenience method to prettyStringify the given object
94080 value: function stringify(obj, options) {
94081 return jsonStringifyPrettyCompact(obj, options);
94086 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
94088 function _clip(a, b, which) {
94090 UNION: index$1.union,
94091 DIFFERENCE: index$1.difference
94093 var coords = fn(a.geometry.coordinates, b.geometry.coordinates);
94098 type: whichType(coords),
94099 coordinates: coords
94101 }; // is this a Polygon or a MultiPolygon?
94103 function whichType(coords) {
94104 var a = Array.isArray(coords);
94105 var b = a && Array.isArray(coords[0]);
94106 var c = b && Array.isArray(coords[0][0]);
94107 var d = c && Array.isArray(coords[0][0][0]);
94108 return d ? 'MultiPolygon' : 'Polygon';
94110 } // Reduce an array of locations into a single GeoJSON feature
94113 function _locationReducer(accumulator, location) {
94114 /* eslint-disable no-console, no-invalid-this */
94118 var resolved = this.resolveLocation(location);
94120 if (!resolved || !resolved.feature) {
94121 console.warn("Warning: Couldn't resolve location \"".concat(location, "\""));
94122 return accumulator;
94125 result = !accumulator ? resolved.feature : _clip(accumulator, resolved.feature, 'UNION');
94127 console.warn("Warning: Error resolving location \"".concat(location, "\""));
94129 result = accumulator;
94133 /* eslint-enable no-console, no-invalid-this */
94136 function _cloneDeep(obj) {
94137 return JSON.parse(JSON.stringify(obj));
94138 } // Sorting the location lists is ok because they end up unioned together.
94139 // This sorting makes it possible to generate a deterministic id.
94142 function _sortLocations(a, b) {
94148 var aRank = rank[a.type];
94149 var bRank = rank[b.type];
94150 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
94154 function uiSuccess(context) {
94156 var dispatch$1 = dispatch('cancel');
94162 ensureOSMCommunityIndex(); // start fetching the data
94164 function ensureOSMCommunityIndex() {
94165 var data = _mainFileFetcher;
94166 return Promise.all([data.get('oci_resources'), data.get('oci_features')]).then(function (vals) {
94167 if (_oci) return _oci;
94168 var ociResources = vals[0].resources;
94169 var loco = new _default$2(vals[1]);
94170 var ociFeatures = {};
94171 Object.values(ociResources).forEach(function (resource) {
94172 var feature = loco.resolveLocationSet(resource.locationSet).feature;
94173 var ociFeature = ociFeatures[feature.id];
94176 ociFeature = JSON.parse(JSON.stringify(feature)); // deep clone
94178 ociFeature.properties.resourceIDs = new Set();
94179 ociFeatures[feature.id] = ociFeature;
94182 ociFeature.properties.resourceIDs.add(resource.id);
94185 features: ociFeatures,
94186 resources: ociResources,
94187 query: whichPolygon_1({
94188 type: 'FeatureCollection',
94189 features: Object.values(ociFeatures)
94193 } // string-to-date parsing in JavaScript is weird
94196 function parseEventDate(when) {
94198 var raw = when.trim();
94201 if (!/Z$/.test(raw)) {
94202 // if no trailing 'Z', add one
94203 raw += 'Z'; // this forces date to be parsed as a UTC date
94206 var parsed = new Date(raw);
94207 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
94210 function success(selection) {
94211 var header = selection.append('div').attr('class', 'header fillL');
94212 header.append('h3').html(_t.html('success.just_edited'));
94213 header.append('button').attr('class', 'close').on('click', function () {
94214 return dispatch$1.call('cancel');
94215 }).call(svgIcon('#iD-icon-close'));
94216 var body = selection.append('div').attr('class', 'body save-success fillL');
94217 var summary = body.append('div').attr('class', 'save-summary');
94218 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
94221 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'));
94222 var osm = context.connection();
94224 var changesetURL = osm.changesetURL(_changeset.id);
94225 var table = summary.append('table').attr('class', 'summary-table');
94226 var row = table.append('tr').attr('class', 'summary-row');
94227 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');
94228 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
94229 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
94230 summaryDetail.append('div').html(_t.html('success.changeset_id', {
94231 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
94232 })); // Get OSM community index features intersecting the map..
94234 ensureOSMCommunityIndex().then(function (oci) {
94235 var communities = [];
94236 var properties = oci.query(context.map().center(), true) || []; // Gather the communities from the result
94238 properties.forEach(function (props) {
94239 var resourceIDs = Array.from(props.resourceIDs);
94240 resourceIDs.forEach(function (resourceID) {
94241 var resource = oci.resources[resourceID];
94243 area: props.area || Infinity,
94244 order: resource.order || 0,
94248 }); // sort communities by feature area ascending, community order descending
94250 communities.sort(function (a, b) {
94251 return a.area - b.area || b.order - a.order;
94253 body.call(showCommunityLinks, communities.map(function (c) {
94259 function showCommunityLinks(selection, resources) {
94260 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
94261 communityLinks.append('h3').html(_t.html('success.like_osm'));
94262 var table = communityLinks.append('table').attr('class', 'community-table');
94263 var row = table.selectAll('.community-row').data(resources);
94264 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
94265 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
94267 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
94268 return "#community-".concat(d.type);
94270 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
94271 communityDetail.each(showCommunityDetails);
94272 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'));
94275 function showCommunityDetails(d) {
94276 var selection = select(this);
94277 var communityID = d.id;
94278 var replacements = {
94279 url: linkify(d.url),
94280 signupUrl: linkify(d.signupUrl || d.url)
94282 selection.append('div').attr('class', 'community-name').append('a').attr('target', '_blank').attr('href', d.url).html(_t.html("community.".concat(d.id, ".name")));
94283 var descriptionHTML = _t.html("community.".concat(d.id, ".description"), replacements);
94285 if (d.type === 'reddit') {
94286 // linkify subreddits #4997
94287 descriptionHTML = descriptionHTML.replace(/(\/r\/\w*\/*)/i, function (match) {
94288 return linkify(d.url, match);
94292 selection.append('div').attr('class', 'community-description').html(descriptionHTML);
94294 if (d.extendedDescription || d.languageCodes && d.languageCodes.length) {
94295 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
94298 var nextEvents = (d.events || []).map(function (event) {
94299 event.date = parseEventDate(event.when);
94301 }).filter(function (event) {
94302 // date is valid and future (or today)
94303 var t = event.date.getTime();
94304 var now = new Date().setHours(0, 0, 0, 0);
94305 return !isNaN(t) && t >= now;
94306 }).sort(function (a, b) {
94307 // sort by date ascending
94308 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
94309 }).slice(0, MAXEVENTS); // limit number of events shown
94311 if (nextEvents.length) {
94312 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);
94315 function showMore(selection) {
94316 var more = selection.selectAll('.community-more').data([0]);
94317 var moreEnter = more.enter().append('div').attr('class', 'community-more');
94319 if (d.extendedDescription) {
94320 moreEnter.append('div').attr('class', 'community-extended-description').html(_t.html("community.".concat(d.id, ".extendedDescription"), replacements));
94323 if (d.languageCodes && d.languageCodes.length) {
94324 var languageList = d.languageCodes.map(function (code) {
94325 return _mainLocalizer.languageName(code);
94327 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
94328 languages: languageList
94333 function showNextEvents(selection) {
94334 var events = selection.append('div').attr('class', 'community-events');
94335 var item = events.selectAll('.community-event').data(nextEvents);
94336 var itemEnter = item.enter().append('div').attr('class', 'community-event');
94337 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
94339 }).html(function (d) {
94342 if (d.i18n && d.id) {
94343 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
94350 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
94358 if (d.date.getHours() || d.date.getMinutes()) {
94359 // include time if it has one
94360 options.hour = 'numeric';
94361 options.minute = 'numeric';
94364 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
94366 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
94367 var where = d.where;
94369 if (d.i18n && d.id) {
94370 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
94377 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
94378 var description = d.description;
94380 if (d.i18n && d.id) {
94381 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
94382 "default": description
94386 return description;
94390 function linkify(url, text) {
94391 text = text || url;
94392 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
94396 success.changeset = function (val) {
94397 if (!arguments.length) return _changeset;
94402 success.location = function (val) {
94403 if (!arguments.length) return _location;
94408 return utilRebind(success, dispatch$1, 'on');
94411 function modeSave(context) {
94415 var keybinding = utilKeybinding('modeSave');
94416 var commit = uiCommit(context).on('cancel', cancel);
94418 var _conflictsUi; // uiConflicts
94425 var uploader = context.uploader().on('saveStarted.modeSave', function () {
94427 }) // fire off some async work that we want to be ready later
94428 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
94430 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
94432 function cancel() {
94433 context.enter(modeBrowse(context));
94436 function showProgress(num, total) {
94437 var modal = context.container().select('.loading-modal .modal-section');
94438 var progress = modal.selectAll('.progress').data([0]); // enter/update
94440 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
94446 function showConflicts(changeset, conflicts, origChanges) {
94447 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
94448 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
94449 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
94450 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
94451 selection.remove();
94453 uploader.cancelConflictResolution();
94454 }).on('save', function () {
94455 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
94456 selection.remove();
94457 uploader.processResolvedConflicts(changeset);
94459 selection.call(_conflictsUi);
94462 function showErrors(errors) {
94464 var selection = uiConfirm(context.container());
94465 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
94466 addErrors(selection, errors);
94467 selection.okButton();
94470 function addErrors(selection, data) {
94471 var message = selection.select('.modal-section.message-text');
94472 var items = message.selectAll('.error-container').data(data);
94473 var enter = items.enter().append('div').attr('class', 'error-container');
94474 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
94475 return d.msg || _t('save.unknown_error_details');
94476 }).on('click', function (d3_event) {
94477 d3_event.preventDefault();
94478 var error = select(this);
94479 var detail = select(this.nextElementSibling);
94480 var exp = error.classed('expanded');
94481 detail.style('display', exp ? 'none' : 'block');
94482 error.classed('expanded', !exp);
94484 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
94485 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
94486 return d.details || [];
94487 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
94490 items.exit().remove();
94493 function showSuccess(changeset) {
94496 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
94497 context.ui().sidebar.hide();
94500 context.enter(modeBrowse(context).sidebar(ui));
94503 function keybindingOn() {
94504 select(document).call(keybinding.on('⎋', cancel, true));
94507 function keybindingOff() {
94508 select(document).call(keybinding.unbind);
94509 } // Reverse geocode current map location so we can display a message on
94510 // the success screen like "Thank you for editing around place, region."
94513 function prepareForSuccess() {
94514 _success = uiSuccess(context);
94516 if (!services.geocoder) return;
94517 services.geocoder.reverse(context.map().center(), function (err, result) {
94518 if (err || !result || !result.address) return;
94519 var addr = result.address;
94520 var place = addr && (addr.town || addr.city || addr.county) || '';
94521 var region = addr && (addr.state || addr.country) || '';
94522 var separator = place && region ? _t('success.thank_you_where.separator') : '';
94523 _location = _t('success.thank_you_where.format', {
94525 separator: separator,
94531 mode.selectedIDs = function () {
94532 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
94535 mode.enter = function () {
94537 context.ui().sidebar.expand();
94540 context.ui().sidebar.show(commit);
94544 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
94545 var osm = context.connection();
94552 if (osm.authenticated()) {
94555 osm.authenticate(function (err) {
94565 mode.exit = function () {
94567 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
94568 context.ui().sidebar.hide();
94574 function uiToolOldDrawModes(context) {
94577 label: _t.html('toolbar.add_feature')
94579 var modes = [modeAddPoint(context, {
94580 title: _t.html('modes.add_point.title'),
94582 description: _t.html('modes.add_point.description'),
94583 preset: _mainPresetIndex.item('point'),
94585 }), modeAddLine(context, {
94586 title: _t.html('modes.add_line.title'),
94588 description: _t.html('modes.add_line.description'),
94589 preset: _mainPresetIndex.item('line'),
94591 }), modeAddArea(context, {
94592 title: _t.html('modes.add_area.title'),
94594 description: _t.html('modes.add_area.description'),
94595 preset: _mainPresetIndex.item('area'),
94599 function enabled() {
94600 return osmEditable();
94603 function osmEditable() {
94604 return context.editable();
94607 modes.forEach(function (mode) {
94608 context.keybinding().on(mode.key, function () {
94609 if (!enabled()) return;
94611 if (mode.id === context.mode().id) {
94612 context.enter(modeBrowse(context));
94614 context.enter(mode);
94619 tool.render = function (selection) {
94620 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
94622 var debouncedUpdate = debounce(update, 500, {
94627 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
94628 context.on('enter.modes', update);
94631 function update() {
94632 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
94636 buttons.exit().remove(); // enter
94638 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
94639 return d.id + ' add-button bar-button';
94640 }).on('click.mode-buttons', function (d3_event, d) {
94641 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
94643 var currMode = context.mode().id;
94644 if (/^draw/.test(currMode)) return;
94646 if (d.id === currMode) {
94647 context.enter(modeBrowse(context));
94651 }).call(uiTooltip().placement('bottom').title(function (d) {
94652 return d.description;
94653 }).keys(function (d) {
94655 }).scrollContainer(context.container().select('.top-toolbar')));
94656 buttonsEnter.each(function (d) {
94657 select(this).call(svgIcon('#iD-icon-' + d.button));
94659 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
94661 }); // if we are adding/removing the buttons, check if toolbar has overflowed
94663 if (buttons.enter().size() || buttons.exit().size()) {
94664 context.ui().checkOverflow('.top-toolbar', true);
94668 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
94670 }).classed('active', function (d) {
94671 return context.mode() && context.mode().button === d.button;
94679 function uiToolNotes(context) {
94682 label: _t.html('modes.add_note.label')
94684 var mode = modeAddNote(context);
94686 function enabled() {
94687 return notesEnabled() && notesEditable();
94690 function notesEnabled() {
94691 var noteLayer = context.layers().layer('notes');
94692 return noteLayer && noteLayer.enabled();
94695 function notesEditable() {
94696 var mode = context.mode();
94697 return context.map().notesEditable() && mode && mode.id !== 'save';
94700 context.keybinding().on(mode.key, function () {
94701 if (!enabled()) return;
94703 if (mode.id === context.mode().id) {
94704 context.enter(modeBrowse(context));
94706 context.enter(mode);
94710 tool.render = function (selection) {
94711 var debouncedUpdate = debounce(update, 500, {
94716 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
94717 context.on('enter.notes', update);
94720 function update() {
94721 var showNotes = notesEnabled();
94722 var data = showNotes ? [mode] : [];
94723 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
94727 buttons.exit().remove(); // enter
94729 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
94730 return d.id + ' add-button bar-button';
94731 }).on('click.notes', function (d3_event, d) {
94732 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
94734 var currMode = context.mode().id;
94735 if (/^draw/.test(currMode)) return;
94737 if (d.id === currMode) {
94738 context.enter(modeBrowse(context));
94742 }).call(uiTooltip().placement('bottom').title(function (d) {
94743 return d.description;
94744 }).keys(function (d) {
94746 }).scrollContainer(context.container().select('.top-toolbar')));
94747 buttonsEnter.each(function (d) {
94748 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
94749 }); // if we are adding/removing the buttons, check if toolbar has overflowed
94751 if (buttons.enter().size() || buttons.exit().size()) {
94752 context.ui().checkOverflow('.top-toolbar', true);
94756 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
94758 }).classed('active', function (d) {
94759 return context.mode() && context.mode().button === d.button;
94764 tool.uninstall = function () {
94765 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
94766 context.map().on('move.notes', null).on('drawn.notes', null);
94772 function uiToolSave(context) {
94775 label: _t.html('save.title')
94778 var tooltipBehavior = null;
94779 var history = context.history();
94780 var key = uiCmd('⌘S');
94781 var _numChanges = 0;
94783 function isSaving() {
94784 var mode = context.mode();
94785 return mode && mode.id === 'save';
94788 function isDisabled() {
94789 return _numChanges === 0 || isSaving();
94792 function save(d3_event) {
94793 d3_event.preventDefault();
94795 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
94796 context.enter(modeSave(context));
94800 function bgColor() {
94803 if (_numChanges === 0) {
94805 } else if (_numChanges <= 50) {
94806 step = _numChanges / 50;
94807 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
94809 step = Math.min((_numChanges - 50) / 50, 1.0);
94810 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
94814 function updateCount() {
94815 var val = history.difference().summary().length;
94816 if (val === _numChanges) return;
94819 if (tooltipBehavior) {
94820 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
94824 button.classed('disabled', isDisabled()).style('background', bgColor());
94825 button.select('span.count').html(_numChanges);
94829 tool.render = function (selection) {
94830 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
94831 var lastPointerUpType;
94832 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
94833 lastPointerUpType = d3_event.pointerType;
94834 }).on('click', function (d3_event) {
94837 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
94838 // there are no tooltips for touch interactions so flash feedback instead
94839 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
94842 lastPointerUpType = null;
94843 }).call(tooltipBehavior);
94844 button.call(svgIcon('#iD-icon-save'));
94845 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
94847 context.keybinding().on(key, save, true);
94848 context.history().on('change.save', updateCount);
94849 context.on('enter.save', function () {
94851 button.classed('disabled', isDisabled());
94854 button.call(tooltipBehavior.hide);
94860 tool.uninstall = function () {
94861 context.keybinding().off(key, true);
94862 context.history().on('change.save', null);
94863 context.on('enter.save', null);
94865 tooltipBehavior = null;
94871 function uiToolSidebarToggle(context) {
94873 id: 'sidebar_toggle',
94874 label: _t.html('toolbar.inspect')
94877 tool.render = function (selection) {
94878 selection.append('button').attr('class', 'bar-button').on('click', function () {
94879 context.ui().sidebar.toggle();
94880 }).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')));
94886 function uiToolUndoRedo(context) {
94889 label: _t.html('toolbar.undo_redo')
94894 action: function action() {
94897 annotation: function annotation() {
94898 return context.history().undoAnnotation();
94900 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
94904 action: function action() {
94907 annotation: function annotation() {
94908 return context.history().redoAnnotation();
94910 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
94913 function editable() {
94914 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
94915 /* ignore min zoom */
94919 tool.render = function (selection) {
94920 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
94921 return d.annotation() ? _t.html(d.id + '.tooltip', {
94922 action: d.annotation()
94923 }) : _t.html(d.id + '.nothing');
94924 }).keys(function (d) {
94926 }).scrollContainer(context.container().select('.top-toolbar'));
94927 var lastPointerUpType;
94928 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
94929 return 'disabled ' + d.id + '-button bar-button';
94930 }).on('pointerup', function (d3_event) {
94931 // `pointerup` is always called before `click`
94932 lastPointerUpType = d3_event.pointerType;
94933 }).on('click', function (d3_event, d) {
94934 d3_event.preventDefault();
94935 var annotation = d.annotation();
94937 if (editable() && annotation) {
94941 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
94942 // there are no tooltips for touch interactions so flash feedback instead
94943 var text = annotation ? _t(d.id + '.tooltip', {
94945 }) : _t(d.id + '.nothing');
94946 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
94949 lastPointerUpType = null;
94950 }).call(tooltipBehavior);
94951 buttons.each(function (d) {
94952 select(this).call(svgIcon('#' + d.icon));
94954 context.keybinding().on(commands[0].cmd, function (d3_event) {
94955 d3_event.preventDefault();
94956 if (editable()) commands[0].action();
94957 }).on(commands[1].cmd, function (d3_event) {
94958 d3_event.preventDefault();
94959 if (editable()) commands[1].action();
94962 var debouncedUpdate = debounce(update, 500, {
94967 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
94968 context.history().on('change.undo_redo', function (difference) {
94969 if (difference) update();
94971 context.on('enter.undo_redo', update);
94973 function update() {
94974 buttons.classed('disabled', function (d) {
94975 return !editable() || !d.annotation();
94976 }).each(function () {
94977 var selection = select(this);
94979 if (!selection.select('.tooltip.in').empty()) {
94980 selection.call(tooltipBehavior.updateContent);
94986 tool.uninstall = function () {
94987 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
94988 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
94989 context.history().on('change.undo_redo', null);
94990 context.on('enter.undo_redo', null);
94996 function uiTopToolbar(context) {
94997 var sidebarToggle = uiToolSidebarToggle(context),
94998 modes = uiToolOldDrawModes(context),
94999 notes = uiToolNotes(context),
95000 undoRedo = uiToolUndoRedo(context),
95001 save = uiToolSave(context);
95003 function notesEnabled() {
95004 var noteLayer = context.layers().layer('notes');
95005 return noteLayer && noteLayer.enabled();
95008 function topToolbar(bar) {
95009 bar.on('wheel.topToolbar', function (d3_event) {
95010 if (!d3_event.deltaX) {
95011 // translate vertical scrolling into horizontal scrolling in case
95012 // the user doesn't have an input device that can scroll horizontally
95013 bar.node().scrollLeft += d3_event.deltaY;
95017 var debouncedUpdate = debounce(update, 500, {
95022 context.layers().on('change.topToolbar', debouncedUpdate);
95025 function update() {
95026 var tools = [sidebarToggle, 'spacer', modes];
95027 tools.push('spacer');
95029 if (notesEnabled()) {
95030 tools = tools.concat([notes, 'spacer']);
95033 tools = tools.concat([undoRedo, save]);
95034 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
95037 toolbarItems.exit().each(function (d) {
95042 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
95043 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
95044 if (d.klass) classes += ' ' + d.klass;
95047 var actionableItems = itemsEnter.filter(function (d) {
95048 return d !== 'spacer';
95050 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
95051 select(this).call(d.render, bar);
95053 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
95062 var sawVersion = null;
95063 var isNewVersion = false;
95064 var isNewUser = false;
95065 function uiVersion(context) {
95066 var currVersion = context.version;
95067 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
95069 if (sawVersion === null && matchedVersion !== null) {
95070 if (corePreferences('sawVersion')) {
95072 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
95075 isNewVersion = true;
95078 corePreferences('sawVersion', currVersion);
95079 sawVersion = currVersion;
95082 return function (selection) {
95083 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
95085 if (isNewVersion && !isNewUser) {
95086 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', {
95087 version: currVersion
95088 })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
95093 function uiZoom(context) {
95096 icon: 'iD-icon-plus',
95097 title: _t.html('zoom.in'),
95099 disabled: function disabled() {
95100 return !context.map().canZoomIn();
95102 disabledTitle: _t.html('zoom.disabled.in'),
95106 icon: 'iD-icon-minus',
95107 title: _t.html('zoom.out'),
95109 disabled: function disabled() {
95110 return !context.map().canZoomOut();
95112 disabledTitle: _t.html('zoom.disabled.out'),
95116 function zoomIn(d3_event) {
95117 if (d3_event.shiftKey) return;
95118 d3_event.preventDefault();
95119 context.map().zoomIn();
95122 function zoomOut(d3_event) {
95123 if (d3_event.shiftKey) return;
95124 d3_event.preventDefault();
95125 context.map().zoomOut();
95128 function zoomInFurther(d3_event) {
95129 if (d3_event.shiftKey) return;
95130 d3_event.preventDefault();
95131 context.map().zoomInFurther();
95134 function zoomOutFurther(d3_event) {
95135 if (d3_event.shiftKey) return;
95136 d3_event.preventDefault();
95137 context.map().zoomOutFurther();
95140 return function (selection) {
95141 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
95142 if (d.disabled()) {
95143 return d.disabledTitle;
95147 }).keys(function (d) {
95150 var lastPointerUpType;
95151 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
95153 }).on('pointerup.editor', function (d3_event) {
95154 lastPointerUpType = d3_event.pointerType;
95155 }).on('click.editor', function (d3_event, d) {
95156 if (!d.disabled()) {
95157 d.action(d3_event);
95158 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
95159 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
95162 lastPointerUpType = null;
95163 }).call(tooltipBehavior);
95164 buttons.each(function (d) {
95165 select(this).call(svgIcon('#' + d.icon, 'light'));
95167 utilKeybinding.plusKeys.forEach(function (key) {
95168 context.keybinding().on([key], zoomIn);
95169 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
95171 utilKeybinding.minusKeys.forEach(function (key) {
95172 context.keybinding().on([key], zoomOut);
95173 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
95176 function updateButtonStates() {
95177 buttons.classed('disabled', function (d) {
95178 return d.disabled();
95179 }).each(function () {
95180 var selection = select(this);
95182 if (!selection.select('.tooltip.in').empty()) {
95183 selection.call(tooltipBehavior.updateContent);
95188 updateButtonStates();
95189 context.map().on('move.uiZoom', updateButtonStates);
95193 function uiZoomToSelection(context) {
95194 function isDisabled() {
95195 var mode = context.mode();
95196 return !mode || !mode.zoomToSelected;
95199 var _lastPointerUpType;
95201 function pointerup(d3_event) {
95202 _lastPointerUpType = d3_event.pointerType;
95205 function click(d3_event) {
95206 d3_event.preventDefault();
95208 if (isDisabled()) {
95209 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
95210 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
95213 var mode = context.mode();
95215 if (mode && mode.zoomToSelected) {
95216 mode.zoomToSelected();
95220 _lastPointerUpType = null;
95223 return function (selection) {
95224 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
95225 if (isDisabled()) {
95226 return _t.html('inspector.zoom_to.no_selection');
95229 return _t.html('inspector.zoom_to.title');
95230 }).keys([_t('inspector.zoom_to.key')]);
95231 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
95233 function setEnabledState() {
95234 button.classed('disabled', isDisabled());
95236 if (!button.select('.tooltip.in').empty()) {
95237 button.call(tooltipBehavior.updateContent);
95241 context.on('enter.uiZoomToSelection', setEnabledState);
95246 function uiPane(id, context) {
95250 var _description = '';
95251 var _iconName = '';
95253 var _sections; // array of uiSection objects
95256 var _paneSelection = select(null);
95264 pane.label = function (val) {
95265 if (!arguments.length) return _label;
95270 pane.key = function (val) {
95271 if (!arguments.length) return _key;
95276 pane.description = function (val) {
95277 if (!arguments.length) return _description;
95278 _description = val;
95282 pane.iconName = function (val) {
95283 if (!arguments.length) return _iconName;
95288 pane.sections = function (val) {
95289 if (!arguments.length) return _sections;
95294 pane.selection = function () {
95295 return _paneSelection;
95298 function hidePane() {
95299 context.ui().togglePanes();
95302 pane.togglePane = function (d3_event) {
95303 if (d3_event) d3_event.preventDefault();
95305 _paneTooltip.hide();
95307 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
95310 pane.renderToggleButton = function (selection) {
95311 if (!_paneTooltip) {
95312 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
95315 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
95318 pane.renderContent = function (selection) {
95319 // override to fully customize content
95321 _sections.forEach(function (section) {
95322 selection.call(section.render);
95327 pane.renderPane = function (selection) {
95328 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
95330 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
95332 heading.append('h2').html(_label);
95333 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
95335 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
95338 context.keybinding().on(_key, pane.togglePane);
95345 function uiSectionBackgroundDisplayOptions(context) {
95346 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
95348 var _detected = utilDetect();
95350 var _storedOpacity = corePreferences('background-opacity');
95354 var _maxVal = _detected.cssfilters ? 3 : 1;
95356 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
95359 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
95365 function clamp(x, min, max) {
95366 return Math.max(min, Math.min(x, max));
95369 function updateValue(d, val) {
95370 val = clamp(val, _minVal, _maxVal);
95372 context.background()[d](val);
95374 if (d === 'brightness') {
95375 corePreferences('background-opacity', val);
95378 section.reRender();
95381 function renderDisclosureContent(selection) {
95382 var container = selection.selectAll('.display-options-container').data([0]);
95383 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
95385 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
95386 return 'display-control display-control-' + d;
95388 slidersEnter.append('h5').html(function (d) {
95389 return _t.html('background.' + d);
95390 }).append('span').attr('class', function (d) {
95391 return 'display-option-value display-option-value-' + d;
95393 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
95394 sildersControlEnter.append('input').attr('class', function (d) {
95395 return 'display-option-input display-option-input-' + d;
95396 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
95397 var val = select(this).property('value');
95399 if (!val && d3_event && d3_event.target) {
95400 val = d3_event.target.value;
95403 updateValue(d, val);
95405 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
95406 return 'display-option-reset display-option-reset-' + d;
95407 }).on('click', function (d3_event, d) {
95408 if (d3_event.button !== 0) return;
95410 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
95412 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
95413 d3_event.preventDefault();
95415 for (var i = 0; i < _sliders.length; i++) {
95416 updateValue(_sliders[i], 1);
95420 container = containerEnter.merge(container);
95421 container.selectAll('.display-option-input').property('value', function (d) {
95422 return _options[d];
95424 container.selectAll('.display-option-value').html(function (d) {
95425 return Math.floor(_options[d] * 100) + '%';
95427 container.selectAll('.display-option-reset').classed('disabled', function (d) {
95428 return _options[d] === 1;
95429 }); // first time only, set brightness if needed
95431 if (containerEnter.size() && _options.brightness !== 1) {
95432 context.background().brightness(_options.brightness);
95439 function uiSettingsCustomBackground() {
95440 var dispatch$1 = dispatch('change');
95442 function render(selection) {
95443 // keep separate copies of original and current settings
95444 var _origSettings = {
95445 template: corePreferences('background-custom-template')
95447 var _currSettings = {
95448 template: corePreferences('background-custom-template')
95450 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
95451 var modal = uiConfirm(selection).okButton();
95452 modal.classed('settings-modal settings-custom-background', true);
95453 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
95454 var textSection = modal.select('.modal-section.message-text');
95455 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, "`");
95456 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
95457 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
95459 var buttonSection = modal.select('.modal-section.buttons');
95460 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
95461 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
95462 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
95464 function isSaveDisabled() {
95466 } // restore the original template
95469 function clickCancel() {
95470 textSection.select('.field-template').property('value', _origSettings.template);
95471 corePreferences('background-custom-template', _origSettings.template);
95474 } // accept the current template
95477 function clickSave() {
95478 _currSettings.template = textSection.select('.field-template').property('value');
95479 corePreferences('background-custom-template', _currSettings.template);
95482 dispatch$1.call('change', this, _currSettings);
95486 return utilRebind(render, dispatch$1, 'on');
95489 function uiSectionBackgroundList(context) {
95490 var _backgroundList = select(null);
95492 var _customSource = context.background().findSource('custom');
95494 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
95496 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
95498 function previousBackgroundID() {
95499 return corePreferences('background-last-used-toggle');
95502 function renderDisclosureContent(selection) {
95503 // the background list
95504 var container = selection.selectAll('.layer-background-list').data([0]);
95505 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
95507 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
95508 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'));
95509 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
95510 d3_event.preventDefault();
95511 uiMapInMap.toggle();
95513 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
95514 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'));
95515 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
95516 d3_event.preventDefault();
95517 context.ui().info.toggle('background');
95519 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
95520 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'));
95521 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
95522 d3_event.preventDefault();
95523 context.ui().info.toggle('location');
95525 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
95527 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'));
95529 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
95530 chooseBackground(d);
95532 return !d.isHidden() && !d.overlay;
95536 function setTooltips(selection) {
95537 selection.each(function (d, i, nodes) {
95538 var item = select(this).select('label');
95539 var span = item.select('span');
95540 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
95541 var description = d.description();
95542 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
95543 item.call(uiTooltip().destroyAny);
95545 if (d.id === previousBackgroundID()) {
95546 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
95547 } else if (description || isOverflowing) {
95548 item.call(uiTooltip().placement(placement).title(description || d.label()));
95553 function drawListItems(layerList, type, change, filter) {
95554 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
95555 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
95557 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
95558 // arrow key navigation of radio values likes to work in the order
95559 // they were added, not the display document order.
95560 .data(sources, function (d, i) {
95561 return d.id + '---' + i;
95563 layerLinks.exit().remove();
95564 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
95565 return d.id === 'custom';
95566 }).classed('best', function (d) {
95569 var label = enter.append('label');
95570 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
95572 }).on('change', change);
95573 label.append('span').html(function (d) {
95576 enter.filter(function (d) {
95577 return d.id === 'custom';
95578 }).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) {
95579 d3_event.preventDefault();
95581 }).call(svgIcon('#iD-icon-more'));
95582 enter.filter(function (d) {
95584 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
95585 layerList.call(updateLayerSelections);
95588 function updateLayerSelections(selection) {
95589 function active(d) {
95590 return context.background().showsLayer(d);
95593 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
95594 return d.id === previousBackgroundID();
95595 }).call(setTooltips).selectAll('input').property('checked', active);
95598 function chooseBackground(d) {
95599 if (d.id === 'custom' && !d.template()) {
95600 return editCustom();
95603 var previousBackground = context.background().baseLayerSource();
95604 corePreferences('background-last-used-toggle', previousBackground.id);
95605 corePreferences('background-last-used', d.id);
95606 context.background().baseLayerSource(d);
95609 function customChanged(d) {
95610 if (d && d.template) {
95611 _customSource.template(d.template);
95613 chooseBackground(_customSource);
95615 _customSource.template('');
95617 chooseBackground(context.background().findSource('none'));
95621 function editCustom() {
95622 context.container().call(_settingsCustomBackground);
95625 context.background().on('change.background_list', function () {
95626 _backgroundList.call(updateLayerSelections);
95628 context.map().on('move.background_list', debounce(function () {
95629 // layers in-view may have changed due to map move
95630 window.requestIdleCallback(section.reRender);
95635 function uiSectionBackgroundOffset(context) {
95636 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
95638 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
95640 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
95642 function updateValue() {
95643 var meters = geoOffsetToMeters(context.background().offset());
95644 var x = +meters[0].toFixed(2);
95645 var y = +meters[1].toFixed(2);
95646 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
95647 context.container().selectAll('.nudge-reset').classed('disabled', function () {
95648 return x === 0 && y === 0;
95652 function resetOffset() {
95653 context.background().offset([0, 0]);
95657 function nudge(d) {
95658 context.background().nudge(d, context.map().zoom());
95662 function inputOffset() {
95663 var input = select(this);
95664 var d = input.node().value;
95665 if (d === '') return resetOffset();
95666 d = d.replace(/;/g, ',').split(',').map(function (n) {
95667 // if n is NaN, it will always get mapped to false.
95668 return !isNaN(n) && n;
95671 if (d.length !== 2 || !d[0] || !d[1]) {
95672 input.classed('error', true);
95676 context.background().offset(geoMetersToOffset(d));
95680 function dragOffset(d3_event) {
95681 if (d3_event.button !== 0) return;
95682 var origin = [d3_event.clientX, d3_event.clientY];
95683 var pointerId = d3_event.pointerId || 'mouse';
95684 context.container().append('div').attr('class', 'nudge-surface');
95685 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
95687 if (_pointerPrefix === 'pointer') {
95688 select(window).on('pointercancel.drag-bg-offset', pointerup);
95691 function pointermove(d3_event) {
95692 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
95693 var latest = [d3_event.clientX, d3_event.clientY];
95694 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
95699 function pointerup(d3_event) {
95700 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
95701 if (d3_event.button !== 0) return;
95702 context.container().selectAll('.nudge-surface').remove();
95703 select(window).on('.drag-bg-offset', null);
95707 function renderDisclosureContent(selection) {
95708 var container = selection.selectAll('.nudge-container').data([0]);
95709 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
95710 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
95711 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
95712 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
95713 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
95714 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
95715 return d[0] + ' nudge';
95716 }).on('click', function (d3_event, d) {
95719 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
95720 d3_event.preventDefault();
95722 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
95726 context.background().on('change.backgroundOffset-update', updateValue);
95730 function uiSectionOverlayList(context) {
95731 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
95733 var _overlayList = select(null);
95735 function setTooltips(selection) {
95736 selection.each(function (d, i, nodes) {
95737 var item = select(this).select('label');
95738 var span = item.select('span');
95739 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
95740 var description = d.description();
95741 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
95742 item.call(uiTooltip().destroyAny);
95744 if (description || isOverflowing) {
95745 item.call(uiTooltip().placement(placement).title(description || d.name()));
95750 function updateLayerSelections(selection) {
95751 function active(d) {
95752 return context.background().showsLayer(d);
95755 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
95758 function chooseOverlay(d3_event, d) {
95759 d3_event.preventDefault();
95760 context.background().toggleOverlayLayer(d);
95762 _overlayList.call(updateLayerSelections);
95764 document.activeElement.blur();
95767 function drawListItems(layerList, type, change, filter) {
95768 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
95769 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
95772 layerLinks.exit().remove();
95773 var enter = layerLinks.enter().append('li');
95774 var label = enter.append('label');
95775 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
95776 label.append('span').html(function (d) {
95779 layerList.selectAll('li').sort(sortSources);
95780 layerList.call(updateLayerSelections);
95782 function sortSources(a, b) {
95783 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
95787 function renderDisclosureContent(selection) {
95788 var container = selection.selectAll('.layer-overlay-list').data([0]);
95789 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
95791 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
95792 return !d.isHidden() && d.overlay;
95796 context.map().on('move.overlay_list', debounce(function () {
95797 // layers in-view may have changed due to map move
95798 window.requestIdleCallback(section.reRender);
95803 function uiPaneBackground(context) {
95804 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)]);
95805 return backgroundPane;
95808 function uiPaneHelp(context) {
95809 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']]];
95811 'help.help.open_data_h': 3,
95812 'help.help.before_start_h': 3,
95813 'help.help.open_source_h': 3,
95814 'help.overview.navigation_h': 3,
95815 'help.overview.features_h': 3,
95816 'help.editing.select_h': 3,
95817 'help.editing.multiselect_h': 3,
95818 'help.editing.undo_redo_h': 3,
95819 'help.editing.save_h': 3,
95820 'help.editing.upload_h': 3,
95821 'help.editing.backups_h': 3,
95822 'help.editing.keyboard_h': 3,
95823 'help.feature_editor.type_h': 3,
95824 'help.feature_editor.fields_h': 3,
95825 'help.feature_editor.tags_h': 3,
95826 'help.points.add_point_h': 3,
95827 'help.points.move_point_h': 3,
95828 'help.points.delete_point_h': 3,
95829 'help.lines.add_line_h': 3,
95830 'help.lines.modify_line_h': 3,
95831 'help.lines.connect_line_h': 3,
95832 'help.lines.disconnect_line_h': 3,
95833 'help.lines.move_line_h': 3,
95834 'help.lines.delete_line_h': 3,
95835 'help.areas.point_or_area_h': 3,
95836 'help.areas.add_area_h': 3,
95837 'help.areas.square_area_h': 3,
95838 'help.areas.modify_area_h': 3,
95839 'help.areas.delete_area_h': 3,
95840 'help.relations.edit_relation_h': 3,
95841 'help.relations.maintain_relation_h': 3,
95842 'help.relations.relation_types_h': 2,
95843 'help.relations.multipolygon_h': 3,
95844 'help.relations.turn_restriction_h': 3,
95845 'help.relations.route_h': 3,
95846 'help.relations.boundary_h': 3,
95847 'help.notes.add_note_h': 3,
95848 'help.notes.update_note_h': 3,
95849 'help.notes.save_note_h': 3,
95850 'help.imagery.sources_h': 3,
95851 'help.imagery.offsets_h': 3,
95852 'help.streetlevel.using_h': 3,
95853 'help.gps.using_h': 3,
95854 'help.qa.tools_h': 3,
95855 'help.qa.issues_h': 3
95856 }; // For each section, squash all the texts into a single markdown document
95858 var docs = docKeys.map(function (key) {
95859 var helpkey = 'help.' + key[0];
95860 var helpPaneReplacements = {
95861 version: context.version
95863 var text = key[1].reduce(function (all, part) {
95864 var subkey = helpkey + '.' + part;
95865 var depth = headings[subkey]; // is this subkey a heading?
95867 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
95869 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
95872 title: _t.html(helpkey + '.title'),
95873 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
95874 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
95877 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
95879 helpPane.renderContent = function (content) {
95880 function clickHelp(d, i) {
95881 var rtl = _mainLocalizer.textDirection() === 'rtl';
95882 content.property('scrollTop', 0);
95883 helpPane.selection().select('.pane-heading h2').html(d.title);
95884 body.html(d.content);
95885 body.selectAll('a').attr('target', '_blank');
95886 menuItems.classed('selected', function (m) {
95887 return m.title === d.title;
95892 nav.call(drawNext).call(drawPrevious);
95894 nav.call(drawPrevious).call(drawNext);
95897 function drawNext(selection) {
95898 if (i < docs.length - 1) {
95899 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
95900 d3_event.preventDefault();
95901 clickHelp(docs[i + 1], i + 1);
95903 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
95907 function drawPrevious(selection) {
95909 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
95910 d3_event.preventDefault();
95911 clickHelp(docs[i - 1], i - 1);
95913 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
95918 function clickWalkthrough(d3_event) {
95919 d3_event.preventDefault();
95920 if (context.inIntro()) return;
95921 context.container().call(uiIntro(context));
95922 context.ui().togglePanes();
95925 function clickShortcuts(d3_event) {
95926 d3_event.preventDefault();
95927 context.container().call(context.ui().shortcuts, true);
95930 var toc = content.append('ul').attr('class', 'toc');
95931 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
95933 }).on('click', function (d3_event, d) {
95934 d3_event.preventDefault();
95935 clickHelp(d, docs.indexOf(d));
95937 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);
95938 shortcuts.append('div').html(_t.html('shortcuts.title'));
95939 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
95940 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
95941 walkthrough.append('div').html(_t.html('splash.walkthrough'));
95942 var helpContent = content.append('div').attr('class', 'left-content');
95943 var body = helpContent.append('div').attr('class', 'body');
95944 var nav = helpContent.append('div').attr('class', 'nav');
95945 clickHelp(docs[0], 0);
95951 function uiSectionValidationIssues(id, severity, context) {
95953 var section = uiSection(id, context).label(function () {
95954 if (!_issues) return '';
95955 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
95956 return _t('inspector.title_count', {
95957 title: _t.html('issues.' + severity + 's.list_title'),
95958 count: issueCountText
95960 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
95961 return _issues && _issues.length;
95964 function getOptions() {
95966 what: corePreferences('validate-what') || 'edited',
95967 where: corePreferences('validate-where') || 'all'
95969 } // get and cache the issues to display, unordered
95972 function reloadIssues() {
95973 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
95976 function renderDisclosureContent(selection) {
95977 var center = context.map().center();
95978 var graph = context.graph(); // sort issues by distance away from the center of the map
95980 var issues = _issues.map(function withDistance(issue) {
95981 var extent = issue.extent(graph);
95982 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
95983 return Object.assign(issue, {
95986 }).sort(function byDistance(a, b) {
95987 return a.dist - b.dist;
95988 }); // cut off at 1000
95991 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
95993 selection.call(drawIssuesList, issues);
95996 function drawIssuesList(selection, issues) {
95997 var list = selection.selectAll('.issues-list').data([0]);
95998 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
95999 var items = list.selectAll('li').data(issues, function (d) {
96003 items.exit().remove(); // Enter
96005 var itemsEnter = items.enter().append('li').attr('class', function (d) {
96006 return 'issue severity-' + d.severity;
96008 var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
96009 context.validator().focusIssue(d);
96010 }).on('mouseover', function (d3_event, d) {
96011 utilHighlightEntities(d.entityIds, true, context);
96012 }).on('mouseout', function (d3_event, d) {
96013 utilHighlightEntities(d.entityIds, false, context);
96015 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
96016 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
96017 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
96018 select(this).call(svgIcon(iconName));
96020 textEnter.append('span').attr('class', 'issue-message');
96024 .attr('class', 'issue-autofix')
96025 .each(function(d) {
96026 if (!d.autoFix) return;
96029 .attr('title', t('issues.fix_one.title'))
96030 .datum(d.autoFix) // set button datum to the autofix
96031 .attr('class', 'autofix action')
96032 .on('click', function(d3_event, d) {
96033 d3_event.preventDefault();
96034 d3_event.stopPropagation();
96035 var issuesEntityIDs = d.issue.entityIds;
96036 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
96037 context.perform.apply(context, d.autoArgs);
96038 context.validator().validate();
96040 .call(svgIcon('#iD-icon-wrench'));
96045 items = items.merge(itemsEnter).order();
96046 items.selectAll('.issue-message').html(function (d) {
96047 return d.message(context);
96051 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
96052 var autoFixAll = selection.selectAll('.autofix-all')
96053 .data(canAutoFix.length ? [0] : []);
96058 var autoFixAllEnter = autoFixAll.enter()
96059 .insert('div', '.issues-list')
96060 .attr('class', 'autofix-all');
96061 var linkEnter = autoFixAllEnter
96063 .attr('class', 'autofix-all-link')
96064 .attr('href', '#');
96067 .attr('class', 'autofix-all-link-text')
96068 .html(t.html('issues.fix_all.title'));
96071 .attr('class', 'autofix-all-link-icon')
96072 .call(svgIcon('#iD-icon-wrench'));
96073 if (severity === 'warning') {
96074 renderIgnoredIssuesReset(selection);
96077 autoFixAll = autoFixAll
96078 .merge(autoFixAllEnter);
96079 autoFixAll.selectAll('.autofix-all-link')
96080 .on('click', function() {
96081 context.pauseChangeDispatch();
96082 context.perform(actionNoop());
96083 canAutoFix.forEach(function(issue) {
96084 var args = issue.autoFix.autoArgs.slice(); // copy
96085 if (typeof args[args.length - 1] !== 'function') {
96088 args.push(t('issues.fix_all.annotation'));
96089 context.replace.apply(context, args);
96091 context.resumeChangeDispatch();
96092 context.validator().validate();
96097 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
96098 window.requestIdleCallback(function () {
96100 section.reRender();
96103 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
96104 window.requestIdleCallback(function () {
96105 if (getOptions().where === 'visible') {
96106 // must refetch issues if they are viewport-dependent
96108 } // always reload list to re-sort-by-distance
96111 section.reRender();
96117 function uiSectionValidationOptions(context) {
96118 var section = uiSection('issues-options', context).content(renderContent);
96120 function renderContent(selection) {
96121 var container = selection.selectAll('.issues-options-container').data([0]);
96122 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
96125 values: ['edited', 'all']
96128 values: ['visible', 'all']
96130 var options = container.selectAll('.issues-option').data(data, function (d) {
96133 var optionsEnter = options.enter().append('div').attr('class', function (d) {
96134 return 'issues-option issues-option-' + d.key;
96136 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
96137 return _t.html('issues.options.' + d.key + '.title');
96139 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
96140 return d.values.map(function (val) {
96146 }).enter().append('label');
96147 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
96148 return 'issues-option-' + d.key;
96149 }).attr('value', function (d) {
96151 }).property('checked', function (d) {
96152 return getOptions()[d.key] === d.value;
96153 }).on('change', function (d3_event, d) {
96154 updateOptionValue(d3_event, d.key, d.value);
96156 valuesEnter.append('span').html(function (d) {
96157 return _t.html('issues.options.' + d.key + '.' + d.value);
96161 function getOptions() {
96163 what: corePreferences('validate-what') || 'edited',
96165 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
96170 function updateOptionValue(d3_event, d, val) {
96171 if (!val && d3_event && d3_event.target) {
96172 val = d3_event.target.value;
96175 corePreferences('validate-' + d, val);
96176 context.validator().validate();
96182 function uiSectionValidationRules(context) {
96184 var MAXSQUARE = 20;
96185 var DEFAULTSQUARE = 5; // see also unsquare_way.js
96187 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
96189 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
96190 return key !== 'maprules';
96191 }).sort(function (key1, key2) {
96192 // alphabetize by localized title
96193 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
96196 function renderDisclosureContent(selection) {
96197 var container = selection.selectAll('.issues-rulelist-container').data([0]);
96198 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
96199 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
96200 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
96201 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
96202 d3_event.preventDefault();
96203 context.validator().disableRules(_ruleKeys);
96205 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
96206 d3_event.preventDefault();
96207 context.validator().disableRules([]);
96210 container = container.merge(containerEnter);
96211 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
96214 function drawListItems(selection, data, type, name, change, active) {
96215 var items = selection.selectAll('li').data(data); // Exit
96217 items.exit().remove(); // Enter
96219 var enter = items.enter().append('li');
96221 if (name === 'rule') {
96222 enter.call(uiTooltip().title(function (d) {
96223 return _t.html('issues.' + d + '.tip');
96224 }).placement('top'));
96227 var label = enter.append('label');
96228 label.append('input').attr('type', type).attr('name', name).on('change', change);
96229 label.append('span').html(function (d) {
96232 if (d === 'unsquare_way') {
96233 params.val = '<span class="square-degrees"></span>';
96236 return _t.html('issues.' + d + '.title', params);
96239 items = items.merge(enter);
96240 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
96242 var degStr = corePreferences('validate-square-degrees');
96244 if (degStr === null) {
96245 degStr = DEFAULTSQUARE.toString();
96248 var span = items.selectAll('.square-degrees');
96249 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
96251 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) {
96252 d3_event.preventDefault();
96253 d3_event.stopPropagation();
96255 }).on('keyup', function (d3_event) {
96256 if (d3_event.keyCode === 13) {
96261 }).on('blur', changeSquare).merge(input).property('value', degStr);
96264 function changeSquare() {
96265 var input = select(this);
96266 var degStr = utilGetSetValue(input).trim();
96267 var degNum = parseFloat(degStr, 10);
96269 if (!isFinite(degNum)) {
96270 degNum = DEFAULTSQUARE;
96271 } else if (degNum > MAXSQUARE) {
96272 degNum = MAXSQUARE;
96273 } else if (degNum < MINSQUARE) {
96274 degNum = MINSQUARE;
96277 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
96279 degStr = degNum.toString();
96280 input.property('value', degStr);
96281 corePreferences('validate-square-degrees', degStr);
96282 context.validator().reloadUnsquareIssues();
96285 function isRuleEnabled(d) {
96286 return context.validator().isRuleEnabled(d);
96289 function toggleRule(d3_event, d) {
96290 context.validator().toggleRule(d);
96293 context.validator().on('validated.uiSectionValidationRules', function () {
96294 window.requestIdleCallback(section.reRender);
96299 function uiSectionValidationStatus(context) {
96300 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
96301 var issues = context.validator().getIssues(getOptions());
96302 return issues.length === 0;
96305 function getOptions() {
96307 what: corePreferences('validate-what') || 'edited',
96308 where: corePreferences('validate-where') || 'all'
96312 function renderContent(selection) {
96313 var box = selection.selectAll('.box').data([0]);
96314 var boxEnter = box.enter().append('div').attr('class', 'box');
96315 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
96316 var noIssuesMessage = boxEnter.append('span');
96317 noIssuesMessage.append('strong').attr('class', 'message');
96318 noIssuesMessage.append('br');
96319 noIssuesMessage.append('span').attr('class', 'details');
96320 renderIgnoredIssuesReset(selection);
96321 setNoIssuesText(selection);
96324 function renderIgnoredIssuesReset(selection) {
96325 var ignoredIssues = context.validator().getIssues({
96328 includeDisabledRules: true,
96329 includeIgnored: 'only'
96331 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
96333 resetIgnored.exit().remove(); // enter
96335 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
96336 resetIgnoredEnter.append('a').attr('href', '#'); // update
96338 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
96339 resetIgnored.select('a').html(_t('inspector.title_count', {
96340 title: _t.html('issues.reset_ignored'),
96341 count: ignoredIssues.length
96343 resetIgnored.on('click', function (d3_event) {
96344 d3_event.preventDefault();
96345 context.validator().resetIgnoredIssues();
96349 function setNoIssuesText(selection) {
96350 var opts = getOptions();
96352 function checkForHiddenIssues(cases) {
96353 for (var type in cases) {
96354 var hiddenOpts = cases[type];
96355 var hiddenIssues = context.validator().getIssues(hiddenOpts);
96357 if (hiddenIssues.length) {
96358 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
96359 count: hiddenIssues.length.toString()
96365 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
96370 if (opts.what === 'edited' && opts.where === 'visible') {
96371 messageType = 'edits_in_view';
96372 checkForHiddenIssues({
96384 includeDisabledRules: 'only'
96386 everything_else_elsewhere: {
96390 disabled_rules_elsewhere: {
96393 includeDisabledRules: 'only'
96398 includeIgnored: 'only'
96400 ignored_issues_elsewhere: {
96403 includeIgnored: 'only'
96406 } else if (opts.what === 'edited' && opts.where === 'all') {
96407 messageType = 'edits';
96408 checkForHiddenIssues({
96416 includeDisabledRules: 'only'
96421 includeIgnored: 'only'
96424 } else if (opts.what === 'all' && opts.where === 'visible') {
96425 messageType = 'everything_in_view';
96426 checkForHiddenIssues({
96434 includeDisabledRules: 'only'
96436 disabled_rules_elsewhere: {
96439 includeDisabledRules: 'only'
96444 includeIgnored: 'only'
96446 ignored_issues_elsewhere: {
96449 includeIgnored: 'only'
96452 } else if (opts.what === 'all' && opts.where === 'all') {
96453 messageType = 'everything';
96454 checkForHiddenIssues({
96458 includeDisabledRules: 'only'
96463 includeIgnored: 'only'
96468 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
96469 messageType = 'no_edits';
96472 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
96475 context.validator().on('validated.uiSectionValidationStatus', function () {
96476 window.requestIdleCallback(section.reRender);
96478 context.map().on('move.uiSectionValidationStatus', debounce(function () {
96479 window.requestIdleCallback(section.reRender);
96484 function uiPaneIssues(context) {
96485 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)]);
96489 function uiSettingsCustomData(context) {
96490 var dispatch$1 = dispatch('change');
96492 function render(selection) {
96493 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
96495 var _origSettings = {
96496 fileList: dataLayer && dataLayer.fileList() || null,
96497 url: corePreferences('settings-custom-data-url')
96499 var _currSettings = {
96500 fileList: dataLayer && dataLayer.fileList() || null,
96501 url: corePreferences('settings-custom-data-url')
96502 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
96504 var modal = uiConfirm(selection).okButton();
96505 modal.classed('settings-modal settings-custom-data', true);
96506 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
96507 var textSection = modal.select('.modal-section.message-text');
96508 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
96509 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
96510 .on('change', function (d3_event) {
96511 var files = d3_event.target.files;
96513 if (files && files.length) {
96514 _currSettings.url = '';
96515 textSection.select('.field-url').property('value', '');
96516 _currSettings.fileList = files;
96518 _currSettings.fileList = null;
96521 textSection.append('h4').html(_t.html('settings.custom_data.or'));
96522 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
96523 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
96525 var buttonSection = modal.select('.modal-section.buttons');
96526 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
96527 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
96528 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
96530 function isSaveDisabled() {
96532 } // restore the original url
96535 function clickCancel() {
96536 textSection.select('.field-url').property('value', _origSettings.url);
96537 corePreferences('settings-custom-data-url', _origSettings.url);
96540 } // accept the current url
96543 function clickSave() {
96544 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
96546 if (_currSettings.url) {
96547 _currSettings.fileList = null;
96550 if (_currSettings.fileList) {
96551 _currSettings.url = '';
96554 corePreferences('settings-custom-data-url', _currSettings.url);
96557 dispatch$1.call('change', this, _currSettings);
96561 return utilRebind(render, dispatch$1, 'on');
96564 function uiSectionDataLayers(context) {
96565 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
96566 var layers = context.layers();
96567 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
96569 function renderDisclosureContent(selection) {
96570 var container = selection.selectAll('.data-layer-container').data([0]);
96571 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
96572 .call(drawPanelItems);
96575 function showsLayer(which) {
96576 var layer = layers.layer(which);
96579 return layer.enabled();
96585 function setLayer(which, enabled) {
96586 // Don't allow layer changes while drawing - #6584
96587 var mode = context.mode();
96588 if (mode && /^draw/.test(mode.id)) return;
96589 var layer = layers.layer(which);
96592 layer.enabled(enabled);
96594 if (!enabled && (which === 'osm' || which === 'notes')) {
96595 context.enter(modeBrowse(context));
96600 function toggleLayer(which) {
96601 setLayer(which, !showsLayer(which));
96604 function drawOsmItems(selection) {
96605 var osmKeys = ['osm', 'notes'];
96606 var osmLayers = layers.all().filter(function (obj) {
96607 return osmKeys.indexOf(obj.id) !== -1;
96609 var ul = selection.selectAll('.layer-list-osm').data([0]);
96610 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
96611 var li = ul.selectAll('.list-item').data(osmLayers);
96612 li.exit().remove();
96613 var liEnter = li.enter().append('li').attr('class', function (d) {
96614 return 'list-item list-item-' + d.id;
96616 var labelEnter = liEnter.append('label').each(function (d) {
96617 if (d.id === 'osm') {
96618 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
96620 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
96623 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
96626 labelEnter.append('span').html(function (d) {
96627 return _t.html('map_data.layers.' + d.id + '.title');
96630 li.merge(liEnter).classed('active', function (d) {
96631 return d.layer.enabled();
96632 }).selectAll('input').property('checked', function (d) {
96633 return d.layer.enabled();
96637 function drawQAItems(selection) {
96638 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
96639 var qaLayers = layers.all().filter(function (obj) {
96640 return qaKeys.indexOf(obj.id) !== -1;
96642 var ul = selection.selectAll('.layer-list-qa').data([0]);
96643 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
96644 var li = ul.selectAll('.list-item').data(qaLayers);
96645 li.exit().remove();
96646 var liEnter = li.enter().append('li').attr('class', function (d) {
96647 return 'list-item list-item-' + d.id;
96649 var labelEnter = liEnter.append('label').each(function (d) {
96650 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
96652 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
96655 labelEnter.append('span').html(function (d) {
96656 return _t.html('map_data.layers.' + d.id + '.title');
96659 li.merge(liEnter).classed('active', function (d) {
96660 return d.layer.enabled();
96661 }).selectAll('input').property('checked', function (d) {
96662 return d.layer.enabled();
96664 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
96665 // https://github.com/osmus/detroit-mapping-challenge
96668 function drawVectorItems(selection) {
96669 var dataLayer = layers.layer('data');
96671 name: 'Detroit Neighborhoods/Parks',
96672 src: 'neighborhoods-parks',
96673 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
96674 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'
96676 name: 'Detroit Composite POIs',
96677 src: 'composite-poi',
96678 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
96679 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'
96681 name: 'Detroit All-The-Places POIs',
96682 src: 'alltheplaces-poi',
96683 tooltip: 'Public domain business location data created by web scrapers.',
96684 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'
96685 }]; // Only show this if the map is around Detroit..
96687 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
96688 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
96689 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
96690 container.exit().remove();
96691 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
96692 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
96693 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
96694 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');
96695 container = container.merge(containerEnter);
96696 var ul = container.selectAll('.layer-list-vectortile');
96697 var li = ul.selectAll('.list-item').data(vtData);
96698 li.exit().remove();
96699 var liEnter = li.enter().append('li').attr('class', function (d) {
96700 return 'list-item list-item-' + d.src;
96702 var labelEnter = liEnter.append('label').each(function (d) {
96703 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
96705 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
96706 labelEnter.append('span').html(function (d) {
96710 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
96712 function isVTLayerSelected(d) {
96713 return dataLayer && dataLayer.template() === d.template;
96716 function selectVTLayer(d3_event, d) {
96717 corePreferences('settings-custom-data-url', d.template);
96720 dataLayer.template(d.template, d.src);
96721 dataLayer.enabled(true);
96726 function drawCustomDataItems(selection) {
96727 var dataLayer = layers.layer('data');
96728 var hasData = dataLayer && dataLayer.hasData();
96729 var showsData = hasData && dataLayer.enabled();
96730 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
96732 ul.exit().remove(); // Enter
96734 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
96735 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
96736 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
96737 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
96738 toggleLayer('data');
96740 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
96741 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) {
96742 d3_event.preventDefault();
96744 }).call(svgIcon('#iD-icon-more'));
96745 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) {
96746 if (select(this).classed('disabled')) return;
96747 d3_event.preventDefault();
96748 d3_event.stopPropagation();
96749 dataLayer.fitZoom();
96750 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
96752 ul = ul.merge(ulEnter);
96753 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
96754 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
96757 function editCustom() {
96758 context.container().call(settingsCustomData);
96761 function customChanged(d) {
96762 var dataLayer = layers.layer('data');
96765 dataLayer.url(d.url);
96766 } else if (d && d.fileList) {
96767 dataLayer.fileList(d.fileList);
96771 function drawPanelItems(selection) {
96772 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
96773 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'));
96774 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96775 d3_event.preventDefault();
96776 context.ui().info.toggle('history');
96778 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
96779 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'));
96780 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96781 d3_event.preventDefault();
96782 context.ui().info.toggle('measurement');
96784 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
96787 context.layers().on('change.uiSectionDataLayers', section.reRender);
96788 context.map().on('move.uiSectionDataLayers', debounce(function () {
96789 // Detroit layers may have moved in or out of view
96790 window.requestIdleCallback(section.reRender);
96795 function uiSectionMapFeatures(context) {
96796 var _features = context.features().keys();
96798 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96800 function renderDisclosureContent(selection) {
96801 var container = selection.selectAll('.layer-feature-list-container').data([0]);
96802 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
96803 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
96804 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
96805 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
96806 d3_event.preventDefault();
96807 context.features().disableAll();
96809 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
96810 d3_event.preventDefault();
96811 context.features().enableAll();
96814 container = container.merge(containerEnter);
96815 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
96818 function drawListItems(selection, data, type, name, change, active) {
96819 var items = selection.selectAll('li').data(data); // Exit
96821 items.exit().remove(); // Enter
96823 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
96824 var tip = _t.html(name + '.' + d + '.tooltip');
96826 if (autoHiddenFeature(d)) {
96827 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
96828 tip += '<div>' + msg + '</div>';
96832 }).placement('top'));
96833 var label = enter.append('label');
96834 label.append('input').attr('type', type).attr('name', name).on('change', change);
96835 label.append('span').html(function (d) {
96836 return _t.html(name + '.' + d + '.description');
96839 items = items.merge(enter);
96840 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
96843 function autoHiddenFeature(d) {
96844 return context.features().autoHidden(d);
96847 function showsFeature(d) {
96848 return context.features().enabled(d);
96851 function clickFeature(d3_event, d) {
96852 context.features().toggle(d);
96855 function showsLayer(id) {
96856 var layer = context.layers().layer(id);
96857 return layer && layer.enabled();
96861 context.features().on('change.map_features', section.reRender);
96865 function uiSectionMapStyleOptions(context) {
96866 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96868 function renderDisclosureContent(selection) {
96869 var container = selection.selectAll('.layer-fill-list').data([0]);
96870 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
96871 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
96872 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
96873 return context.surface().classed('highlight-edited');
96877 function drawListItems(selection, data, type, name, change, active) {
96878 var items = selection.selectAll('li').data(data); // Exit
96880 items.exit().remove(); // Enter
96882 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
96883 return _t.html(name + '.' + d + '.tooltip');
96884 }).keys(function (d) {
96885 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
96886 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
96887 return key ? [key] : null;
96888 }).placement('top'));
96889 var label = enter.append('label');
96890 label.append('input').attr('type', type).attr('name', name).on('change', change);
96891 label.append('span').html(function (d) {
96892 return _t.html(name + '.' + d + '.description');
96895 items = items.merge(enter);
96896 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
96899 function isActiveFill(d) {
96900 return context.map().activeAreaFill() === d;
96903 function toggleHighlightEdited(d3_event) {
96904 d3_event.preventDefault();
96905 context.map().toggleHighlightEdited();
96908 function setFill(d3_event, d) {
96909 context.map().activeAreaFill(d);
96912 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
96916 function uiSectionPhotoOverlays(context) {
96917 var layers = context.layers();
96918 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96920 function renderDisclosureContent(selection) {
96921 var container = selection.selectAll('.photo-overlay-container').data([0]);
96922 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
96925 function drawPhotoItems(selection) {
96926 var photoKeys = context.photos().overlayLayerIDs();
96927 var photoLayers = layers.all().filter(function (obj) {
96928 return photoKeys.indexOf(obj.id) !== -1;
96930 var data = photoLayers.filter(function (obj) {
96931 return obj.layer.supported();
96934 function layerSupported(d) {
96935 return d.layer && d.layer.supported();
96938 function layerEnabled(d) {
96939 return layerSupported(d) && d.layer.enabled();
96942 var ul = selection.selectAll('.layer-list-photos').data([0]);
96943 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
96944 var li = ul.selectAll('.list-item-photos').data(data);
96945 li.exit().remove();
96946 var liEnter = li.enter().append('li').attr('class', function (d) {
96947 var classes = 'list-item-photos list-item-' + d.id;
96949 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
96950 classes += ' indented';
96955 var labelEnter = liEnter.append('label').each(function (d) {
96957 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';
96958 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
96960 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
96963 labelEnter.append('span').html(function (d) {
96965 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
96966 return _t.html(id.replace(/-/g, '_') + '.title');
96969 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
96972 function drawPhotoTypeItems(selection) {
96973 var data = context.photos().allPhotoTypes();
96975 function typeEnabled(d) {
96976 return context.photos().showsPhotoType(d);
96979 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
96980 ul.exit().remove();
96981 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
96982 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
96983 li.exit().remove();
96984 var liEnter = li.enter().append('li').attr('class', function (d) {
96985 return 'list-item-photo-types list-item-' + d;
96987 var labelEnter = liEnter.append('label').each(function (d) {
96988 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
96990 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
96991 context.photos().togglePhotoType(d);
96993 labelEnter.append('span').html(function (d) {
96994 return _t.html('photo_overlays.photo_type.' + d + '.title');
96997 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
97000 function drawDateFilter(selection) {
97001 var data = context.photos().dateFilters();
97003 function filterEnabled(d) {
97004 return context.photos().dateFilterValue(d);
97007 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
97008 ul.exit().remove();
97009 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
97010 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
97011 li.exit().remove();
97012 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
97013 var labelEnter = liEnter.append('label').each(function (d) {
97014 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
97016 labelEnter.append('span').html(function (d) {
97017 return _t.html('photo_overlays.date_filter.' + d + '.title');
97019 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
97020 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97021 }).on('change', function (d3_event, d) {
97022 var value = utilGetSetValue(select(this)).trim();
97023 context.photos().setDateFilter(d, value, true); // reload the displayed dates
97025 li.selectAll('input').each(function (d) {
97026 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97029 li = li.merge(liEnter).classed('active', filterEnabled);
97032 function drawUsernameFilter(selection) {
97033 function filterEnabled() {
97034 return context.photos().usernames();
97037 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
97038 ul.exit().remove();
97039 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
97040 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
97041 li.exit().remove();
97042 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
97043 var labelEnter = liEnter.append('label').each(function () {
97044 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
97046 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
97047 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
97048 var value = select(this).property('value');
97049 context.photos().setUsernameFilter(value, true);
97050 select(this).property('value', usernameValue);
97052 li.merge(liEnter).classed('active', filterEnabled);
97054 function usernameValue() {
97055 var usernames = context.photos().usernames();
97056 if (usernames) return usernames.join('; ');
97061 function toggleLayer(which) {
97062 setLayer(which, !showsLayer(which));
97065 function showsLayer(which) {
97066 var layer = layers.layer(which);
97069 return layer.enabled();
97075 function setLayer(which, enabled) {
97076 var layer = layers.layer(which);
97079 layer.enabled(enabled);
97083 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
97084 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
97088 function uiPaneMapData(context) {
97089 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)]);
97090 return mapDataPane;
97093 function uiSectionPrivacy(context) {
97094 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
97096 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
97098 function renderDisclosureContent(selection) {
97100 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
97101 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'));
97102 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97103 d3_event.preventDefault();
97104 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
97105 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
97108 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
97110 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'));
97113 function update() {
97114 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
97121 function uiPanePreferences(context) {
97122 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)]);
97123 return preferencesPane;
97126 function uiInit(context) {
97127 var _initCounter = 0;
97128 var _needWidth = {};
97130 var _lastPointerType;
97132 function render(container) {
97133 container.on('click.ui', function (d3_event) {
97134 // we're only concerned with the primary mouse button
97135 if (d3_event.button !== 0) return;
97136 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
97138 var isOkayTarget = d3_event.composedPath().some(function (node) {
97139 // we only care about element nodes
97140 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
97141 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
97142 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
97143 node.nodeName === 'A');
97145 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
97147 d3_event.preventDefault();
97149 var detected = utilDetect(); // only WebKit supports gesture events
97151 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
97152 // but we only need to do this on desktop Safari anyway. – #7694
97153 !detected.isMobileWebKit) {
97154 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
97155 // CSS property, but on desktop Safari we need to manually cancel the
97156 // default gesture events.
97157 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
97158 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
97159 d3_event.preventDefault();
97163 if ('PointerEvent' in window) {
97164 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
97165 var pointerType = d3_event.pointerType || 'mouse';
97167 if (_lastPointerType !== pointerType) {
97168 _lastPointerType = pointerType;
97169 container.attr('pointer', pointerType);
97173 _lastPointerType = 'mouse';
97174 container.attr('pointer', 'mouse');
97177 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
97179 container.call(uiFullScreen(context));
97180 var map = context.map();
97181 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
97183 map.on('hitMinZoom.ui', function () {
97184 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
97186 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
97187 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
97188 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
97190 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
97191 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
97192 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
97193 // pressing, even if it's not targeted. This conflicts with long-pressing
97194 // to show the edit menu. We add a selectable offscreen element as the first
97195 // child to trick Safari into not showing the selection UI.
97197 overMap.append('div').attr('class', 'select-trap').text('t');
97198 overMap.call(uiMapInMap(context)).call(uiNotice(context));
97199 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
97201 var controls = overMap.append('div').attr('class', 'map-controls');
97202 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
97203 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
97204 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
97205 // This should happen after map is initialized, as some require surface()
97207 var panes = overMap.append('div').attr('class', 'map-panes');
97208 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
97209 uiPanes.forEach(function (pane) {
97210 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
97211 panes.call(pane.renderPane);
97213 ui.info = uiInfo(context);
97214 overMap.call(ui.info);
97215 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
97216 .classed('hide', true).call(ui.photoviewer);
97217 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
97219 var about = content.append('div').attr('class', 'map-footer');
97220 about.append('div').attr('class', 'api-status').call(uiStatus(context));
97221 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
97222 footer.append('div').attr('class', 'flash-wrap footer-hide');
97223 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
97224 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
97225 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
97226 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
97227 var apiConnections = context.apiConnections();
97229 if (apiConnections && apiConnections.length > 1) {
97230 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
97233 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
97234 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
97235 var issueLinks = aboutList.append('li');
97236 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'));
97237 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'));
97238 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
97240 if (!context.embed()) {
97241 aboutList.call(uiAccount(context));
97242 } // Setup map dimensions and move map to initial center/zoom.
97243 // This should happen after .main-content and toolbars exist.
97247 map.redrawEnable(true);
97248 ui.hash = behaviorHash(context);
97251 if (!ui.hash.hadHash) {
97252 map.centerZoom([0, 0], 2);
97256 window.onbeforeunload = function () {
97257 return context.save();
97260 window.onunload = function () {
97261 context.history().unlock();
97264 select(window).on('resize.editor', function () {
97267 var panPixels = 80;
97268 context.keybinding().on('⌫', function (d3_event) {
97269 d3_event.preventDefault();
97270 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
97271 .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) {
97273 d3_event.stopImmediatePropagation();
97274 d3_event.preventDefault();
97277 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
97279 if (previousBackground) {
97280 var currentBackground = context.background().baseLayerSource();
97281 corePreferences('background-last-used-toggle', currentBackground.id);
97282 corePreferences('background-last-used', previousBackground.id);
97283 context.background().baseLayerSource(previousBackground);
97285 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
97286 d3_event.preventDefault();
97287 d3_event.stopPropagation();
97288 context.map().toggleWireframe();
97289 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
97290 d3_event.preventDefault();
97291 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
97293 var mode = context.mode();
97294 if (mode && /^draw/.test(mode.id)) return;
97295 var layer = context.layers().layer('osm');
97298 layer.enabled(!layer.enabled());
97300 if (!layer.enabled()) {
97301 context.enter(modeBrowse(context));
97304 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
97305 d3_event.preventDefault();
97306 context.map().toggleHighlightEdited();
97308 context.on('enter.editor', function (entered) {
97309 container.classed('mode-' + entered.id, true);
97310 }).on('exit.editor', function (exited) {
97311 container.classed('mode-' + exited.id, false);
97313 context.enter(modeBrowse(context));
97315 if (!_initCounter++) {
97316 if (!ui.hash.startWalkthrough) {
97317 context.container().call(uiSplash(context)).call(uiRestore(context));
97320 context.container().call(ui.shortcuts);
97323 var osm = context.connection();
97324 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
97327 osm.on('authLoading.ui', function () {
97328 context.container().call(auth);
97329 }).on('authDone.ui', function () {
97336 if (ui.hash.startWalkthrough) {
97337 ui.hash.startWalkthrough = false;
97338 context.container().call(uiIntro(context));
97342 return function (d3_event) {
97343 if (d3_event.shiftKey) return;
97344 if (context.container().select('.combobox').size()) return;
97345 d3_event.preventDefault();
97346 context.map().pan(d, 100);
97353 var _loadPromise; // renders the iD interface into the container node
97356 ui.ensureLoaded = function () {
97357 if (_loadPromise) return _loadPromise;
97358 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
97359 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
97360 if (!context.container().empty()) render(context.container());
97361 })["catch"](function (err) {
97362 return console.error(err);
97363 }); // eslint-disable-line
97364 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
97365 // for example to switch the locale while iD is running.
97368 ui.restart = function () {
97369 context.keybinding().clear();
97370 _loadPromise = null;
97371 context.container().selectAll('*').remove();
97375 ui.lastPointerType = function () {
97376 return _lastPointerType;
97379 ui.svgDefs = svgDefs(context);
97380 ui.flash = uiFlash(context);
97381 ui.sidebar = uiSidebar(context);
97382 ui.photoviewer = uiPhotoviewer(context);
97383 ui.shortcuts = uiShortcuts(context);
97385 ui.onResize = function (withPan) {
97386 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
97387 // This will call `getBoundingClientRect` and trigger reflow,
97388 // but the values will be cached for later use.
97390 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
97391 utilGetDimensions(context.container().select('.sidebar'), true);
97393 if (withPan !== undefined) {
97394 map.redrawEnable(false);
97396 map.redrawEnable(true);
97399 map.dimensions(mapDimensions);
97400 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
97402 ui.checkOverflow('.top-toolbar');
97403 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
97405 var resizeWindowEvent = document.createEvent('Event');
97406 resizeWindowEvent.initEvent('resizeWindow', true, true);
97407 document.dispatchEvent(resizeWindowEvent);
97408 }; // Call checkOverflow when resizing or whenever the contents change.
97411 ui.checkOverflow = function (selector, reset) {
97413 delete _needWidth[selector];
97416 var selection = context.container().select(selector);
97417 if (selection.empty()) return;
97418 var scrollWidth = selection.property('scrollWidth');
97419 var clientWidth = selection.property('clientWidth');
97420 var needed = _needWidth[selector] || scrollWidth;
97422 if (scrollWidth > clientWidth) {
97423 // overflow happening
97424 selection.classed('narrow', true);
97426 if (!_needWidth[selector]) {
97427 _needWidth[selector] = scrollWidth;
97429 } else if (scrollWidth >= needed) {
97430 selection.classed('narrow', false);
97434 ui.togglePanes = function (showPane) {
97435 var hidePanes = context.container().selectAll('.map-pane.shown');
97436 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
97437 hidePanes.classed('shown', false).classed('hide', true);
97438 context.container().selectAll('.map-pane-control button').classed('active', false);
97441 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
97442 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
97443 showPane.classed('shown', true).classed('hide', false);
97445 if (hidePanes.empty()) {
97446 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
97448 showPane.style(side, '0px');
97451 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
97452 select(this).classed('shown', false).classed('hide', true);
97457 var _editMenu = uiEditMenu(context);
97459 ui.editMenu = function () {
97463 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
97464 // remove any displayed menu
97465 ui.closeEditMenu();
97466 if (!operations && context.mode().operations) operations = context.mode().operations();
97467 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
97469 if (!context.map().editableDataEnabled()) return;
97470 var surfaceNode = context.surface().node();
97472 if (surfaceNode.focus) {
97473 // FF doesn't support it
97474 // focus the surface or else clicking off the menu may not trigger modeBrowse
97475 surfaceNode.focus();
97478 operations.forEach(function (operation) {
97479 if (operation.point) operation.point(anchorPoint);
97482 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
97485 context.map().supersurface.call(_editMenu);
97488 ui.closeEditMenu = function () {
97489 // remove any existing menu no matter how it was added
97490 context.map().supersurface.select('.edit-menu').remove();
97493 var _saveLoading = select(null);
97495 context.uploader().on('saveStarted.ui', function () {
97496 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
97497 context.container().call(_saveLoading); // block input during upload
97498 }).on('saveEnded.ui', function () {
97499 _saveLoading.close();
97501 _saveLoading = select(null);
97506 function coreContext() {
97509 var dispatch$1 = dispatch('enter', 'exit', 'change');
97510 var context = utilRebind({}, dispatch$1, 'on');
97512 var _deferred = new Set();
97514 context.version = '2.19.6';
97515 context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
97517 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
97518 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
97520 // An osmChangeset object. Not loaded until needed.
97522 context.changeset = null;
97523 var _defaultChangesetComment = context.initialHashParams.comment;
97524 var _defaultChangesetSource = context.initialHashParams.source;
97525 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
97527 context.defaultChangesetComment = function (val) {
97528 if (!arguments.length) return _defaultChangesetComment;
97529 _defaultChangesetComment = val;
97533 context.defaultChangesetSource = function (val) {
97534 if (!arguments.length) return _defaultChangesetSource;
97535 _defaultChangesetSource = val;
97539 context.defaultChangesetHashtags = function (val) {
97540 if (!arguments.length) return _defaultChangesetHashtags;
97541 _defaultChangesetHashtags = val;
97544 /* Document title */
97546 /* (typically shown as the label for the browser window/tab) */
97547 // If true, iD will update the title based on what the user is doing
97550 var _setsDocumentTitle = true;
97552 context.setsDocumentTitle = function (val) {
97553 if (!arguments.length) return _setsDocumentTitle;
97554 _setsDocumentTitle = val;
97556 }; // The part of the title that is always the same
97559 var _documentTitleBase = document.title;
97561 context.documentTitleBase = function (val) {
97562 if (!arguments.length) return _documentTitleBase;
97563 _documentTitleBase = val;
97566 /* User interface and keybinding */
97571 context.ui = function () {
97575 context.lastPointerType = function () {
97576 return _ui.lastPointerType();
97579 var _keybinding = utilKeybinding('context');
97581 context.keybinding = function () {
97582 return _keybinding;
97585 select(document).call(_keybinding);
97586 /* Straight accessors. Avoid using these if you can. */
97587 // Instantiate the connection here because it doesn't require passing in
97588 // `context` and it's needed for pre-init calls like `preauth`
97590 var _connection = services.osm;
97598 context.connection = function () {
97599 return _connection;
97602 context.history = function () {
97606 context.validator = function () {
97610 context.uploader = function () {
97616 context.preauth = function (options) {
97618 _connection["switch"](options);
97623 /* connection options for source switcher (optional) */
97626 var _apiConnections;
97628 context.apiConnections = function (val) {
97629 if (!arguments.length) return _apiConnections;
97630 _apiConnections = val;
97632 }; // A string or array or locale codes to prefer over the browser's settings
97635 context.locale = function (locale) {
97636 if (!arguments.length) return _mainLocalizer.localeCode();
97637 _mainLocalizer.preferredLocaleCodes(locale);
97641 function afterLoad(cid, callback) {
97642 return function (err, result) {
97644 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
97645 if (err.status === 400 || err.status === 401 || err.status === 403) {
97647 _connection.logout();
97651 if (typeof callback === 'function') {
97656 } else if (_connection && _connection.getConnectionId() !== cid) {
97657 if (typeof callback === 'function') {
97659 message: 'Connection Switched',
97666 _history.merge(result.data, result.extent);
97668 if (typeof callback === 'function') {
97669 callback(err, result);
97677 context.loadTiles = function (projection, callback) {
97678 var handle = window.requestIdleCallback(function () {
97679 _deferred["delete"](handle);
97681 if (_connection && context.editableDataEnabled()) {
97682 var cid = _connection.getConnectionId();
97684 _connection.loadTiles(projection, afterLoad(cid, callback));
97688 _deferred.add(handle);
97691 context.loadTileAtLoc = function (loc, callback) {
97692 var handle = window.requestIdleCallback(function () {
97693 _deferred["delete"](handle);
97695 if (_connection && context.editableDataEnabled()) {
97696 var cid = _connection.getConnectionId();
97698 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
97702 _deferred.add(handle);
97705 context.loadEntity = function (entityID, callback) {
97707 var cid = _connection.getConnectionId();
97709 _connection.loadEntity(entityID, afterLoad(cid, callback));
97713 context.zoomToEntity = function (entityID, zoomTo) {
97714 // be sure to load the entity even if we're not going to zoom to it
97715 context.loadEntity(entityID, function (err, result) {
97718 if (zoomTo !== false) {
97719 var entity = result.data.find(function (e) {
97720 return e.id === entityID;
97724 _map.zoomTo(entity);
97729 _map.on('drawn.zoomToEntity', function () {
97730 if (!context.hasEntity(entityID)) return;
97732 _map.on('drawn.zoomToEntity', null);
97734 context.on('enter.zoomToEntity', null);
97735 context.enter(modeSelect(context, [entityID]));
97738 context.on('enter.zoomToEntity', function () {
97739 if (_mode.id !== 'browse') {
97740 _map.on('drawn.zoomToEntity', null);
97742 context.on('enter.zoomToEntity', null);
97747 var _minEditableZoom = 16;
97749 context.minEditableZoom = function (val) {
97750 if (!arguments.length) return _minEditableZoom;
97751 _minEditableZoom = val;
97754 _connection.tileZoom(val);
97758 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
97761 context.maxCharsForTagKey = function () {
97765 context.maxCharsForTagValue = function () {
97769 context.maxCharsForRelationRole = function () {
97773 function cleanOsmString(val, maxChars) {
97774 // be lenient with input
97775 if (val === undefined || val === null) {
97778 val = val.toString();
97779 } // remove whitespace
97782 val = val.trim(); // use the canonical form of the string
97784 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
97786 return utilUnicodeCharsTruncated(val, maxChars);
97789 context.cleanTagKey = function (val) {
97790 return cleanOsmString(val, context.maxCharsForTagKey());
97793 context.cleanTagValue = function (val) {
97794 return cleanOsmString(val, context.maxCharsForTagValue());
97797 context.cleanRelationRole = function (val) {
97798 return cleanOsmString(val, context.maxCharsForRelationRole());
97803 var _inIntro = false;
97805 context.inIntro = function (val) {
97806 if (!arguments.length) return _inIntro;
97809 }; // Immediately save the user's history to localstorage, if possible
97810 // This is called someteimes, but also on the `window.onbeforeunload` handler
97813 context.save = function () {
97814 // no history save, no message onbeforeunload
97815 if (_inIntro || context.container().select('.modal').size()) return;
97818 if (_mode && _mode.id === 'save') {
97819 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
97821 if (services.osm && services.osm.isChangesetInflight()) {
97822 _history.clearSaved();
97827 canSave = context.selectedIDs().every(function (id) {
97828 var entity = context.hasEntity(id);
97829 return entity && !entity.isDegenerate();
97837 if (_history.hasChanges()) {
97838 return _t('save.unsaved_changes');
97840 }; // Debounce save, since it's a synchronous localStorage write,
97841 // and history changes can happen frequently (e.g. when dragging).
97844 context.debouncedSave = debounce(context.save, 350);
97846 function withDebouncedSave(fn) {
97847 return function () {
97848 var result = fn.apply(_history, arguments);
97849 context.debouncedSave();
97856 context.hasEntity = function (id) {
97857 return _history.graph().hasEntity(id);
97860 context.entity = function (id) {
97861 return _history.graph().entity(id);
97868 context.mode = function () {
97872 context.enter = function (newMode) {
97876 dispatch$1.call('exit', _this, _mode);
97883 dispatch$1.call('enter', _this, _mode);
97886 context.selectedIDs = function () {
97887 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
97890 context.activeID = function () {
97891 return _mode && _mode.activeID && _mode.activeID();
97894 var _selectedNoteID;
97896 context.selectedNoteID = function (noteID) {
97897 if (!arguments.length) return _selectedNoteID;
97898 _selectedNoteID = noteID;
97900 }; // NOTE: Don't change the name of this until UI v3 is merged
97903 var _selectedErrorID;
97905 context.selectedErrorID = function (errorID) {
97906 if (!arguments.length) return _selectedErrorID;
97907 _selectedErrorID = errorID;
97913 context.install = function (behavior) {
97914 return context.surface().call(behavior);
97917 context.uninstall = function (behavior) {
97918 return context.surface().call(behavior.off);
97925 context.copyGraph = function () {
97931 context.copyIDs = function (val) {
97932 if (!arguments.length) return _copyIDs;
97934 _copyGraph = _history.graph();
97940 context.copyLonLat = function (val) {
97941 if (!arguments.length) return _copyLonLat;
97950 context.background = function () {
97951 return _background;
97958 context.features = function () {
97962 context.hasHiddenConnections = function (id) {
97963 var graph = _history.graph();
97965 var entity = graph.entity(id);
97966 return _features.hasHiddenConnections(entity, graph);
97973 context.photos = function () {
97981 context.map = function () {
97985 context.layers = function () {
97986 return _map.layers();
97989 context.surface = function () {
97990 return _map.surface;
97993 context.editableDataEnabled = function () {
97994 return _map.editableDataEnabled();
97997 context.surfaceRect = function () {
97998 return _map.surface.node().getBoundingClientRect();
98001 context.editable = function () {
98002 // don't allow editing during save
98003 var mode = context.mode();
98004 if (!mode || mode.id === 'save') return false;
98005 return _map.editableDataEnabled();
98010 var _debugFlags = {
98014 // label collision bounding boxes
98016 // imagery bounding polygons
98019 downloaded: false // downloaded data from osm
98023 context.debugFlags = function () {
98024 return _debugFlags;
98027 context.getDebug = function (flag) {
98028 return flag && _debugFlags[flag];
98031 context.setDebug = function (flag, val) {
98032 if (arguments.length === 1) val = true;
98033 _debugFlags[flag] = val;
98034 dispatch$1.call('change');
98040 var _container = select(null);
98042 context.container = function (val) {
98043 if (!arguments.length) return _container;
98046 _container.classed('ideditor', true);
98051 context.containerNode = function (val) {
98052 if (!arguments.length) return context.container().node();
98053 context.container(select(val));
98059 context.embed = function (val) {
98060 if (!arguments.length) return _embed;
98067 var _assetPath = '';
98069 context.assetPath = function (val) {
98070 if (!arguments.length) return _assetPath;
98072 _mainFileFetcher.assetPath(val);
98076 var _assetMap = {};
98078 context.assetMap = function (val) {
98079 if (!arguments.length) return _assetMap;
98081 _mainFileFetcher.assetMap(val);
98085 context.asset = function (val) {
98086 if (/^http(s)?:\/\//i.test(val)) return val;
98087 var filename = _assetPath + val;
98088 return _assetMap[filename] || filename;
98091 context.imagePath = function (val) {
98092 return context.asset("img/".concat(val));
98094 /* reset (aka flush) */
98097 context.reset = context.flush = function () {
98098 context.debouncedSave.cancel();
98099 Array.from(_deferred).forEach(function (handle) {
98100 window.cancelIdleCallback(handle);
98102 _deferred["delete"](handle);
98104 Object.values(services).forEach(function (service) {
98105 if (service && typeof service.reset === 'function') {
98106 service.reset(context);
98109 context.changeset = null;
98111 _validator.reset();
98117 _uploader.reset(); // don't leave stale state in the inspector
98120 context.container().select('.inspector-wrap *').remove();
98126 context.projection = geoRawMercator();
98127 context.curtainProjection = geoRawMercator();
98130 context.init = function () {
98131 instantiateInternal();
98132 initializeDependents();
98133 return context; // Load variables and properties. No property of `context` should be accessed
98134 // until this is complete since load statuses are indeterminate. The order
98135 // of instantiation shouldn't matter.
98137 function instantiateInternal() {
98138 _history = coreHistory(context);
98139 context.graph = _history.graph;
98140 context.pauseChangeDispatch = _history.pauseChangeDispatch;
98141 context.resumeChangeDispatch = _history.resumeChangeDispatch;
98142 context.perform = withDebouncedSave(_history.perform);
98143 context.replace = withDebouncedSave(_history.replace);
98144 context.pop = withDebouncedSave(_history.pop);
98145 context.overwrite = withDebouncedSave(_history.overwrite);
98146 context.undo = withDebouncedSave(_history.undo);
98147 context.redo = withDebouncedSave(_history.redo);
98148 _validator = coreValidator(context);
98149 _uploader = coreUploader(context);
98150 _background = rendererBackground(context);
98151 _features = rendererFeatures(context);
98152 _map = rendererMap(context);
98153 _photos = rendererPhotos(context);
98154 _ui = uiInit(context);
98155 } // Set up objects that might need to access properties of `context`. The order
98156 // might matter if dependents make calls to each other. Be wary of async calls.
98159 function initializeDependents() {
98160 if (context.initialHashParams.presets) {
98161 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
98164 if (context.initialHashParams.locale) {
98165 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
98166 } // kick off some async work
98169 _mainLocalizer.ensureLoaded();
98171 _background.ensureLoaded();
98173 _mainPresetIndex.ensureLoaded();
98174 Object.values(services).forEach(function (service) {
98175 if (service && typeof service.init === 'function') {
98186 if (services.maprules && context.initialHashParams.maprules) {
98187 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
98188 services.maprules.init();
98189 mapcss.forEach(function (mapcssSelector) {
98190 return services.maprules.addRule(mapcssSelector);
98192 })["catch"](function () {
98195 } // if the container isn't available, e.g. when testing, don't load the UI
98198 if (!context.container().empty()) {
98199 _ui.ensureLoaded().then(function () {
98209 // This is only done in testing because of the performance penalty.
98211 var debug = false; // Reexport just what our tests use, see #4379
98213 dispatch: dispatch,
98214 geoMercator: mercator,
98215 geoProjection: projection,
98216 polygonArea: d3_polygonArea,
98217 polygonCentroid: d3_polygonCentroid,
98219 selectAll: selectAll,
98220 timerFlush: timerFlush
98223 var iD = /*#__PURE__*/Object.freeze({
98227 actionAddEntity: actionAddEntity,
98228 actionAddMember: actionAddMember,
98229 actionAddMidpoint: actionAddMidpoint,
98230 actionAddVertex: actionAddVertex,
98231 actionChangeMember: actionChangeMember,
98232 actionChangePreset: actionChangePreset,
98233 actionChangeTags: actionChangeTags,
98234 actionCircularize: actionCircularize,
98235 actionConnect: actionConnect,
98236 actionCopyEntities: actionCopyEntities,
98237 actionDeleteMember: actionDeleteMember,
98238 actionDeleteMultiple: actionDeleteMultiple,
98239 actionDeleteNode: actionDeleteNode,
98240 actionDeleteRelation: actionDeleteRelation,
98241 actionDeleteWay: actionDeleteWay,
98242 actionDiscardTags: actionDiscardTags,
98243 actionDisconnect: actionDisconnect,
98244 actionExtract: actionExtract,
98245 actionJoin: actionJoin,
98246 actionMerge: actionMerge,
98247 actionMergeNodes: actionMergeNodes,
98248 actionMergePolygon: actionMergePolygon,
98249 actionMergeRemoteChanges: actionMergeRemoteChanges,
98250 actionMove: actionMove,
98251 actionMoveMember: actionMoveMember,
98252 actionMoveNode: actionMoveNode,
98253 actionNoop: actionNoop,
98254 actionOrthogonalize: actionOrthogonalize,
98255 actionRestrictTurn: actionRestrictTurn,
98256 actionReverse: actionReverse,
98257 actionRevert: actionRevert,
98258 actionRotate: actionRotate,
98259 actionScale: actionScale,
98260 actionSplit: actionSplit,
98261 actionStraightenNodes: actionStraightenNodes,
98262 actionStraightenWay: actionStraightenWay,
98263 actionUnrestrictTurn: actionUnrestrictTurn,
98264 actionReflect: actionReflect,
98265 actionUpgradeTags: actionUpgradeTags,
98266 behaviorAddWay: behaviorAddWay,
98267 behaviorBreathe: behaviorBreathe,
98268 behaviorDrag: behaviorDrag,
98269 behaviorDrawWay: behaviorDrawWay,
98270 behaviorDraw: behaviorDraw,
98271 behaviorEdit: behaviorEdit,
98272 behaviorHash: behaviorHash,
98273 behaviorHover: behaviorHover,
98274 behaviorLasso: behaviorLasso,
98275 behaviorOperation: behaviorOperation,
98276 behaviorPaste: behaviorPaste,
98277 behaviorSelect: behaviorSelect,
98278 coreContext: coreContext,
98279 coreFileFetcher: coreFileFetcher,
98280 fileFetcher: _mainFileFetcher,
98281 coreDifference: coreDifference,
98282 coreGraph: coreGraph,
98283 coreHistory: coreHistory,
98284 coreLocalizer: coreLocalizer,
98286 localizer: _mainLocalizer,
98287 prefs: corePreferences,
98288 coreTree: coreTree,
98289 coreUploader: coreUploader,
98290 coreValidator: coreValidator,
98291 geoExtent: geoExtent,
98292 geoLatToMeters: geoLatToMeters,
98293 geoLonToMeters: geoLonToMeters,
98294 geoMetersToLat: geoMetersToLat,
98295 geoMetersToLon: geoMetersToLon,
98296 geoMetersToOffset: geoMetersToOffset,
98297 geoOffsetToMeters: geoOffsetToMeters,
98298 geoScaleToZoom: geoScaleToZoom,
98299 geoSphericalClosestNode: geoSphericalClosestNode,
98300 geoSphericalDistance: geoSphericalDistance,
98301 geoZoomToScale: geoZoomToScale,
98302 geoAngle: geoAngle,
98303 geoChooseEdge: geoChooseEdge,
98304 geoEdgeEqual: geoEdgeEqual,
98305 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
98306 geoHasLineIntersections: geoHasLineIntersections,
98307 geoHasSelfIntersections: geoHasSelfIntersections,
98308 geoRotate: geoRotate,
98309 geoLineIntersection: geoLineIntersection,
98310 geoPathHasIntersections: geoPathHasIntersections,
98311 geoPathIntersections: geoPathIntersections,
98312 geoPathLength: geoPathLength,
98313 geoPointInPolygon: geoPointInPolygon,
98314 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
98315 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
98316 geoViewportEdge: geoViewportEdge,
98317 geoRawMercator: geoRawMercator,
98318 geoVecAdd: geoVecAdd,
98319 geoVecAngle: geoVecAngle,
98320 geoVecCross: geoVecCross,
98321 geoVecDot: geoVecDot,
98322 geoVecEqual: geoVecEqual,
98323 geoVecFloor: geoVecFloor,
98324 geoVecInterp: geoVecInterp,
98325 geoVecLength: geoVecLength,
98326 geoVecLengthSquare: geoVecLengthSquare,
98327 geoVecNormalize: geoVecNormalize,
98328 geoVecNormalizedDot: geoVecNormalizedDot,
98329 geoVecProject: geoVecProject,
98330 geoVecSubtract: geoVecSubtract,
98331 geoVecScale: geoVecScale,
98332 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
98333 geoOrthoCalcScore: geoOrthoCalcScore,
98334 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
98335 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
98336 modeAddArea: modeAddArea,
98337 modeAddLine: modeAddLine,
98338 modeAddPoint: modeAddPoint,
98339 modeAddNote: modeAddNote,
98340 modeBrowse: modeBrowse,
98341 modeDragNode: modeDragNode,
98342 modeDragNote: modeDragNote,
98343 modeDrawArea: modeDrawArea,
98344 modeDrawLine: modeDrawLine,
98345 modeMove: modeMove,
98346 modeRotate: modeRotate,
98347 modeSave: modeSave,
98348 modeSelect: modeSelect,
98349 modeSelectData: modeSelectData,
98350 modeSelectError: modeSelectError,
98351 modeSelectNote: modeSelectNote,
98352 operationCircularize: operationCircularize,
98353 operationContinue: operationContinue,
98354 operationCopy: operationCopy,
98355 operationDelete: operationDelete,
98356 operationDisconnect: operationDisconnect,
98357 operationDowngrade: operationDowngrade,
98358 operationExtract: operationExtract,
98359 operationMerge: operationMerge,
98360 operationMove: operationMove,
98361 operationOrthogonalize: operationOrthogonalize,
98362 operationPaste: operationPaste,
98363 operationReflectShort: operationReflectShort,
98364 operationReflectLong: operationReflectLong,
98365 operationReverse: operationReverse,
98366 operationRotate: operationRotate,
98367 operationSplit: operationSplit,
98368 operationStraighten: operationStraighten,
98369 osmChangeset: osmChangeset,
98370 osmEntity: osmEntity,
98373 osmRelation: osmRelation,
98376 osmIntersection: osmIntersection,
98378 osmInferRestriction: osmInferRestriction,
98379 osmLanes: osmLanes,
98380 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
98381 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
98382 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
98383 osmJoinWays: osmJoinWays,
98384 get osmAreaKeys () { return osmAreaKeys; },
98385 osmSetAreaKeys: osmSetAreaKeys,
98386 osmTagSuggestingArea: osmTagSuggestingArea,
98387 get osmPointTags () { return osmPointTags; },
98388 osmSetPointTags: osmSetPointTags,
98389 get osmVertexTags () { return osmVertexTags; },
98390 osmSetVertexTags: osmSetVertexTags,
98391 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
98392 osmOneWayTags: osmOneWayTags,
98393 osmPavedTags: osmPavedTags,
98394 osmIsInterestingTag: osmIsInterestingTag,
98395 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
98396 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
98397 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
98398 presetCategory: presetCategory,
98399 presetCollection: presetCollection,
98400 presetField: presetField,
98401 presetPreset: presetPreset,
98402 presetManager: _mainPresetIndex,
98403 presetIndex: presetIndex,
98404 rendererBackgroundSource: rendererBackgroundSource,
98405 rendererBackground: rendererBackground,
98406 rendererFeatures: rendererFeatures,
98407 rendererMap: rendererMap,
98408 rendererPhotos: rendererPhotos,
98409 rendererTileLayer: rendererTileLayer,
98410 services: services,
98411 serviceKeepRight: serviceKeepRight,
98412 serviceImproveOSM: serviceImproveOSM,
98413 serviceOsmose: serviceOsmose,
98414 serviceMapillary: serviceMapillary,
98415 serviceMapRules: serviceMapRules,
98416 serviceNominatim: serviceNominatim,
98417 serviceOpenstreetcam: serviceOpenstreetcam,
98418 serviceOsm: serviceOsm,
98419 serviceOsmWikibase: serviceOsmWikibase,
98420 serviceStreetside: serviceStreetside,
98421 serviceTaginfo: serviceTaginfo,
98422 serviceVectorTile: serviceVectorTile,
98423 serviceWikidata: serviceWikidata,
98424 serviceWikipedia: serviceWikipedia,
98425 svgAreas: svgAreas,
98427 svgDebug: svgDebug,
98429 svgKeepRight: svgKeepRight,
98431 svgGeolocate: svgGeolocate,
98432 svgLabels: svgLabels,
98433 svgLayers: svgLayers,
98434 svgLines: svgLines,
98435 svgMapillaryImages: svgMapillaryImages,
98436 svgMapillarySigns: svgMapillarySigns,
98437 svgMidpoints: svgMidpoints,
98438 svgNotes: svgNotes,
98439 svgMarkerSegments: svgMarkerSegments,
98440 svgOpenstreetcamImages: svgOpenstreetcamImages,
98442 svgPassiveVertex: svgPassiveVertex,
98444 svgPointTransform: svgPointTransform,
98445 svgPoints: svgPoints,
98446 svgRelationMemberTags: svgRelationMemberTags,
98447 svgSegmentWay: svgSegmentWay,
98448 svgStreetside: svgStreetside,
98449 svgTagClasses: svgTagClasses,
98450 svgTagPattern: svgTagPattern,
98451 svgTouch: svgTouch,
98452 svgTurns: svgTurns,
98453 svgVertices: svgVertices,
98454 uiFieldDefaultCheck: uiFieldCheck,
98455 uiFieldOnewayCheck: uiFieldCheck,
98456 uiFieldCheck: uiFieldCheck,
98457 uiFieldManyCombo: uiFieldCombo,
98458 uiFieldMultiCombo: uiFieldCombo,
98459 uiFieldNetworkCombo: uiFieldCombo,
98460 uiFieldSemiCombo: uiFieldCombo,
98461 uiFieldTypeCombo: uiFieldCombo,
98462 uiFieldCombo: uiFieldCombo,
98463 uiFieldUrl: uiFieldText,
98464 uiFieldIdentifier: uiFieldText,
98465 uiFieldNumber: uiFieldText,
98466 uiFieldTel: uiFieldText,
98467 uiFieldEmail: uiFieldText,
98468 uiFieldText: uiFieldText,
98469 uiFieldAccess: uiFieldAccess,
98470 uiFieldAddress: uiFieldAddress,
98471 uiFieldCycleway: uiFieldCycleway,
98472 uiFieldLanes: uiFieldLanes,
98473 uiFieldLocalized: uiFieldLocalized,
98474 uiFieldMaxspeed: uiFieldMaxspeed,
98475 uiFieldStructureRadio: uiFieldRadio,
98476 uiFieldRadio: uiFieldRadio,
98477 uiFieldRestrictions: uiFieldRestrictions,
98478 uiFieldTextarea: uiFieldTextarea,
98479 uiFieldWikidata: uiFieldWikidata,
98480 uiFieldWikipedia: uiFieldWikipedia,
98481 uiFields: uiFields,
98483 uiPanelBackground: uiPanelBackground,
98484 uiPanelHistory: uiPanelHistory,
98485 uiPanelLocation: uiPanelLocation,
98486 uiPanelMeasurement: uiPanelMeasurement,
98487 uiInfoPanels: uiInfoPanels,
98488 uiPaneBackground: uiPaneBackground,
98489 uiPaneHelp: uiPaneHelp,
98490 uiPaneIssues: uiPaneIssues,
98491 uiPaneMapData: uiPaneMapData,
98492 uiPanePreferences: uiPanePreferences,
98493 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
98494 uiSectionBackgroundList: uiSectionBackgroundList,
98495 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
98496 uiSectionChanges: uiSectionChanges,
98497 uiSectionDataLayers: uiSectionDataLayers,
98498 uiSectionEntityIssues: uiSectionEntityIssues,
98499 uiSectionFeatureType: uiSectionFeatureType,
98500 uiSectionMapFeatures: uiSectionMapFeatures,
98501 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
98502 uiSectionOverlayList: uiSectionOverlayList,
98503 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
98504 uiSectionPresetFields: uiSectionPresetFields,
98505 uiSectionPrivacy: uiSectionPrivacy,
98506 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
98507 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
98508 uiSectionRawTagEditor: uiSectionRawTagEditor,
98509 uiSectionSelectionList: uiSectionSelectionList,
98510 uiSectionValidationIssues: uiSectionValidationIssues,
98511 uiSectionValidationOptions: uiSectionValidationOptions,
98512 uiSectionValidationRules: uiSectionValidationRules,
98513 uiSectionValidationStatus: uiSectionValidationStatus,
98514 uiSettingsCustomBackground: uiSettingsCustomBackground,
98515 uiSettingsCustomData: uiSettingsCustomData,
98517 uiAccount: uiAccount,
98518 uiAttribution: uiAttribution,
98519 uiChangesetEditor: uiChangesetEditor,
98521 uiCombobox: uiCombobox,
98522 uiCommit: uiCommit,
98523 uiCommitWarnings: uiCommitWarnings,
98524 uiConfirm: uiConfirm,
98525 uiConflicts: uiConflicts,
98526 uiContributors: uiContributors,
98527 uiCurtain: uiCurtain,
98528 uiDataEditor: uiDataEditor,
98529 uiDataHeader: uiDataHeader,
98530 uiDisclosure: uiDisclosure,
98531 uiEditMenu: uiEditMenu,
98532 uiEntityEditor: uiEntityEditor,
98533 uiFeatureInfo: uiFeatureInfo,
98534 uiFeatureList: uiFeatureList,
98536 uiFieldHelp: uiFieldHelp,
98538 uiFormFields: uiFormFields,
98539 uiFullScreen: uiFullScreen,
98540 uiGeolocate: uiGeolocate,
98541 uiImproveOsmComments: uiImproveOsmComments,
98542 uiImproveOsmDetails: uiImproveOsmDetails,
98543 uiImproveOsmEditor: uiImproveOsmEditor,
98544 uiImproveOsmHeader: uiImproveOsmHeader,
98546 uiInspector: uiInspector,
98547 uiIssuesInfo: uiIssuesInfo,
98548 uiKeepRightDetails: uiKeepRightDetails,
98549 uiKeepRightEditor: uiKeepRightEditor,
98550 uiKeepRightHeader: uiKeepRightHeader,
98552 uiLoading: uiLoading,
98553 uiMapInMap: uiMapInMap,
98555 uiNotice: uiNotice,
98556 uiNoteComments: uiNoteComments,
98557 uiNoteEditor: uiNoteEditor,
98558 uiNoteHeader: uiNoteHeader,
98559 uiNoteReport: uiNoteReport,
98560 uiPopover: uiPopover,
98561 uiPresetIcon: uiPresetIcon,
98562 uiPresetList: uiPresetList,
98563 uiRestore: uiRestore,
98565 uiSidebar: uiSidebar,
98566 uiSourceSwitch: uiSourceSwitch,
98567 uiSpinner: uiSpinner,
98568 uiSplash: uiSplash,
98569 uiStatus: uiStatus,
98570 uiSuccess: uiSuccess,
98571 uiTagReference: uiTagReference,
98572 uiToggle: uiToggle,
98573 uiTooltip: uiTooltip,
98574 uiVersion: uiVersion,
98575 uiViewOnOSM: uiViewOnOSM,
98576 uiViewOnKeepRight: uiViewOnKeepRight,
98578 utilAesEncrypt: utilAesEncrypt,
98579 utilAesDecrypt: utilAesDecrypt,
98580 utilArrayChunk: utilArrayChunk,
98581 utilArrayDifference: utilArrayDifference,
98582 utilArrayFlatten: utilArrayFlatten,
98583 utilArrayGroupBy: utilArrayGroupBy,
98584 utilArrayIdentical: utilArrayIdentical,
98585 utilArrayIntersection: utilArrayIntersection,
98586 utilArrayUnion: utilArrayUnion,
98587 utilArrayUniq: utilArrayUniq,
98588 utilArrayUniqBy: utilArrayUniqBy,
98589 utilAsyncMap: utilAsyncMap,
98590 utilCleanTags: utilCleanTags,
98591 utilCombinedTags: utilCombinedTags,
98592 utilDeepMemberSelector: utilDeepMemberSelector,
98593 utilDetect: utilDetect,
98594 utilDisplayName: utilDisplayName,
98595 utilDisplayNameForPath: utilDisplayNameForPath,
98596 utilDisplayType: utilDisplayType,
98597 utilDisplayLabel: utilDisplayLabel,
98598 utilEntityRoot: utilEntityRoot,
98599 utilEditDistance: utilEditDistance,
98600 utilEntitySelector: utilEntitySelector,
98601 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
98602 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
98603 utilFastMouse: utilFastMouse,
98604 utilFunctor: utilFunctor,
98605 utilGetAllNodes: utilGetAllNodes,
98606 utilGetSetValue: utilGetSetValue,
98607 utilHashcode: utilHashcode,
98608 utilHighlightEntities: utilHighlightEntities,
98609 utilKeybinding: utilKeybinding,
98610 utilNoAuto: utilNoAuto,
98611 utilObjectOmit: utilObjectOmit,
98612 utilPrefixCSSProperty: utilPrefixCSSProperty,
98613 utilPrefixDOMProperty: utilPrefixDOMProperty,
98614 utilQsString: utilQsString,
98615 utilRebind: utilRebind,
98616 utilSafeClassName: utilSafeClassName,
98617 utilSetTransform: utilSetTransform,
98618 utilSessionMutex: utilSessionMutex,
98619 utilStringQs: utilStringQs,
98620 utilTagDiff: utilTagDiff,
98621 utilTagText: utilTagText,
98622 utilTiler: utilTiler,
98623 utilTotalExtent: utilTotalExtent,
98624 utilTriggerEvent: utilTriggerEvent,
98625 utilUnicodeCharsCount: utilUnicodeCharsCount,
98626 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
98627 utilUniqueDomId: utilUniqueDomId,
98628 utilWrap: utilWrap,
98629 validationAlmostJunction: validationAlmostJunction,
98630 validationCloseNodes: validationCloseNodes,
98631 validationCrossingWays: validationCrossingWays,
98632 validationDisconnectedWay: validationDisconnectedWay,
98633 validationFormatting: validationFormatting,
98634 validationHelpRequest: validationHelpRequest,
98635 validationImpossibleOneway: validationImpossibleOneway,
98636 validationIncompatibleSource: validationIncompatibleSource,
98637 validationMaprules: validationMaprules,
98638 validationMismatchedGeometry: validationMismatchedGeometry,
98639 validationMissingRole: validationMissingRole,
98640 validationMissingTag: validationMissingTag,
98641 validationOutdatedTags: validationOutdatedTags,
98642 validationPrivateData: validationPrivateData,
98643 validationSuspiciousName: validationSuspiciousName,
98644 validationUnsquareWay: validationUnsquareWay
98647 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
98648 var start = Date.now();
98649 return window.requestAnimationFrame(function () {
98652 timeRemaining: function timeRemaining() {
98653 return Math.max(0, 50 - (Date.now() - start));
98659 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
98660 window.cancelAnimationFrame(id);