3 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5 function createCommonjsModule(fn) {
6 var module = { exports: {} };
7 return fn(module, module.exports), module.exports;
10 var check = function (it) {
11 return it && it.Math == Math && it;
14 // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
16 // eslint-disable-next-line es/no-global-this -- safe
17 check(typeof globalThis == 'object' && globalThis) ||
18 check(typeof window == 'object' && window) ||
19 // eslint-disable-next-line no-restricted-globals -- safe
20 check(typeof self == 'object' && self) ||
21 check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
22 // eslint-disable-next-line no-new-func -- fallback
23 (function () { return this; })() || Function('return this')();
25 var fails = function (exec) {
33 // Detect IE8's incomplete defineProperty implementation
34 var descriptors = !fails(function () {
35 // eslint-disable-next-line es/no-object-defineproperty -- required for testing
36 return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
39 var $propertyIsEnumerable$1 = {}.propertyIsEnumerable;
40 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
41 var getOwnPropertyDescriptor$5 = Object.getOwnPropertyDescriptor;
44 var NASHORN_BUG = getOwnPropertyDescriptor$5 && !$propertyIsEnumerable$1.call({ 1: 2 }, 1);
46 // `Object.prototype.propertyIsEnumerable` method implementation
47 // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
48 var f$7 = NASHORN_BUG ? function propertyIsEnumerable(V) {
49 var descriptor = getOwnPropertyDescriptor$5(this, V);
50 return !!descriptor && descriptor.enumerable;
51 } : $propertyIsEnumerable$1;
53 var objectPropertyIsEnumerable = {
57 var createPropertyDescriptor = function (bitmap, value) {
59 enumerable: !(bitmap & 1),
60 configurable: !(bitmap & 2),
61 writable: !(bitmap & 4),
66 var toString$1 = {}.toString;
68 var classofRaw = function (it) {
69 return toString$1.call(it).slice(8, -1);
72 var split$2 = ''.split;
74 // fallback for non-array-like ES3 and non-enumerable old V8 strings
75 var indexedObject = fails(function () {
76 // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
77 // eslint-disable-next-line no-prototype-builtins -- safe
78 return !Object('z').propertyIsEnumerable(0);
80 return classofRaw(it) == 'String' ? split$2.call(it, '') : Object(it);
83 // `RequireObjectCoercible` abstract operation
84 // https://tc39.es/ecma262/#sec-requireobjectcoercible
85 var requireObjectCoercible = function (it) {
86 if (it == undefined) throw TypeError("Can't call method on " + it);
90 // toObject with fallback for non-array-like ES3 strings
94 var toIndexedObject = function (it) {
95 return indexedObject(requireObjectCoercible(it));
98 var isObject$4 = function (it) {
99 return typeof it === 'object' ? it !== null : typeof it === 'function';
102 // `ToPrimitive` abstract operation
103 // https://tc39.es/ecma262/#sec-toprimitive
104 // instead of the ES6 spec version, we didn't implement @@toPrimitive case
105 // and the second argument - flag - preferred type is a string
106 var toPrimitive = function (input, PREFERRED_STRING) {
107 if (!isObject$4(input)) return input;
109 if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
110 if (typeof (fn = input.valueOf) == 'function' && !isObject$4(val = fn.call(input))) return val;
111 if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
112 throw TypeError("Can't convert object to primitive value");
115 // `ToObject` abstract operation
116 // https://tc39.es/ecma262/#sec-toobject
117 var toObject = function (argument) {
118 return Object(requireObjectCoercible(argument));
121 var hasOwnProperty$3 = {}.hasOwnProperty;
123 var has$1 = Object.hasOwn || function hasOwn(it, key) {
124 return hasOwnProperty$3.call(toObject(it), key);
127 var document$3 = global$2.document;
128 // typeof document.createElement is 'object' in old IE
129 var EXISTS = isObject$4(document$3) && isObject$4(document$3.createElement);
131 var documentCreateElement = function (it) {
132 return EXISTS ? document$3.createElement(it) : {};
135 // Thank's IE8 for his funny defineProperty
136 var ie8DomDefine = !descriptors && !fails(function () {
137 // eslint-disable-next-line es/no-object-defineproperty -- requied for testing
138 return Object.defineProperty(documentCreateElement('div'), 'a', {
139 get: function () { return 7; }
143 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
144 var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor;
146 // `Object.getOwnPropertyDescriptor` method
147 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
148 var f$6 = descriptors ? $getOwnPropertyDescriptor$1 : function getOwnPropertyDescriptor(O, P) {
149 O = toIndexedObject(O);
150 P = toPrimitive(P, true);
151 if (ie8DomDefine) try {
152 return $getOwnPropertyDescriptor$1(O, P);
153 } catch (error) { /* empty */ }
154 if (has$1(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
157 var objectGetOwnPropertyDescriptor = {
161 var anObject = function (it) {
162 if (!isObject$4(it)) {
163 throw TypeError(String(it) + ' is not an object');
167 // eslint-disable-next-line es/no-object-defineproperty -- safe
168 var $defineProperty$1 = Object.defineProperty;
170 // `Object.defineProperty` method
171 // https://tc39.es/ecma262/#sec-object.defineproperty
172 var f$5 = descriptors ? $defineProperty$1 : function defineProperty(O, P, Attributes) {
174 P = toPrimitive(P, true);
175 anObject(Attributes);
176 if (ie8DomDefine) try {
177 return $defineProperty$1(O, P, Attributes);
178 } catch (error) { /* empty */ }
179 if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
180 if ('value' in Attributes) O[P] = Attributes.value;
184 var objectDefineProperty = {
188 var createNonEnumerableProperty = descriptors ? function (object, key, value) {
189 return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
190 } : function (object, key, value) {
195 var setGlobal = function (key, value) {
197 createNonEnumerableProperty(global$2, key, value);
199 global$2[key] = value;
203 var SHARED = '__core-js_shared__';
204 var store$1 = global$2[SHARED] || setGlobal(SHARED, {});
206 var sharedStore = store$1;
208 var functionToString = Function.toString;
210 // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper
211 if (typeof sharedStore.inspectSource != 'function') {
212 sharedStore.inspectSource = function (it) {
213 return functionToString.call(it);
217 var inspectSource = sharedStore.inspectSource;
219 var WeakMap$1 = global$2.WeakMap;
221 var nativeWeakMap = typeof WeakMap$1 === 'function' && /native code/.test(inspectSource(WeakMap$1));
225 var shared = createCommonjsModule(function (module) {
226 (module.exports = function (key, value) {
227 return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
228 })('versions', []).push({
231 copyright: '© 2021 Denis Pushkarev (zloirock.ru)'
236 var postfix = Math.random();
238 var uid = function (key) {
239 return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id$1 + postfix).toString(36);
242 var keys$3 = shared('keys');
244 var sharedKey = function (key) {
245 return keys$3[key] || (keys$3[key] = uid(key));
248 var hiddenKeys$1 = {};
250 var OBJECT_ALREADY_INITIALIZED = 'Object already initialized';
251 var WeakMap = global$2.WeakMap;
252 var set$4, get$5, has;
254 var enforce = function (it) {
255 return has(it) ? get$5(it) : set$4(it, {});
258 var getterFor = function (TYPE) {
259 return function (it) {
261 if (!isObject$4(it) || (state = get$5(it)).type !== TYPE) {
262 throw TypeError('Incompatible receiver, ' + TYPE + ' required');
267 if (nativeWeakMap || sharedStore.state) {
268 var store = sharedStore.state || (sharedStore.state = new WeakMap());
269 var wmget = store.get;
270 var wmhas = store.has;
271 var wmset = store.set;
272 set$4 = function (it, metadata) {
273 if (wmhas.call(store, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
274 metadata.facade = it;
275 wmset.call(store, it, metadata);
278 get$5 = function (it) {
279 return wmget.call(store, it) || {};
281 has = function (it) {
282 return wmhas.call(store, it);
285 var STATE = sharedKey('state');
286 hiddenKeys$1[STATE] = true;
287 set$4 = function (it, metadata) {
288 if (has$1(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
289 metadata.facade = it;
290 createNonEnumerableProperty(it, STATE, metadata);
293 get$5 = function (it) {
294 return has$1(it, STATE) ? it[STATE] : {};
296 has = function (it) {
297 return has$1(it, STATE);
301 var internalState = {
309 var redefine = createCommonjsModule(function (module) {
310 var getInternalState = internalState.get;
311 var enforceInternalState = internalState.enforce;
312 var TEMPLATE = String(String).split('String');
314 (module.exports = function (O, key, value, options) {
315 var unsafe = options ? !!options.unsafe : false;
316 var simple = options ? !!options.enumerable : false;
317 var noTargetGet = options ? !!options.noTargetGet : false;
319 if (typeof value == 'function') {
320 if (typeof key == 'string' && !has$1(value, 'name')) {
321 createNonEnumerableProperty(value, 'name', key);
323 state = enforceInternalState(value);
325 state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
328 if (O === global$2) {
329 if (simple) O[key] = value;
330 else setGlobal(key, value);
332 } else if (!unsafe) {
334 } else if (!noTargetGet && O[key]) {
337 if (simple) O[key] = value;
338 else createNonEnumerableProperty(O, key, value);
339 // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
340 })(Function.prototype, 'toString', function toString() {
341 return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
347 var aFunction$1 = function (variable) {
348 return typeof variable == 'function' ? variable : undefined;
351 var getBuiltIn = function (namespace, method) {
352 return arguments.length < 2 ? aFunction$1(path[namespace]) || aFunction$1(global$2[namespace])
353 : path[namespace] && path[namespace][method] || global$2[namespace] && global$2[namespace][method];
356 var ceil$1 = Math.ceil;
357 var floor$7 = Math.floor;
359 // `ToInteger` abstract operation
360 // https://tc39.es/ecma262/#sec-tointeger
361 var toInteger = function (argument) {
362 return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor$7 : ceil$1)(argument);
365 var min$9 = Math.min;
367 // `ToLength` abstract operation
368 // https://tc39.es/ecma262/#sec-tolength
369 var toLength = function (argument) {
370 return argument > 0 ? min$9(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
373 var max$4 = Math.max;
374 var min$8 = Math.min;
376 // Helper for a popular repeating case of the spec:
377 // Let integer be ? ToInteger(index).
378 // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
379 var toAbsoluteIndex = function (index, length) {
380 var integer = toInteger(index);
381 return integer < 0 ? max$4(integer + length, 0) : min$8(integer, length);
384 // `Array.prototype.{ indexOf, includes }` methods implementation
385 var createMethod$6 = function (IS_INCLUDES) {
386 return function ($this, el, fromIndex) {
387 var O = toIndexedObject($this);
388 var length = toLength(O.length);
389 var index = toAbsoluteIndex(fromIndex, length);
391 // Array#includes uses SameValueZero equality algorithm
392 // eslint-disable-next-line no-self-compare -- NaN check
393 if (IS_INCLUDES && el != el) while (length > index) {
395 // eslint-disable-next-line no-self-compare -- NaN check
396 if (value != value) return true;
397 // Array#indexOf ignores holes, Array#includes - not
398 } else for (;length > index; index++) {
399 if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
400 } return !IS_INCLUDES && -1;
404 var arrayIncludes = {
405 // `Array.prototype.includes` method
406 // https://tc39.es/ecma262/#sec-array.prototype.includes
407 includes: createMethod$6(true),
408 // `Array.prototype.indexOf` method
409 // https://tc39.es/ecma262/#sec-array.prototype.indexof
410 indexOf: createMethod$6(false)
413 var indexOf = arrayIncludes.indexOf;
416 var objectKeysInternal = function (object, names) {
417 var O = toIndexedObject(object);
421 for (key in O) !has$1(hiddenKeys$1, key) && has$1(O, key) && result.push(key);
422 // Don't enum bug & hidden keys
423 while (names.length > i) if (has$1(O, key = names[i++])) {
424 ~indexOf(result, key) || result.push(key);
429 // IE8- don't enum bug keys
434 'propertyIsEnumerable',
440 var hiddenKeys = enumBugKeys.concat('length', 'prototype');
442 // `Object.getOwnPropertyNames` method
443 // https://tc39.es/ecma262/#sec-object.getownpropertynames
444 // eslint-disable-next-line es/no-object-getownpropertynames -- safe
445 var f$4 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
446 return objectKeysInternal(O, hiddenKeys);
449 var objectGetOwnPropertyNames = {
453 // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe
454 var f$3 = Object.getOwnPropertySymbols;
456 var objectGetOwnPropertySymbols = {
460 // all object keys, includes non-enumerable and symbols
461 var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
462 var keys = objectGetOwnPropertyNames.f(anObject(it));
463 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
464 return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
467 var copyConstructorProperties = function (target, source) {
468 var keys = ownKeys(source);
469 var defineProperty = objectDefineProperty.f;
470 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
471 for (var i = 0; i < keys.length; i++) {
473 if (!has$1(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
477 var replacement = /#|\.prototype\./;
479 var isForced = function (feature, detection) {
480 var value = data[normalize$1(feature)];
481 return value == POLYFILL ? true
482 : value == NATIVE ? false
483 : typeof detection == 'function' ? fails(detection)
487 var normalize$1 = isForced.normalize = function (string) {
488 return String(string).replace(replacement, '.').toLowerCase();
491 var data = isForced.data = {};
492 var NATIVE = isForced.NATIVE = 'N';
493 var POLYFILL = isForced.POLYFILL = 'P';
495 var isForced_1 = isForced;
497 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
505 options.target - name of the target object
506 options.global - target is the global object
507 options.stat - export as static methods of target
508 options.proto - export as prototype methods of target
509 options.real - real prototype method for the `pure` version
510 options.forced - export even if the native feature is available
511 options.bind - bind methods to the target, required for the `pure` version
512 options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
513 options.unsafe - use the simple assignment of property instead of delete + defineProperty
514 options.sham - add a flag to not completely full polyfills
515 options.enumerable - export as enumerable property
516 options.noTargetGet - prevent calling a getter on target
518 var _export = function (options, source) {
519 var TARGET = options.target;
520 var GLOBAL = options.global;
521 var STATIC = options.stat;
522 var FORCED, target, key, targetProperty, sourceProperty, descriptor;
526 target = global$2[TARGET] || setGlobal(TARGET, {});
528 target = (global$2[TARGET] || {}).prototype;
530 if (target) for (key in source) {
531 sourceProperty = source[key];
532 if (options.noTargetGet) {
533 descriptor = getOwnPropertyDescriptor$4(target, key);
534 targetProperty = descriptor && descriptor.value;
535 } else targetProperty = target[key];
536 FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
537 // contained in target
538 if (!FORCED && targetProperty !== undefined) {
539 if (typeof sourceProperty === typeof targetProperty) continue;
540 copyConstructorProperties(sourceProperty, targetProperty);
542 // add a flag to not completely full polyfills
543 if (options.sham || (targetProperty && targetProperty.sham)) {
544 createNonEnumerableProperty(sourceProperty, 'sham', true);
547 redefine(target, key, sourceProperty, options);
552 // https://tc39.es/ecma262/#sec-date.now
553 _export({ target: 'Date', stat: true }, {
554 now: function now() {
555 return new Date().getTime();
559 var DatePrototype$1 = Date.prototype;
560 var INVALID_DATE = 'Invalid Date';
561 var TO_STRING$1 = 'toString';
562 var nativeDateToString = DatePrototype$1[TO_STRING$1];
563 var getTime$1 = DatePrototype$1.getTime;
565 // `Date.prototype.toString` method
566 // https://tc39.es/ecma262/#sec-date.prototype.tostring
567 if (new Date(NaN) + '' != INVALID_DATE) {
568 redefine(DatePrototype$1, TO_STRING$1, function toString() {
569 var value = getTime$1.call(this);
570 // eslint-disable-next-line no-self-compare -- NaN check
571 return value === value ? nativeDateToString.call(this) : INVALID_DATE;
575 function _typeof(obj) {
576 "@babel/helpers - typeof";
578 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
579 _typeof = function (obj) {
583 _typeof = function (obj) {
584 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
591 function _classCallCheck$1(instance, Constructor) {
592 if (!(instance instanceof Constructor)) {
593 throw new TypeError("Cannot call a class as a function");
597 function _defineProperties$1(target, props) {
598 for (var i = 0; i < props.length; i++) {
599 var descriptor = props[i];
600 descriptor.enumerable = descriptor.enumerable || false;
601 descriptor.configurable = true;
602 if ("value" in descriptor) descriptor.writable = true;
603 Object.defineProperty(target, descriptor.key, descriptor);
607 function _createClass$1(Constructor, protoProps, staticProps) {
608 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
609 if (staticProps) _defineProperties$1(Constructor, staticProps);
613 function _defineProperty(obj, key, value) {
615 Object.defineProperty(obj, key, {
628 function _slicedToArray(arr, i) {
629 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
632 function _toConsumableArray(arr) {
633 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
636 function _arrayWithoutHoles(arr) {
637 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
640 function _arrayWithHoles(arr) {
641 if (Array.isArray(arr)) return arr;
644 function _iterableToArray(iter) {
645 if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
648 function _iterableToArrayLimit(arr, i) {
649 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
651 if (_i == null) return;
659 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
662 if (i && _arr.length === i) break;
669 if (!_n && _i["return"] != null) _i["return"]();
678 function _unsupportedIterableToArray(o, minLen) {
680 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
681 var n = Object.prototype.toString.call(o).slice(8, -1);
682 if (n === "Object" && o.constructor) n = o.constructor.name;
683 if (n === "Map" || n === "Set") return Array.from(o);
684 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
687 function _arrayLikeToArray(arr, len) {
688 if (len == null || len > arr.length) len = arr.length;
690 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
695 function _nonIterableSpread() {
696 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
699 function _nonIterableRest() {
700 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
703 function _createForOfIteratorHelper(o, allowArrayLike) {
704 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
707 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
711 var F = function () {};
716 if (i >= o.length) return {
731 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
734 var normalCompletion = true,
742 var step = it.next();
743 normalCompletion = step.done;
752 if (!normalCompletion && it.return != null) it.return();
754 if (didErr) throw err;
760 var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
762 var process$4 = global$2.process;
763 var versions = process$4 && process$4.versions;
764 var v8 = versions && versions.v8;
765 var match, version$1;
768 match = v8.split('.');
769 version$1 = match[0] < 4 ? 1 : match[0] + match[1];
770 } else if (engineUserAgent) {
771 match = engineUserAgent.match(/Edge\/(\d+)/);
772 if (!match || match[1] >= 74) {
773 match = engineUserAgent.match(/Chrome\/(\d+)/);
774 if (match) version$1 = match[1];
778 var engineV8Version = version$1 && +version$1;
780 /* eslint-disable es/no-symbol -- required for testing */
782 // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing
783 var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
784 var symbol = Symbol();
785 // Chrome 38 Symbol has incorrect toString conversion
786 // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances
787 return !String(symbol) || !(Object(symbol) instanceof Symbol) ||
788 // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
789 !Symbol.sham && engineV8Version && engineV8Version < 41;
792 /* eslint-disable es/no-symbol -- required for testing */
794 var useSymbolAsUid = nativeSymbol
796 && typeof Symbol.iterator == 'symbol';
798 var WellKnownSymbolsStore$1 = shared('wks');
799 var Symbol$1 = global$2.Symbol;
800 var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
802 var wellKnownSymbol = function (name) {
803 if (!has$1(WellKnownSymbolsStore$1, name) || !(nativeSymbol || typeof WellKnownSymbolsStore$1[name] == 'string')) {
804 if (nativeSymbol && has$1(Symbol$1, name)) {
805 WellKnownSymbolsStore$1[name] = Symbol$1[name];
807 WellKnownSymbolsStore$1[name] = createWellKnownSymbol('Symbol.' + name);
809 } return WellKnownSymbolsStore$1[name];
812 var f$2 = wellKnownSymbol;
814 var wellKnownSymbolWrapped = {
818 var defineProperty$9 = objectDefineProperty.f;
820 var defineWellKnownSymbol = function (NAME) {
821 var Symbol = path.Symbol || (path.Symbol = {});
822 if (!has$1(Symbol, NAME)) defineProperty$9(Symbol, NAME, {
823 value: wellKnownSymbolWrapped.f(NAME)
827 // `Symbol.iterator` well-known symbol
828 // https://tc39.es/ecma262/#sec-symbol.iterator
829 defineWellKnownSymbol('iterator');
831 // `Object.keys` method
832 // https://tc39.es/ecma262/#sec-object.keys
833 // eslint-disable-next-line es/no-object-keys -- safe
834 var objectKeys = Object.keys || function keys(O) {
835 return objectKeysInternal(O, enumBugKeys);
838 // `Object.defineProperties` method
839 // https://tc39.es/ecma262/#sec-object.defineproperties
840 // eslint-disable-next-line es/no-object-defineproperties -- safe
841 var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
843 var keys = objectKeys(Properties);
844 var length = keys.length;
847 while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
851 var html = getBuiltIn('document', 'documentElement');
855 var PROTOTYPE$2 = 'prototype';
856 var SCRIPT = 'script';
857 var IE_PROTO$1 = sharedKey('IE_PROTO');
859 var EmptyConstructor = function () { /* empty */ };
861 var scriptTag = function (content) {
862 return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
865 // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
866 var NullProtoObjectViaActiveX = function (activeXDocument) {
867 activeXDocument.write(scriptTag(''));
868 activeXDocument.close();
869 var temp = activeXDocument.parentWindow.Object;
870 activeXDocument = null; // avoid memory leak
874 // Create object with fake `null` prototype: use iframe Object with cleared prototype
875 var NullProtoObjectViaIFrame = function () {
876 // Thrash, waste and sodomy: IE GC bug
877 var iframe = documentCreateElement('iframe');
878 var JS = 'java' + SCRIPT + ':';
880 iframe.style.display = 'none';
881 html.appendChild(iframe);
882 // https://github.com/zloirock/core-js/issues/475
883 iframe.src = String(JS);
884 iframeDocument = iframe.contentWindow.document;
885 iframeDocument.open();
886 iframeDocument.write(scriptTag('document.F=Object'));
887 iframeDocument.close();
888 return iframeDocument.F;
891 // Check for document.domain and active x support
892 // No need to use active x approach when document.domain is not set
893 // see https://github.com/es-shims/es5-shim/issues/150
894 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
897 var NullProtoObject = function () {
899 /* global ActiveXObject -- old IE */
900 activeXDocument = document.domain && new ActiveXObject('htmlfile');
901 } catch (error) { /* ignore */ }
902 NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
903 var length = enumBugKeys.length;
904 while (length--) delete NullProtoObject[PROTOTYPE$2][enumBugKeys[length]];
905 return NullProtoObject();
908 hiddenKeys$1[IE_PROTO$1] = true;
910 // `Object.create` method
911 // https://tc39.es/ecma262/#sec-object.create
912 var objectCreate = Object.create || function create(O, Properties) {
915 EmptyConstructor[PROTOTYPE$2] = anObject(O);
916 result = new EmptyConstructor();
917 EmptyConstructor[PROTOTYPE$2] = null;
918 // add "__proto__" for Object.getPrototypeOf polyfill
919 result[IE_PROTO$1] = O;
920 } else result = NullProtoObject();
921 return Properties === undefined ? result : objectDefineProperties(result, Properties);
924 var UNSCOPABLES = wellKnownSymbol('unscopables');
925 var ArrayPrototype$1 = Array.prototype;
927 // Array.prototype[@@unscopables]
928 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
929 if (ArrayPrototype$1[UNSCOPABLES] == undefined) {
930 objectDefineProperty.f(ArrayPrototype$1, UNSCOPABLES, {
932 value: objectCreate(null)
936 // add a key to Array.prototype[@@unscopables]
937 var addToUnscopables = function (key) {
938 ArrayPrototype$1[UNSCOPABLES][key] = true;
943 var correctPrototypeGetter = !fails(function () {
944 function F() { /* empty */ }
945 F.prototype.constructor = null;
946 // eslint-disable-next-line es/no-object-getprototypeof -- required for testing
947 return Object.getPrototypeOf(new F()) !== F.prototype;
950 var IE_PROTO = sharedKey('IE_PROTO');
951 var ObjectPrototype$3 = Object.prototype;
953 // `Object.getPrototypeOf` method
954 // https://tc39.es/ecma262/#sec-object.getprototypeof
955 // eslint-disable-next-line es/no-object-getprototypeof -- safe
956 var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
958 if (has$1(O, IE_PROTO)) return O[IE_PROTO];
959 if (typeof O.constructor == 'function' && O instanceof O.constructor) {
960 return O.constructor.prototype;
961 } return O instanceof Object ? ObjectPrototype$3 : null;
964 var ITERATOR$8 = wellKnownSymbol('iterator');
965 var BUGGY_SAFARI_ITERATORS$1 = false;
967 var returnThis$2 = function () { return this; };
969 // `%IteratorPrototype%` object
970 // https://tc39.es/ecma262/#sec-%iteratorprototype%-object
971 var IteratorPrototype$2, PrototypeOfArrayIteratorPrototype, arrayIterator;
973 /* eslint-disable es/no-array-prototype-keys -- safe */
975 arrayIterator = [].keys();
976 // Safari 8 has buggy iterators w/o `next`
977 if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true;
979 PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
980 if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$2 = PrototypeOfArrayIteratorPrototype;
984 var NEW_ITERATOR_PROTOTYPE = IteratorPrototype$2 == undefined || fails(function () {
986 // FF44- legacy iterators case
987 return IteratorPrototype$2[ITERATOR$8].call(test) !== test;
990 if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$2 = {};
992 // `%IteratorPrototype%[@@iterator]()` method
993 // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
994 if (!has$1(IteratorPrototype$2, ITERATOR$8)) {
995 createNonEnumerableProperty(IteratorPrototype$2, ITERATOR$8, returnThis$2);
998 var iteratorsCore = {
999 IteratorPrototype: IteratorPrototype$2,
1000 BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1
1003 var defineProperty$8 = objectDefineProperty.f;
1007 var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
1009 var setToStringTag = function (it, TAG, STATIC) {
1010 if (it && !has$1(it = STATIC ? it : it.prototype, TO_STRING_TAG$4)) {
1011 defineProperty$8(it, TO_STRING_TAG$4, { configurable: true, value: TAG });
1015 var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1021 var returnThis$1 = function () { return this; };
1023 var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1024 var TO_STRING_TAG = NAME + ' Iterator';
1025 IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1026 setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1027 iterators[TO_STRING_TAG] = returnThis$1;
1028 return IteratorConstructor;
1031 var aPossiblePrototype = function (it) {
1032 if (!isObject$4(it) && it !== null) {
1033 throw TypeError("Can't set " + String(it) + ' as a prototype');
1037 /* eslint-disable no-proto -- safe */
1039 // `Object.setPrototypeOf` method
1040 // https://tc39.es/ecma262/#sec-object.setprototypeof
1041 // Works with __proto__ only. Old v8 can't work with null proto objects.
1042 // eslint-disable-next-line es/no-object-setprototypeof -- safe
1043 var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1044 var CORRECT_SETTER = false;
1048 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
1049 setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1050 setter.call(test, []);
1051 CORRECT_SETTER = test instanceof Array;
1052 } catch (error) { /* empty */ }
1053 return function setPrototypeOf(O, proto) {
1055 aPossiblePrototype(proto);
1056 if (CORRECT_SETTER) setter.call(O, proto);
1057 else O.__proto__ = proto;
1062 var IteratorPrototype = iteratorsCore.IteratorPrototype;
1063 var BUGGY_SAFARI_ITERATORS = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1064 var ITERATOR$7 = wellKnownSymbol('iterator');
1066 var VALUES = 'values';
1067 var ENTRIES = 'entries';
1069 var returnThis = function () { return this; };
1071 var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1072 createIteratorConstructor(IteratorConstructor, NAME, next);
1074 var getIterationMethod = function (KIND) {
1075 if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1076 if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND];
1078 case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1079 case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1080 case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1081 } return function () { return new IteratorConstructor(this); };
1084 var TO_STRING_TAG = NAME + ' Iterator';
1085 var INCORRECT_VALUES_NAME = false;
1086 var IterablePrototype = Iterable.prototype;
1087 var nativeIterator = IterablePrototype[ITERATOR$7]
1088 || IterablePrototype['@@iterator']
1089 || DEFAULT && IterablePrototype[DEFAULT];
1090 var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT);
1091 var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1092 var CurrentIteratorPrototype, methods, KEY;
1095 if (anyNativeIterator) {
1096 CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1097 if (IteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) {
1098 if (objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype) {
1099 if (objectSetPrototypeOf) {
1100 objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype);
1101 } else if (typeof CurrentIteratorPrototype[ITERATOR$7] != 'function') {
1102 createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$7, returnThis);
1105 // Set @@toStringTag to native iterators
1106 setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1110 // fix Array.prototype.{ values, @@iterator }.name in V8 / FF
1111 if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1112 INCORRECT_VALUES_NAME = true;
1113 defaultIterator = function values() { return nativeIterator.call(this); };
1117 if (IterablePrototype[ITERATOR$7] !== defaultIterator) {
1118 createNonEnumerableProperty(IterablePrototype, ITERATOR$7, defaultIterator);
1120 iterators[NAME] = defaultIterator;
1122 // export additional methods
1125 values: getIterationMethod(VALUES),
1126 keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1127 entries: getIterationMethod(ENTRIES)
1129 if (FORCED) for (KEY in methods) {
1130 if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1131 redefine(IterablePrototype, KEY, methods[KEY]);
1133 } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods);
1139 var ARRAY_ITERATOR = 'Array Iterator';
1140 var setInternalState$7 = internalState.set;
1141 var getInternalState$5 = internalState.getterFor(ARRAY_ITERATOR);
1143 // `Array.prototype.entries` method
1144 // https://tc39.es/ecma262/#sec-array.prototype.entries
1145 // `Array.prototype.keys` method
1146 // https://tc39.es/ecma262/#sec-array.prototype.keys
1147 // `Array.prototype.values` method
1148 // https://tc39.es/ecma262/#sec-array.prototype.values
1149 // `Array.prototype[@@iterator]` method
1150 // https://tc39.es/ecma262/#sec-array.prototype-@@iterator
1151 // `CreateArrayIterator` internal method
1152 // https://tc39.es/ecma262/#sec-createarrayiterator
1153 var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1154 setInternalState$7(this, {
1155 type: ARRAY_ITERATOR,
1156 target: toIndexedObject(iterated), // target
1157 index: 0, // next index
1160 // `%ArrayIteratorPrototype%.next` method
1161 // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
1163 var state = getInternalState$5(this);
1164 var target = state.target;
1165 var kind = state.kind;
1166 var index = state.index++;
1167 if (!target || index >= target.length) {
1168 state.target = undefined;
1169 return { value: undefined, done: true };
1171 if (kind == 'keys') return { value: index, done: false };
1172 if (kind == 'values') return { value: target[index], done: false };
1173 return { value: [index, target[index]], done: false };
1176 // argumentsList[@@iterator] is %ArrayProto_values%
1177 // https://tc39.es/ecma262/#sec-createunmappedargumentsobject
1178 // https://tc39.es/ecma262/#sec-createmappedargumentsobject
1179 iterators.Arguments = iterators.Array;
1181 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
1182 addToUnscopables('keys');
1183 addToUnscopables('values');
1184 addToUnscopables('entries');
1186 var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
1189 test$2[TO_STRING_TAG$3] = 'z';
1191 var toStringTagSupport = String(test$2) === '[object z]';
1193 var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1195 var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
1197 // fallback for IE11 Script Access Denied error
1198 var tryGet = function (it, key) {
1201 } catch (error) { /* empty */ }
1204 // getting tag from ES6+ `Object.prototype.toString`
1205 var classof = toStringTagSupport ? classofRaw : function (it) {
1207 return it === undefined ? 'Undefined' : it === null ? 'Null'
1208 // @@toStringTag case
1209 : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag
1211 : CORRECT_ARGUMENTS ? classofRaw(O)
1212 // ES3 arguments fallback
1213 : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
1216 // `Object.prototype.toString` method implementation
1217 // https://tc39.es/ecma262/#sec-object.prototype.tostring
1218 var objectToString$1 = toStringTagSupport ? {}.toString : function toString() {
1219 return '[object ' + classof(this) + ']';
1222 // `Object.prototype.toString` method
1223 // https://tc39.es/ecma262/#sec-object.prototype.tostring
1224 if (!toStringTagSupport) {
1225 redefine(Object.prototype, 'toString', objectToString$1, { unsafe: true });
1228 // `String.prototype.{ codePointAt, at }` methods implementation
1229 var createMethod$5 = function (CONVERT_TO_STRING) {
1230 return function ($this, pos) {
1231 var S = String(requireObjectCoercible($this));
1232 var position = toInteger(pos);
1233 var size = S.length;
1235 if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
1236 first = S.charCodeAt(position);
1237 return first < 0xD800 || first > 0xDBFF || position + 1 === size
1238 || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
1239 ? CONVERT_TO_STRING ? S.charAt(position) : first
1240 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
1244 var stringMultibyte = {
1245 // `String.prototype.codePointAt` method
1246 // https://tc39.es/ecma262/#sec-string.prototype.codepointat
1247 codeAt: createMethod$5(false),
1248 // `String.prototype.at` method
1249 // https://github.com/mathiasbynens/String.prototype.at
1250 charAt: createMethod$5(true)
1253 var charAt$1 = stringMultibyte.charAt;
1257 var STRING_ITERATOR = 'String Iterator';
1258 var setInternalState$6 = internalState.set;
1259 var getInternalState$4 = internalState.getterFor(STRING_ITERATOR);
1261 // `String.prototype[@@iterator]` method
1262 // https://tc39.es/ecma262/#sec-string.prototype-@@iterator
1263 defineIterator(String, 'String', function (iterated) {
1264 setInternalState$6(this, {
1265 type: STRING_ITERATOR,
1266 string: String(iterated),
1269 // `%StringIteratorPrototype%.next` method
1270 // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next
1271 }, function next() {
1272 var state = getInternalState$4(this);
1273 var string = state.string;
1274 var index = state.index;
1276 if (index >= string.length) return { value: undefined, done: true };
1277 point = charAt$1(string, index);
1278 state.index += point.length;
1279 return { value: point, done: false };
1282 // iterable DOM collections
1283 // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
1284 var domIterables = {
1286 CSSStyleDeclaration: 0,
1292 DataTransferItemList: 0,
1294 HTMLAllCollection: 0,
1297 HTMLSelectElement: 0,
1302 PaintRequestList: 0,
1310 SVGTransformList: 0,
1311 SourceBufferList: 0,
1313 TextTrackCueList: 0,
1318 var ITERATOR$6 = wellKnownSymbol('iterator');
1319 var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
1320 var ArrayValues = es_array_iterator.values;
1322 for (var COLLECTION_NAME$1 in domIterables) {
1323 var Collection$1 = global$2[COLLECTION_NAME$1];
1324 var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
1325 if (CollectionPrototype$1) {
1326 // some Chrome versions have non-configurable methods on DOMTokenList
1327 if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try {
1328 createNonEnumerableProperty(CollectionPrototype$1, ITERATOR$6, ArrayValues);
1330 CollectionPrototype$1[ITERATOR$6] = ArrayValues;
1332 if (!CollectionPrototype$1[TO_STRING_TAG$1]) {
1333 createNonEnumerableProperty(CollectionPrototype$1, TO_STRING_TAG$1, COLLECTION_NAME$1);
1335 if (domIterables[COLLECTION_NAME$1]) for (var METHOD_NAME in es_array_iterator) {
1336 // some Chrome versions have non-configurable methods on DOMTokenList
1337 if (CollectionPrototype$1[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
1338 createNonEnumerableProperty(CollectionPrototype$1, METHOD_NAME, es_array_iterator[METHOD_NAME]);
1340 CollectionPrototype$1[METHOD_NAME] = es_array_iterator[METHOD_NAME];
1346 // `IsArray` abstract operation
1347 // https://tc39.es/ecma262/#sec-isarray
1348 // eslint-disable-next-line es/no-array-isarray -- safe
1349 var isArray = Array.isArray || function isArray(arg) {
1350 return classofRaw(arg) == 'Array';
1353 /* eslint-disable es/no-object-getownpropertynames -- safe */
1355 var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
1357 var toString = {}.toString;
1359 var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
1360 ? Object.getOwnPropertyNames(window) : [];
1362 var getWindowNames = function (it) {
1364 return $getOwnPropertyNames$1(it);
1366 return windowNames.slice();
1370 // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
1371 var f$1 = function getOwnPropertyNames(it) {
1372 return windowNames && toString.call(it) == '[object Window]'
1373 ? getWindowNames(it)
1374 : $getOwnPropertyNames$1(toIndexedObject(it));
1377 var objectGetOwnPropertyNamesExternal = {
1381 var aFunction = function (it) {
1382 if (typeof it != 'function') {
1383 throw TypeError(String(it) + ' is not a function');
1387 // optional / simple context binding
1388 var functionBindContext = function (fn, that, length) {
1390 if (that === undefined) return fn;
1392 case 0: return function () {
1393 return fn.call(that);
1395 case 1: return function (a) {
1396 return fn.call(that, a);
1398 case 2: return function (a, b) {
1399 return fn.call(that, a, b);
1401 case 3: return function (a, b, c) {
1402 return fn.call(that, a, b, c);
1405 return function (/* ...args */) {
1406 return fn.apply(that, arguments);
1410 var SPECIES$6 = wellKnownSymbol('species');
1412 // `ArraySpeciesCreate` abstract operation
1413 // https://tc39.es/ecma262/#sec-arrayspeciescreate
1414 var arraySpeciesCreate = function (originalArray, length) {
1416 if (isArray(originalArray)) {
1417 C = originalArray.constructor;
1418 // cross-realm fallback
1419 if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
1420 else if (isObject$4(C)) {
1422 if (C === null) C = undefined;
1424 } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
1429 // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterOut }` methods implementation
1430 var createMethod$4 = function (TYPE) {
1431 var IS_MAP = TYPE == 1;
1432 var IS_FILTER = TYPE == 2;
1433 var IS_SOME = TYPE == 3;
1434 var IS_EVERY = TYPE == 4;
1435 var IS_FIND_INDEX = TYPE == 6;
1436 var IS_FILTER_OUT = TYPE == 7;
1437 var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
1438 return function ($this, callbackfn, that, specificCreate) {
1439 var O = toObject($this);
1440 var self = indexedObject(O);
1441 var boundFunction = functionBindContext(callbackfn, that, 3);
1442 var length = toLength(self.length);
1444 var create = specificCreate || arraySpeciesCreate;
1445 var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_OUT ? create($this, 0) : undefined;
1447 for (;length > index; index++) if (NO_HOLES || index in self) {
1448 value = self[index];
1449 result = boundFunction(value, index, O);
1451 if (IS_MAP) target[index] = result; // map
1452 else if (result) switch (TYPE) {
1453 case 3: return true; // some
1454 case 5: return value; // find
1455 case 6: return index; // findIndex
1456 case 2: push.call(target, value); // filter
1457 } else switch (TYPE) {
1458 case 4: return false; // every
1459 case 7: push.call(target, value); // filterOut
1463 return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
1467 var arrayIteration = {
1468 // `Array.prototype.forEach` method
1469 // https://tc39.es/ecma262/#sec-array.prototype.foreach
1470 forEach: createMethod$4(0),
1471 // `Array.prototype.map` method
1472 // https://tc39.es/ecma262/#sec-array.prototype.map
1473 map: createMethod$4(1),
1474 // `Array.prototype.filter` method
1475 // https://tc39.es/ecma262/#sec-array.prototype.filter
1476 filter: createMethod$4(2),
1477 // `Array.prototype.some` method
1478 // https://tc39.es/ecma262/#sec-array.prototype.some
1479 some: createMethod$4(3),
1480 // `Array.prototype.every` method
1481 // https://tc39.es/ecma262/#sec-array.prototype.every
1482 every: createMethod$4(4),
1483 // `Array.prototype.find` method
1484 // https://tc39.es/ecma262/#sec-array.prototype.find
1485 find: createMethod$4(5),
1486 // `Array.prototype.findIndex` method
1487 // https://tc39.es/ecma262/#sec-array.prototype.findIndex
1488 findIndex: createMethod$4(6),
1489 // `Array.prototype.filterOut` method
1490 // https://github.com/tc39/proposal-array-filtering
1491 filterOut: createMethod$4(7)
1494 var $forEach$2 = arrayIteration.forEach;
1496 var HIDDEN = sharedKey('hidden');
1497 var SYMBOL = 'Symbol';
1498 var PROTOTYPE$1 = 'prototype';
1499 var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
1500 var setInternalState$5 = internalState.set;
1501 var getInternalState$3 = internalState.getterFor(SYMBOL);
1502 var ObjectPrototype$2 = Object[PROTOTYPE$1];
1503 var $Symbol = global$2.Symbol;
1504 var $stringify = getBuiltIn('JSON', 'stringify');
1505 var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
1506 var nativeDefineProperty = objectDefineProperty.f;
1507 var nativeGetOwnPropertyNames = objectGetOwnPropertyNamesExternal.f;
1508 var nativePropertyIsEnumerable = objectPropertyIsEnumerable.f;
1509 var AllSymbols = shared('symbols');
1510 var ObjectPrototypeSymbols = shared('op-symbols');
1511 var StringToSymbolRegistry = shared('string-to-symbol-registry');
1512 var SymbolToStringRegistry = shared('symbol-to-string-registry');
1513 var WellKnownSymbolsStore = shared('wks');
1514 var QObject = global$2.QObject;
1515 // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
1516 var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
1518 // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
1519 var setSymbolDescriptor = descriptors && fails(function () {
1520 return objectCreate(nativeDefineProperty({}, 'a', {
1521 get: function () { return nativeDefineProperty(this, 'a', { value: 7 }).a; }
1523 }) ? function (O, P, Attributes) {
1524 var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$2, P);
1525 if (ObjectPrototypeDescriptor) delete ObjectPrototype$2[P];
1526 nativeDefineProperty(O, P, Attributes);
1527 if (ObjectPrototypeDescriptor && O !== ObjectPrototype$2) {
1528 nativeDefineProperty(ObjectPrototype$2, P, ObjectPrototypeDescriptor);
1530 } : nativeDefineProperty;
1532 var wrap$2 = function (tag, description) {
1533 var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
1534 setInternalState$5(symbol, {
1537 description: description
1539 if (!descriptors) symbol.description = description;
1543 var isSymbol$1 = useSymbolAsUid ? function (it) {
1544 return typeof it == 'symbol';
1546 return Object(it) instanceof $Symbol;
1549 var $defineProperty = function defineProperty(O, P, Attributes) {
1550 if (O === ObjectPrototype$2) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
1552 var key = toPrimitive(P, true);
1553 anObject(Attributes);
1554 if (has$1(AllSymbols, key)) {
1555 if (!Attributes.enumerable) {
1556 if (!has$1(O, HIDDEN)) nativeDefineProperty(O, HIDDEN, createPropertyDescriptor(1, {}));
1557 O[HIDDEN][key] = true;
1559 if (has$1(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
1560 Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
1561 } return setSymbolDescriptor(O, key, Attributes);
1562 } return nativeDefineProperty(O, key, Attributes);
1565 var $defineProperties = function defineProperties(O, Properties) {
1567 var properties = toIndexedObject(Properties);
1568 var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
1569 $forEach$2(keys, function (key) {
1570 if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
1575 var $create = function create(O, Properties) {
1576 return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
1579 var $propertyIsEnumerable = function propertyIsEnumerable(V) {
1580 var P = toPrimitive(V, true);
1581 var enumerable = nativePropertyIsEnumerable.call(this, P);
1582 if (this === ObjectPrototype$2 && has$1(AllSymbols, P) && !has$1(ObjectPrototypeSymbols, P)) return false;
1583 return enumerable || !has$1(this, P) || !has$1(AllSymbols, P) || has$1(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
1586 var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
1587 var it = toIndexedObject(O);
1588 var key = toPrimitive(P, true);
1589 if (it === ObjectPrototype$2 && has$1(AllSymbols, key) && !has$1(ObjectPrototypeSymbols, key)) return;
1590 var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
1591 if (descriptor && has$1(AllSymbols, key) && !(has$1(it, HIDDEN) && it[HIDDEN][key])) {
1592 descriptor.enumerable = true;
1597 var $getOwnPropertyNames = function getOwnPropertyNames(O) {
1598 var names = nativeGetOwnPropertyNames(toIndexedObject(O));
1600 $forEach$2(names, function (key) {
1601 if (!has$1(AllSymbols, key) && !has$1(hiddenKeys$1, key)) result.push(key);
1606 var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
1607 var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$2;
1608 var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
1610 $forEach$2(names, function (key) {
1611 if (has$1(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has$1(ObjectPrototype$2, key))) {
1612 result.push(AllSymbols[key]);
1618 // `Symbol` constructor
1619 // https://tc39.es/ecma262/#sec-symbol-constructor
1620 if (!nativeSymbol) {
1621 $Symbol = function Symbol() {
1622 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
1623 var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
1624 var tag = uid(description);
1625 var setter = function (value) {
1626 if (this === ObjectPrototype$2) setter.call(ObjectPrototypeSymbols, value);
1627 if (has$1(this, HIDDEN) && has$1(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
1628 setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
1630 if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype$2, tag, { configurable: true, set: setter });
1631 return wrap$2(tag, description);
1634 redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
1635 return getInternalState$3(this).tag;
1638 redefine($Symbol, 'withoutSetter', function (description) {
1639 return wrap$2(uid(description), description);
1642 objectPropertyIsEnumerable.f = $propertyIsEnumerable;
1643 objectDefineProperty.f = $defineProperty;
1644 objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
1645 objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1646 objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1648 wellKnownSymbolWrapped.f = function (name) {
1649 return wrap$2(wellKnownSymbol(name), name);
1653 // https://github.com/tc39/proposal-Symbol-description
1654 nativeDefineProperty($Symbol[PROTOTYPE$1], 'description', {
1656 get: function description() {
1657 return getInternalState$3(this).description;
1661 redefine(ObjectPrototype$2, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1666 _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1670 $forEach$2(objectKeys(WellKnownSymbolsStore), function (name) {
1671 defineWellKnownSymbol(name);
1674 _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1675 // `Symbol.for` method
1676 // https://tc39.es/ecma262/#sec-symbol.for
1677 'for': function (key) {
1678 var string = String(key);
1679 if (has$1(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1680 var symbol = $Symbol(string);
1681 StringToSymbolRegistry[string] = symbol;
1682 SymbolToStringRegistry[symbol] = string;
1685 // `Symbol.keyFor` method
1686 // https://tc39.es/ecma262/#sec-symbol.keyfor
1687 keyFor: function keyFor(sym) {
1688 if (!isSymbol$1(sym)) throw TypeError(sym + ' is not a symbol');
1689 if (has$1(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1691 useSetter: function () { USE_SETTER = true; },
1692 useSimple: function () { USE_SETTER = false; }
1695 _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1696 // `Object.create` method
1697 // https://tc39.es/ecma262/#sec-object.create
1699 // `Object.defineProperty` method
1700 // https://tc39.es/ecma262/#sec-object.defineproperty
1701 defineProperty: $defineProperty,
1702 // `Object.defineProperties` method
1703 // https://tc39.es/ecma262/#sec-object.defineproperties
1704 defineProperties: $defineProperties,
1705 // `Object.getOwnPropertyDescriptor` method
1706 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors
1707 getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1710 _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1711 // `Object.getOwnPropertyNames` method
1712 // https://tc39.es/ecma262/#sec-object.getownpropertynames
1713 getOwnPropertyNames: $getOwnPropertyNames,
1714 // `Object.getOwnPropertySymbols` method
1715 // https://tc39.es/ecma262/#sec-object.getownpropertysymbols
1716 getOwnPropertySymbols: $getOwnPropertySymbols
1719 // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1720 // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1721 _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1722 getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1723 return objectGetOwnPropertySymbols.f(toObject(it));
1727 // `JSON.stringify` method behavior with symbols
1728 // https://tc39.es/ecma262/#sec-json.stringify
1730 var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1731 var symbol = $Symbol();
1732 // MS Edge converts symbol values to JSON as {}
1733 return $stringify([symbol]) != '[null]'
1734 // WebKit converts symbol values to JSON as null
1735 || $stringify({ a: symbol }) != '{}'
1736 // V8 throws on boxed symbols
1737 || $stringify(Object(symbol)) != '{}';
1740 _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1741 // eslint-disable-next-line no-unused-vars -- required for `.length`
1742 stringify: function stringify(it, replacer, space) {
1746 while (arguments.length > index) args.push(arguments[index++]);
1747 $replacer = replacer;
1748 if (!isObject$4(replacer) && it === undefined || isSymbol$1(it)) return; // IE8 returns string on undefined
1749 if (!isArray(replacer)) replacer = function (key, value) {
1750 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1751 if (!isSymbol$1(value)) return value;
1754 return $stringify.apply(null, args);
1759 // `Symbol.prototype[@@toPrimitive]` method
1760 // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive
1761 if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1762 createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1764 // `Symbol.prototype[@@toStringTag]` property
1765 // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag
1766 setToStringTag($Symbol, SYMBOL);
1768 hiddenKeys$1[HIDDEN] = true;
1770 var defineProperty$7 = objectDefineProperty.f;
1773 var NativeSymbol = global$2.Symbol;
1775 if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1777 NativeSymbol().description !== undefined
1779 var EmptyStringDescriptionStore = {};
1780 // wrap Symbol constructor for correct work with undefined description
1781 var SymbolWrapper = function Symbol() {
1782 var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1783 var result = this instanceof SymbolWrapper
1784 ? new NativeSymbol(description)
1785 // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1786 : description === undefined ? NativeSymbol() : NativeSymbol(description);
1787 if (description === '') EmptyStringDescriptionStore[result] = true;
1790 copyConstructorProperties(SymbolWrapper, NativeSymbol);
1791 var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1792 symbolPrototype.constructor = SymbolWrapper;
1794 var symbolToString = symbolPrototype.toString;
1795 var native = String(NativeSymbol('test')) == 'Symbol(test)';
1796 var regexp = /^Symbol\((.*)\)[^)]+$/;
1797 defineProperty$7(symbolPrototype, 'description', {
1799 get: function description() {
1800 var symbol = isObject$4(this) ? this.valueOf() : this;
1801 var string = symbolToString.call(symbol);
1802 if (has$1(EmptyStringDescriptionStore, symbol)) return '';
1803 var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1804 return desc === '' ? undefined : desc;
1808 _export({ global: true, forced: true }, {
1809 Symbol: SymbolWrapper
1813 // eslint-disable-next-line es/no-typed-arrays -- safe
1814 var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1816 var redefineAll = function (target, src, options) {
1817 for (var key in src) redefine(target, key, src[key], options);
1821 var anInstance = function (it, Constructor, name) {
1822 if (!(it instanceof Constructor)) {
1823 throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1827 // `ToIndex` abstract operation
1828 // https://tc39.es/ecma262/#sec-toindex
1829 var toIndex = function (it) {
1830 if (it === undefined) return 0;
1831 var number = toInteger(it);
1832 var length = toLength(number);
1833 if (number !== length) throw RangeError('Wrong length or index');
1837 // IEEE754 conversions based on https://github.com/feross/ieee754
1838 var abs$4 = Math.abs;
1839 var pow$2 = Math.pow;
1840 var floor$6 = Math.floor;
1841 var log$2 = Math.log;
1844 var pack = function (number, mantissaLength, bytes) {
1845 var buffer = new Array(bytes);
1846 var exponentLength = bytes * 8 - mantissaLength - 1;
1847 var eMax = (1 << exponentLength) - 1;
1848 var eBias = eMax >> 1;
1849 var rt = mantissaLength === 23 ? pow$2(2, -24) - pow$2(2, -77) : 0;
1850 var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1852 var exponent, mantissa, c;
1853 number = abs$4(number);
1854 // eslint-disable-next-line no-self-compare -- NaN check
1855 if (number != number || number === Infinity) {
1856 // eslint-disable-next-line no-self-compare -- NaN check
1857 mantissa = number != number ? 1 : 0;
1860 exponent = floor$6(log$2(number) / LN2);
1861 if (number * (c = pow$2(2, -exponent)) < 1) {
1865 if (exponent + eBias >= 1) {
1868 number += rt * pow$2(2, 1 - eBias);
1870 if (number * c >= 2) {
1874 if (exponent + eBias >= eMax) {
1877 } else if (exponent + eBias >= 1) {
1878 mantissa = (number * c - 1) * pow$2(2, mantissaLength);
1879 exponent = exponent + eBias;
1881 mantissa = number * pow$2(2, eBias - 1) * pow$2(2, mantissaLength);
1885 for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1886 exponent = exponent << mantissaLength | mantissa;
1887 exponentLength += mantissaLength;
1888 for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1889 buffer[--index] |= sign * 128;
1893 var unpack = function (buffer, mantissaLength) {
1894 var bytes = buffer.length;
1895 var exponentLength = bytes * 8 - mantissaLength - 1;
1896 var eMax = (1 << exponentLength) - 1;
1897 var eBias = eMax >> 1;
1898 var nBits = exponentLength - 7;
1899 var index = bytes - 1;
1900 var sign = buffer[index--];
1901 var exponent = sign & 127;
1904 for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1905 mantissa = exponent & (1 << -nBits) - 1;
1906 exponent >>= -nBits;
1907 nBits += mantissaLength;
1908 for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1909 if (exponent === 0) {
1910 exponent = 1 - eBias;
1911 } else if (exponent === eMax) {
1912 return mantissa ? NaN : sign ? -Infinity : Infinity;
1914 mantissa = mantissa + pow$2(2, mantissaLength);
1915 exponent = exponent - eBias;
1916 } return (sign ? -1 : 1) * mantissa * pow$2(2, exponent - mantissaLength);
1924 // `Array.prototype.fill` method implementation
1925 // https://tc39.es/ecma262/#sec-array.prototype.fill
1926 var arrayFill = function fill(value /* , start = 0, end = @length */) {
1927 var O = toObject(this);
1928 var length = toLength(O.length);
1929 var argumentsLength = arguments.length;
1930 var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1931 var end = argumentsLength > 2 ? arguments[2] : undefined;
1932 var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1933 while (endPos > index) O[index++] = value;
1937 var getOwnPropertyNames$3 = objectGetOwnPropertyNames.f;
1938 var defineProperty$6 = objectDefineProperty.f;
1943 var getInternalState$2 = internalState.get;
1944 var setInternalState$4 = internalState.set;
1945 var ARRAY_BUFFER$1 = 'ArrayBuffer';
1946 var DATA_VIEW = 'DataView';
1947 var PROTOTYPE = 'prototype';
1948 var WRONG_LENGTH = 'Wrong length';
1949 var WRONG_INDEX = 'Wrong index';
1950 var NativeArrayBuffer$1 = global$2[ARRAY_BUFFER$1];
1951 var $ArrayBuffer = NativeArrayBuffer$1;
1952 var $DataView = global$2[DATA_VIEW];
1953 var $DataViewPrototype = $DataView && $DataView[PROTOTYPE];
1954 var ObjectPrototype$1 = Object.prototype;
1955 var RangeError$1 = global$2.RangeError;
1957 var packIEEE754 = ieee754$1.pack;
1958 var unpackIEEE754 = ieee754$1.unpack;
1960 var packInt8 = function (number) {
1961 return [number & 0xFF];
1964 var packInt16 = function (number) {
1965 return [number & 0xFF, number >> 8 & 0xFF];
1968 var packInt32 = function (number) {
1969 return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1972 var unpackInt32 = function (buffer) {
1973 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1976 var packFloat32 = function (number) {
1977 return packIEEE754(number, 23, 4);
1980 var packFloat64 = function (number) {
1981 return packIEEE754(number, 52, 8);
1984 var addGetter = function (Constructor, key) {
1985 defineProperty$6(Constructor[PROTOTYPE], key, { get: function () { return getInternalState$2(this)[key]; } });
1988 var get$4 = function (view, count, index, isLittleEndian) {
1989 var intIndex = toIndex(index);
1990 var store = getInternalState$2(view);
1991 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1992 var bytes = getInternalState$2(store.buffer).bytes;
1993 var start = intIndex + store.byteOffset;
1994 var pack = bytes.slice(start, start + count);
1995 return isLittleEndian ? pack : pack.reverse();
1998 var set$3 = function (view, count, index, conversion, value, isLittleEndian) {
1999 var intIndex = toIndex(index);
2000 var store = getInternalState$2(view);
2001 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
2002 var bytes = getInternalState$2(store.buffer).bytes;
2003 var start = intIndex + store.byteOffset;
2004 var pack = conversion(+value);
2005 for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
2008 if (!arrayBufferNative) {
2009 $ArrayBuffer = function ArrayBuffer(length) {
2010 anInstance(this, $ArrayBuffer, ARRAY_BUFFER$1);
2011 var byteLength = toIndex(length);
2012 setInternalState$4(this, {
2013 bytes: arrayFill.call(new Array(byteLength), 0),
2014 byteLength: byteLength
2016 if (!descriptors) this.byteLength = byteLength;
2019 $DataView = function DataView(buffer, byteOffset, byteLength) {
2020 anInstance(this, $DataView, DATA_VIEW);
2021 anInstance(buffer, $ArrayBuffer, DATA_VIEW);
2022 var bufferLength = getInternalState$2(buffer).byteLength;
2023 var offset = toInteger(byteOffset);
2024 if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
2025 byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
2026 if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
2027 setInternalState$4(this, {
2029 byteLength: byteLength,
2033 this.buffer = buffer;
2034 this.byteLength = byteLength;
2035 this.byteOffset = offset;
2040 addGetter($ArrayBuffer, 'byteLength');
2041 addGetter($DataView, 'buffer');
2042 addGetter($DataView, 'byteLength');
2043 addGetter($DataView, 'byteOffset');
2046 redefineAll($DataView[PROTOTYPE], {
2047 getInt8: function getInt8(byteOffset) {
2048 return get$4(this, 1, byteOffset)[0] << 24 >> 24;
2050 getUint8: function getUint8(byteOffset) {
2051 return get$4(this, 1, byteOffset)[0];
2053 getInt16: function getInt16(byteOffset /* , littleEndian */) {
2054 var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2055 return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
2057 getUint16: function getUint16(byteOffset /* , littleEndian */) {
2058 var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2059 return bytes[1] << 8 | bytes[0];
2061 getInt32: function getInt32(byteOffset /* , littleEndian */) {
2062 return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
2064 getUint32: function getUint32(byteOffset /* , littleEndian */) {
2065 return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
2067 getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
2068 return unpackIEEE754(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
2070 getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
2071 return unpackIEEE754(get$4(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
2073 setInt8: function setInt8(byteOffset, value) {
2074 set$3(this, 1, byteOffset, packInt8, value);
2076 setUint8: function setUint8(byteOffset, value) {
2077 set$3(this, 1, byteOffset, packInt8, value);
2079 setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
2080 set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2082 setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
2083 set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2085 setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
2086 set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2088 setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
2089 set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2091 setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
2092 set$3(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
2094 setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
2095 set$3(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
2099 /* eslint-disable no-new -- required for testing */
2100 if (!fails(function () {
2101 NativeArrayBuffer$1(1);
2102 }) || !fails(function () {
2103 new NativeArrayBuffer$1(-1);
2104 }) || fails(function () {
2105 new NativeArrayBuffer$1();
2106 new NativeArrayBuffer$1(1.5);
2107 new NativeArrayBuffer$1(NaN);
2108 return NativeArrayBuffer$1.name != ARRAY_BUFFER$1;
2110 /* eslint-enable no-new -- required for testing */
2111 $ArrayBuffer = function ArrayBuffer(length) {
2112 anInstance(this, $ArrayBuffer);
2113 return new NativeArrayBuffer$1(toIndex(length));
2115 var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE] = NativeArrayBuffer$1[PROTOTYPE];
2116 for (var keys$2 = getOwnPropertyNames$3(NativeArrayBuffer$1), j$2 = 0, key$1; keys$2.length > j$2;) {
2117 if (!((key$1 = keys$2[j$2++]) in $ArrayBuffer)) {
2118 createNonEnumerableProperty($ArrayBuffer, key$1, NativeArrayBuffer$1[key$1]);
2121 ArrayBufferPrototype.constructor = $ArrayBuffer;
2124 // WebKit bug - the same parent prototype for typed arrays and data view
2125 if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$1) {
2126 objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$1);
2129 // iOS Safari 7.x bug
2130 var testView = new $DataView(new $ArrayBuffer(2));
2131 var $setInt8 = $DataViewPrototype.setInt8;
2132 testView.setInt8(0, 2147483648);
2133 testView.setInt8(1, 2147483649);
2134 if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
2135 setInt8: function setInt8(byteOffset, value) {
2136 $setInt8.call(this, byteOffset, value << 24 >> 24);
2138 setUint8: function setUint8(byteOffset, value) {
2139 $setInt8.call(this, byteOffset, value << 24 >> 24);
2141 }, { unsafe: true });
2144 setToStringTag($ArrayBuffer, ARRAY_BUFFER$1);
2145 setToStringTag($DataView, DATA_VIEW);
2148 ArrayBuffer: $ArrayBuffer,
2152 var SPECIES$5 = wellKnownSymbol('species');
2154 // `SpeciesConstructor` abstract operation
2155 // https://tc39.es/ecma262/#sec-speciesconstructor
2156 var speciesConstructor = function (O, defaultConstructor) {
2157 var C = anObject(O).constructor;
2159 return C === undefined || (S = anObject(C)[SPECIES$5]) == undefined ? defaultConstructor : aFunction(S);
2162 var ArrayBuffer$3 = arrayBuffer.ArrayBuffer;
2163 var DataView$1 = arrayBuffer.DataView;
2164 var nativeArrayBufferSlice = ArrayBuffer$3.prototype.slice;
2166 var INCORRECT_SLICE = fails(function () {
2167 return !new ArrayBuffer$3(2).slice(1, undefined).byteLength;
2170 // `ArrayBuffer.prototype.slice` method
2171 // https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
2172 _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2173 slice: function slice(start, end) {
2174 if (nativeArrayBufferSlice !== undefined && end === undefined) {
2175 return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2177 var length = anObject(this).byteLength;
2178 var first = toAbsoluteIndex(start, length);
2179 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2180 var result = new (speciesConstructor(this, ArrayBuffer$3))(toLength(fin - first));
2181 var viewSource = new DataView$1(this);
2182 var viewTarget = new DataView$1(result);
2184 while (first < fin) {
2185 viewTarget.setUint8(index++, viewSource.getUint8(first++));
2190 // `DataView` constructor
2191 // https://tc39.es/ecma262/#sec-dataview-constructor
2192 _export({ global: true, forced: !arrayBufferNative }, {
2193 DataView: arrayBuffer.DataView
2196 var defineProperty$5 = objectDefineProperty.f;
2202 var Int8Array$3 = global$2.Int8Array;
2203 var Int8ArrayPrototype = Int8Array$3 && Int8Array$3.prototype;
2204 var Uint8ClampedArray = global$2.Uint8ClampedArray;
2205 var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2206 var TypedArray = Int8Array$3 && objectGetPrototypeOf(Int8Array$3);
2207 var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2208 var ObjectPrototype = Object.prototype;
2209 var isPrototypeOf = ObjectPrototype.isPrototypeOf;
2211 var TO_STRING_TAG = wellKnownSymbol('toStringTag');
2212 var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2213 // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2214 var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferNative && !!objectSetPrototypeOf && classof(global$2.opera) !== 'Opera';
2215 var TYPED_ARRAY_TAG_REQIRED = false;
2218 var TypedArrayConstructorsList = {
2221 Uint8ClampedArray: 1,
2230 var BigIntArrayConstructorsList = {
2235 var isView = function isView(it) {
2236 if (!isObject$4(it)) return false;
2237 var klass = classof(it);
2238 return klass === 'DataView'
2239 || has$1(TypedArrayConstructorsList, klass)
2240 || has$1(BigIntArrayConstructorsList, klass);
2243 var isTypedArray = function (it) {
2244 if (!isObject$4(it)) return false;
2245 var klass = classof(it);
2246 return has$1(TypedArrayConstructorsList, klass)
2247 || has$1(BigIntArrayConstructorsList, klass);
2250 var aTypedArray$m = function (it) {
2251 if (isTypedArray(it)) return it;
2252 throw TypeError('Target is not a typed array');
2255 var aTypedArrayConstructor$4 = function (C) {
2256 if (objectSetPrototypeOf) {
2257 if (isPrototypeOf.call(TypedArray, C)) return C;
2258 } else for (var ARRAY in TypedArrayConstructorsList) if (has$1(TypedArrayConstructorsList, NAME$1)) {
2259 var TypedArrayConstructor = global$2[ARRAY];
2260 if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2263 } throw TypeError('Target is not a typed array constructor');
2266 var exportTypedArrayMethod$n = function (KEY, property, forced) {
2267 if (!descriptors) return;
2268 if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2269 var TypedArrayConstructor = global$2[ARRAY];
2270 if (TypedArrayConstructor && has$1(TypedArrayConstructor.prototype, KEY)) try {
2271 delete TypedArrayConstructor.prototype[KEY];
2272 } catch (error) { /* empty */ }
2274 if (!TypedArrayPrototype[KEY] || forced) {
2275 redefine(TypedArrayPrototype, KEY, forced ? property
2276 : NATIVE_ARRAY_BUFFER_VIEWS$2 && Int8ArrayPrototype[KEY] || property);
2280 var exportTypedArrayStaticMethod$1 = function (KEY, property, forced) {
2281 var ARRAY, TypedArrayConstructor;
2282 if (!descriptors) return;
2283 if (objectSetPrototypeOf) {
2284 if (forced) for (ARRAY in TypedArrayConstructorsList) {
2285 TypedArrayConstructor = global$2[ARRAY];
2286 if (TypedArrayConstructor && has$1(TypedArrayConstructor, KEY)) try {
2287 delete TypedArrayConstructor[KEY];
2288 } catch (error) { /* empty */ }
2290 if (!TypedArray[KEY] || forced) {
2291 // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2293 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS$2 && TypedArray[KEY] || property);
2294 } catch (error) { /* empty */ }
2297 for (ARRAY in TypedArrayConstructorsList) {
2298 TypedArrayConstructor = global$2[ARRAY];
2299 if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2300 redefine(TypedArrayConstructor, KEY, property);
2305 for (NAME$1 in TypedArrayConstructorsList) {
2306 if (!global$2[NAME$1]) NATIVE_ARRAY_BUFFER_VIEWS$2 = false;
2309 // WebKit bug - typed arrays constructors prototype is Object.prototype
2310 if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2311 // eslint-disable-next-line no-shadow -- safe
2312 TypedArray = function TypedArray() {
2313 throw TypeError('Incorrect invocation');
2315 if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2316 if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1], TypedArray);
2320 if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype) {
2321 TypedArrayPrototype = TypedArray.prototype;
2322 if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2323 if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1].prototype, TypedArrayPrototype);
2327 // WebKit bug - one more object in Uint8ClampedArray prototype chain
2328 if (NATIVE_ARRAY_BUFFER_VIEWS$2 && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2329 objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2332 if (descriptors && !has$1(TypedArrayPrototype, TO_STRING_TAG)) {
2333 TYPED_ARRAY_TAG_REQIRED = true;
2334 defineProperty$5(TypedArrayPrototype, TO_STRING_TAG, { get: function () {
2335 return isObject$4(this) ? this[TYPED_ARRAY_TAG] : undefined;
2337 for (NAME$1 in TypedArrayConstructorsList) if (global$2[NAME$1]) {
2338 createNonEnumerableProperty(global$2[NAME$1], TYPED_ARRAY_TAG, NAME$1);
2342 var arrayBufferViewCore = {
2343 NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS$2,
2344 TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2345 aTypedArray: aTypedArray$m,
2346 aTypedArrayConstructor: aTypedArrayConstructor$4,
2347 exportTypedArrayMethod: exportTypedArrayMethod$n,
2348 exportTypedArrayStaticMethod: exportTypedArrayStaticMethod$1,
2350 isTypedArray: isTypedArray,
2351 TypedArray: TypedArray,
2352 TypedArrayPrototype: TypedArrayPrototype
2355 var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2357 // `ArrayBuffer.isView` method
2358 // https://tc39.es/ecma262/#sec-arraybuffer.isview
2359 _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2360 isView: arrayBufferViewCore.isView
2363 var SPECIES$4 = wellKnownSymbol('species');
2365 var setSpecies = function (CONSTRUCTOR_NAME) {
2366 var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
2367 var defineProperty = objectDefineProperty.f;
2369 if (descriptors && Constructor && !Constructor[SPECIES$4]) {
2370 defineProperty(Constructor, SPECIES$4, {
2372 get: function () { return this; }
2377 var ARRAY_BUFFER = 'ArrayBuffer';
2378 var ArrayBuffer$2 = arrayBuffer[ARRAY_BUFFER];
2379 var NativeArrayBuffer = global$2[ARRAY_BUFFER];
2381 // `ArrayBuffer` constructor
2382 // https://tc39.es/ecma262/#sec-arraybuffer-constructor
2383 _export({ global: true, forced: NativeArrayBuffer !== ArrayBuffer$2 }, {
2384 ArrayBuffer: ArrayBuffer$2
2387 setSpecies(ARRAY_BUFFER);
2389 var arrayMethodIsStrict = function (METHOD_NAME, argument) {
2390 var method = [][METHOD_NAME];
2391 return !!method && fails(function () {
2392 // eslint-disable-next-line no-useless-call,no-throw-literal -- required for testing
2393 method.call(null, argument || function () { throw 1; }, 1);
2397 /* eslint-disable es/no-array-prototype-indexof -- required for testing */
2399 var $indexOf$1 = arrayIncludes.indexOf;
2402 var nativeIndexOf = [].indexOf;
2404 var NEGATIVE_ZERO$1 = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
2405 var STRICT_METHOD$7 = arrayMethodIsStrict('indexOf');
2407 // `Array.prototype.indexOf` method
2408 // https://tc39.es/ecma262/#sec-array.prototype.indexof
2409 _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO$1 || !STRICT_METHOD$7 }, {
2410 indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
2411 return NEGATIVE_ZERO$1
2413 ? nativeIndexOf.apply(this, arguments) || 0
2414 : $indexOf$1(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
2418 var SPECIES$3 = wellKnownSymbol('species');
2420 var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
2421 // We can't use this feature detection in V8 since it causes
2422 // deoptimization and serious performance degradation
2423 // https://github.com/zloirock/core-js/issues/677
2424 return engineV8Version >= 51 || !fails(function () {
2426 var constructor = array.constructor = {};
2427 constructor[SPECIES$3] = function () {
2430 return array[METHOD_NAME](Boolean).foo !== 1;
2434 var $map$1 = arrayIteration.map;
2437 var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('map');
2439 // `Array.prototype.map` method
2440 // https://tc39.es/ecma262/#sec-array.prototype.map
2441 // with adding support of @@species
2442 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, {
2443 map: function map(callbackfn /* , thisArg */) {
2444 return $map$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2448 var $forEach$1 = arrayIteration.forEach;
2451 var STRICT_METHOD$6 = arrayMethodIsStrict('forEach');
2453 // `Array.prototype.forEach` method implementation
2454 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2455 var arrayForEach = !STRICT_METHOD$6 ? function forEach(callbackfn /* , thisArg */) {
2456 return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2457 // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2460 // `Array.prototype.forEach` method
2461 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2462 // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2463 _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
2464 forEach: arrayForEach
2467 for (var COLLECTION_NAME in domIterables) {
2468 var Collection = global$2[COLLECTION_NAME];
2469 var CollectionPrototype = Collection && Collection.prototype;
2470 // some Chrome versions have non-configurable methods on DOMTokenList
2471 if (CollectionPrototype && CollectionPrototype.forEach !== arrayForEach) try {
2472 createNonEnumerableProperty(CollectionPrototype, 'forEach', arrayForEach);
2474 CollectionPrototype.forEach = arrayForEach;
2478 // `Array.isArray` method
2479 // https://tc39.es/ecma262/#sec-array.isarray
2480 _export({ target: 'Array', stat: true }, {
2484 var getOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2486 // eslint-disable-next-line es/no-object-getownpropertynames -- required for testing
2487 var FAILS_ON_PRIMITIVES$4 = fails(function () { return !Object.getOwnPropertyNames(1); });
2489 // `Object.getOwnPropertyNames` method
2490 // https://tc39.es/ecma262/#sec-object.getownpropertynames
2491 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4 }, {
2492 getOwnPropertyNames: getOwnPropertyNames$2
2495 var nativePromiseConstructor = global$2.Promise;
2497 var ITERATOR$5 = wellKnownSymbol('iterator');
2498 var ArrayPrototype = Array.prototype;
2500 // check on default Array iterator
2501 var isArrayIteratorMethod = function (it) {
2502 return it !== undefined && (iterators.Array === it || ArrayPrototype[ITERATOR$5] === it);
2505 var ITERATOR$4 = wellKnownSymbol('iterator');
2507 var getIteratorMethod = function (it) {
2508 if (it != undefined) return it[ITERATOR$4]
2510 || iterators[classof(it)];
2513 var iteratorClose = function (iterator) {
2514 var returnMethod = iterator['return'];
2515 if (returnMethod !== undefined) {
2516 return anObject(returnMethod.call(iterator)).value;
2520 var Result = function (stopped, result) {
2521 this.stopped = stopped;
2522 this.result = result;
2525 var iterate = function (iterable, unboundFunction, options) {
2526 var that = options && options.that;
2527 var AS_ENTRIES = !!(options && options.AS_ENTRIES);
2528 var IS_ITERATOR = !!(options && options.IS_ITERATOR);
2529 var INTERRUPTED = !!(options && options.INTERRUPTED);
2530 var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
2531 var iterator, iterFn, index, length, result, next, step;
2533 var stop = function (condition) {
2534 if (iterator) iteratorClose(iterator);
2535 return new Result(true, condition);
2538 var callFn = function (value) {
2541 return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
2542 } return INTERRUPTED ? fn(value, stop) : fn(value);
2546 iterator = iterable;
2548 iterFn = getIteratorMethod(iterable);
2549 if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2550 // optimisation for array iterators
2551 if (isArrayIteratorMethod(iterFn)) {
2552 for (index = 0, length = toLength(iterable.length); length > index; index++) {
2553 result = callFn(iterable[index]);
2554 if (result && result instanceof Result) return result;
2555 } return new Result(false);
2557 iterator = iterFn.call(iterable);
2560 next = iterator.next;
2561 while (!(step = next.call(iterator)).done) {
2563 result = callFn(step.value);
2565 iteratorClose(iterator);
2568 if (typeof result == 'object' && result && result instanceof Result) return result;
2569 } return new Result(false);
2572 var ITERATOR$3 = wellKnownSymbol('iterator');
2573 var SAFE_CLOSING = false;
2577 var iteratorWithReturn = {
2579 return { done: !!called++ };
2581 'return': function () {
2582 SAFE_CLOSING = true;
2585 iteratorWithReturn[ITERATOR$3] = function () {
2588 // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing
2589 Array.from(iteratorWithReturn, function () { throw 2; });
2590 } catch (error) { /* empty */ }
2592 var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2593 if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2594 var ITERATION_SUPPORT = false;
2597 object[ITERATOR$3] = function () {
2600 return { done: ITERATION_SUPPORT = true };
2605 } catch (error) { /* empty */ }
2606 return ITERATION_SUPPORT;
2609 var engineIsIos = /(?:iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2611 var engineIsNode = classofRaw(global$2.process) == 'process';
2613 var location$1 = global$2.location;
2614 var set$2 = global$2.setImmediate;
2615 var clear = global$2.clearImmediate;
2616 var process$3 = global$2.process;
2617 var MessageChannel = global$2.MessageChannel;
2618 var Dispatch$1 = global$2.Dispatch;
2621 var ONREADYSTATECHANGE = 'onreadystatechange';
2622 var defer, channel, port;
2624 var run = function (id) {
2625 // eslint-disable-next-line no-prototype-builtins -- safe
2626 if (queue.hasOwnProperty(id)) {
2633 var runner = function (id) {
2634 return function () {
2639 var listener = function (event) {
2643 var post = function (id) {
2644 // old engines have not location.origin
2645 global$2.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2648 // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2649 if (!set$2 || !clear) {
2650 set$2 = function setImmediate(fn) {
2653 while (arguments.length > i) args.push(arguments[i++]);
2654 queue[++counter] = function () {
2655 // eslint-disable-next-line no-new-func -- spec requirement
2656 (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2661 clear = function clearImmediate(id) {
2666 defer = function (id) {
2667 process$3.nextTick(runner(id));
2669 // Sphere (JS game engine) Dispatch API
2670 } else if (Dispatch$1 && Dispatch$1.now) {
2671 defer = function (id) {
2672 Dispatch$1.now(runner(id));
2674 // Browsers with MessageChannel, includes WebWorkers
2675 // except iOS - https://github.com/zloirock/core-js/issues/624
2676 } else if (MessageChannel && !engineIsIos) {
2677 channel = new MessageChannel();
2678 port = channel.port2;
2679 channel.port1.onmessage = listener;
2680 defer = functionBindContext(port.postMessage, port, 1);
2681 // Browsers with postMessage, skip WebWorkers
2682 // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2684 global$2.addEventListener &&
2685 typeof postMessage == 'function' &&
2686 !global$2.importScripts &&
2687 location$1 && location$1.protocol !== 'file:' &&
2691 global$2.addEventListener('message', listener, false);
2693 } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2694 defer = function (id) {
2695 html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2696 html.removeChild(this);
2700 // Rest old browsers
2702 defer = function (id) {
2703 setTimeout(runner(id), 0);
2713 var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(engineUserAgent);
2715 var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
2716 var macrotask = task$1.set;
2721 var MutationObserver = global$2.MutationObserver || global$2.WebKitMutationObserver;
2722 var document$2 = global$2.document;
2723 var process$2 = global$2.process;
2724 var Promise$1 = global$2.Promise;
2725 // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2726 var queueMicrotaskDescriptor = getOwnPropertyDescriptor$3(global$2, 'queueMicrotask');
2727 var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2729 var flush, head, last, notify$1, toggle, node, promise, then;
2731 // modern engines have queueMicrotask method
2732 if (!queueMicrotask) {
2733 flush = function () {
2735 if (engineIsNode && (parent = process$2.domain)) parent.exit();
2742 if (head) notify$1();
2743 else last = undefined;
2747 if (parent) parent.enter();
2750 // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2751 // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898
2752 if (!engineIsIos && !engineIsNode && !engineIsWebosWebkit && MutationObserver && document$2) {
2754 node = document$2.createTextNode('');
2755 new MutationObserver(flush).observe(node, { characterData: true });
2756 notify$1 = function () {
2757 node.data = toggle = !toggle;
2759 // environments with maybe non-completely correct, but existent Promise
2760 } else if (Promise$1 && Promise$1.resolve) {
2761 // Promise.resolve without an argument throws an error in LG WebOS 2
2762 promise = Promise$1.resolve(undefined);
2763 // workaround of WebKit ~ iOS Safari 10.1 bug
2764 promise.constructor = Promise$1;
2765 then = promise.then;
2766 notify$1 = function () {
2767 then.call(promise, flush);
2769 // Node.js without promises
2770 } else if (engineIsNode) {
2771 notify$1 = function () {
2772 process$2.nextTick(flush);
2774 // for other environments - macrotask based on:
2777 // - window.postMessag
2778 // - onreadystatechange
2781 notify$1 = function () {
2782 // strange IE + webpack dev server bug - use .call(global)
2783 macrotask.call(global$2, flush);
2788 var microtask = queueMicrotask || function (fn) {
2789 var task = { fn: fn, next: undefined };
2790 if (last) last.next = task;
2797 var PromiseCapability = function (C) {
2798 var resolve, reject;
2799 this.promise = new C(function ($$resolve, $$reject) {
2800 if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2801 resolve = $$resolve;
2804 this.resolve = aFunction(resolve);
2805 this.reject = aFunction(reject);
2808 // `NewPromiseCapability` abstract operation
2809 // https://tc39.es/ecma262/#sec-newpromisecapability
2810 var f = function (C) {
2811 return new PromiseCapability(C);
2814 var newPromiseCapability$1 = {
2818 var promiseResolve = function (C, x) {
2820 if (isObject$4(x) && x.constructor === C) return x;
2821 var promiseCapability = newPromiseCapability$1.f(C);
2822 var resolve = promiseCapability.resolve;
2824 return promiseCapability.promise;
2827 var hostReportErrors = function (a, b) {
2828 var console = global$2.console;
2829 if (console && console.error) {
2830 arguments.length === 1 ? console.error(a) : console.error(a, b);
2834 var perform = function (exec) {
2836 return { error: false, value: exec() };
2838 return { error: true, value: error };
2842 var engineIsBrowser = typeof window == 'object';
2844 var task = task$1.set;
2857 var SPECIES$2 = wellKnownSymbol('species');
2858 var PROMISE = 'Promise';
2859 var getInternalState$1 = internalState.get;
2860 var setInternalState$3 = internalState.set;
2861 var getInternalPromiseState = internalState.getterFor(PROMISE);
2862 var NativePromisePrototype = nativePromiseConstructor && nativePromiseConstructor.prototype;
2863 var PromiseConstructor = nativePromiseConstructor;
2864 var PromiseConstructorPrototype = NativePromisePrototype;
2865 var TypeError$1 = global$2.TypeError;
2866 var document$1 = global$2.document;
2867 var process$1 = global$2.process;
2868 var newPromiseCapability = newPromiseCapability$1.f;
2869 var newGenericPromiseCapability = newPromiseCapability;
2870 var DISPATCH_EVENT = !!(document$1 && document$1.createEvent && global$2.dispatchEvent);
2871 var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
2872 var UNHANDLED_REJECTION = 'unhandledrejection';
2873 var REJECTION_HANDLED = 'rejectionhandled';
2879 var SUBCLASSING = false;
2880 var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2882 var FORCED$f = isForced_1(PROMISE, function () {
2883 var PROMISE_CONSTRUCTOR_SOURCE = inspectSource(PromiseConstructor);
2884 var GLOBAL_CORE_JS_PROMISE = PROMISE_CONSTRUCTOR_SOURCE !== String(PromiseConstructor);
2885 // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2886 // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2887 // We can't detect it synchronously, so just check versions
2888 if (!GLOBAL_CORE_JS_PROMISE && engineV8Version === 66) return true;
2889 // We can't use @@species feature detection in V8 since it causes
2890 // deoptimization and performance degradation
2891 // https://github.com/zloirock/core-js/issues/679
2892 if (engineV8Version >= 51 && /native code/.test(PROMISE_CONSTRUCTOR_SOURCE)) return false;
2893 // Detect correctness of subclassing with @@species support
2894 var promise = new PromiseConstructor(function (resolve) { resolve(1); });
2895 var FakePromise = function (exec) {
2896 exec(function () { /* empty */ }, function () { /* empty */ });
2898 var constructor = promise.constructor = {};
2899 constructor[SPECIES$2] = FakePromise;
2900 SUBCLASSING = promise.then(function () { /* empty */ }) instanceof FakePromise;
2901 if (!SUBCLASSING) return true;
2902 // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2903 return !GLOBAL_CORE_JS_PROMISE && engineIsBrowser && !NATIVE_REJECTION_EVENT;
2906 var INCORRECT_ITERATION$1 = FORCED$f || !checkCorrectnessOfIteration(function (iterable) {
2907 PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2911 var isThenable = function (it) {
2913 return isObject$4(it) && typeof (then = it.then) == 'function' ? then : false;
2916 var notify = function (state, isReject) {
2917 if (state.notified) return;
2918 state.notified = true;
2919 var chain = state.reactions;
2920 microtask(function () {
2921 var value = state.value;
2922 var ok = state.state == FULFILLED;
2924 // variable length - can't use forEach
2925 while (chain.length > index) {
2926 var reaction = chain[index++];
2927 var handler = ok ? reaction.ok : reaction.fail;
2928 var resolve = reaction.resolve;
2929 var reject = reaction.reject;
2930 var domain = reaction.domain;
2931 var result, then, exited;
2935 if (state.rejection === UNHANDLED) onHandleUnhandled(state);
2936 state.rejection = HANDLED;
2938 if (handler === true) result = value;
2940 if (domain) domain.enter();
2941 result = handler(value); // can throw
2947 if (result === reaction.promise) {
2948 reject(TypeError$1('Promise-chain cycle'));
2949 } else if (then = isThenable(result)) {
2950 then.call(result, resolve, reject);
2951 } else resolve(result);
2952 } else reject(value);
2954 if (domain && !exited) domain.exit();
2958 state.reactions = [];
2959 state.notified = false;
2960 if (isReject && !state.rejection) onUnhandled(state);
2964 var dispatchEvent$1 = function (name, promise, reason) {
2966 if (DISPATCH_EVENT) {
2967 event = document$1.createEvent('Event');
2968 event.promise = promise;
2969 event.reason = reason;
2970 event.initEvent(name, false, true);
2971 global$2.dispatchEvent(event);
2972 } else event = { promise: promise, reason: reason };
2973 if (!NATIVE_REJECTION_EVENT && (handler = global$2['on' + name])) handler(event);
2974 else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2977 var onUnhandled = function (state) {
2978 task.call(global$2, function () {
2979 var promise = state.facade;
2980 var value = state.value;
2981 var IS_UNHANDLED = isUnhandled(state);
2984 result = perform(function () {
2986 process$1.emit('unhandledRejection', value, promise);
2987 } else dispatchEvent$1(UNHANDLED_REJECTION, promise, value);
2989 // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2990 state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
2991 if (result.error) throw result.value;
2996 var isUnhandled = function (state) {
2997 return state.rejection !== HANDLED && !state.parent;
3000 var onHandleUnhandled = function (state) {
3001 task.call(global$2, function () {
3002 var promise = state.facade;
3004 process$1.emit('rejectionHandled', promise);
3005 } else dispatchEvent$1(REJECTION_HANDLED, promise, state.value);
3009 var bind$2 = function (fn, state, unwrap) {
3010 return function (value) {
3011 fn(state, value, unwrap);
3015 var internalReject = function (state, value, unwrap) {
3016 if (state.done) return;
3018 if (unwrap) state = unwrap;
3019 state.value = value;
3020 state.state = REJECTED;
3021 notify(state, true);
3024 var internalResolve = function (state, value, unwrap) {
3025 if (state.done) return;
3027 if (unwrap) state = unwrap;
3029 if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
3030 var then = isThenable(value);
3032 microtask(function () {
3033 var wrapper = { done: false };
3036 bind$2(internalResolve, wrapper, state),
3037 bind$2(internalReject, wrapper, state)
3040 internalReject(wrapper, error, state);
3044 state.value = value;
3045 state.state = FULFILLED;
3046 notify(state, false);
3049 internalReject({ done: false }, error, state);
3053 // constructor polyfill
3055 // 25.4.3.1 Promise(executor)
3056 PromiseConstructor = function Promise(executor) {
3057 anInstance(this, PromiseConstructor, PROMISE);
3058 aFunction(executor);
3059 Internal.call(this);
3060 var state = getInternalState$1(this);
3062 executor(bind$2(internalResolve, state), bind$2(internalReject, state));
3064 internalReject(state, error);
3067 PromiseConstructorPrototype = PromiseConstructor.prototype;
3068 // eslint-disable-next-line no-unused-vars -- required for `.length`
3069 Internal = function Promise(executor) {
3070 setInternalState$3(this, {
3081 Internal.prototype = redefineAll(PromiseConstructorPrototype, {
3082 // `Promise.prototype.then` method
3083 // https://tc39.es/ecma262/#sec-promise.prototype.then
3084 then: function then(onFulfilled, onRejected) {
3085 var state = getInternalPromiseState(this);
3086 var reaction = newPromiseCapability(speciesConstructor(this, PromiseConstructor));
3087 reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
3088 reaction.fail = typeof onRejected == 'function' && onRejected;
3089 reaction.domain = engineIsNode ? process$1.domain : undefined;
3090 state.parent = true;
3091 state.reactions.push(reaction);
3092 if (state.state != PENDING) notify(state, false);
3093 return reaction.promise;
3095 // `Promise.prototype.catch` method
3096 // https://tc39.es/ecma262/#sec-promise.prototype.catch
3097 'catch': function (onRejected) {
3098 return this.then(undefined, onRejected);
3101 OwnPromiseCapability = function () {
3102 var promise = new Internal();
3103 var state = getInternalState$1(promise);
3104 this.promise = promise;
3105 this.resolve = bind$2(internalResolve, state);
3106 this.reject = bind$2(internalReject, state);
3108 newPromiseCapability$1.f = newPromiseCapability = function (C) {
3109 return C === PromiseConstructor || C === PromiseWrapper
3110 ? new OwnPromiseCapability(C)
3111 : newGenericPromiseCapability(C);
3114 if (typeof nativePromiseConstructor == 'function' && NativePromisePrototype !== Object.prototype) {
3115 nativeThen = NativePromisePrototype.then;
3118 // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs
3119 redefine(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) {
3121 return new PromiseConstructor(function (resolve, reject) {
3122 nativeThen.call(that, resolve, reject);
3123 }).then(onFulfilled, onRejected);
3124 // https://github.com/zloirock/core-js/issues/640
3125 }, { unsafe: true });
3127 // makes sure that native promise-based APIs `Promise#catch` properly works with patched `Promise#then`
3128 redefine(NativePromisePrototype, 'catch', PromiseConstructorPrototype['catch'], { unsafe: true });
3131 // make `.constructor === Promise` work for native promise-based APIs
3133 delete NativePromisePrototype.constructor;
3134 } catch (error) { /* empty */ }
3136 // make `instanceof Promise` work for native promise-based APIs
3137 if (objectSetPrototypeOf) {
3138 objectSetPrototypeOf(NativePromisePrototype, PromiseConstructorPrototype);
3143 _export({ global: true, wrap: true, forced: FORCED$f }, {
3144 Promise: PromiseConstructor
3147 setToStringTag(PromiseConstructor, PROMISE, false);
3148 setSpecies(PROMISE);
3150 PromiseWrapper = getBuiltIn(PROMISE);
3153 _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3154 // `Promise.reject` method
3155 // https://tc39.es/ecma262/#sec-promise.reject
3156 reject: function reject(r) {
3157 var capability = newPromiseCapability(this);
3158 capability.reject.call(undefined, r);
3159 return capability.promise;
3163 _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3164 // `Promise.resolve` method
3165 // https://tc39.es/ecma262/#sec-promise.resolve
3166 resolve: function resolve(x) {
3167 return promiseResolve(this, x);
3171 _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION$1 }, {
3172 // `Promise.all` method
3173 // https://tc39.es/ecma262/#sec-promise.all
3174 all: function all(iterable) {
3176 var capability = newPromiseCapability(C);
3177 var resolve = capability.resolve;
3178 var reject = capability.reject;
3179 var result = perform(function () {
3180 var $promiseResolve = aFunction(C.resolve);
3184 iterate(iterable, function (promise) {
3185 var index = counter++;
3186 var alreadyCalled = false;
3187 values.push(undefined);
3189 $promiseResolve.call(C, promise).then(function (value) {
3190 if (alreadyCalled) return;
3191 alreadyCalled = true;
3192 values[index] = value;
3193 --remaining || resolve(values);
3196 --remaining || resolve(values);
3198 if (result.error) reject(result.value);
3199 return capability.promise;
3201 // `Promise.race` method
3202 // https://tc39.es/ecma262/#sec-promise.race
3203 race: function race(iterable) {
3205 var capability = newPromiseCapability(C);
3206 var reject = capability.reject;
3207 var result = perform(function () {
3208 var $promiseResolve = aFunction(C.resolve);
3209 iterate(iterable, function (promise) {
3210 $promiseResolve.call(C, promise).then(capability.resolve, reject);
3213 if (result.error) reject(result.value);
3214 return capability.promise;
3218 /* eslint-disable no-new -- required for testing */
3220 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3222 var ArrayBuffer$1 = global$2.ArrayBuffer;
3223 var Int8Array$2 = global$2.Int8Array;
3225 var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS || !fails(function () {
3227 }) || !fails(function () {
3228 new Int8Array$2(-1);
3229 }) || !checkCorrectnessOfIteration(function (iterable) {
3231 new Int8Array$2(null);
3232 new Int8Array$2(1.5);
3233 new Int8Array$2(iterable);
3234 }, true) || fails(function () {
3235 // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3236 return new Int8Array$2(new ArrayBuffer$1(2), 1, undefined).length !== 1;
3239 var toPositiveInteger = function (it) {
3240 var result = toInteger(it);
3241 if (result < 0) throw RangeError("The argument can't be less than 0");
3245 var toOffset = function (it, BYTES) {
3246 var offset = toPositiveInteger(it);
3247 if (offset % BYTES) throw RangeError('Wrong offset');
3251 var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
3253 var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3254 var O = toObject(source);
3255 var argumentsLength = arguments.length;
3256 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3257 var mapping = mapfn !== undefined;
3258 var iteratorMethod = getIteratorMethod(O);
3259 var i, length, result, step, iterator, next;
3260 if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3261 iterator = iteratorMethod.call(O);
3262 next = iterator.next;
3264 while (!(step = next.call(iterator)).done) {
3268 if (mapping && argumentsLength > 2) {
3269 mapfn = functionBindContext(mapfn, arguments[2], 2);
3271 length = toLength(O.length);
3272 result = new (aTypedArrayConstructor$3(this))(length);
3273 for (i = 0; length > i; i++) {
3274 result[i] = mapping ? mapfn(O[i], i) : O[i];
3279 // makes subclassing work correct for wrapped built-ins
3280 var inheritIfRequired = function ($this, dummy, Wrapper) {
3281 var NewTarget, NewTargetPrototype;
3283 // it can work only with native `setPrototypeOf`
3284 objectSetPrototypeOf &&
3285 // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3286 typeof (NewTarget = dummy.constructor) == 'function' &&
3287 NewTarget !== Wrapper &&
3288 isObject$4(NewTargetPrototype = NewTarget.prototype) &&
3289 NewTargetPrototype !== Wrapper.prototype
3290 ) objectSetPrototypeOf($this, NewTargetPrototype);
3294 var typedArrayConstructor = createCommonjsModule(function (module) {
3313 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3315 var forEach = arrayIteration.forEach;
3322 var getInternalState = internalState.get;
3323 var setInternalState = internalState.set;
3324 var nativeDefineProperty = objectDefineProperty.f;
3325 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3326 var round = Math.round;
3327 var RangeError = global$2.RangeError;
3328 var ArrayBuffer = arrayBuffer.ArrayBuffer;
3329 var DataView = arrayBuffer.DataView;
3330 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3331 var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3332 var TypedArray = arrayBufferViewCore.TypedArray;
3333 var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3334 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3335 var isTypedArray = arrayBufferViewCore.isTypedArray;
3336 var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3337 var WRONG_LENGTH = 'Wrong length';
3339 var fromList = function (C, list) {
3341 var length = list.length;
3342 var result = new (aTypedArrayConstructor(C))(length);
3343 while (length > index) result[index] = list[index++];
3347 var addGetter = function (it, key) {
3348 nativeDefineProperty(it, key, { get: function () {
3349 return getInternalState(this)[key];
3353 var isArrayBuffer = function (it) {
3355 return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3358 var isTypedArrayIndex = function (target, key) {
3359 return isTypedArray(target)
3360 && typeof key != 'symbol'
3362 && String(+key) == String(key);
3365 var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3366 return isTypedArrayIndex(target, key = toPrimitive(key, true))
3367 ? createPropertyDescriptor(2, target[key])
3368 : nativeGetOwnPropertyDescriptor(target, key);
3371 var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3372 if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3373 && isObject$4(descriptor)
3374 && has$1(descriptor, 'value')
3375 && !has$1(descriptor, 'get')
3376 && !has$1(descriptor, 'set')
3377 // TODO: add validation descriptor w/o calling accessors
3378 && !descriptor.configurable
3379 && (!has$1(descriptor, 'writable') || descriptor.writable)
3380 && (!has$1(descriptor, 'enumerable') || descriptor.enumerable)
3382 target[key] = descriptor.value;
3384 } return nativeDefineProperty(target, key, descriptor);
3388 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3389 objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3390 objectDefineProperty.f = wrappedDefineProperty;
3391 addGetter(TypedArrayPrototype, 'buffer');
3392 addGetter(TypedArrayPrototype, 'byteOffset');
3393 addGetter(TypedArrayPrototype, 'byteLength');
3394 addGetter(TypedArrayPrototype, 'length');
3397 _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3398 getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3399 defineProperty: wrappedDefineProperty
3402 module.exports = function (TYPE, wrapper, CLAMPED) {
3403 var BYTES = TYPE.match(/\d+$/)[0] / 8;
3404 var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3405 var GETTER = 'get' + TYPE;
3406 var SETTER = 'set' + TYPE;
3407 var NativeTypedArrayConstructor = global$2[CONSTRUCTOR_NAME];
3408 var TypedArrayConstructor = NativeTypedArrayConstructor;
3409 var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3412 var getter = function (that, index) {
3413 var data = getInternalState(that);
3414 return data.view[GETTER](index * BYTES + data.byteOffset, true);
3417 var setter = function (that, index, value) {
3418 var data = getInternalState(that);
3419 if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3420 data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3423 var addElement = function (that, index) {
3424 nativeDefineProperty(that, index, {
3426 return getter(this, index);
3428 set: function (value) {
3429 return setter(this, index, value);
3435 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3436 TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3437 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3440 var buffer, byteLength, length;
3441 if (!isObject$4(data)) {
3442 length = toIndex(data);
3443 byteLength = length * BYTES;
3444 buffer = new ArrayBuffer(byteLength);
3445 } else if (isArrayBuffer(data)) {
3447 byteOffset = toOffset(offset, BYTES);
3448 var $len = data.byteLength;
3449 if ($length === undefined) {
3450 if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3451 byteLength = $len - byteOffset;
3452 if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3454 byteLength = toLength($length) * BYTES;
3455 if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3457 length = byteLength / BYTES;
3458 } else if (isTypedArray(data)) {
3459 return fromList(TypedArrayConstructor, data);
3461 return typedArrayFrom.call(TypedArrayConstructor, data);
3463 setInternalState(that, {
3465 byteOffset: byteOffset,
3466 byteLength: byteLength,
3468 view: new DataView(buffer)
3470 while (index < length) addElement(that, index++);
3473 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3474 TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3475 } else if (typedArrayConstructorsRequireWrappers) {
3476 TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3477 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3478 return inheritIfRequired(function () {
3479 if (!isObject$4(data)) return new NativeTypedArrayConstructor(toIndex(data));
3480 if (isArrayBuffer(data)) return $length !== undefined
3481 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3482 : typedArrayOffset !== undefined
3483 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3484 : new NativeTypedArrayConstructor(data);
3485 if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3486 return typedArrayFrom.call(TypedArrayConstructor, data);
3487 }(), dummy, TypedArrayConstructor);
3490 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3491 forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3492 if (!(key in TypedArrayConstructor)) {
3493 createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3496 TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3499 if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3500 createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3503 if (TYPED_ARRAY_TAG) {
3504 createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3507 exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3510 global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3513 if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3514 createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3517 if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3518 createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3521 setSpecies(CONSTRUCTOR_NAME);
3523 } else module.exports = function () { /* empty */ };
3526 // `Uint8Array` constructor
3527 // https://tc39.es/ecma262/#sec-typedarray-objects
3528 typedArrayConstructor('Uint8', function (init) {
3529 return function Uint8Array(data, byteOffset, length) {
3530 return init(this, data, byteOffset, length);
3534 var min$7 = Math.min;
3536 // `Array.prototype.copyWithin` method implementation
3537 // https://tc39.es/ecma262/#sec-array.prototype.copywithin
3538 // eslint-disable-next-line es/no-array-prototype-copywithin -- safe
3539 var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3540 var O = toObject(this);
3541 var len = toLength(O.length);
3542 var to = toAbsoluteIndex(target, len);
3543 var from = toAbsoluteIndex(start, len);
3544 var end = arguments.length > 2 ? arguments[2] : undefined;
3545 var count = min$7((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3547 if (from < to && to < from + count) {
3552 while (count-- > 0) {
3553 if (from in O) O[to] = O[from];
3560 var aTypedArray$l = arrayBufferViewCore.aTypedArray;
3561 var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
3563 // `%TypedArray%.prototype.copyWithin` method
3564 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
3565 exportTypedArrayMethod$m('copyWithin', function copyWithin(target, start /* , end */) {
3566 return arrayCopyWithin.call(aTypedArray$l(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3569 var $every$1 = arrayIteration.every;
3571 var aTypedArray$k = arrayBufferViewCore.aTypedArray;
3572 var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
3574 // `%TypedArray%.prototype.every` method
3575 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.every
3576 exportTypedArrayMethod$l('every', function every(callbackfn /* , thisArg */) {
3577 return $every$1(aTypedArray$k(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3580 var aTypedArray$j = arrayBufferViewCore.aTypedArray;
3581 var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
3583 // `%TypedArray%.prototype.fill` method
3584 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill
3585 // eslint-disable-next-line no-unused-vars -- required for `.length`
3586 exportTypedArrayMethod$k('fill', function fill(value /* , start, end */) {
3587 return arrayFill.apply(aTypedArray$j(this), arguments);
3590 var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3593 var typedArrayFromSpeciesAndList = function (instance, list) {
3594 var C = speciesConstructor(instance, instance.constructor);
3596 var length = list.length;
3597 var result = new (aTypedArrayConstructor$2(C))(length);
3598 while (length > index) result[index] = list[index++];
3602 var $filter$1 = arrayIteration.filter;
3605 var aTypedArray$i = arrayBufferViewCore.aTypedArray;
3606 var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
3608 // `%TypedArray%.prototype.filter` method
3609 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter
3610 exportTypedArrayMethod$j('filter', function filter(callbackfn /* , thisArg */) {
3611 var list = $filter$1(aTypedArray$i(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3612 return typedArrayFromSpeciesAndList(this, list);
3615 var $find$1 = arrayIteration.find;
3617 var aTypedArray$h = arrayBufferViewCore.aTypedArray;
3618 var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
3620 // `%TypedArray%.prototype.find` method
3621 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.find
3622 exportTypedArrayMethod$i('find', function find(predicate /* , thisArg */) {
3623 return $find$1(aTypedArray$h(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3626 var $findIndex$1 = arrayIteration.findIndex;
3628 var aTypedArray$g = arrayBufferViewCore.aTypedArray;
3629 var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
3631 // `%TypedArray%.prototype.findIndex` method
3632 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex
3633 exportTypedArrayMethod$h('findIndex', function findIndex(predicate /* , thisArg */) {
3634 return $findIndex$1(aTypedArray$g(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3637 var $forEach = arrayIteration.forEach;
3639 var aTypedArray$f = arrayBufferViewCore.aTypedArray;
3640 var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
3642 // `%TypedArray%.prototype.forEach` method
3643 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
3644 exportTypedArrayMethod$g('forEach', function forEach(callbackfn /* , thisArg */) {
3645 $forEach(aTypedArray$f(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3648 var $includes$1 = arrayIncludes.includes;
3650 var aTypedArray$e = arrayBufferViewCore.aTypedArray;
3651 var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
3653 // `%TypedArray%.prototype.includes` method
3654 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes
3655 exportTypedArrayMethod$f('includes', function includes(searchElement /* , fromIndex */) {
3656 return $includes$1(aTypedArray$e(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3659 var $indexOf = arrayIncludes.indexOf;
3661 var aTypedArray$d = arrayBufferViewCore.aTypedArray;
3662 var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
3664 // `%TypedArray%.prototype.indexOf` method
3665 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof
3666 exportTypedArrayMethod$e('indexOf', function indexOf(searchElement /* , fromIndex */) {
3667 return $indexOf(aTypedArray$d(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3670 var ITERATOR$2 = wellKnownSymbol('iterator');
3671 var Uint8Array$2 = global$2.Uint8Array;
3672 var arrayValues = es_array_iterator.values;
3673 var arrayKeys = es_array_iterator.keys;
3674 var arrayEntries = es_array_iterator.entries;
3675 var aTypedArray$c = arrayBufferViewCore.aTypedArray;
3676 var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
3677 var nativeTypedArrayIterator = Uint8Array$2 && Uint8Array$2.prototype[ITERATOR$2];
3679 var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
3680 && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
3682 var typedArrayValues = function values() {
3683 return arrayValues.call(aTypedArray$c(this));
3686 // `%TypedArray%.prototype.entries` method
3687 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries
3688 exportTypedArrayMethod$d('entries', function entries() {
3689 return arrayEntries.call(aTypedArray$c(this));
3691 // `%TypedArray%.prototype.keys` method
3692 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys
3693 exportTypedArrayMethod$d('keys', function keys() {
3694 return arrayKeys.call(aTypedArray$c(this));
3696 // `%TypedArray%.prototype.values` method
3697 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
3698 exportTypedArrayMethod$d('values', typedArrayValues, !CORRECT_ITER_NAME);
3699 // `%TypedArray%.prototype[@@iterator]` method
3700 // https://tc39.es/ecma262/#sec-%typedarray%.prototype-@@iterator
3701 exportTypedArrayMethod$d(ITERATOR$2, typedArrayValues, !CORRECT_ITER_NAME);
3703 var aTypedArray$b = arrayBufferViewCore.aTypedArray;
3704 var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
3705 var $join = [].join;
3707 // `%TypedArray%.prototype.join` method
3708 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.join
3709 // eslint-disable-next-line no-unused-vars -- required for `.length`
3710 exportTypedArrayMethod$c('join', function join(separator) {
3711 return $join.apply(aTypedArray$b(this), arguments);
3714 /* eslint-disable es/no-array-prototype-lastindexof -- safe */
3720 var min$6 = Math.min;
3721 var $lastIndexOf = [].lastIndexOf;
3722 var NEGATIVE_ZERO = !!$lastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
3723 var STRICT_METHOD$5 = arrayMethodIsStrict('lastIndexOf');
3724 var FORCED$e = NEGATIVE_ZERO || !STRICT_METHOD$5;
3726 // `Array.prototype.lastIndexOf` method implementation
3727 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
3728 var arrayLastIndexOf = FORCED$e ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
3730 if (NEGATIVE_ZERO) return $lastIndexOf.apply(this, arguments) || 0;
3731 var O = toIndexedObject(this);
3732 var length = toLength(O.length);
3733 var index = length - 1;
3734 if (arguments.length > 1) index = min$6(index, toInteger(arguments[1]));
3735 if (index < 0) index = length + index;
3736 for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
3740 var aTypedArray$a = arrayBufferViewCore.aTypedArray;
3741 var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
3743 // `%TypedArray%.prototype.lastIndexOf` method
3744 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof
3745 // eslint-disable-next-line no-unused-vars -- required for `.length`
3746 exportTypedArrayMethod$b('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
3747 return arrayLastIndexOf.apply(aTypedArray$a(this), arguments);
3750 var $map = arrayIteration.map;
3753 var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
3754 var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3755 var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
3757 // `%TypedArray%.prototype.map` method
3758 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
3759 exportTypedArrayMethod$a('map', function map(mapfn /* , thisArg */) {
3760 return $map(aTypedArray$9(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
3761 return new (aTypedArrayConstructor$1(speciesConstructor(O, O.constructor)))(length);
3765 // `Array.prototype.{ reduce, reduceRight }` methods implementation
3766 var createMethod$3 = function (IS_RIGHT) {
3767 return function (that, callbackfn, argumentsLength, memo) {
3768 aFunction(callbackfn);
3769 var O = toObject(that);
3770 var self = indexedObject(O);
3771 var length = toLength(O.length);
3772 var index = IS_RIGHT ? length - 1 : 0;
3773 var i = IS_RIGHT ? -1 : 1;
3774 if (argumentsLength < 2) while (true) {
3775 if (index in self) {
3781 if (IS_RIGHT ? index < 0 : length <= index) {
3782 throw TypeError('Reduce of empty array with no initial value');
3785 for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
3786 memo = callbackfn(memo, self[index], index, O);
3793 // `Array.prototype.reduce` method
3794 // https://tc39.es/ecma262/#sec-array.prototype.reduce
3795 left: createMethod$3(false),
3796 // `Array.prototype.reduceRight` method
3797 // https://tc39.es/ecma262/#sec-array.prototype.reduceright
3798 right: createMethod$3(true)
3801 var $reduce$1 = arrayReduce.left;
3803 var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
3804 var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
3806 // `%TypedArray%.prototype.reduce` method
3807 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce
3808 exportTypedArrayMethod$9('reduce', function reduce(callbackfn /* , initialValue */) {
3809 return $reduce$1(aTypedArray$8(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3812 var $reduceRight = arrayReduce.right;
3814 var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
3815 var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
3817 // `%TypedArray%.prototype.reduceRicht` method
3818 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright
3819 exportTypedArrayMethod$8('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
3820 return $reduceRight(aTypedArray$7(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3823 var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
3824 var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
3825 var floor$5 = Math.floor;
3827 // `%TypedArray%.prototype.reverse` method
3828 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse
3829 exportTypedArrayMethod$7('reverse', function reverse() {
3831 var length = aTypedArray$6(that).length;
3832 var middle = floor$5(length / 2);
3835 while (index < middle) {
3836 value = that[index];
3837 that[index++] = that[--length];
3838 that[length] = value;
3842 var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
3843 var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
3845 var FORCED$d = fails(function () {
3846 // eslint-disable-next-line es/no-typed-arrays -- required for testing
3847 new Int8Array(1).set({});
3850 // `%TypedArray%.prototype.set` method
3851 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
3852 exportTypedArrayMethod$6('set', function set(arrayLike /* , offset */) {
3853 aTypedArray$5(this);
3854 var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
3855 var length = this.length;
3856 var src = toObject(arrayLike);
3857 var len = toLength(src.length);
3859 if (len + offset > length) throw RangeError('Wrong length');
3860 while (index < len) this[offset + index] = src[index++];
3863 var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3864 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3865 var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
3866 var $slice$1 = [].slice;
3868 var FORCED$c = fails(function () {
3869 // eslint-disable-next-line es/no-typed-arrays -- required for testing
3870 new Int8Array(1).slice();
3873 // `%TypedArray%.prototype.slice` method
3874 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice
3875 exportTypedArrayMethod$5('slice', function slice(start, end) {
3876 var list = $slice$1.call(aTypedArray$4(this), start, end);
3877 var C = speciesConstructor(this, this.constructor);
3879 var length = list.length;
3880 var result = new (aTypedArrayConstructor(C))(length);
3881 while (length > index) result[index] = list[index++];
3885 var $some$1 = arrayIteration.some;
3887 var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3888 var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3890 // `%TypedArray%.prototype.some` method
3891 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
3892 exportTypedArrayMethod$4('some', function some(callbackfn /* , thisArg */) {
3893 return $some$1(aTypedArray$3(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3896 // TODO: use something more complex like timsort?
3897 var floor$4 = Math.floor;
3899 var mergeSort = function (array, comparefn) {
3900 var length = array.length;
3901 var middle = floor$4(length / 2);
3902 return length < 8 ? insertionSort(array, comparefn) : merge$5(
3903 mergeSort(array.slice(0, middle), comparefn),
3904 mergeSort(array.slice(middle), comparefn),
3909 var insertionSort = function (array, comparefn) {
3910 var length = array.length;
3914 while (i < length) {
3917 while (j && comparefn(array[j - 1], element) > 0) {
3918 array[j] = array[--j];
3920 if (j !== i++) array[j] = element;
3924 var merge$5 = function (left, right, comparefn) {
3925 var llength = left.length;
3926 var rlength = right.length;
3931 while (lindex < llength || rindex < rlength) {
3932 if (lindex < llength && rindex < rlength) {
3933 result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]);
3935 result.push(lindex < llength ? left[lindex++] : right[rindex++]);
3940 var arraySort = mergeSort;
3942 var firefox = engineUserAgent.match(/firefox\/(\d+)/i);
3944 var engineFfVersion = !!firefox && +firefox[1];
3946 var engineIsIeOrEdge = /MSIE|Trident/.test(engineUserAgent);
3948 var webkit = engineUserAgent.match(/AppleWebKit\/(\d+)\./);
3950 var engineWebkitVersion = !!webkit && +webkit[1];
3952 var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3953 var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3954 var Uint16Array = global$2.Uint16Array;
3955 var nativeSort$1 = Uint16Array && Uint16Array.prototype.sort;
3958 var ACCEPT_INCORRECT_ARGUMENTS = !!nativeSort$1 && !fails(function () {
3959 var array = new Uint16Array(2);
3964 var STABLE_SORT$1 = !!nativeSort$1 && !fails(function () {
3965 // feature detection can be too slow, so check engines versions
3966 if (engineV8Version) return engineV8Version < 74;
3967 if (engineFfVersion) return engineFfVersion < 67;
3968 if (engineIsIeOrEdge) return true;
3969 if (engineWebkitVersion) return engineWebkitVersion < 602;
3971 var array = new Uint16Array(516);
3972 var expected = Array(516);
3975 for (index = 0; index < 516; index++) {
3977 array[index] = 515 - index;
3978 expected[index] = index - 2 * mod + 3;
3981 array.sort(function (a, b) {
3982 return (a / 4 | 0) - (b / 4 | 0);
3985 for (index = 0; index < 516; index++) {
3986 if (array[index] !== expected[index]) return true;
3990 var getSortCompare$1 = function (comparefn) {
3991 return function (x, y) {
3992 if (comparefn !== undefined) return +comparefn(x, y) || 0;
3993 // eslint-disable-next-line no-self-compare -- NaN check
3994 if (y !== y) return -1;
3995 // eslint-disable-next-line no-self-compare -- NaN check
3996 if (x !== x) return 1;
3997 if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1;
4002 // `%TypedArray%.prototype.sort` method
4003 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
4004 exportTypedArrayMethod$3('sort', function sort(comparefn) {
4006 if (comparefn !== undefined) aFunction(comparefn);
4007 if (STABLE_SORT$1) return nativeSort$1.call(array, comparefn);
4009 aTypedArray$2(array);
4010 var arrayLength = toLength(array.length);
4011 var items = Array(arrayLength);
4014 for (index = 0; index < arrayLength; index++) {
4015 items[index] = array[index];
4018 items = arraySort(array, getSortCompare$1(comparefn));
4020 for (index = 0; index < arrayLength; index++) {
4021 array[index] = items[index];
4025 }, !STABLE_SORT$1 || ACCEPT_INCORRECT_ARGUMENTS);
4027 var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
4028 var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
4030 // `%TypedArray%.prototype.subarray` method
4031 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray
4032 exportTypedArrayMethod$2('subarray', function subarray(begin, end) {
4033 var O = aTypedArray$1(this);
4034 var length = O.length;
4035 var beginIndex = toAbsoluteIndex(begin, length);
4036 return new (speciesConstructor(O, O.constructor))(
4038 O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
4039 toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
4043 var Int8Array$1 = global$2.Int8Array;
4044 var aTypedArray = arrayBufferViewCore.aTypedArray;
4045 var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
4046 var $toLocaleString = [].toLocaleString;
4047 var $slice = [].slice;
4049 // iOS Safari 6.x fails here
4050 var TO_LOCALE_STRING_BUG = !!Int8Array$1 && fails(function () {
4051 $toLocaleString.call(new Int8Array$1(1));
4054 var FORCED$b = fails(function () {
4055 return [1, 2].toLocaleString() != new Int8Array$1([1, 2]).toLocaleString();
4056 }) || !fails(function () {
4057 Int8Array$1.prototype.toLocaleString.call([1, 2]);
4060 // `%TypedArray%.prototype.toLocaleString` method
4061 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
4062 exportTypedArrayMethod$1('toLocaleString', function toLocaleString() {
4063 return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice.call(aTypedArray(this)) : aTypedArray(this), arguments);
4066 var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod;
4070 var Uint8Array$1 = global$2.Uint8Array;
4071 var Uint8ArrayPrototype = Uint8Array$1 && Uint8Array$1.prototype || {};
4072 var arrayToString = [].toString;
4073 var arrayJoin = [].join;
4075 if (fails(function () { arrayToString.call({}); })) {
4076 arrayToString = function toString() {
4077 return arrayJoin.call(this);
4081 var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
4083 // `%TypedArray%.prototype.toString` method
4084 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tostring
4085 exportTypedArrayMethod('toString', arrayToString, IS_NOT_ARRAY_METHOD);
4087 var nativeJoin = [].join;
4089 var ES3_STRINGS = indexedObject != Object;
4090 var STRICT_METHOD$4 = arrayMethodIsStrict('join', ',');
4092 // `Array.prototype.join` method
4093 // https://tc39.es/ecma262/#sec-array.prototype.join
4094 _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$4 }, {
4095 join: function join(separator) {
4096 return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
4100 var createProperty = function (object, key, value) {
4101 var propertyKey = toPrimitive(key);
4102 if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
4103 else object[propertyKey] = value;
4106 var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('slice');
4108 var SPECIES$1 = wellKnownSymbol('species');
4109 var nativeSlice = [].slice;
4110 var max$3 = Math.max;
4112 // `Array.prototype.slice` method
4113 // https://tc39.es/ecma262/#sec-array.prototype.slice
4114 // fallback for not array-like ES3 strings and DOM objects
4115 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, {
4116 slice: function slice(start, end) {
4117 var O = toIndexedObject(this);
4118 var length = toLength(O.length);
4119 var k = toAbsoluteIndex(start, length);
4120 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
4121 // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
4122 var Constructor, result, n;
4124 Constructor = O.constructor;
4125 // cross-realm fallback
4126 if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
4127 Constructor = undefined;
4128 } else if (isObject$4(Constructor)) {
4129 Constructor = Constructor[SPECIES$1];
4130 if (Constructor === null) Constructor = undefined;
4132 if (Constructor === Array || Constructor === undefined) {
4133 return nativeSlice.call(O, k, fin);
4136 result = new (Constructor === undefined ? Array : Constructor)(max$3(fin - k, 0));
4137 for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
4143 var ITERATOR$1 = wellKnownSymbol('iterator');
4145 var nativeUrl = !fails(function () {
4146 var url = new URL('b?a=1&b=2&c=3', 'http://a');
4147 var searchParams = url.searchParams;
4149 url.pathname = 'c%20d';
4150 searchParams.forEach(function (value, key) {
4151 searchParams['delete']('b');
4152 result += key + value;
4154 return (isPure && !url.toJSON)
4155 || !searchParams.sort
4156 || url.href !== 'http://a/c%20d?a=1&c=3'
4157 || searchParams.get('c') !== '3'
4158 || String(new URLSearchParams('?a=1')) !== 'a=1'
4159 || !searchParams[ITERATOR$1]
4161 || new URL('https://a@b').username !== 'a'
4162 || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
4163 // not punycoded in Edge
4164 || new URL('http://тест').host !== 'xn--e1aybc'
4165 // not escaped in Chrome 62-
4166 || new URL('http://a#б').hash !== '#%D0%B1'
4167 // fails in Chrome 66-
4168 || result !== 'a1c3'
4170 || new URL('http://x', undefined).host !== 'x';
4173 // eslint-disable-next-line es/no-object-assign -- safe
4174 var $assign = Object.assign;
4175 // eslint-disable-next-line es/no-object-defineproperty -- required for testing
4176 var defineProperty$4 = Object.defineProperty;
4178 // `Object.assign` method
4179 // https://tc39.es/ecma262/#sec-object.assign
4180 var objectAssign = !$assign || fails(function () {
4181 // should have correct order of operations (Edge bug)
4182 if (descriptors && $assign({ b: 1 }, $assign(defineProperty$4({}, 'a', {
4185 defineProperty$4(this, 'b', {
4190 }), { b: 2 })).b !== 1) return true;
4191 // should work with symbols and should have deterministic property order (V8 bug)
4194 // eslint-disable-next-line es/no-symbol -- safe
4195 var symbol = Symbol();
4196 var alphabet = 'abcdefghijklmnopqrst';
4198 alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4199 return $assign({}, A)[symbol] != 7 || objectKeys($assign({}, B)).join('') != alphabet;
4200 }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length`
4201 var T = toObject(target);
4202 var argumentsLength = arguments.length;
4204 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4205 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4206 while (argumentsLength > index) {
4207 var S = indexedObject(arguments[index++]);
4208 var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4209 var length = keys.length;
4212 while (length > j) {
4214 if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4219 // call something on iterator step with safe closing on error
4220 var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
4222 return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
4224 iteratorClose(iterator);
4229 // `Array.from` method implementation
4230 // https://tc39.es/ecma262/#sec-array.from
4231 var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4232 var O = toObject(arrayLike);
4233 var C = typeof this == 'function' ? this : Array;
4234 var argumentsLength = arguments.length;
4235 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4236 var mapping = mapfn !== undefined;
4237 var iteratorMethod = getIteratorMethod(O);
4239 var length, result, step, iterator, next, value;
4240 if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4241 // if the target is not iterable or it's an array with the default iterator - use a simple case
4242 if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4243 iterator = iteratorMethod.call(O);
4244 next = iterator.next;
4246 for (;!(step = next.call(iterator)).done; index++) {
4247 value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4248 createProperty(result, index, value);
4251 length = toLength(O.length);
4252 result = new C(length);
4253 for (;length > index; index++) {
4254 value = mapping ? mapfn(O[index], index) : O[index];
4255 createProperty(result, index, value);
4258 result.length = index;
4262 // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4263 var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4269 var initialBias = 72;
4270 var initialN = 128; // 0x80
4271 var delimiter = '-'; // '\x2D'
4272 var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4273 var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4274 var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4275 var baseMinusTMin = base - tMin;
4276 var floor$3 = Math.floor;
4277 var stringFromCharCode = String.fromCharCode;
4280 * Creates an array containing the numeric code points of each Unicode
4281 * character in the string. While JavaScript uses UCS-2 internally,
4282 * this function will convert a pair of surrogate halves (each of which
4283 * UCS-2 exposes as separate characters) into a single code point,
4286 var ucs2decode = function (string) {
4289 var length = string.length;
4290 while (counter < length) {
4291 var value = string.charCodeAt(counter++);
4292 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4293 // It's a high surrogate, and there is a next character.
4294 var extra = string.charCodeAt(counter++);
4295 if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4296 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4298 // It's an unmatched surrogate; only append this code unit, in case the
4299 // next code unit is the high surrogate of a surrogate pair.
4311 * Converts a digit/integer into a basic code point.
4313 var digitToBasic = function (digit) {
4314 // 0..25 map to ASCII a..z or A..Z
4315 // 26..35 map to ASCII 0..9
4316 return digit + 22 + 75 * (digit < 26);
4320 * Bias adaptation function as per section 3.4 of RFC 3492.
4321 * https://tools.ietf.org/html/rfc3492#section-3.4
4323 var adapt = function (delta, numPoints, firstTime) {
4325 delta = firstTime ? floor$3(delta / damp) : delta >> 1;
4326 delta += floor$3(delta / numPoints);
4327 for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4328 delta = floor$3(delta / baseMinusTMin);
4330 return floor$3(k + (baseMinusTMin + 1) * delta / (delta + skew));
4334 * Converts a string of Unicode symbols (e.g. a domain name label) to a
4335 * Punycode string of ASCII-only symbols.
4337 // eslint-disable-next-line max-statements -- TODO
4338 var encode = function (input) {
4341 // Convert the input in UCS-2 to an array of Unicode code points.
4342 input = ucs2decode(input);
4344 // Cache the length.
4345 var inputLength = input.length;
4347 // Initialize the state.
4350 var bias = initialBias;
4351 var i, currentValue;
4353 // Handle the basic code points.
4354 for (i = 0; i < input.length; i++) {
4355 currentValue = input[i];
4356 if (currentValue < 0x80) {
4357 output.push(stringFromCharCode(currentValue));
4361 var basicLength = output.length; // number of basic code points.
4362 var handledCPCount = basicLength; // number of code points that have been handled;
4364 // Finish the basic string with a delimiter unless it's empty.
4366 output.push(delimiter);
4369 // Main encoding loop:
4370 while (handledCPCount < inputLength) {
4371 // All non-basic code points < n have been handled already. Find the next larger one:
4373 for (i = 0; i < input.length; i++) {
4374 currentValue = input[i];
4375 if (currentValue >= n && currentValue < m) {
4380 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4381 var handledCPCountPlusOne = handledCPCount + 1;
4382 if (m - n > floor$3((maxInt - delta) / handledCPCountPlusOne)) {
4383 throw RangeError(OVERFLOW_ERROR);
4386 delta += (m - n) * handledCPCountPlusOne;
4389 for (i = 0; i < input.length; i++) {
4390 currentValue = input[i];
4391 if (currentValue < n && ++delta > maxInt) {
4392 throw RangeError(OVERFLOW_ERROR);
4394 if (currentValue == n) {
4395 // Represent delta as a generalized variable-length integer.
4397 for (var k = base; /* no condition */; k += base) {
4398 var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4400 var qMinusT = q - t;
4401 var baseMinusT = base - t;
4402 output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4403 q = floor$3(qMinusT / baseMinusT);
4406 output.push(stringFromCharCode(digitToBasic(q)));
4407 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4416 return output.join('');
4419 var stringPunycodeToAscii = function (input) {
4421 var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4423 for (i = 0; i < labels.length; i++) {
4425 encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4427 return encoded.join('.');
4430 var getIterator = function (it) {
4431 var iteratorMethod = getIteratorMethod(it);
4432 if (typeof iteratorMethod != 'function') {
4433 throw TypeError(String(it) + ' is not iterable');
4434 } return anObject(iteratorMethod.call(it));
4437 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4459 var $fetch = getBuiltIn('fetch');
4460 var Headers$1 = getBuiltIn('Headers');
4461 var ITERATOR = wellKnownSymbol('iterator');
4462 var URL_SEARCH_PARAMS = 'URLSearchParams';
4463 var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4464 var setInternalState$2 = internalState.set;
4465 var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4466 var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4469 var sequences = Array(4);
4471 var percentSequence = function (bytes) {
4472 return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4475 var percentDecode = function (sequence) {
4477 return decodeURIComponent(sequence);
4483 var deserialize = function (it) {
4484 var result = it.replace(plus, ' ');
4487 return decodeURIComponent(result);
4490 result = result.replace(percentSequence(bytes--), percentDecode);
4496 var find$1 = /[!'()~]|%20/g;
4507 var replacer = function (match) {
4508 return replace$1[match];
4511 var serialize = function (it) {
4512 return encodeURIComponent(it).replace(find$1, replacer);
4515 var parseSearchParams = function (result, query) {
4517 var attributes = query.split('&');
4519 var attribute, entry;
4520 while (index < attributes.length) {
4521 attribute = attributes[index++];
4522 if (attribute.length) {
4523 entry = attribute.split('=');
4525 key: deserialize(entry.shift()),
4526 value: deserialize(entry.join('='))
4533 var updateSearchParams = function (query) {
4534 this.entries.length = 0;
4535 parseSearchParams(this.entries, query);
4538 var validateArgumentsLength = function (passed, required) {
4539 if (passed < required) throw TypeError('Not enough arguments');
4542 var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4543 setInternalState$2(this, {
4544 type: URL_SEARCH_PARAMS_ITERATOR,
4545 iterator: getIterator(getInternalParamsState(params).entries),
4548 }, 'Iterator', function next() {
4549 var state = getInternalIteratorState(this);
4550 var kind = state.kind;
4551 var step = state.iterator.next();
4552 var entry = step.value;
4554 step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4558 // `URLSearchParams` constructor
4559 // https://url.spec.whatwg.org/#interface-urlsearchparams
4560 var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4561 anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4562 var init = arguments.length > 0 ? arguments[0] : undefined;
4565 var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4567 setInternalState$2(that, {
4568 type: URL_SEARCH_PARAMS,
4570 updateURL: function () { /* empty */ },
4571 updateSearchParams: updateSearchParams
4574 if (init !== undefined) {
4575 if (isObject$4(init)) {
4576 iteratorMethod = getIteratorMethod(init);
4577 if (typeof iteratorMethod === 'function') {
4578 iterator = iteratorMethod.call(init);
4579 next = iterator.next;
4580 while (!(step = next.call(iterator)).done) {
4581 entryIterator = getIterator(anObject(step.value));
4582 entryNext = entryIterator.next;
4584 (first = entryNext.call(entryIterator)).done ||
4585 (second = entryNext.call(entryIterator)).done ||
4586 !entryNext.call(entryIterator).done
4587 ) throw TypeError('Expected sequence with length 2');
4588 entries.push({ key: first.value + '', value: second.value + '' });
4590 } else for (key in init) if (has$1(init, key)) entries.push({ key: key, value: init[key] + '' });
4592 parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4597 var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4599 redefineAll(URLSearchParamsPrototype, {
4600 // `URLSearchParams.prototype.append` method
4601 // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4602 append: function append(name, value) {
4603 validateArgumentsLength(arguments.length, 2);
4604 var state = getInternalParamsState(this);
4605 state.entries.push({ key: name + '', value: value + '' });
4608 // `URLSearchParams.prototype.delete` method
4609 // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4610 'delete': function (name) {
4611 validateArgumentsLength(arguments.length, 1);
4612 var state = getInternalParamsState(this);
4613 var entries = state.entries;
4614 var key = name + '';
4616 while (index < entries.length) {
4617 if (entries[index].key === key) entries.splice(index, 1);
4622 // `URLSearchParams.prototype.get` method
4623 // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4624 get: function get(name) {
4625 validateArgumentsLength(arguments.length, 1);
4626 var entries = getInternalParamsState(this).entries;
4627 var key = name + '';
4629 for (; index < entries.length; index++) {
4630 if (entries[index].key === key) return entries[index].value;
4634 // `URLSearchParams.prototype.getAll` method
4635 // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4636 getAll: function getAll(name) {
4637 validateArgumentsLength(arguments.length, 1);
4638 var entries = getInternalParamsState(this).entries;
4639 var key = name + '';
4642 for (; index < entries.length; index++) {
4643 if (entries[index].key === key) result.push(entries[index].value);
4647 // `URLSearchParams.prototype.has` method
4648 // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4649 has: function has(name) {
4650 validateArgumentsLength(arguments.length, 1);
4651 var entries = getInternalParamsState(this).entries;
4652 var key = name + '';
4654 while (index < entries.length) {
4655 if (entries[index++].key === key) return true;
4659 // `URLSearchParams.prototype.set` method
4660 // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4661 set: function set(name, value) {
4662 validateArgumentsLength(arguments.length, 1);
4663 var state = getInternalParamsState(this);
4664 var entries = state.entries;
4666 var key = name + '';
4667 var val = value + '';
4670 for (; index < entries.length; index++) {
4671 entry = entries[index];
4672 if (entry.key === key) {
4673 if (found) entries.splice(index--, 1);
4680 if (!found) entries.push({ key: key, value: val });
4683 // `URLSearchParams.prototype.sort` method
4684 // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4685 sort: function sort() {
4686 var state = getInternalParamsState(this);
4687 var entries = state.entries;
4688 // Array#sort is not stable in some engines
4689 var slice = entries.slice();
4690 var entry, entriesIndex, sliceIndex;
4692 for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
4693 entry = slice[sliceIndex];
4694 for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
4695 if (entries[entriesIndex].key > entry.key) {
4696 entries.splice(entriesIndex, 0, entry);
4700 if (entriesIndex === sliceIndex) entries.push(entry);
4704 // `URLSearchParams.prototype.forEach` method
4705 forEach: function forEach(callback /* , thisArg */) {
4706 var entries = getInternalParamsState(this).entries;
4707 var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
4710 while (index < entries.length) {
4711 entry = entries[index++];
4712 boundFunction(entry.value, entry.key, this);
4715 // `URLSearchParams.prototype.keys` method
4716 keys: function keys() {
4717 return new URLSearchParamsIterator(this, 'keys');
4719 // `URLSearchParams.prototype.values` method
4720 values: function values() {
4721 return new URLSearchParamsIterator(this, 'values');
4723 // `URLSearchParams.prototype.entries` method
4724 entries: function entries() {
4725 return new URLSearchParamsIterator(this, 'entries');
4727 }, { enumerable: true });
4729 // `URLSearchParams.prototype[@@iterator]` method
4730 redefine(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries);
4732 // `URLSearchParams.prototype.toString` method
4733 // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
4734 redefine(URLSearchParamsPrototype, 'toString', function toString() {
4735 var entries = getInternalParamsState(this).entries;
4739 while (index < entries.length) {
4740 entry = entries[index++];
4741 result.push(serialize(entry.key) + '=' + serialize(entry.value));
4742 } return result.join('&');
4743 }, { enumerable: true });
4745 setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4747 _export({ global: true, forced: !nativeUrl }, {
4748 URLSearchParams: URLSearchParamsConstructor
4751 // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
4752 // https://github.com/zloirock/core-js/issues/674
4753 if (!nativeUrl && typeof $fetch == 'function' && typeof Headers$1 == 'function') {
4754 _export({ global: true, enumerable: true, forced: true }, {
4755 fetch: function fetch(input /* , init */) {
4757 var init, body, headers;
4758 if (arguments.length > 1) {
4759 init = arguments[1];
4760 if (isObject$4(init)) {
4762 if (classof(body) === URL_SEARCH_PARAMS) {
4763 headers = init.headers ? new Headers$1(init.headers) : new Headers$1();
4764 if (!headers.has('content-type')) {
4765 headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
4767 init = objectCreate(init, {
4768 body: createPropertyDescriptor(0, String(body)),
4769 headers: createPropertyDescriptor(0, headers)
4774 } return $fetch.apply(this, args);
4779 var web_urlSearchParams = {
4780 URLSearchParams: URLSearchParamsConstructor,
4781 getState: getInternalParamsState
4784 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4796 var codeAt = stringMultibyte.codeAt;
4802 var NativeURL = global$2.URL;
4803 var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
4804 var getInternalSearchParamsState = web_urlSearchParams.getState;
4805 var setInternalState$1 = internalState.set;
4806 var getInternalURLState = internalState.getterFor('URL');
4807 var floor$2 = Math.floor;
4808 var pow$1 = Math.pow;
4810 var INVALID_AUTHORITY = 'Invalid authority';
4811 var INVALID_SCHEME = 'Invalid scheme';
4812 var INVALID_HOST = 'Invalid host';
4813 var INVALID_PORT = 'Invalid port';
4815 var ALPHA = /[A-Za-z]/;
4816 // eslint-disable-next-line regexp/no-obscure-range -- safe
4817 var ALPHANUMERIC = /[\d+-.A-Za-z]/;
4819 var HEX_START = /^0x/i;
4820 var OCT = /^[0-7]+$/;
4822 var HEX = /^[\dA-Fa-f]+$/;
4823 /* eslint-disable no-control-regex -- safe */
4824 var FORBIDDEN_HOST_CODE_POINT = /[\0\t\n\r #%/:<>?@[\\\]^|]/;
4825 var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\0\t\n\r #/:<>?@[\\\]^|]/;
4826 var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
4827 var TAB_AND_NEW_LINE = /[\t\n\r]/g;
4828 /* eslint-enable no-control-regex -- safe */
4831 var parseHost = function (url, input) {
4832 var result, codePoints, index;
4833 if (input.charAt(0) == '[') {
4834 if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
4835 result = parseIPv6(input.slice(1, -1));
4836 if (!result) return INVALID_HOST;
4839 } else if (!isSpecial(url)) {
4840 if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
4842 codePoints = arrayFrom(input);
4843 for (index = 0; index < codePoints.length; index++) {
4844 result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
4848 input = stringPunycodeToAscii(input);
4849 if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
4850 result = parseIPv4(input);
4851 if (result === null) return INVALID_HOST;
4856 var parseIPv4 = function (input) {
4857 var parts = input.split('.');
4858 var partsLength, numbers, index, part, radix, number, ipv4;
4859 if (parts.length && parts[parts.length - 1] == '') {
4862 partsLength = parts.length;
4863 if (partsLength > 4) return input;
4865 for (index = 0; index < partsLength; index++) {
4866 part = parts[index];
4867 if (part == '') return input;
4869 if (part.length > 1 && part.charAt(0) == '0') {
4870 radix = HEX_START.test(part) ? 16 : 8;
4871 part = part.slice(radix == 8 ? 1 : 2);
4876 if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
4877 number = parseInt(part, radix);
4879 numbers.push(number);
4881 for (index = 0; index < partsLength; index++) {
4882 number = numbers[index];
4883 if (index == partsLength - 1) {
4884 if (number >= pow$1(256, 5 - partsLength)) return null;
4885 } else if (number > 255) return null;
4887 ipv4 = numbers.pop();
4888 for (index = 0; index < numbers.length; index++) {
4889 ipv4 += numbers[index] * pow$1(256, 3 - index);
4894 // eslint-disable-next-line max-statements -- TODO
4895 var parseIPv6 = function (input) {
4896 var address = [0, 0, 0, 0, 0, 0, 0, 0];
4898 var compress = null;
4900 var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
4902 var char = function () {
4903 return input.charAt(pointer);
4906 if (char() == ':') {
4907 if (input.charAt(1) != ':') return;
4910 compress = pieceIndex;
4913 if (pieceIndex == 8) return;
4914 if (char() == ':') {
4915 if (compress !== null) return;
4918 compress = pieceIndex;
4922 while (length < 4 && HEX.test(char())) {
4923 value = value * 16 + parseInt(char(), 16);
4927 if (char() == '.') {
4928 if (length == 0) return;
4930 if (pieceIndex > 6) return;
4934 if (numbersSeen > 0) {
4935 if (char() == '.' && numbersSeen < 4) pointer++;
4938 if (!DIGIT.test(char())) return;
4939 while (DIGIT.test(char())) {
4940 number = parseInt(char(), 10);
4941 if (ipv4Piece === null) ipv4Piece = number;
4942 else if (ipv4Piece == 0) return;
4943 else ipv4Piece = ipv4Piece * 10 + number;
4944 if (ipv4Piece > 255) return;
4947 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
4949 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
4951 if (numbersSeen != 4) return;
4953 } else if (char() == ':') {
4955 if (!char()) return;
4956 } else if (char()) return;
4957 address[pieceIndex++] = value;
4959 if (compress !== null) {
4960 swaps = pieceIndex - compress;
4962 while (pieceIndex != 0 && swaps > 0) {
4963 swap = address[pieceIndex];
4964 address[pieceIndex--] = address[compress + swaps - 1];
4965 address[compress + --swaps] = swap;
4967 } else if (pieceIndex != 8) return;
4971 var findLongestZeroSequence = function (ipv6) {
4972 var maxIndex = null;
4974 var currStart = null;
4977 for (; index < 8; index++) {
4978 if (ipv6[index] !== 0) {
4979 if (currLength > maxLength) {
4980 maxIndex = currStart;
4981 maxLength = currLength;
4986 if (currStart === null) currStart = index;
4990 if (currLength > maxLength) {
4991 maxIndex = currStart;
4992 maxLength = currLength;
4997 var serializeHost = function (host) {
4998 var result, index, compress, ignore0;
5000 if (typeof host == 'number') {
5002 for (index = 0; index < 4; index++) {
5003 result.unshift(host % 256);
5004 host = floor$2(host / 256);
5005 } return result.join('.');
5007 } else if (typeof host == 'object') {
5009 compress = findLongestZeroSequence(host);
5010 for (index = 0; index < 8; index++) {
5011 if (ignore0 && host[index] === 0) continue;
5012 if (ignore0) ignore0 = false;
5013 if (compress === index) {
5014 result += index ? ':' : '::';
5017 result += host[index].toString(16);
5018 if (index < 7) result += ':';
5021 return '[' + result + ']';
5025 var C0ControlPercentEncodeSet = {};
5026 var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
5027 ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
5029 var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
5030 '#': 1, '?': 1, '{': 1, '}': 1
5032 var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
5033 '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
5036 var percentEncode = function (char, set) {
5037 var code = codeAt(char, 0);
5038 return code > 0x20 && code < 0x7F && !has$1(set, char) ? char : encodeURIComponent(char);
5041 var specialSchemes = {
5050 var isSpecial = function (url) {
5051 return has$1(specialSchemes, url.scheme);
5054 var includesCredentials = function (url) {
5055 return url.username != '' || url.password != '';
5058 var cannotHaveUsernamePasswordPort = function (url) {
5059 return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
5062 var isWindowsDriveLetter = function (string, normalized) {
5064 return string.length == 2 && ALPHA.test(string.charAt(0))
5065 && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
5068 var startsWithWindowsDriveLetter = function (string) {
5070 return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
5071 string.length == 2 ||
5072 ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
5076 var shortenURLsPath = function (url) {
5077 var path = url.path;
5078 var pathSize = path.length;
5079 if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
5084 var isSingleDot = function (segment) {
5085 return segment === '.' || segment.toLowerCase() === '%2e';
5088 var isDoubleDot = function (segment) {
5089 segment = segment.toLowerCase();
5090 return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
5094 var SCHEME_START = {};
5097 var SPECIAL_RELATIVE_OR_AUTHORITY = {};
5098 var PATH_OR_AUTHORITY = {};
5100 var RELATIVE_SLASH = {};
5101 var SPECIAL_AUTHORITY_SLASHES = {};
5102 var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
5108 var FILE_SLASH = {};
5110 var PATH_START = {};
5112 var CANNOT_BE_A_BASE_URL_PATH = {};
5116 // eslint-disable-next-line max-statements -- TODO
5117 var parseURL = function (url, input, stateOverride, base) {
5118 var state = stateOverride || SCHEME_START;
5122 var seenBracket = false;
5123 var seenPasswordToken = false;
5124 var codePoints, char, bufferCodePoints, failure;
5126 if (!stateOverride) {
5134 url.fragment = null;
5135 url.cannotBeABaseURL = false;
5136 input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
5139 input = input.replace(TAB_AND_NEW_LINE, '');
5141 codePoints = arrayFrom(input);
5143 while (pointer <= codePoints.length) {
5144 char = codePoints[pointer];
5147 if (char && ALPHA.test(char)) {
5148 buffer += char.toLowerCase();
5150 } else if (!stateOverride) {
5153 } else return INVALID_SCHEME;
5157 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
5158 buffer += char.toLowerCase();
5159 } else if (char == ':') {
5160 if (stateOverride && (
5161 (isSpecial(url) != has$1(specialSchemes, buffer)) ||
5162 (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
5163 (url.scheme == 'file' && !url.host)
5165 url.scheme = buffer;
5166 if (stateOverride) {
5167 if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5171 if (url.scheme == 'file') {
5173 } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5174 state = SPECIAL_RELATIVE_OR_AUTHORITY;
5175 } else if (isSpecial(url)) {
5176 state = SPECIAL_AUTHORITY_SLASHES;
5177 } else if (codePoints[pointer + 1] == '/') {
5178 state = PATH_OR_AUTHORITY;
5181 url.cannotBeABaseURL = true;
5183 state = CANNOT_BE_A_BASE_URL_PATH;
5185 } else if (!stateOverride) {
5190 } else return INVALID_SCHEME;
5194 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5195 if (base.cannotBeABaseURL && char == '#') {
5196 url.scheme = base.scheme;
5197 url.path = base.path.slice();
5198 url.query = base.query;
5200 url.cannotBeABaseURL = true;
5204 state = base.scheme == 'file' ? FILE : RELATIVE;
5207 case SPECIAL_RELATIVE_OR_AUTHORITY:
5208 if (char == '/' && codePoints[pointer + 1] == '/') {
5209 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5216 case PATH_OR_AUTHORITY:
5226 url.scheme = base.scheme;
5228 url.username = base.username;
5229 url.password = base.password;
5230 url.host = base.host;
5231 url.port = base.port;
5232 url.path = base.path.slice();
5233 url.query = base.query;
5234 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5235 state = RELATIVE_SLASH;
5236 } else if (char == '?') {
5237 url.username = base.username;
5238 url.password = base.password;
5239 url.host = base.host;
5240 url.port = base.port;
5241 url.path = base.path.slice();
5244 } else if (char == '#') {
5245 url.username = base.username;
5246 url.password = base.password;
5247 url.host = base.host;
5248 url.port = base.port;
5249 url.path = base.path.slice();
5250 url.query = base.query;
5254 url.username = base.username;
5255 url.password = base.password;
5256 url.host = base.host;
5257 url.port = base.port;
5258 url.path = base.path.slice();
5264 case RELATIVE_SLASH:
5265 if (isSpecial(url) && (char == '/' || char == '\\')) {
5266 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5267 } else if (char == '/') {
5270 url.username = base.username;
5271 url.password = base.password;
5272 url.host = base.host;
5273 url.port = base.port;
5278 case SPECIAL_AUTHORITY_SLASHES:
5279 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5280 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5284 case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5285 if (char != '/' && char != '\\') {
5292 if (seenAt) buffer = '%40' + buffer;
5294 bufferCodePoints = arrayFrom(buffer);
5295 for (var i = 0; i < bufferCodePoints.length; i++) {
5296 var codePoint = bufferCodePoints[i];
5297 if (codePoint == ':' && !seenPasswordToken) {
5298 seenPasswordToken = true;
5301 var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5302 if (seenPasswordToken) url.password += encodedCodePoints;
5303 else url.username += encodedCodePoints;
5307 char == EOF || char == '/' || char == '?' || char == '#' ||
5308 (char == '\\' && isSpecial(url))
5310 if (seenAt && buffer == '') return INVALID_AUTHORITY;
5311 pointer -= arrayFrom(buffer).length + 1;
5314 } else buffer += char;
5319 if (stateOverride && url.scheme == 'file') {
5322 } else if (char == ':' && !seenBracket) {
5323 if (buffer == '') return INVALID_HOST;
5324 failure = parseHost(url, buffer);
5325 if (failure) return failure;
5328 if (stateOverride == HOSTNAME) return;
5330 char == EOF || char == '/' || char == '?' || char == '#' ||
5331 (char == '\\' && isSpecial(url))
5333 if (isSpecial(url) && buffer == '') return INVALID_HOST;
5334 if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5335 failure = parseHost(url, buffer);
5336 if (failure) return failure;
5339 if (stateOverride) return;
5342 if (char == '[') seenBracket = true;
5343 else if (char == ']') seenBracket = false;
5348 if (DIGIT.test(char)) {
5351 char == EOF || char == '/' || char == '?' || char == '#' ||
5352 (char == '\\' && isSpecial(url)) ||
5356 var port = parseInt(buffer, 10);
5357 if (port > 0xFFFF) return INVALID_PORT;
5358 url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5361 if (stateOverride) return;
5364 } else return INVALID_PORT;
5368 url.scheme = 'file';
5369 if (char == '/' || char == '\\') state = FILE_SLASH;
5370 else if (base && base.scheme == 'file') {
5372 url.host = base.host;
5373 url.path = base.path.slice();
5374 url.query = base.query;
5375 } else if (char == '?') {
5376 url.host = base.host;
5377 url.path = base.path.slice();
5380 } else if (char == '#') {
5381 url.host = base.host;
5382 url.path = base.path.slice();
5383 url.query = base.query;
5387 if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5388 url.host = base.host;
5389 url.path = base.path.slice();
5390 shortenURLsPath(url);
5401 if (char == '/' || char == '\\') {
5405 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5406 if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5407 else url.host = base.host;
5413 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5414 if (!stateOverride && isWindowsDriveLetter(buffer)) {
5416 } else if (buffer == '') {
5418 if (stateOverride) return;
5421 failure = parseHost(url, buffer);
5422 if (failure) return failure;
5423 if (url.host == 'localhost') url.host = '';
5424 if (stateOverride) return;
5428 } else buffer += char;
5432 if (isSpecial(url)) {
5434 if (char != '/' && char != '\\') continue;
5435 } else if (!stateOverride && char == '?') {
5438 } else if (!stateOverride && char == '#') {
5441 } else if (char != EOF) {
5443 if (char != '/') continue;
5448 char == EOF || char == '/' ||
5449 (char == '\\' && isSpecial(url)) ||
5450 (!stateOverride && (char == '?' || char == '#'))
5452 if (isDoubleDot(buffer)) {
5453 shortenURLsPath(url);
5454 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5457 } else if (isSingleDot(buffer)) {
5458 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5462 if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5463 if (url.host) url.host = '';
5464 buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5466 url.path.push(buffer);
5469 if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5470 while (url.path.length > 1 && url.path[0] === '') {
5477 } else if (char == '#') {
5482 buffer += percentEncode(char, pathPercentEncodeSet);
5485 case CANNOT_BE_A_BASE_URL_PATH:
5489 } else if (char == '#') {
5492 } else if (char != EOF) {
5493 url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5497 if (!stateOverride && char == '#') {
5500 } else if (char != EOF) {
5501 if (char == "'" && isSpecial(url)) url.query += '%27';
5502 else if (char == '#') url.query += '%23';
5503 else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5507 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5515 // `URL` constructor
5516 // https://url.spec.whatwg.org/#url-class
5517 var URLConstructor = function URL(url /* , base */) {
5518 var that = anInstance(this, URLConstructor, 'URL');
5519 var base = arguments.length > 1 ? arguments[1] : undefined;
5520 var urlString = String(url);
5521 var state = setInternalState$1(that, { type: 'URL' });
5522 var baseState, failure;
5523 if (base !== undefined) {
5524 if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5526 failure = parseURL(baseState = {}, String(base));
5527 if (failure) throw TypeError(failure);
5530 failure = parseURL(state, urlString, null, baseState);
5531 if (failure) throw TypeError(failure);
5532 var searchParams = state.searchParams = new URLSearchParams$1();
5533 var searchParamsState = getInternalSearchParamsState(searchParams);
5534 searchParamsState.updateSearchParams(state.query);
5535 searchParamsState.updateURL = function () {
5536 state.query = String(searchParams) || null;
5539 that.href = serializeURL.call(that);
5540 that.origin = getOrigin.call(that);
5541 that.protocol = getProtocol.call(that);
5542 that.username = getUsername.call(that);
5543 that.password = getPassword.call(that);
5544 that.host = getHost.call(that);
5545 that.hostname = getHostname.call(that);
5546 that.port = getPort.call(that);
5547 that.pathname = getPathname.call(that);
5548 that.search = getSearch.call(that);
5549 that.searchParams = getSearchParams.call(that);
5550 that.hash = getHash.call(that);
5554 var URLPrototype = URLConstructor.prototype;
5556 var serializeURL = function () {
5557 var url = getInternalURLState(this);
5558 var scheme = url.scheme;
5559 var username = url.username;
5560 var password = url.password;
5561 var host = url.host;
5562 var port = url.port;
5563 var path = url.path;
5564 var query = url.query;
5565 var fragment = url.fragment;
5566 var output = scheme + ':';
5567 if (host !== null) {
5569 if (includesCredentials(url)) {
5570 output += username + (password ? ':' + password : '') + '@';
5572 output += serializeHost(host);
5573 if (port !== null) output += ':' + port;
5574 } else if (scheme == 'file') output += '//';
5575 output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5576 if (query !== null) output += '?' + query;
5577 if (fragment !== null) output += '#' + fragment;
5581 var getOrigin = function () {
5582 var url = getInternalURLState(this);
5583 var scheme = url.scheme;
5584 var port = url.port;
5585 if (scheme == 'blob') try {
5586 return new URLConstructor(scheme.path[0]).origin;
5590 if (scheme == 'file' || !isSpecial(url)) return 'null';
5591 return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5594 var getProtocol = function () {
5595 return getInternalURLState(this).scheme + ':';
5598 var getUsername = function () {
5599 return getInternalURLState(this).username;
5602 var getPassword = function () {
5603 return getInternalURLState(this).password;
5606 var getHost = function () {
5607 var url = getInternalURLState(this);
5608 var host = url.host;
5609 var port = url.port;
5610 return host === null ? ''
5611 : port === null ? serializeHost(host)
5612 : serializeHost(host) + ':' + port;
5615 var getHostname = function () {
5616 var host = getInternalURLState(this).host;
5617 return host === null ? '' : serializeHost(host);
5620 var getPort = function () {
5621 var port = getInternalURLState(this).port;
5622 return port === null ? '' : String(port);
5625 var getPathname = function () {
5626 var url = getInternalURLState(this);
5627 var path = url.path;
5628 return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5631 var getSearch = function () {
5632 var query = getInternalURLState(this).query;
5633 return query ? '?' + query : '';
5636 var getSearchParams = function () {
5637 return getInternalURLState(this).searchParams;
5640 var getHash = function () {
5641 var fragment = getInternalURLState(this).fragment;
5642 return fragment ? '#' + fragment : '';
5645 var accessorDescriptor = function (getter, setter) {
5646 return { get: getter, set: setter, configurable: true, enumerable: true };
5650 objectDefineProperties(URLPrototype, {
5651 // `URL.prototype.href` accessors pair
5652 // https://url.spec.whatwg.org/#dom-url-href
5653 href: accessorDescriptor(serializeURL, function (href) {
5654 var url = getInternalURLState(this);
5655 var urlString = String(href);
5656 var failure = parseURL(url, urlString);
5657 if (failure) throw TypeError(failure);
5658 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5660 // `URL.prototype.origin` getter
5661 // https://url.spec.whatwg.org/#dom-url-origin
5662 origin: accessorDescriptor(getOrigin),
5663 // `URL.prototype.protocol` accessors pair
5664 // https://url.spec.whatwg.org/#dom-url-protocol
5665 protocol: accessorDescriptor(getProtocol, function (protocol) {
5666 var url = getInternalURLState(this);
5667 parseURL(url, String(protocol) + ':', SCHEME_START);
5669 // `URL.prototype.username` accessors pair
5670 // https://url.spec.whatwg.org/#dom-url-username
5671 username: accessorDescriptor(getUsername, function (username) {
5672 var url = getInternalURLState(this);
5673 var codePoints = arrayFrom(String(username));
5674 if (cannotHaveUsernamePasswordPort(url)) return;
5676 for (var i = 0; i < codePoints.length; i++) {
5677 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5680 // `URL.prototype.password` accessors pair
5681 // https://url.spec.whatwg.org/#dom-url-password
5682 password: accessorDescriptor(getPassword, function (password) {
5683 var url = getInternalURLState(this);
5684 var codePoints = arrayFrom(String(password));
5685 if (cannotHaveUsernamePasswordPort(url)) return;
5687 for (var i = 0; i < codePoints.length; i++) {
5688 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5691 // `URL.prototype.host` accessors pair
5692 // https://url.spec.whatwg.org/#dom-url-host
5693 host: accessorDescriptor(getHost, function (host) {
5694 var url = getInternalURLState(this);
5695 if (url.cannotBeABaseURL) return;
5696 parseURL(url, String(host), HOST);
5698 // `URL.prototype.hostname` accessors pair
5699 // https://url.spec.whatwg.org/#dom-url-hostname
5700 hostname: accessorDescriptor(getHostname, function (hostname) {
5701 var url = getInternalURLState(this);
5702 if (url.cannotBeABaseURL) return;
5703 parseURL(url, String(hostname), HOSTNAME);
5705 // `URL.prototype.port` accessors pair
5706 // https://url.spec.whatwg.org/#dom-url-port
5707 port: accessorDescriptor(getPort, function (port) {
5708 var url = getInternalURLState(this);
5709 if (cannotHaveUsernamePasswordPort(url)) return;
5710 port = String(port);
5711 if (port == '') url.port = null;
5712 else parseURL(url, port, PORT);
5714 // `URL.prototype.pathname` accessors pair
5715 // https://url.spec.whatwg.org/#dom-url-pathname
5716 pathname: accessorDescriptor(getPathname, function (pathname) {
5717 var url = getInternalURLState(this);
5718 if (url.cannotBeABaseURL) return;
5720 parseURL(url, pathname + '', PATH_START);
5722 // `URL.prototype.search` accessors pair
5723 // https://url.spec.whatwg.org/#dom-url-search
5724 search: accessorDescriptor(getSearch, function (search) {
5725 var url = getInternalURLState(this);
5726 search = String(search);
5730 if ('?' == search.charAt(0)) search = search.slice(1);
5732 parseURL(url, search, QUERY);
5734 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5736 // `URL.prototype.searchParams` getter
5737 // https://url.spec.whatwg.org/#dom-url-searchparams
5738 searchParams: accessorDescriptor(getSearchParams),
5739 // `URL.prototype.hash` accessors pair
5740 // https://url.spec.whatwg.org/#dom-url-hash
5741 hash: accessorDescriptor(getHash, function (hash) {
5742 var url = getInternalURLState(this);
5743 hash = String(hash);
5745 url.fragment = null;
5748 if ('#' == hash.charAt(0)) hash = hash.slice(1);
5750 parseURL(url, hash, FRAGMENT);
5755 // `URL.prototype.toJSON` method
5756 // https://url.spec.whatwg.org/#dom-url-tojson
5757 redefine(URLPrototype, 'toJSON', function toJSON() {
5758 return serializeURL.call(this);
5759 }, { enumerable: true });
5761 // `URL.prototype.toString` method
5762 // https://url.spec.whatwg.org/#URL-stringification-behavior
5763 redefine(URLPrototype, 'toString', function toString() {
5764 return serializeURL.call(this);
5765 }, { enumerable: true });
5768 var nativeCreateObjectURL = NativeURL.createObjectURL;
5769 var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
5770 // `URL.createObjectURL` method
5771 // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
5772 // eslint-disable-next-line no-unused-vars -- required for `.length`
5773 if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
5774 return nativeCreateObjectURL.apply(NativeURL, arguments);
5776 // `URL.revokeObjectURL` method
5777 // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
5778 // eslint-disable-next-line no-unused-vars -- required for `.length`
5779 if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
5780 return nativeRevokeObjectURL.apply(NativeURL, arguments);
5784 setToStringTag(URLConstructor, 'URL');
5786 _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
5790 // `RegExp.prototype.flags` getter implementation
5791 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
5792 var regexpFlags = function () {
5793 var that = anObject(this);
5795 if (that.global) result += 'g';
5796 if (that.ignoreCase) result += 'i';
5797 if (that.multiline) result += 'm';
5798 if (that.dotAll) result += 's';
5799 if (that.unicode) result += 'u';
5800 if (that.sticky) result += 'y';
5804 var TO_STRING = 'toString';
5805 var RegExpPrototype$2 = RegExp.prototype;
5806 var nativeToString = RegExpPrototype$2[TO_STRING];
5808 var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
5809 // FF44- RegExp#toString has a wrong name
5810 var INCORRECT_NAME = nativeToString.name != TO_STRING;
5812 // `RegExp.prototype.toString` method
5813 // https://tc39.es/ecma262/#sec-regexp.prototype.tostring
5814 if (NOT_GENERIC || INCORRECT_NAME) {
5815 redefine(RegExp.prototype, TO_STRING, function toString() {
5816 var R = anObject(this);
5817 var p = String(R.source);
5819 var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype$2) ? regexpFlags.call(R) : rf);
5820 return '/' + p + '/' + f;
5821 }, { unsafe: true });
5824 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
5825 var RE = function (s, f) {
5826 return RegExp(s, f);
5829 var UNSUPPORTED_Y$3 = fails(function () {
5830 var re = RE('a', 'y');
5832 return re.exec('abcd') != null;
5835 var BROKEN_CARET = fails(function () {
5836 // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
5837 var re = RE('^r', 'gy');
5839 return re.exec('str') != null;
5842 var regexpStickyHelpers = {
5843 UNSUPPORTED_Y: UNSUPPORTED_Y$3,
5844 BROKEN_CARET: BROKEN_CARET
5847 var regexpUnsupportedDotAll = fails(function () {
5848 // babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError
5849 var re = RegExp('.', (typeof '').charAt(0));
5850 return !(re.dotAll && re.exec('\n') && re.flags === 's');
5853 var regexpUnsupportedNcg = fails(function () {
5854 // babel-minify transpiles RegExp('.', 'g') -> /./g and it causes SyntaxError
5855 var re = RegExp('(?<a>b)', (typeof '').charAt(5));
5856 return re.exec('b').groups.a !== 'b' ||
5857 'b'.replace(re, '$<a>c') !== 'bc';
5860 /* eslint-disable regexp/no-assertion-capturing-group, regexp/no-empty-group, regexp/no-lazy-ends -- testing */
5861 /* eslint-disable regexp/no-useless-quantifier -- testing */
5866 var getInternalState = internalState.get;
5870 var nativeExec = RegExp.prototype.exec;
5871 var nativeReplace = shared('native-string-replace', String.prototype.replace);
5873 var patchedExec = nativeExec;
5875 var UPDATES_LAST_INDEX_WRONG = (function () {
5878 nativeExec.call(re1, 'a');
5879 nativeExec.call(re2, 'a');
5880 return re1.lastIndex !== 0 || re2.lastIndex !== 0;
5883 var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
5885 // nonparticipating capturing group, copied from es5-shim's String#split patch.
5886 var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
5888 var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$2 || regexpUnsupportedDotAll || regexpUnsupportedNcg;
5891 // eslint-disable-next-line max-statements -- TODO
5892 patchedExec = function exec(str) {
5894 var state = getInternalState(re);
5895 var raw = state.raw;
5896 var result, reCopy, lastIndex, match, i, object, group;
5899 raw.lastIndex = re.lastIndex;
5900 result = patchedExec.call(raw, str);
5901 re.lastIndex = raw.lastIndex;
5905 var groups = state.groups;
5906 var sticky = UNSUPPORTED_Y$2 && re.sticky;
5907 var flags = regexpFlags.call(re);
5908 var source = re.source;
5913 flags = flags.replace('y', '');
5914 if (flags.indexOf('g') === -1) {
5918 strCopy = String(str).slice(re.lastIndex);
5919 // Support anchored sticky behavior.
5920 if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
5921 source = '(?: ' + source + ')';
5922 strCopy = ' ' + strCopy;
5925 // ^(? + rx + ) is needed, in combination with some str slicing, to
5926 // simulate the 'y' flag.
5927 reCopy = new RegExp('^(?:' + source + ')', flags);
5930 if (NPCG_INCLUDED) {
5931 reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
5933 if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
5935 match = nativeExec.call(sticky ? reCopy : re, strCopy);
5939 match.input = match.input.slice(charsAdded);
5940 match[0] = match[0].slice(charsAdded);
5941 match.index = re.lastIndex;
5942 re.lastIndex += match[0].length;
5943 } else re.lastIndex = 0;
5944 } else if (UPDATES_LAST_INDEX_WRONG && match) {
5945 re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
5947 if (NPCG_INCLUDED && match && match.length > 1) {
5948 // Fix browsers whose `exec` methods don't consistently return `undefined`
5949 // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
5950 nativeReplace.call(match[0], reCopy, function () {
5951 for (i = 1; i < arguments.length - 2; i++) {
5952 if (arguments[i] === undefined) match[i] = undefined;
5957 if (match && groups) {
5958 match.groups = object = objectCreate(null);
5959 for (i = 0; i < groups.length; i++) {
5961 object[group[0]] = match[group[1]];
5969 var regexpExec = patchedExec;
5971 // `RegExp.prototype.exec` method
5972 // https://tc39.es/ecma262/#sec-regexp.prototype.exec
5973 _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
5977 // TODO: Remove from `core-js@4` since it's moved to entry points
5985 var SPECIES = wellKnownSymbol('species');
5986 var RegExpPrototype$1 = RegExp.prototype;
5988 var fixRegexpWellKnownSymbolLogic = function (KEY, exec, FORCED, SHAM) {
5989 var SYMBOL = wellKnownSymbol(KEY);
5991 var DELEGATES_TO_SYMBOL = !fails(function () {
5992 // String methods call symbol-named RegEp methods
5994 O[SYMBOL] = function () { return 7; };
5995 return ''[KEY](O) != 7;
5998 var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
5999 // Symbol-named RegExp methods call .exec
6000 var execCalled = false;
6003 if (KEY === 'split') {
6004 // We can't use real regex here since it causes deoptimization
6005 // and serious performance degradation in V8
6006 // https://github.com/zloirock/core-js/issues/306
6008 // RegExp[@@split] doesn't call the regex's exec method, but first creates
6009 // a new one. We need to return the patched regex when creating the new one.
6010 re.constructor = {};
6011 re.constructor[SPECIES] = function () { return re; };
6013 re[SYMBOL] = /./[SYMBOL];
6016 re.exec = function () { execCalled = true; return null; };
6023 !DELEGATES_TO_SYMBOL ||
6024 !DELEGATES_TO_EXEC ||
6027 var nativeRegExpMethod = /./[SYMBOL];
6028 var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
6029 var $exec = regexp.exec;
6030 if ($exec === regexpExec || $exec === RegExpPrototype$1.exec) {
6031 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
6032 // The native String method already delegates to @@method (this
6033 // polyfilled function), leasing to infinite recursion.
6034 // We avoid it by directly calling the native @@method method.
6035 return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
6037 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
6039 return { done: false };
6042 redefine(String.prototype, KEY, methods[0]);
6043 redefine(RegExpPrototype$1, SYMBOL, methods[1]);
6046 if (SHAM) createNonEnumerableProperty(RegExpPrototype$1[SYMBOL], 'sham', true);
6049 var charAt = stringMultibyte.charAt;
6051 // `AdvanceStringIndex` abstract operation
6052 // https://tc39.es/ecma262/#sec-advancestringindex
6053 var advanceStringIndex = function (S, index, unicode) {
6054 return index + (unicode ? charAt(S, index).length : 1);
6057 var floor$1 = Math.floor;
6058 var replace = ''.replace;
6059 var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d{1,2}|<[^>]*>)/g;
6060 var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d{1,2})/g;
6062 // `GetSubstitution` abstract operation
6063 // https://tc39.es/ecma262/#sec-getsubstitution
6064 var getSubstitution = function (matched, str, position, captures, namedCaptures, replacement) {
6065 var tailPos = position + matched.length;
6066 var m = captures.length;
6067 var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
6068 if (namedCaptures !== undefined) {
6069 namedCaptures = toObject(namedCaptures);
6070 symbols = SUBSTITUTION_SYMBOLS;
6072 return replace.call(replacement, symbols, function (match, ch) {
6074 switch (ch.charAt(0)) {
6075 case '$': return '$';
6076 case '&': return matched;
6077 case '`': return str.slice(0, position);
6078 case "'": return str.slice(tailPos);
6080 capture = namedCaptures[ch.slice(1, -1)];
6084 if (n === 0) return match;
6086 var f = floor$1(n / 10);
6087 if (f === 0) return match;
6088 if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
6091 capture = captures[n - 1];
6093 return capture === undefined ? '' : capture;
6097 // `RegExpExec` abstract operation
6098 // https://tc39.es/ecma262/#sec-regexpexec
6099 var regexpExecAbstract = function (R, S) {
6101 if (typeof exec === 'function') {
6102 var result = exec.call(R, S);
6103 if (typeof result !== 'object') {
6104 throw TypeError('RegExp exec method returned something other than an Object or null');
6109 if (classofRaw(R) !== 'RegExp') {
6110 throw TypeError('RegExp#exec called on incompatible receiver');
6113 return regexpExec.call(R, S);
6116 var REPLACE = wellKnownSymbol('replace');
6117 var max$2 = Math.max;
6118 var min$5 = Math.min;
6120 var maybeToString = function (it) {
6121 return it === undefined ? it : String(it);
6124 // IE <= 11 replaces $0 with the whole match, as if it was $&
6125 // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
6126 var REPLACE_KEEPS_$0 = (function () {
6127 // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing
6128 return 'a'.replace(/./, '$0') === '$0';
6131 // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
6132 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
6134 return /./[REPLACE]('a', '$0') === '';
6139 var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
6141 re.exec = function () {
6143 result.groups = { a: '7' };
6146 return ''.replace(re, '$<a>') !== '7';
6150 fixRegexpWellKnownSymbolLogic('replace', function (_, nativeReplace, maybeCallNative) {
6151 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
6154 // `String.prototype.replace` method
6155 // https://tc39.es/ecma262/#sec-string.prototype.replace
6156 function replace(searchValue, replaceValue) {
6157 var O = requireObjectCoercible(this);
6158 var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
6159 return replacer !== undefined
6160 ? replacer.call(searchValue, O, replaceValue)
6161 : nativeReplace.call(String(O), searchValue, replaceValue);
6163 // `RegExp.prototype[@@replace]` method
6164 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
6165 function (string, replaceValue) {
6167 typeof replaceValue === 'string' &&
6168 replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1 &&
6169 replaceValue.indexOf('$<') === -1
6171 var res = maybeCallNative(nativeReplace, this, string, replaceValue);
6172 if (res.done) return res.value;
6175 var rx = anObject(this);
6176 var S = String(string);
6178 var functionalReplace = typeof replaceValue === 'function';
6179 if (!functionalReplace) replaceValue = String(replaceValue);
6181 var global = rx.global;
6183 var fullUnicode = rx.unicode;
6188 var result = regexpExecAbstract(rx, S);
6189 if (result === null) break;
6191 results.push(result);
6194 var matchStr = String(result[0]);
6195 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
6198 var accumulatedResult = '';
6199 var nextSourcePosition = 0;
6200 for (var i = 0; i < results.length; i++) {
6201 result = results[i];
6203 var matched = String(result[0]);
6204 var position = max$2(min$5(toInteger(result.index), S.length), 0);
6206 // NOTE: This is equivalent to
6207 // captures = result.slice(1).map(maybeToString)
6208 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
6209 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
6210 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
6211 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
6212 var namedCaptures = result.groups;
6213 if (functionalReplace) {
6214 var replacerArgs = [matched].concat(captures, position, S);
6215 if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
6216 var replacement = String(replaceValue.apply(undefined, replacerArgs));
6218 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
6220 if (position >= nextSourcePosition) {
6221 accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
6222 nextSourcePosition = position + matched.length;
6225 return accumulatedResult + S.slice(nextSourcePosition);
6228 }, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE);
6230 var MATCH$2 = wellKnownSymbol('match');
6232 // `IsRegExp` abstract operation
6233 // https://tc39.es/ecma262/#sec-isregexp
6234 var isRegexp = function (it) {
6236 return isObject$4(it) && ((isRegExp = it[MATCH$2]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
6239 var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y;
6240 var arrayPush = [].push;
6241 var min$4 = Math.min;
6242 var MAX_UINT32 = 0xFFFFFFFF;
6244 // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
6245 // Weex JS has frozen built-in prototypes, so use try / catch wrapper
6246 var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
6247 // eslint-disable-next-line regexp/no-empty-group -- required for testing
6249 var originalExec = re.exec;
6250 re.exec = function () { return originalExec.apply(this, arguments); };
6251 var result = 'ab'.split(re);
6252 return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
6256 fixRegexpWellKnownSymbolLogic('split', function (SPLIT, nativeSplit, maybeCallNative) {
6259 'abbc'.split(/(b)*/)[1] == 'c' ||
6260 // eslint-disable-next-line regexp/no-empty-group -- required for testing
6261 'test'.split(/(?:)/, -1).length != 4 ||
6262 'ab'.split(/(?:ab)*/).length != 2 ||
6263 '.'.split(/(.?)(.?)/).length != 4 ||
6264 // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing
6265 '.'.split(/()()/).length > 1 ||
6266 ''.split(/.?/).length
6268 // based on es5-shim implementation, need to rework it
6269 internalSplit = function (separator, limit) {
6270 var string = String(requireObjectCoercible(this));
6271 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6272 if (lim === 0) return [];
6273 if (separator === undefined) return [string];
6274 // If `separator` is not a regex, use native split
6275 if (!isRegexp(separator)) {
6276 return nativeSplit.call(string, separator, lim);
6279 var flags = (separator.ignoreCase ? 'i' : '') +
6280 (separator.multiline ? 'm' : '') +
6281 (separator.unicode ? 'u' : '') +
6282 (separator.sticky ? 'y' : '');
6283 var lastLastIndex = 0;
6284 // Make `global` and avoid `lastIndex` issues by working with a copy
6285 var separatorCopy = new RegExp(separator.source, flags + 'g');
6286 var match, lastIndex, lastLength;
6287 while (match = regexpExec.call(separatorCopy, string)) {
6288 lastIndex = separatorCopy.lastIndex;
6289 if (lastIndex > lastLastIndex) {
6290 output.push(string.slice(lastLastIndex, match.index));
6291 if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
6292 lastLength = match[0].length;
6293 lastLastIndex = lastIndex;
6294 if (output.length >= lim) break;
6296 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
6298 if (lastLastIndex === string.length) {
6299 if (lastLength || !separatorCopy.test('')) output.push('');
6300 } else output.push(string.slice(lastLastIndex));
6301 return output.length > lim ? output.slice(0, lim) : output;
6304 } else if ('0'.split(undefined, 0).length) {
6305 internalSplit = function (separator, limit) {
6306 return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
6308 } else internalSplit = nativeSplit;
6311 // `String.prototype.split` method
6312 // https://tc39.es/ecma262/#sec-string.prototype.split
6313 function split(separator, limit) {
6314 var O = requireObjectCoercible(this);
6315 var splitter = separator == undefined ? undefined : separator[SPLIT];
6316 return splitter !== undefined
6317 ? splitter.call(separator, O, limit)
6318 : internalSplit.call(String(O), separator, limit);
6320 // `RegExp.prototype[@@split]` method
6321 // https://tc39.es/ecma262/#sec-regexp.prototype-@@split
6323 // NOTE: This cannot be properly polyfilled in engines that don't support
6325 function (string, limit) {
6326 var res = maybeCallNative(internalSplit, this, string, limit, internalSplit !== nativeSplit);
6327 if (res.done) return res.value;
6329 var rx = anObject(this);
6330 var S = String(string);
6331 var C = speciesConstructor(rx, RegExp);
6333 var unicodeMatching = rx.unicode;
6334 var flags = (rx.ignoreCase ? 'i' : '') +
6335 (rx.multiline ? 'm' : '') +
6336 (rx.unicode ? 'u' : '') +
6337 (UNSUPPORTED_Y$1 ? 'g' : 'y');
6339 // ^(? + rx + ) is needed, in combination with some S slicing, to
6340 // simulate the 'y' flag.
6341 var splitter = new C(UNSUPPORTED_Y$1 ? '^(?:' + rx.source + ')' : rx, flags);
6342 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6343 if (lim === 0) return [];
6344 if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
6348 while (q < S.length) {
6349 splitter.lastIndex = UNSUPPORTED_Y$1 ? 0 : q;
6350 var z = regexpExecAbstract(splitter, UNSUPPORTED_Y$1 ? S.slice(q) : S);
6354 (e = min$4(toLength(splitter.lastIndex + (UNSUPPORTED_Y$1 ? q : 0)), S.length)) === p
6356 q = advanceStringIndex(S, q, unicodeMatching);
6358 A.push(S.slice(p, q));
6359 if (A.length === lim) return A;
6360 for (var i = 1; i <= z.length - 1; i++) {
6362 if (A.length === lim) return A;
6371 }, !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC, UNSUPPORTED_Y$1);
6373 // a string of all valid unicode whitespaces
6374 var whitespaces = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
6375 '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
6377 var whitespace = '[' + whitespaces + ']';
6378 var ltrim = RegExp('^' + whitespace + whitespace + '*');
6379 var rtrim$2 = RegExp(whitespace + whitespace + '*$');
6381 // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
6382 var createMethod$2 = function (TYPE) {
6383 return function ($this) {
6384 var string = String(requireObjectCoercible($this));
6385 if (TYPE & 1) string = string.replace(ltrim, '');
6386 if (TYPE & 2) string = string.replace(rtrim$2, '');
6392 // `String.prototype.{ trimLeft, trimStart }` methods
6393 // https://tc39.es/ecma262/#sec-string.prototype.trimstart
6394 start: createMethod$2(1),
6395 // `String.prototype.{ trimRight, trimEnd }` methods
6396 // https://tc39.es/ecma262/#sec-string.prototype.trimend
6397 end: createMethod$2(2),
6398 // `String.prototype.trim` method
6399 // https://tc39.es/ecma262/#sec-string.prototype.trim
6400 trim: createMethod$2(3)
6403 var non = '\u200B\u0085\u180E';
6405 // check that a method works with the correct list
6406 // of whitespaces and has a correct name
6407 var stringTrimForced = function (METHOD_NAME) {
6408 return fails(function () {
6409 return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
6413 var $trim = stringTrim.trim;
6416 // `String.prototype.trim` method
6417 // https://tc39.es/ecma262/#sec-string.prototype.trim
6418 _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
6419 trim: function trim() {
6424 var defineProperty$3 = objectDefineProperty.f;
6426 var FunctionPrototype = Function.prototype;
6427 var FunctionPrototypeToString = FunctionPrototype.toString;
6428 var nameRE = /^\s*function ([^ (]*)/;
6431 // Function instances `.name` property
6432 // https://tc39.es/ecma262/#sec-function-instances-name
6433 if (descriptors && !(NAME in FunctionPrototype)) {
6434 defineProperty$3(FunctionPrototype, NAME, {
6438 return FunctionPrototypeToString.call(this).match(nameRE)[1];
6446 // `Object.create` method
6447 // https://tc39.es/ecma262/#sec-object.create
6448 _export({ target: 'Object', stat: true, sham: !descriptors }, {
6449 create: objectCreate
6452 var slice$3 = [].slice;
6453 var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
6455 var wrap$1 = function (scheduler) {
6456 return function (handler, timeout /* , ...arguments */) {
6457 var boundArgs = arguments.length > 2;
6458 var args = boundArgs ? slice$3.call(arguments, 2) : undefined;
6459 return scheduler(boundArgs ? function () {
6460 // eslint-disable-next-line no-new-func -- spec requirement
6461 (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
6462 } : handler, timeout);
6466 // ie9- setTimeout & setInterval additional parameters fix
6467 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
6468 _export({ global: true, bind: true, forced: MSIE }, {
6469 // `setTimeout` method
6470 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
6471 setTimeout: wrap$1(global$2.setTimeout),
6472 // `setInterval` method
6473 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
6474 setInterval: wrap$1(global$2.setInterval)
6477 var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6479 searchParams: 'URLSearchParams' in global$1,
6480 iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6481 blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6489 formData: 'FormData' in global$1,
6490 arrayBuffer: 'ArrayBuffer' in global$1
6493 function isDataView(obj) {
6494 return obj && DataView.prototype.isPrototypeOf(obj);
6497 if (support.arrayBuffer) {
6498 var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6500 var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6501 return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6505 function normalizeName(name) {
6506 if (typeof name !== 'string') {
6507 name = String(name);
6510 if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6511 throw new TypeError('Invalid character in header field name: "' + name + '"');
6514 return name.toLowerCase();
6517 function normalizeValue(value) {
6518 if (typeof value !== 'string') {
6519 value = String(value);
6523 } // Build a destructive iterator for the value list
6526 function iteratorFor(items) {
6528 next: function next() {
6529 var value = items.shift();
6531 done: value === undefined,
6537 if (support.iterable) {
6538 iterator[Symbol.iterator] = function () {
6546 function Headers(headers) {
6549 if (headers instanceof Headers) {
6550 headers.forEach(function (value, name) {
6551 this.append(name, value);
6553 } else if (Array.isArray(headers)) {
6554 headers.forEach(function (header) {
6555 this.append(header[0], header[1]);
6557 } else if (headers) {
6558 Object.getOwnPropertyNames(headers).forEach(function (name) {
6559 this.append(name, headers[name]);
6564 Headers.prototype.append = function (name, value) {
6565 name = normalizeName(name);
6566 value = normalizeValue(value);
6567 var oldValue = this.map[name];
6568 this.map[name] = oldValue ? oldValue + ', ' + value : value;
6571 Headers.prototype['delete'] = function (name) {
6572 delete this.map[normalizeName(name)];
6575 Headers.prototype.get = function (name) {
6576 name = normalizeName(name);
6577 return this.has(name) ? this.map[name] : null;
6580 Headers.prototype.has = function (name) {
6581 return this.map.hasOwnProperty(normalizeName(name));
6584 Headers.prototype.set = function (name, value) {
6585 this.map[normalizeName(name)] = normalizeValue(value);
6588 Headers.prototype.forEach = function (callback, thisArg) {
6589 for (var name in this.map) {
6590 if (this.map.hasOwnProperty(name)) {
6591 callback.call(thisArg, this.map[name], name, this);
6596 Headers.prototype.keys = function () {
6598 this.forEach(function (value, name) {
6601 return iteratorFor(items);
6604 Headers.prototype.values = function () {
6606 this.forEach(function (value) {
6609 return iteratorFor(items);
6612 Headers.prototype.entries = function () {
6614 this.forEach(function (value, name) {
6615 items.push([name, value]);
6617 return iteratorFor(items);
6620 if (support.iterable) {
6621 Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
6624 function consumed(body) {
6625 if (body.bodyUsed) {
6626 return Promise.reject(new TypeError('Already read'));
6629 body.bodyUsed = true;
6632 function fileReaderReady(reader) {
6633 return new Promise(function (resolve, reject) {
6634 reader.onload = function () {
6635 resolve(reader.result);
6638 reader.onerror = function () {
6639 reject(reader.error);
6644 function readBlobAsArrayBuffer(blob) {
6645 var reader = new FileReader();
6646 var promise = fileReaderReady(reader);
6647 reader.readAsArrayBuffer(blob);
6651 function readBlobAsText(blob) {
6652 var reader = new FileReader();
6653 var promise = fileReaderReady(reader);
6654 reader.readAsText(blob);
6658 function readArrayBufferAsText(buf) {
6659 var view = new Uint8Array(buf);
6660 var chars = new Array(view.length);
6662 for (var i = 0; i < view.length; i++) {
6663 chars[i] = String.fromCharCode(view[i]);
6666 return chars.join('');
6669 function bufferClone(buf) {
6671 return buf.slice(0);
6673 var view = new Uint8Array(buf.byteLength);
6674 view.set(new Uint8Array(buf));
6680 this.bodyUsed = false;
6682 this._initBody = function (body) {
6684 fetch-mock wraps the Response object in an ES6 Proxy to
6685 provide useful test harness features such as flush. However, on
6686 ES5 browsers without fetch or Proxy support pollyfills must be used;
6687 the proxy-pollyfill is unable to proxy an attribute unless it exists
6688 on the object before the Proxy is created. This change ensures
6689 Response.bodyUsed exists on the instance, while maintaining the
6690 semantic of setting Request.bodyUsed in the constructor before
6691 _initBody is called.
6693 this.bodyUsed = this.bodyUsed;
6694 this._bodyInit = body;
6697 this._bodyText = '';
6698 } else if (typeof body === 'string') {
6699 this._bodyText = body;
6700 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6701 this._bodyBlob = body;
6702 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6703 this._bodyFormData = body;
6704 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6705 this._bodyText = body.toString();
6706 } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6707 this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6709 this._bodyInit = new Blob([this._bodyArrayBuffer]);
6710 } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6711 this._bodyArrayBuffer = bufferClone(body);
6713 this._bodyText = body = Object.prototype.toString.call(body);
6716 if (!this.headers.get('content-type')) {
6717 if (typeof body === 'string') {
6718 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6719 } else if (this._bodyBlob && this._bodyBlob.type) {
6720 this.headers.set('content-type', this._bodyBlob.type);
6721 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6722 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6728 this.blob = function () {
6729 var rejected = consumed(this);
6735 if (this._bodyBlob) {
6736 return Promise.resolve(this._bodyBlob);
6737 } else if (this._bodyArrayBuffer) {
6738 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6739 } else if (this._bodyFormData) {
6740 throw new Error('could not read FormData body as blob');
6742 return Promise.resolve(new Blob([this._bodyText]));
6746 this.arrayBuffer = function () {
6747 if (this._bodyArrayBuffer) {
6748 var isConsumed = consumed(this);
6754 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6755 return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6757 return Promise.resolve(this._bodyArrayBuffer);
6760 return this.blob().then(readBlobAsArrayBuffer);
6765 this.text = function () {
6766 var rejected = consumed(this);
6772 if (this._bodyBlob) {
6773 return readBlobAsText(this._bodyBlob);
6774 } else if (this._bodyArrayBuffer) {
6775 return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6776 } else if (this._bodyFormData) {
6777 throw new Error('could not read FormData body as text');
6779 return Promise.resolve(this._bodyText);
6783 if (support.formData) {
6784 this.formData = function () {
6785 return this.text().then(decode);
6789 this.json = function () {
6790 return this.text().then(JSON.parse);
6794 } // HTTP methods whose capitalization should be normalized
6797 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6799 function normalizeMethod(method) {
6800 var upcased = method.toUpperCase();
6801 return methods.indexOf(upcased) > -1 ? upcased : method;
6804 function Request(input, options) {
6805 if (!(this instanceof Request)) {
6806 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6809 options = options || {};
6810 var body = options.body;
6812 if (input instanceof Request) {
6813 if (input.bodyUsed) {
6814 throw new TypeError('Already read');
6817 this.url = input.url;
6818 this.credentials = input.credentials;
6820 if (!options.headers) {
6821 this.headers = new Headers(input.headers);
6824 this.method = input.method;
6825 this.mode = input.mode;
6826 this.signal = input.signal;
6828 if (!body && input._bodyInit != null) {
6829 body = input._bodyInit;
6830 input.bodyUsed = true;
6833 this.url = String(input);
6836 this.credentials = options.credentials || this.credentials || 'same-origin';
6838 if (options.headers || !this.headers) {
6839 this.headers = new Headers(options.headers);
6842 this.method = normalizeMethod(options.method || this.method || 'GET');
6843 this.mode = options.mode || this.mode || null;
6844 this.signal = options.signal || this.signal;
6845 this.referrer = null;
6847 if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6848 throw new TypeError('Body not allowed for GET or HEAD requests');
6851 this._initBody(body);
6853 if (this.method === 'GET' || this.method === 'HEAD') {
6854 if (options.cache === 'no-store' || options.cache === 'no-cache') {
6855 // Search for a '_' parameter in the query string
6856 var reParamSearch = /([?&])_=[^&]*/;
6858 if (reParamSearch.test(this.url)) {
6859 // If it already exists then set the value with the current time
6860 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6862 // Otherwise add a new '_' parameter to the end with the current time
6863 var reQueryString = /\?/;
6864 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6870 Request.prototype.clone = function () {
6871 return new Request(this, {
6872 body: this._bodyInit
6876 function decode(body) {
6877 var form = new FormData();
6878 body.trim().split('&').forEach(function (bytes) {
6880 var split = bytes.split('=');
6881 var name = split.shift().replace(/\+/g, ' ');
6882 var value = split.join('=').replace(/\+/g, ' ');
6883 form.append(decodeURIComponent(name), decodeURIComponent(value));
6889 function parseHeaders(rawHeaders) {
6890 var headers = new Headers(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6891 // https://tools.ietf.org/html/rfc7230#section-3.2
6893 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
6894 // https://github.com/github/fetch/issues/748
6895 // https://github.com/zloirock/core-js/issues/751
6897 preProcessedHeaders.split('\r').map(function (header) {
6898 return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
6899 }).forEach(function (line) {
6900 var parts = line.split(':');
6901 var key = parts.shift().trim();
6904 var value = parts.join(':').trim();
6905 headers.append(key, value);
6911 Body.call(Request.prototype);
6912 function Response(bodyInit, options) {
6913 if (!(this instanceof Response)) {
6914 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6921 this.type = 'default';
6922 this.status = options.status === undefined ? 200 : options.status;
6923 this.ok = this.status >= 200 && this.status < 300;
6924 this.statusText = options.statusText === undefined ? '' : '' + options.statusText;
6925 this.headers = new Headers(options.headers);
6926 this.url = options.url || '';
6928 this._initBody(bodyInit);
6930 Body.call(Response.prototype);
6932 Response.prototype.clone = function () {
6933 return new Response(this._bodyInit, {
6934 status: this.status,
6935 statusText: this.statusText,
6936 headers: new Headers(this.headers),
6941 Response.error = function () {
6942 var response = new Response(null, {
6946 response.type = 'error';
6950 var redirectStatuses = [301, 302, 303, 307, 308];
6952 Response.redirect = function (url, status) {
6953 if (redirectStatuses.indexOf(status) === -1) {
6954 throw new RangeError('Invalid status code');
6957 return new Response(null, {
6965 var DOMException$2 = global$1.DOMException;
6968 new DOMException$2();
6970 DOMException$2 = function DOMException(message, name) {
6971 this.message = message;
6973 var error = Error(message);
6974 this.stack = error.stack;
6977 DOMException$2.prototype = Object.create(Error.prototype);
6978 DOMException$2.prototype.constructor = DOMException$2;
6981 function fetch$1(input, init) {
6982 return new Promise(function (resolve, reject) {
6983 var request = new Request(input, init);
6985 if (request.signal && request.signal.aborted) {
6986 return reject(new DOMException$2('Aborted', 'AbortError'));
6989 var xhr = new XMLHttpRequest();
6991 function abortXhr() {
6995 xhr.onload = function () {
6998 statusText: xhr.statusText,
6999 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
7001 options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
7002 var body = 'response' in xhr ? xhr.response : xhr.responseText;
7003 setTimeout(function () {
7004 resolve(new Response(body, options));
7008 xhr.onerror = function () {
7009 setTimeout(function () {
7010 reject(new TypeError('Network request failed'));
7014 xhr.ontimeout = function () {
7015 setTimeout(function () {
7016 reject(new TypeError('Network request failed'));
7020 xhr.onabort = function () {
7021 setTimeout(function () {
7022 reject(new DOMException$2('Aborted', 'AbortError'));
7026 function fixUrl(url) {
7028 return url === '' && global$1.location.href ? global$1.location.href : url;
7034 xhr.open(request.method, fixUrl(request.url), true);
7036 if (request.credentials === 'include') {
7037 xhr.withCredentials = true;
7038 } else if (request.credentials === 'omit') {
7039 xhr.withCredentials = false;
7042 if ('responseType' in xhr) {
7044 xhr.responseType = 'blob';
7045 } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
7046 xhr.responseType = 'arraybuffer';
7050 if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers)) {
7051 Object.getOwnPropertyNames(init.headers).forEach(function (name) {
7052 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
7055 request.headers.forEach(function (value, name) {
7056 xhr.setRequestHeader(name, value);
7060 if (request.signal) {
7061 request.signal.addEventListener('abort', abortXhr);
7063 xhr.onreadystatechange = function () {
7064 // DONE (success or failure)
7065 if (xhr.readyState === 4) {
7066 request.signal.removeEventListener('abort', abortXhr);
7071 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
7074 fetch$1.polyfill = true;
7076 if (!global$1.fetch) {
7077 global$1.fetch = fetch$1;
7078 global$1.Headers = Headers;
7079 global$1.Request = Request;
7080 global$1.Response = Response;
7083 // `Object.defineProperty` method
7084 // https://tc39.es/ecma262/#sec-object.defineproperty
7085 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
7086 defineProperty: objectDefineProperty.f
7089 // `Object.setPrototypeOf` method
7090 // https://tc39.es/ecma262/#sec-object.setprototypeof
7091 _export({ target: 'Object', stat: true }, {
7092 setPrototypeOf: objectSetPrototypeOf
7095 var FAILS_ON_PRIMITIVES$3 = fails(function () { objectGetPrototypeOf(1); });
7097 // `Object.getPrototypeOf` method
7098 // https://tc39.es/ecma262/#sec-object.getprototypeof
7099 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3, sham: !correctPrototypeGetter }, {
7100 getPrototypeOf: function getPrototypeOf(it) {
7101 return objectGetPrototypeOf(toObject(it));
7105 var slice$2 = [].slice;
7108 var construct = function (C, argsLength, args) {
7109 if (!(argsLength in factories)) {
7110 for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
7111 // eslint-disable-next-line no-new-func -- we have no proper alternatives, IE8- only
7112 factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
7113 } return factories[argsLength](C, args);
7116 // `Function.prototype.bind` method implementation
7117 // https://tc39.es/ecma262/#sec-function.prototype.bind
7118 var functionBind = Function.bind || function bind(that /* , ...args */) {
7119 var fn = aFunction(this);
7120 var partArgs = slice$2.call(arguments, 1);
7121 var boundFunction = function bound(/* args... */) {
7122 var args = partArgs.concat(slice$2.call(arguments));
7123 return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
7125 if (isObject$4(fn.prototype)) boundFunction.prototype = fn.prototype;
7126 return boundFunction;
7129 var nativeConstruct = getBuiltIn('Reflect', 'construct');
7131 // `Reflect.construct` method
7132 // https://tc39.es/ecma262/#sec-reflect.construct
7133 // MS Edge supports only 2 arguments and argumentsList argument is optional
7134 // FF Nightly sets third argument as `new.target`, but does not create `this` from it
7135 var NEW_TARGET_BUG = fails(function () {
7136 function F() { /* empty */ }
7137 return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
7139 var ARGS_BUG = !fails(function () {
7140 nativeConstruct(function () { /* empty */ });
7142 var FORCED$a = NEW_TARGET_BUG || ARGS_BUG;
7144 _export({ target: 'Reflect', stat: true, forced: FORCED$a, sham: FORCED$a }, {
7145 construct: function construct(Target, args /* , newTarget */) {
7148 var newTarget = arguments.length < 3 ? Target : aFunction(arguments[2]);
7149 if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
7150 if (Target == newTarget) {
7151 // w/o altered newTarget, optimization for 0-4 arguments
7152 switch (args.length) {
7153 case 0: return new Target();
7154 case 1: return new Target(args[0]);
7155 case 2: return new Target(args[0], args[1]);
7156 case 3: return new Target(args[0], args[1], args[2]);
7157 case 4: return new Target(args[0], args[1], args[2], args[3]);
7159 // w/o altered newTarget, lot of arguments case
7161 $args.push.apply($args, args);
7162 return new (functionBind.apply(Target, $args))();
7164 // with altered newTarget, not support built-in constructors
7165 var proto = newTarget.prototype;
7166 var instance = objectCreate(isObject$4(proto) ? proto : Object.prototype);
7167 var result = Function.apply.call(Target, instance, args);
7168 return isObject$4(result) ? result : instance;
7172 // `Reflect.get` method
7173 // https://tc39.es/ecma262/#sec-reflect.get
7174 function get$3(target, propertyKey /* , receiver */) {
7175 var receiver = arguments.length < 3 ? target : arguments[2];
7176 var descriptor, prototype;
7177 if (anObject(target) === receiver) return target[propertyKey];
7178 if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has$1(descriptor, 'value')
7180 : descriptor.get === undefined
7182 : descriptor.get.call(receiver);
7183 if (isObject$4(prototype = objectGetPrototypeOf(target))) return get$3(prototype, propertyKey, receiver);
7186 _export({ target: 'Reflect', stat: true }, {
7190 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
7193 var FAILS_ON_PRIMITIVES$2 = fails(function () { nativeGetOwnPropertyDescriptor(1); });
7194 var FORCED$9 = !descriptors || FAILS_ON_PRIMITIVES$2;
7196 // `Object.getOwnPropertyDescriptor` method
7197 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
7198 _export({ target: 'Object', stat: true, forced: FORCED$9, sham: !descriptors }, {
7199 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
7200 return nativeGetOwnPropertyDescriptor(toIndexedObject(it), key);
7204 var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('splice');
7206 var max$1 = Math.max;
7207 var min$3 = Math.min;
7208 var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7209 var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
7211 // `Array.prototype.splice` method
7212 // https://tc39.es/ecma262/#sec-array.prototype.splice
7213 // with adding support of @@species
7214 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, {
7215 splice: function splice(start, deleteCount /* , ...items */) {
7216 var O = toObject(this);
7217 var len = toLength(O.length);
7218 var actualStart = toAbsoluteIndex(start, len);
7219 var argumentsLength = arguments.length;
7220 var insertCount, actualDeleteCount, A, k, from, to;
7221 if (argumentsLength === 0) {
7222 insertCount = actualDeleteCount = 0;
7223 } else if (argumentsLength === 1) {
7225 actualDeleteCount = len - actualStart;
7227 insertCount = argumentsLength - 2;
7228 actualDeleteCount = min$3(max$1(toInteger(deleteCount), 0), len - actualStart);
7230 if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER$1) {
7231 throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
7233 A = arraySpeciesCreate(O, actualDeleteCount);
7234 for (k = 0; k < actualDeleteCount; k++) {
7235 from = actualStart + k;
7236 if (from in O) createProperty(A, k, O[from]);
7238 A.length = actualDeleteCount;
7239 if (insertCount < actualDeleteCount) {
7240 for (k = actualStart; k < len - actualDeleteCount; k++) {
7241 from = k + actualDeleteCount;
7242 to = k + insertCount;
7243 if (from in O) O[to] = O[from];
7246 for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
7247 } else if (insertCount > actualDeleteCount) {
7248 for (k = len - actualDeleteCount; k > actualStart; k--) {
7249 from = k + actualDeleteCount - 1;
7250 to = k + insertCount - 1;
7251 if (from in O) O[to] = O[from];
7255 for (k = 0; k < insertCount; k++) {
7256 O[k + actualStart] = arguments[k + 2];
7258 O.length = len - actualDeleteCount + insertCount;
7263 // `Symbol.toStringTag` well-known symbol
7264 // https://tc39.es/ecma262/#sec-symbol.tostringtag
7265 defineWellKnownSymbol('toStringTag');
7267 // JSON[@@toStringTag] property
7268 // https://tc39.es/ecma262/#sec-json-@@tostringtag
7269 setToStringTag(global$2.JSON, 'JSON', true);
7271 // Math[@@toStringTag] property
7272 // https://tc39.es/ecma262/#sec-math-@@tostringtag
7273 setToStringTag(Math, 'Math', true);
7275 (function (factory) {
7279 function _classCallCheck(instance, Constructor) {
7280 if (!(instance instanceof Constructor)) {
7281 throw new TypeError("Cannot call a class as a function");
7285 function _defineProperties(target, props) {
7286 for (var i = 0; i < props.length; i++) {
7287 var descriptor = props[i];
7288 descriptor.enumerable = descriptor.enumerable || false;
7289 descriptor.configurable = true;
7290 if ("value" in descriptor) descriptor.writable = true;
7291 Object.defineProperty(target, descriptor.key, descriptor);
7295 function _createClass(Constructor, protoProps, staticProps) {
7296 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7297 if (staticProps) _defineProperties(Constructor, staticProps);
7301 function _inherits(subClass, superClass) {
7302 if (typeof superClass !== "function" && superClass !== null) {
7303 throw new TypeError("Super expression must either be null or a function");
7306 subClass.prototype = Object.create(superClass && superClass.prototype, {
7313 if (superClass) _setPrototypeOf(subClass, superClass);
7316 function _getPrototypeOf(o) {
7317 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7318 return o.__proto__ || Object.getPrototypeOf(o);
7320 return _getPrototypeOf(o);
7323 function _setPrototypeOf(o, p) {
7324 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7329 return _setPrototypeOf(o, p);
7332 function _isNativeReflectConstruct() {
7333 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7334 if (Reflect.construct.sham) return false;
7335 if (typeof Proxy === "function") return true;
7338 Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
7345 function _assertThisInitialized(self) {
7346 if (self === void 0) {
7347 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7353 function _possibleConstructorReturn(self, call) {
7354 if (call && (_typeof(call) === "object" || typeof call === "function")) {
7358 return _assertThisInitialized(self);
7361 function _createSuper(Derived) {
7362 var hasNativeReflectConstruct = _isNativeReflectConstruct();
7364 return function _createSuperInternal() {
7365 var Super = _getPrototypeOf(Derived),
7368 if (hasNativeReflectConstruct) {
7369 var NewTarget = _getPrototypeOf(this).constructor;
7371 result = Reflect.construct(Super, arguments, NewTarget);
7373 result = Super.apply(this, arguments);
7376 return _possibleConstructorReturn(this, result);
7380 function _superPropBase(object, property) {
7381 while (!Object.prototype.hasOwnProperty.call(object, property)) {
7382 object = _getPrototypeOf(object);
7383 if (object === null) break;
7389 function _get(target, property, receiver) {
7390 if (typeof Reflect !== "undefined" && Reflect.get) {
7393 _get = function _get(target, property, receiver) {
7394 var base = _superPropBase(target, property);
7397 var desc = Object.getOwnPropertyDescriptor(base, property);
7400 return desc.get.call(receiver);
7407 return _get(target, property, receiver || target);
7410 var Emitter = /*#__PURE__*/function () {
7411 function Emitter() {
7412 _classCallCheck(this, Emitter);
7414 Object.defineProperty(this, 'listeners', {
7421 _createClass(Emitter, [{
7422 key: "addEventListener",
7423 value: function addEventListener(type, callback, options) {
7424 if (!(type in this.listeners)) {
7425 this.listeners[type] = [];
7428 this.listeners[type].push({
7434 key: "removeEventListener",
7435 value: function removeEventListener(type, callback) {
7436 if (!(type in this.listeners)) {
7440 var stack = this.listeners[type];
7442 for (var i = 0, l = stack.length; i < l; i++) {
7443 if (stack[i].callback === callback) {
7450 key: "dispatchEvent",
7451 value: function dispatchEvent(event) {
7452 if (!(event.type in this.listeners)) {
7456 var stack = this.listeners[event.type];
7457 var stackToCall = stack.slice();
7459 for (var i = 0, l = stackToCall.length; i < l; i++) {
7460 var listener = stackToCall[i];
7463 listener.callback.call(this, event);
7465 Promise.resolve().then(function () {
7470 if (listener.options && listener.options.once) {
7471 this.removeEventListener(event.type, listener.callback);
7475 return !event.defaultPrevented;
7482 var AbortSignal = /*#__PURE__*/function (_Emitter) {
7483 _inherits(AbortSignal, _Emitter);
7485 var _super = _createSuper(AbortSignal);
7487 function AbortSignal() {
7490 _classCallCheck(this, AbortSignal);
7492 _this = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7493 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7494 // the parent constructor directly instead as a workaround. For general details, see babel bug:
7495 // https://github.com/babel/babel/issues/3041
7496 // This hack was added as a fix for the issue described here:
7497 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7499 if (!_this.listeners) {
7500 Emitter.call(_assertThisInitialized(_this));
7501 } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7502 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7505 Object.defineProperty(_assertThisInitialized(_this), 'aborted', {
7510 Object.defineProperty(_assertThisInitialized(_this), 'onabort', {
7518 _createClass(AbortSignal, [{
7520 value: function toString() {
7521 return '[object AbortSignal]';
7524 key: "dispatchEvent",
7525 value: function dispatchEvent(event) {
7526 if (event.type === 'abort') {
7527 this.aborted = true;
7529 if (typeof this.onabort === 'function') {
7530 this.onabort.call(this, event);
7534 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7541 var AbortController = /*#__PURE__*/function () {
7542 function AbortController() {
7543 _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7544 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7547 Object.defineProperty(this, 'signal', {
7548 value: new AbortSignal(),
7554 _createClass(AbortController, [{
7556 value: function abort() {
7560 event = new Event('abort');
7562 if (typeof document !== 'undefined') {
7563 if (!document.createEvent) {
7564 // For Internet Explorer 8:
7565 event = document.createEventObject();
7566 event.type = 'abort';
7568 // For Internet Explorer 11:
7569 event = document.createEvent('Event');
7570 event.initEvent('abort', false, false);
7573 // Fallback where document isn't available:
7582 this.signal.dispatchEvent(event);
7586 value: function toString() {
7587 return '[object AbortController]';
7591 return AbortController;
7594 if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7595 // These are necessary to make sure that we get correct output for:
7596 // Object.prototype.toString.call(new AbortController())
7597 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7598 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7601 function polyfillNeeded(self) {
7602 if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7603 console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7605 } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7606 // defining window.Request, and this polyfill need to work on top of unfetch
7607 // so the below feature detection needs the !self.AbortController part.
7608 // The Request.prototype check is also needed because Safari versions 11.1.2
7609 // up to and including 12.1.x has a window.AbortController present but still
7610 // does NOT correctly implement abortable fetch:
7611 // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7614 return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7617 * Note: the "fetch.Request" default value is available for fetch imported from
7618 * the "node-fetch" package and not in browsers. This is OK since browsers
7619 * will be importing umd-polyfill.js from that path "self" is passed the
7620 * decorator so the default value will not be used (because browsers that define
7621 * fetch also has Request). One quirky setup where self.fetch exists but
7622 * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7623 * on top of IE11; for this case the browser will try to use the fetch.Request
7624 * default value which in turn will be undefined but then then "if (Request)"
7625 * will ensure that you get a patched fetch but still no Request (as expected).
7626 * @param {fetch, Request = fetch.Request}
7627 * @returns {fetch: abortableFetch, Request: AbortableRequest}
7631 function abortableFetchDecorator(patchTargets) {
7632 if ('function' === typeof patchTargets) {
7638 var _patchTargets = patchTargets,
7639 fetch = _patchTargets.fetch,
7640 _patchTargets$Request = _patchTargets.Request,
7641 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7642 NativeAbortController = _patchTargets.AbortController,
7643 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7644 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7646 if (!polyfillNeeded({
7648 Request: NativeRequest,
7649 AbortController: NativeAbortController,
7650 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7658 var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7659 // defining window.Request, and this polyfill need to work on top of unfetch
7660 // hence we only patch it if it's available. Also we don't patch it if signal
7661 // is already available on the Request prototype because in this case support
7662 // is present and the patching below can cause a crash since it assigns to
7663 // request.signal which is technically a read-only property. This latter error
7664 // happens when you run the main5.js node-fetch example in the repo
7665 // "abortcontroller-polyfill-examples". The exact error is:
7666 // request.signal = init.signal;
7668 // TypeError: Cannot set property signal of #<Request> which has only a getter
7670 if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7671 Request = function Request(input, init) {
7674 if (init && init.signal) {
7675 signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7676 // been installed because if we're running on top of a browser with a
7677 // working native AbortController (i.e. the polyfill was installed due to
7678 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7679 // fake AbortSignal to the native fetch will trigger:
7680 // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7685 var request = new NativeRequest(input, init);
7688 Object.defineProperty(request, 'signal', {
7699 Request.prototype = NativeRequest.prototype;
7702 var realFetch = fetch;
7704 var abortableFetch = function abortableFetch(input, init) {
7705 var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7711 abortError = new DOMException('Aborted', 'AbortError');
7713 // IE 11 does not support calling the DOMException constructor, use a
7714 // regular error object on it instead.
7715 abortError = new Error('Aborted');
7716 abortError.name = 'AbortError';
7717 } // Return early if already aborted, thus avoiding making an HTTP request
7720 if (signal.aborted) {
7721 return Promise.reject(abortError);
7722 } // Turn an event into a promise, reject it once `abort` is dispatched
7725 var cancellation = new Promise(function (_, reject) {
7726 signal.addEventListener('abort', function () {
7727 return reject(abortError);
7733 if (init && init.signal) {
7734 // Never pass .signal to the native implementation when the polyfill has
7735 // been installed because if we're running on top of a browser with a
7736 // working native AbortController (i.e. the polyfill was installed due to
7737 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7738 // fake AbortSignal to the native fetch will trigger:
7739 // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7741 } // Return the fastest promise (don't need to wait for request to finish)
7744 return Promise.race([cancellation, realFetch(input, init)]);
7747 return realFetch(input, init);
7751 fetch: abortableFetch,
7757 if (!polyfillNeeded(self)) {
7762 console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7766 var _abortableFetch = abortableFetchDecorator(self),
7767 fetch = _abortableFetch.fetch,
7768 Request = _abortableFetch.Request;
7771 self.Request = Request;
7772 Object.defineProperty(self, 'AbortController', {
7776 value: AbortController
7778 Object.defineProperty(self, 'AbortSignal', {
7784 })(typeof self !== 'undefined' ? self : commonjsGlobal);
7787 function actionAddEntity(way) {
7788 return function (graph) {
7789 return graph.replace(way);
7793 var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7794 var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
7795 var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7797 // We can't use this feature detection in V8 since it causes
7798 // deoptimization and serious performance degradation
7799 // https://github.com/zloirock/core-js/issues/679
7800 var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7802 array[IS_CONCAT_SPREADABLE] = false;
7803 return array.concat()[0] !== array;
7806 var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7808 var isConcatSpreadable = function (O) {
7809 if (!isObject$4(O)) return false;
7810 var spreadable = O[IS_CONCAT_SPREADABLE];
7811 return spreadable !== undefined ? !!spreadable : isArray(O);
7814 var FORCED$8 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7816 // `Array.prototype.concat` method
7817 // https://tc39.es/ecma262/#sec-array.prototype.concat
7818 // with adding support of @@isConcatSpreadable and @@species
7819 _export({ target: 'Array', proto: true, forced: FORCED$8 }, {
7820 // eslint-disable-next-line no-unused-vars -- required for `.length`
7821 concat: function concat(arg) {
7822 var O = toObject(this);
7823 var A = arraySpeciesCreate(O, 0);
7825 var i, k, length, len, E;
7826 for (i = -1, length = arguments.length; i < length; i++) {
7827 E = i === -1 ? O : arguments[i];
7828 if (isConcatSpreadable(E)) {
7829 len = toLength(E.length);
7830 if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7831 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7833 if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7834 createProperty(A, n++, E);
7842 // `Object.assign` method
7843 // https://tc39.es/ecma262/#sec-object.assign
7844 // eslint-disable-next-line es/no-object-assign -- required for testing
7845 _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7846 assign: objectAssign
7849 var $filter = arrayIteration.filter;
7852 var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('filter');
7854 // `Array.prototype.filter` method
7855 // https://tc39.es/ecma262/#sec-array.prototype.filter
7856 // with adding support of @@species
7857 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, {
7858 filter: function filter(callbackfn /* , thisArg */) {
7859 return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7863 var FAILS_ON_PRIMITIVES$1 = fails(function () { objectKeys(1); });
7865 // `Object.keys` method
7866 // https://tc39.es/ecma262/#sec-object.keys
7867 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1 }, {
7868 keys: function keys(it) {
7869 return objectKeys(toObject(it));
7873 var nativeReverse = [].reverse;
7874 var test$1 = [1, 2];
7876 // `Array.prototype.reverse` method
7877 // https://tc39.es/ecma262/#sec-array.prototype.reverse
7878 // fix for Safari 12.0 bug
7879 // https://bugs.webkit.org/show_bug.cgi?id=188794
7880 _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7881 reverse: function reverse() {
7882 // eslint-disable-next-line no-self-assign -- dirty hack
7883 if (isArray(this)) this.length = this.length;
7884 return nativeReverse.call(this);
7888 var trim$4 = stringTrim.trim;
7891 var $parseFloat = global$2.parseFloat;
7892 var FORCED$7 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7894 // `parseFloat` method
7895 // https://tc39.es/ecma262/#sec-parsefloat-string
7896 var numberParseFloat = FORCED$7 ? function parseFloat(string) {
7897 var trimmedString = trim$4(String(string));
7898 var result = $parseFloat(trimmedString);
7899 return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7902 // `parseFloat` method
7903 // https://tc39.es/ecma262/#sec-parsefloat-string
7904 _export({ global: true, forced: parseFloat != numberParseFloat }, {
7905 parseFloat: numberParseFloat
7909 Order the nodes of a way in reverse order and reverse any direction dependent tags
7910 other than `oneway`. (We assume that correcting a backwards oneway is the primary
7911 reason for reversing a way.)
7913 In addition, numeric-valued `incline` tags are negated.
7915 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7916 or adjusted tags that don't seem to be used in practice were omitted.
7919 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7920 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7921 http://wiki.openstreetmap.org/wiki/Key:incline
7922 http://wiki.openstreetmap.org/wiki/Route#Members
7923 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7924 http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7925 http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7927 function actionReverse(entityID, options) {
7928 var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7929 var numeric = /^([+\-]?)(?=[\d.])/;
7930 var directionKey = /direction$/;
7931 var turn_lanes = /^turn:lanes:?/;
7932 var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7933 var valueReplacements = {
7938 forward: 'backward',
7939 backward: 'forward',
7940 forwards: 'backward',
7941 backwards: 'forward'
7943 var roleReplacements = {
7944 forward: 'backward',
7945 backward: 'forward',
7946 forwards: 'backward',
7947 backwards: 'forward'
7949 var onewayReplacements = {
7954 var compassReplacements = {
7973 function reverseKey(key) {
7974 for (var i = 0; i < keyReplacements.length; ++i) {
7975 var replacement = keyReplacements[i];
7977 if (replacement[0].test(key)) {
7978 return key.replace(replacement[0], replacement[1]);
7985 function reverseValue(key, value, includeAbsolute) {
7986 if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7988 if (turn_lanes.test(key)) {
7990 } else if (key === 'incline' && numeric.test(value)) {
7991 return value.replace(numeric, function (_, sign) {
7992 return sign === '-' ? '' : '-';
7994 } else if (options && options.reverseOneway && key === 'oneway') {
7995 return onewayReplacements[value] || value;
7996 } else if (includeAbsolute && directionKey.test(key)) {
7997 if (compassReplacements[value]) return compassReplacements[value];
7998 var degrees = parseFloat(value);
8000 if (typeof degrees === 'number' && !isNaN(degrees)) {
8001 if (degrees < 180) {
8007 return degrees.toString();
8011 return valueReplacements[value] || value;
8012 } // Reverse the direction of tags attached to the nodes - #3076
8015 function reverseNodeTags(graph, nodeIDs) {
8016 for (var i = 0; i < nodeIDs.length; i++) {
8017 var node = graph.hasEntity(nodeIDs[i]);
8018 if (!node || !Object.keys(node.tags).length) continue;
8021 for (var key in node.tags) {
8022 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
8025 graph = graph.replace(node.update({
8033 function reverseWay(graph, way) {
8034 var nodes = way.nodes.slice().reverse();
8038 for (var key in way.tags) {
8039 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
8042 graph.parentRelations(way).forEach(function (relation) {
8043 relation.members.forEach(function (member, index) {
8044 if (member.id === way.id && (role = roleReplacements[member.role])) {
8045 relation = relation.updateMember({
8048 graph = graph.replace(relation);
8051 }); // Reverse any associated directions on nodes on the way and then replace
8052 // the way itself with the reversed node ids and updated way tags
8054 return reverseNodeTags(graph, nodes).replace(way.update({
8060 var action = function action(graph) {
8061 var entity = graph.entity(entityID);
8063 if (entity.type === 'way') {
8064 return reverseWay(graph, entity);
8067 return reverseNodeTags(graph, [entityID]);
8070 action.disabled = function (graph) {
8071 var entity = graph.hasEntity(entityID);
8072 if (!entity || entity.type === 'way') return false;
8074 for (var key in entity.tags) {
8075 var value = entity.tags[key];
8077 if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
8082 return 'nondirectional_node';
8085 action.entityID = function () {
8092 function osmIsInterestingTag(key) {
8093 return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
8094 key.indexOf('tiger:') !== 0;
8096 var osmAreaKeys = {};
8097 function osmSetAreaKeys(value) {
8098 osmAreaKeys = value;
8099 } // returns an object with the tag from `tags` that implies an area geometry, if any
8101 function osmTagSuggestingArea(tags) {
8102 if (tags.area === 'yes') return {
8105 if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
8106 // are a few exceptions that should be treated as areas, even in the
8107 // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
8122 var returnTags = {};
8124 for (var key in tags) {
8125 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
8126 returnTags[key] = tags[key];
8130 if (key in lineKeys && tags[key] in lineKeys[key]) {
8131 returnTags[key] = tags[key];
8137 } // Tags that indicate a node can be a standalone point
8138 // e.g. { amenity: { bar: true, parking: true, ... } ... }
8140 var osmPointTags = {};
8141 function osmSetPointTags(value) {
8142 osmPointTags = value;
8143 } // Tags that indicate a node can be part of a way
8144 // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
8146 var osmVertexTags = {};
8147 function osmSetVertexTags(value) {
8148 osmVertexTags = value;
8150 function osmNodeGeometriesForTags(nodeTags) {
8151 var geometries = {};
8153 for (var key in nodeTags) {
8154 if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
8155 geometries.point = true;
8158 if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
8159 geometries.vertex = true;
8160 } // break early if both are already supported
8163 if (geometries.point && geometries.vertex) break;
8168 var osmOneWayTags = {
8173 'magic_carpet': true,
8188 'goods_conveyor': true,
8189 'piste:halfpipe': true
8203 'tidal_channel': true
8205 }; // solid and smooth surfaces akin to the assumed default road surface in OSM
8207 var osmPavedTags = {
8212 'concrete:lanes': true,
8213 'concrete:plates': true
8218 }; // solid, if somewhat uncommon surfaces with a high range of smoothness
8220 var osmSemipavedTags = {
8222 'cobblestone': true,
8223 'cobblestone:flattened': true,
8224 'unhewn_cobblestone': true,
8226 'paving_stones': true,
8231 var osmRightSideIsInsideTags = {
8234 'coastline': 'coastline'
8237 'retaining_wall': true,
8248 }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8249 // (does not include `raceway`)
8251 var osmRoutableHighwayTagValues = {
8258 motorway_link: true,
8261 secondary_link: true,
8262 tertiary_link: true,
8267 living_street: true,
8276 }; // "highway" tag values that generally do not allow motor vehicles
8278 var osmPathHighwayTagValues = {
8286 }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8288 var osmRailwayTrackTagValues = {
8299 }; // "waterway" tag values for line features representing water flow
8301 var osmFlowingWaterwayTagValues = {
8311 var trim$3 = stringTrim.trim;
8314 var $parseInt = global$2.parseInt;
8315 var hex$2 = /^[+-]?0[Xx]/;
8316 var FORCED$6 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8318 // `parseInt` method
8319 // https://tc39.es/ecma262/#sec-parseint-string-radix
8320 var numberParseInt = FORCED$6 ? function parseInt(string, radix) {
8321 var S = trim$3(String(string));
8322 return $parseInt(S, (radix >>> 0) || (hex$2.test(S) ? 16 : 10));
8325 // `parseInt` method
8326 // https://tc39.es/ecma262/#sec-parseint-string-radix
8327 _export({ global: true, forced: parseInt != numberParseInt }, {
8328 parseInt: numberParseInt
8331 var freezing = !fails(function () {
8332 // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing
8333 return Object.isExtensible(Object.preventExtensions({}));
8336 var internalMetadata = createCommonjsModule(function (module) {
8337 var defineProperty = objectDefineProperty.f;
8341 var METADATA = uid('meta');
8344 // eslint-disable-next-line es/no-object-isextensible -- safe
8345 var isExtensible = Object.isExtensible || function () {
8349 var setMetadata = function (it) {
8350 defineProperty(it, METADATA, { value: {
8351 objectID: 'O' + id++, // object ID
8352 weakData: {} // weak collections IDs
8356 var fastKey = function (it, create) {
8357 // return a primitive with prefix
8358 if (!isObject$4(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8359 if (!has$1(it, METADATA)) {
8360 // can't set metadata to uncaught frozen object
8361 if (!isExtensible(it)) return 'F';
8362 // not necessary to add metadata
8363 if (!create) return 'E';
8364 // add missing metadata
8367 } return it[METADATA].objectID;
8370 var getWeakData = function (it, create) {
8371 if (!has$1(it, METADATA)) {
8372 // can't set metadata to uncaught frozen object
8373 if (!isExtensible(it)) return true;
8374 // not necessary to add metadata
8375 if (!create) return false;
8376 // add missing metadata
8378 // return the store of weak collections IDs
8379 } return it[METADATA].weakData;
8382 // add metadata on freeze-family methods calling
8383 var onFreeze = function (it) {
8384 if (freezing && meta.REQUIRED && isExtensible(it) && !has$1(it, METADATA)) setMetadata(it);
8388 var meta = module.exports = {
8391 getWeakData: getWeakData,
8395 hiddenKeys$1[METADATA] = true;
8398 var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8399 var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8400 var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8401 var ADDER = IS_MAP ? 'set' : 'add';
8402 var NativeConstructor = global$2[CONSTRUCTOR_NAME];
8403 var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8404 var Constructor = NativeConstructor;
8407 var fixMethod = function (KEY) {
8408 var nativeMethod = NativePrototype[KEY];
8409 redefine(NativePrototype, KEY,
8410 KEY == 'add' ? function add(value) {
8411 nativeMethod.call(this, value === 0 ? 0 : value);
8413 } : KEY == 'delete' ? function (key) {
8414 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8415 } : KEY == 'get' ? function get(key) {
8416 return IS_WEAK && !isObject$4(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8417 } : KEY == 'has' ? function has(key) {
8418 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8419 } : function set(key, value) {
8420 nativeMethod.call(this, key === 0 ? 0 : key, value);
8426 var REPLACE = isForced_1(
8428 typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8429 new NativeConstructor().entries().next();
8434 // create collection constructor
8435 Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8436 internalMetadata.REQUIRED = true;
8437 } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8438 var instance = new Constructor();
8439 // early implementations not supports chaining
8440 var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8441 // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8442 var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8443 // most early implementations doesn't supports iterables, most modern - not close it correctly
8444 // eslint-disable-next-line no-new -- required for testing
8445 var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8446 // for early implementations -0 and +0 not the same
8447 var BUGGY_ZERO = !IS_WEAK && fails(function () {
8448 // V8 ~ Chromium 42- fails only with 5+ elements
8449 var $instance = new NativeConstructor();
8451 while (index--) $instance[ADDER](index, index);
8452 return !$instance.has(-0);
8455 if (!ACCEPT_ITERABLES) {
8456 Constructor = wrapper(function (dummy, iterable) {
8457 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8458 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8459 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8462 Constructor.prototype = NativePrototype;
8463 NativePrototype.constructor = Constructor;
8466 if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8467 fixMethod('delete');
8469 IS_MAP && fixMethod('get');
8472 if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8474 // weak collections should not contains .clear method
8475 if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8478 exported[CONSTRUCTOR_NAME] = Constructor;
8479 _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8481 setToStringTag(Constructor, CONSTRUCTOR_NAME);
8483 if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8488 var defineProperty$2 = objectDefineProperty.f;
8497 var fastKey = internalMetadata.fastKey;
8500 var setInternalState = internalState.set;
8501 var internalStateGetterFor = internalState.getterFor;
8503 var collectionStrong = {
8504 getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8505 var C = wrapper(function (that, iterable) {
8506 anInstance(that, C, CONSTRUCTOR_NAME);
8507 setInternalState(that, {
8508 type: CONSTRUCTOR_NAME,
8509 index: objectCreate(null),
8514 if (!descriptors) that.size = 0;
8515 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8518 var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8520 var define = function (that, key, value) {
8521 var state = getInternalState(that);
8522 var entry = getEntry(that, key);
8523 var previous, index;
8524 // change existing entry
8526 entry.value = value;
8529 state.last = entry = {
8530 index: index = fastKey(key, true),
8533 previous: previous = state.last,
8537 if (!state.first) state.first = entry;
8538 if (previous) previous.next = entry;
8539 if (descriptors) state.size++;
8542 if (index !== 'F') state.index[index] = entry;
8546 var getEntry = function (that, key) {
8547 var state = getInternalState(that);
8549 var index = fastKey(key);
8551 if (index !== 'F') return state.index[index];
8552 // frozen object case
8553 for (entry = state.first; entry; entry = entry.next) {
8554 if (entry.key == key) return entry;
8558 redefineAll(C.prototype, {
8559 // `{ Map, Set }.prototype.clear()` methods
8560 // https://tc39.es/ecma262/#sec-map.prototype.clear
8561 // https://tc39.es/ecma262/#sec-set.prototype.clear
8562 clear: function clear() {
8564 var state = getInternalState(that);
8565 var data = state.index;
8566 var entry = state.first;
8568 entry.removed = true;
8569 if (entry.previous) entry.previous = entry.previous.next = undefined;
8570 delete data[entry.index];
8573 state.first = state.last = undefined;
8574 if (descriptors) state.size = 0;
8577 // `{ Map, Set }.prototype.delete(key)` methods
8578 // https://tc39.es/ecma262/#sec-map.prototype.delete
8579 // https://tc39.es/ecma262/#sec-set.prototype.delete
8580 'delete': function (key) {
8582 var state = getInternalState(that);
8583 var entry = getEntry(that, key);
8585 var next = entry.next;
8586 var prev = entry.previous;
8587 delete state.index[entry.index];
8588 entry.removed = true;
8589 if (prev) prev.next = next;
8590 if (next) next.previous = prev;
8591 if (state.first == entry) state.first = next;
8592 if (state.last == entry) state.last = prev;
8593 if (descriptors) state.size--;
8597 // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods
8598 // https://tc39.es/ecma262/#sec-map.prototype.foreach
8599 // https://tc39.es/ecma262/#sec-set.prototype.foreach
8600 forEach: function forEach(callbackfn /* , that = undefined */) {
8601 var state = getInternalState(this);
8602 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8604 while (entry = entry ? entry.next : state.first) {
8605 boundFunction(entry.value, entry.key, this);
8606 // revert to the last existing entry
8607 while (entry && entry.removed) entry = entry.previous;
8610 // `{ Map, Set}.prototype.has(key)` methods
8611 // https://tc39.es/ecma262/#sec-map.prototype.has
8612 // https://tc39.es/ecma262/#sec-set.prototype.has
8613 has: function has(key) {
8614 return !!getEntry(this, key);
8618 redefineAll(C.prototype, IS_MAP ? {
8619 // `Map.prototype.get(key)` method
8620 // https://tc39.es/ecma262/#sec-map.prototype.get
8621 get: function get(key) {
8622 var entry = getEntry(this, key);
8623 return entry && entry.value;
8625 // `Map.prototype.set(key, value)` method
8626 // https://tc39.es/ecma262/#sec-map.prototype.set
8627 set: function set(key, value) {
8628 return define(this, key === 0 ? 0 : key, value);
8631 // `Set.prototype.add(value)` method
8632 // https://tc39.es/ecma262/#sec-set.prototype.add
8633 add: function add(value) {
8634 return define(this, value = value === 0 ? 0 : value, value);
8637 if (descriptors) defineProperty$2(C.prototype, 'size', {
8639 return getInternalState(this).size;
8644 setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8645 var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8646 var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8647 var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8648 // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods
8649 // https://tc39.es/ecma262/#sec-map.prototype.entries
8650 // https://tc39.es/ecma262/#sec-map.prototype.keys
8651 // https://tc39.es/ecma262/#sec-map.prototype.values
8652 // https://tc39.es/ecma262/#sec-map.prototype-@@iterator
8653 // https://tc39.es/ecma262/#sec-set.prototype.entries
8654 // https://tc39.es/ecma262/#sec-set.prototype.keys
8655 // https://tc39.es/ecma262/#sec-set.prototype.values
8656 // https://tc39.es/ecma262/#sec-set.prototype-@@iterator
8657 defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8658 setInternalState(this, {
8659 type: ITERATOR_NAME,
8661 state: getInternalCollectionState(iterated),
8666 var state = getInternalIteratorState(this);
8667 var kind = state.kind;
8668 var entry = state.last;
8669 // revert to the last existing entry
8670 while (entry && entry.removed) entry = entry.previous;
8672 if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8673 // or finish the iteration
8674 state.target = undefined;
8675 return { value: undefined, done: true };
8677 // return step by kind
8678 if (kind == 'keys') return { value: entry.key, done: false };
8679 if (kind == 'values') return { value: entry.value, done: false };
8680 return { value: [entry.key, entry.value], done: false };
8681 }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8683 // `{ Map, Set }.prototype[@@species]` accessors
8684 // https://tc39.es/ecma262/#sec-get-map-@@species
8685 // https://tc39.es/ecma262/#sec-get-set-@@species
8686 setSpecies(CONSTRUCTOR_NAME);
8690 // `Set` constructor
8691 // https://tc39.es/ecma262/#sec-set-objects
8692 collection('Set', function (init) {
8693 return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8694 }, collectionStrong);
8696 function d3_ascending (a, b) {
8697 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8700 function d3_bisector (f) {
8704 if (f.length === 1) {
8705 delta = function delta(d, x) {
8709 compare = ascendingComparator(f);
8712 function left(a, x, lo, hi) {
8713 if (lo == null) lo = 0;
8714 if (hi == null) hi = a.length;
8717 var mid = lo + hi >>> 1;
8718 if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8724 function right(a, x, lo, hi) {
8725 if (lo == null) lo = 0;
8726 if (hi == null) hi = a.length;
8729 var mid = lo + hi >>> 1;
8730 if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8736 function center(a, x, lo, hi) {
8737 if (lo == null) lo = 0;
8738 if (hi == null) hi = a.length;
8739 var i = left(a, x, lo, hi - 1);
8740 return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8750 function ascendingComparator(f) {
8751 return function (d, x) {
8752 return d3_ascending(f(d), x);
8756 // `Symbol.asyncIterator` well-known symbol
8757 // https://tc39.es/ecma262/#sec-symbol.asynciterator
8758 defineWellKnownSymbol('asyncIterator');
8760 createCommonjsModule(function (module) {
8761 var runtime = function (exports) {
8763 var Op = Object.prototype;
8764 var hasOwn = Op.hasOwnProperty;
8765 var undefined$1; // More compressible than void 0.
8767 var $Symbol = typeof Symbol === "function" ? Symbol : {};
8768 var iteratorSymbol = $Symbol.iterator || "@@iterator";
8769 var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8770 var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8772 function define(obj, key, value) {
8773 Object.defineProperty(obj, key, {
8783 // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8786 define = function define(obj, key, value) {
8787 return obj[key] = value;
8791 function wrap(innerFn, outerFn, self, tryLocsList) {
8792 // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8793 var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8794 var generator = Object.create(protoGenerator.prototype);
8795 var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8796 // .throw, and .return methods.
8798 generator._invoke = makeInvokeMethod(innerFn, self, context);
8802 exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8803 // record like context.tryEntries[i].completion. This interface could
8804 // have been (and was previously) designed to take a closure to be
8805 // invoked without arguments, but in all the cases we care about we
8806 // already have an existing method we want to call, so there's no need
8807 // to create a new function object. We can even get away with assuming
8808 // the method takes exactly one argument, since that happens to be true
8809 // in every case, so we don't have to touch the arguments object. The
8810 // only additional allocation required is the completion record, which
8811 // has a stable shape and so hopefully should be cheap to allocate.
8813 function tryCatch(fn, obj, arg) {
8817 arg: fn.call(obj, arg)
8827 var GenStateSuspendedStart = "suspendedStart";
8828 var GenStateSuspendedYield = "suspendedYield";
8829 var GenStateExecuting = "executing";
8830 var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8831 // breaking out of the dispatch switch statement.
8833 var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8834 // .constructor.prototype properties for functions that return Generator
8835 // objects. For full spec compliance, you may wish to configure your
8836 // minifier not to mangle the names of these two functions.
8838 function Generator() {}
8840 function GeneratorFunction() {}
8842 function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8843 // don't natively support it.
8846 var IteratorPrototype = {};
8848 IteratorPrototype[iteratorSymbol] = function () {
8852 var getProto = Object.getPrototypeOf;
8853 var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8855 if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8856 // This environment has a native %IteratorPrototype%; use it instead
8858 IteratorPrototype = NativeIteratorPrototype;
8861 var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8862 GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8863 GeneratorFunctionPrototype.constructor = GeneratorFunction;
8864 GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8865 // Iterator interface in terms of a single ._invoke method.
8867 function defineIteratorMethods(prototype) {
8868 ["next", "throw", "return"].forEach(function (method) {
8869 define(prototype, method, function (arg) {
8870 return this._invoke(method, arg);
8875 exports.isGeneratorFunction = function (genFun) {
8876 var ctor = typeof genFun === "function" && genFun.constructor;
8877 return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8878 // do is to check its .name property.
8879 (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8882 exports.mark = function (genFun) {
8883 if (Object.setPrototypeOf) {
8884 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8886 genFun.__proto__ = GeneratorFunctionPrototype;
8887 define(genFun, toStringTagSymbol, "GeneratorFunction");
8890 genFun.prototype = Object.create(Gp);
8892 }; // Within the body of any async function, `await x` is transformed to
8893 // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8894 // `hasOwn.call(value, "__await")` to determine if the yielded value is
8895 // meant to be awaited.
8898 exports.awrap = function (arg) {
8904 function AsyncIterator(generator, PromiseImpl) {
8905 function invoke(method, arg, resolve, reject) {
8906 var record = tryCatch(generator[method], generator, arg);
8908 if (record.type === "throw") {
8911 var result = record.arg;
8912 var value = result.value;
8914 if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8915 return PromiseImpl.resolve(value.__await).then(function (value) {
8916 invoke("next", value, resolve, reject);
8918 invoke("throw", err, resolve, reject);
8922 return PromiseImpl.resolve(value).then(function (unwrapped) {
8923 // When a yielded Promise is resolved, its final value becomes
8924 // the .value of the Promise<{value,done}> result for the
8925 // current iteration.
8926 result.value = unwrapped;
8928 }, function (error) {
8929 // If a rejected Promise was yielded, throw the rejection back
8930 // into the async generator function so it can be handled there.
8931 return invoke("throw", error, resolve, reject);
8936 var previousPromise;
8938 function enqueue(method, arg) {
8939 function callInvokeWithMethodAndArg() {
8940 return new PromiseImpl(function (resolve, reject) {
8941 invoke(method, arg, resolve, reject);
8945 return previousPromise = // If enqueue has been called before, then we want to wait until
8946 // all previous Promises have been resolved before calling invoke,
8947 // so that results are always delivered in the correct order. If
8948 // enqueue has not been called before, then it is important to
8949 // call invoke immediately, without waiting on a callback to fire,
8950 // so that the async generator function has the opportunity to do
8951 // any necessary setup in a predictable way. This predictability
8952 // is why the Promise constructor synchronously invokes its
8953 // executor callback, and why async functions synchronously
8954 // execute code before the first await. Since we implement simple
8955 // async functions in terms of async generators, it is especially
8956 // important to get this right, even though it requires care.
8957 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8958 // invocations of the iterator.
8959 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8960 } // Define the unified helper method that is used to implement .next,
8961 // .throw, and .return (see defineIteratorMethods).
8964 this._invoke = enqueue;
8967 defineIteratorMethods(AsyncIterator.prototype);
8969 AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8973 exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8974 // AsyncIterator objects; they just return a Promise for the value of
8975 // the final result produced by the iterator.
8977 exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8978 if (PromiseImpl === void 0) PromiseImpl = Promise;
8979 var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8980 return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8981 : iter.next().then(function (result) {
8982 return result.done ? result.value : iter.next();
8986 function makeInvokeMethod(innerFn, self, context) {
8987 var state = GenStateSuspendedStart;
8988 return function invoke(method, arg) {
8989 if (state === GenStateExecuting) {
8990 throw new Error("Generator is already running");
8993 if (state === GenStateCompleted) {
8994 if (method === "throw") {
8996 } // Be forgiving, per 25.3.3.3.3 of the spec:
8997 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
9000 return doneResult();
9003 context.method = method;
9007 var delegate = context.delegate;
9010 var delegateResult = maybeInvokeDelegate(delegate, context);
9012 if (delegateResult) {
9013 if (delegateResult === ContinueSentinel) continue;
9014 return delegateResult;
9018 if (context.method === "next") {
9019 // Setting context._sent for legacy support of Babel's
9020 // function.sent implementation.
9021 context.sent = context._sent = context.arg;
9022 } else if (context.method === "throw") {
9023 if (state === GenStateSuspendedStart) {
9024 state = GenStateCompleted;
9028 context.dispatchException(context.arg);
9029 } else if (context.method === "return") {
9030 context.abrupt("return", context.arg);
9033 state = GenStateExecuting;
9034 var record = tryCatch(innerFn, self, context);
9036 if (record.type === "normal") {
9037 // If an exception is thrown from innerFn, we leave state ===
9038 // GenStateExecuting and loop back for another invocation.
9039 state = context.done ? GenStateCompleted : GenStateSuspendedYield;
9041 if (record.arg === ContinueSentinel) {
9049 } else if (record.type === "throw") {
9050 state = GenStateCompleted; // Dispatch the exception by looping back around to the
9051 // context.dispatchException(context.arg) call above.
9053 context.method = "throw";
9054 context.arg = record.arg;
9058 } // Call delegate.iterator[context.method](context.arg) and handle the
9059 // result, either by returning a { value, done } result from the
9060 // delegate iterator, or by modifying context.method and context.arg,
9061 // setting context.delegate to null, and returning the ContinueSentinel.
9064 function maybeInvokeDelegate(delegate, context) {
9065 var method = delegate.iterator[context.method];
9067 if (method === undefined$1) {
9068 // A .throw or .return when the delegate iterator has no .throw
9069 // method always terminates the yield* loop.
9070 context.delegate = null;
9072 if (context.method === "throw") {
9073 // Note: ["return"] must be used for ES3 parsing compatibility.
9074 if (delegate.iterator["return"]) {
9075 // If the delegate iterator has a return method, give it a
9076 // chance to clean up.
9077 context.method = "return";
9078 context.arg = undefined$1;
9079 maybeInvokeDelegate(delegate, context);
9081 if (context.method === "throw") {
9082 // If maybeInvokeDelegate(context) changed context.method from
9083 // "return" to "throw", let that override the TypeError below.
9084 return ContinueSentinel;
9088 context.method = "throw";
9089 context.arg = new TypeError("The iterator does not provide a 'throw' method");
9092 return ContinueSentinel;
9095 var record = tryCatch(method, delegate.iterator, context.arg);
9097 if (record.type === "throw") {
9098 context.method = "throw";
9099 context.arg = record.arg;
9100 context.delegate = null;
9101 return ContinueSentinel;
9104 var info = record.arg;
9107 context.method = "throw";
9108 context.arg = new TypeError("iterator result is not an object");
9109 context.delegate = null;
9110 return ContinueSentinel;
9114 // Assign the result of the finished delegate to the temporary
9115 // variable specified by delegate.resultName (see delegateYield).
9116 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
9118 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
9119 // exception, let the outer generator proceed normally. If
9120 // context.method was "next", forget context.arg since it has been
9121 // "consumed" by the delegate iterator. If context.method was
9122 // "return", allow the original .return call to continue in the
9125 if (context.method !== "return") {
9126 context.method = "next";
9127 context.arg = undefined$1;
9130 // Re-yield the result returned by the delegate method.
9132 } // The delegate iterator is finished, so forget it and continue with
9133 // the outer generator.
9136 context.delegate = null;
9137 return ContinueSentinel;
9138 } // Define Generator.prototype.{next,throw,return} in terms of the
9139 // unified ._invoke helper method.
9142 defineIteratorMethods(Gp);
9143 define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
9144 // @@iterator function is called on it. Some browsers' implementations of the
9145 // iterator prototype chain incorrectly implement this, causing the Generator
9146 // object to not be returned from this call. This ensures that doesn't happen.
9147 // See https://github.com/facebook/regenerator/issues/274 for more details.
9149 Gp[iteratorSymbol] = function () {
9153 Gp.toString = function () {
9154 return "[object Generator]";
9157 function pushTryEntry(locs) {
9163 entry.catchLoc = locs[1];
9167 entry.finallyLoc = locs[2];
9168 entry.afterLoc = locs[3];
9171 this.tryEntries.push(entry);
9174 function resetTryEntry(entry) {
9175 var record = entry.completion || {};
9176 record.type = "normal";
9178 entry.completion = record;
9181 function Context(tryLocsList) {
9182 // The root entry object (effectively a try statement without a catch
9183 // or a finally block) gives us a place to store values thrown from
9184 // locations where there is no enclosing try statement.
9185 this.tryEntries = [{
9188 tryLocsList.forEach(pushTryEntry, this);
9192 exports.keys = function (object) {
9195 for (var key in object) {
9199 keys.reverse(); // Rather than returning an object with a next method, we keep
9200 // things simple and return the next function itself.
9202 return function next() {
9203 while (keys.length) {
9204 var key = keys.pop();
9206 if (key in object) {
9211 } // To avoid creating an additional object, we just hang the .value
9212 // and .done properties off the next function object itself. This
9213 // also ensures that the minifier will not anonymize the function.
9221 function values(iterable) {
9223 var iteratorMethod = iterable[iteratorSymbol];
9225 if (iteratorMethod) {
9226 return iteratorMethod.call(iterable);
9229 if (typeof iterable.next === "function") {
9233 if (!isNaN(iterable.length)) {
9235 next = function next() {
9236 while (++i < iterable.length) {
9237 if (hasOwn.call(iterable, i)) {
9238 next.value = iterable[i];
9244 next.value = undefined$1;
9249 return next.next = next;
9251 } // Return an iterator with no values.
9259 exports.values = values;
9261 function doneResult() {
9268 Context.prototype = {
9269 constructor: Context,
9270 reset: function reset(skipTempReset) {
9272 this.next = 0; // Resetting context._sent for legacy support of Babel's
9273 // function.sent implementation.
9275 this.sent = this._sent = undefined$1;
9277 this.delegate = null;
9278 this.method = "next";
9279 this.arg = undefined$1;
9280 this.tryEntries.forEach(resetTryEntry);
9282 if (!skipTempReset) {
9283 for (var name in this) {
9284 // Not sure about the optimal order of these conditions:
9285 if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9286 this[name] = undefined$1;
9291 stop: function stop() {
9293 var rootEntry = this.tryEntries[0];
9294 var rootRecord = rootEntry.completion;
9296 if (rootRecord.type === "throw") {
9297 throw rootRecord.arg;
9302 dispatchException: function dispatchException(exception) {
9309 function handle(loc, caught) {
9310 record.type = "throw";
9311 record.arg = exception;
9315 // If the dispatched exception was caught by a catch block,
9316 // then let that catch block handle the exception normally.
9317 context.method = "next";
9318 context.arg = undefined$1;
9324 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9325 var entry = this.tryEntries[i];
9326 var record = entry.completion;
9328 if (entry.tryLoc === "root") {
9329 // Exception thrown outside of any try block that could handle
9330 // it, so set the completion value of the entire function to
9331 // throw the exception.
9332 return handle("end");
9335 if (entry.tryLoc <= this.prev) {
9336 var hasCatch = hasOwn.call(entry, "catchLoc");
9337 var hasFinally = hasOwn.call(entry, "finallyLoc");
9339 if (hasCatch && hasFinally) {
9340 if (this.prev < entry.catchLoc) {
9341 return handle(entry.catchLoc, true);
9342 } else if (this.prev < entry.finallyLoc) {
9343 return handle(entry.finallyLoc);
9345 } else if (hasCatch) {
9346 if (this.prev < entry.catchLoc) {
9347 return handle(entry.catchLoc, true);
9349 } else if (hasFinally) {
9350 if (this.prev < entry.finallyLoc) {
9351 return handle(entry.finallyLoc);
9354 throw new Error("try statement without catch or finally");
9359 abrupt: function abrupt(type, arg) {
9360 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9361 var entry = this.tryEntries[i];
9363 if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9364 var finallyEntry = entry;
9369 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9370 // Ignore the finally entry if control is not jumping to a
9371 // location outside the try/catch block.
9372 finallyEntry = null;
9375 var record = finallyEntry ? finallyEntry.completion : {};
9380 this.method = "next";
9381 this.next = finallyEntry.finallyLoc;
9382 return ContinueSentinel;
9385 return this.complete(record);
9387 complete: function complete(record, afterLoc) {
9388 if (record.type === "throw") {
9392 if (record.type === "break" || record.type === "continue") {
9393 this.next = record.arg;
9394 } else if (record.type === "return") {
9395 this.rval = this.arg = record.arg;
9396 this.method = "return";
9398 } else if (record.type === "normal" && afterLoc) {
9399 this.next = afterLoc;
9402 return ContinueSentinel;
9404 finish: function finish(finallyLoc) {
9405 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9406 var entry = this.tryEntries[i];
9408 if (entry.finallyLoc === finallyLoc) {
9409 this.complete(entry.completion, entry.afterLoc);
9410 resetTryEntry(entry);
9411 return ContinueSentinel;
9415 "catch": function _catch(tryLoc) {
9416 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9417 var entry = this.tryEntries[i];
9419 if (entry.tryLoc === tryLoc) {
9420 var record = entry.completion;
9422 if (record.type === "throw") {
9423 var thrown = record.arg;
9424 resetTryEntry(entry);
9429 } // The context.catch method must only be called with a location
9430 // argument that corresponds to a known catch block.
9433 throw new Error("illegal catch attempt");
9435 delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9437 iterator: values(iterable),
9438 resultName: resultName,
9442 if (this.method === "next") {
9443 // Deliberately forget the last sent value so that we don't
9444 // accidentally pass it on to the delegate.
9445 this.arg = undefined$1;
9448 return ContinueSentinel;
9450 }; // Regardless of whether this script is executing as a CommonJS module
9451 // or not, return the runtime object so that we can declare the variable
9452 // regeneratorRuntime in the outer scope, which allows this module to be
9453 // injected easily by `bin/regenerator --include-runtime script.js`.
9456 }( // If this script is executing as a CommonJS module, use module.exports
9457 // as the regeneratorRuntime namespace. Otherwise create a new empty
9458 // object. Either way, the resulting object will be used to initialize
9459 // the regeneratorRuntime variable at the top of this file.
9463 regeneratorRuntime = runtime;
9464 } catch (accidentalStrictMode) {
9465 // This module should not be running in strict mode, so the above
9466 // assignment should always work unless something is misconfigured. Just
9467 // in case runtime.js accidentally runs in strict mode, we can escape
9468 // strict mode using a global Function call. This could conceivably fail
9469 // if a Content Security Policy forbids using Function, but in that case
9470 // the proper solution is to fix the accidental strict mode problem. If
9471 // you've misconfigured your bundler to force strict mode and applied a
9472 // CSP to forbid Function, and you're not willing to fix either of those
9473 // problems, please detail your unique predicament in a GitHub issue.
9474 Function("r", "regeneratorRuntime = r")(runtime);
9478 var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9480 function number$1 (x) {
9481 return x === null ? NaN : +x;
9483 function numbers(values, valueof) {
9484 var _iterator, _step, value, index, _iterator2, _step2, _value;
9486 return regeneratorRuntime.wrap(function numbers$(_context) {
9488 switch (_context.prev = _context.next) {
9490 if (!(valueof === undefined)) {
9495 _iterator = _createForOfIteratorHelper(values);
9501 if ((_step = _iterator.n()).done) {
9506 value = _step.value;
9508 if (!(value != null && (value = +value) >= value)) {
9526 _context.t0 = _context["catch"](2);
9528 _iterator.e(_context.t0);
9535 return _context.finish(16);
9543 _iterator2 = _createForOfIteratorHelper(values);
9549 if ((_step2 = _iterator2.n()).done) {
9554 _value = _step2.value;
9556 if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9574 _context.t1 = _context["catch"](23);
9576 _iterator2.e(_context.t1);
9583 return _context.finish(37);
9587 return _context.stop();
9590 }, _marked$2, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9593 var ascendingBisect = d3_bisector(d3_ascending);
9594 var bisectRight = ascendingBisect.right;
9595 d3_bisector(number$1).center;
9597 var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function (iterable) {
9598 // eslint-disable-next-line es/no-array-from -- required for testing
9599 Array.from(iterable);
9602 // `Array.from` method
9603 // https://tc39.es/ecma262/#sec-array.from
9604 _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, {
9608 // `Array.prototype.fill` method
9609 // https://tc39.es/ecma262/#sec-array.prototype.fill
9610 _export({ target: 'Array', proto: true }, {
9614 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
9615 addToUnscopables('fill');
9617 var $some = arrayIteration.some;
9620 var STRICT_METHOD$3 = arrayMethodIsStrict('some');
9622 // `Array.prototype.some` method
9623 // https://tc39.es/ecma262/#sec-array.prototype.some
9624 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$3 }, {
9625 some: function some(callbackfn /* , thisArg */) {
9626 return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9630 var exportTypedArrayStaticMethod = arrayBufferViewCore.exportTypedArrayStaticMethod;
9633 // `%TypedArray%.from` method
9634 // https://tc39.es/ecma262/#sec-%typedarray%.from
9635 exportTypedArrayStaticMethod('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9637 // `Float64Array` constructor
9638 // https://tc39.es/ecma262/#sec-typedarray-objects
9639 typedArrayConstructor('Float64', function (init) {
9640 return function Float64Array(data, byteOffset, length) {
9641 return init(this, data, byteOffset, length);
9645 function d3_descending (a, b) {
9646 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9649 // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9650 var Adder = /*#__PURE__*/function () {
9652 _classCallCheck$1(this, Adder);
9654 this._partials = new Float64Array(32);
9658 _createClass$1(Adder, [{
9660 value: function add(x) {
9661 var p = this._partials;
9664 for (var j = 0; j < this._n && j < 32; j++) {
9667 lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9668 if (lo) p[i++] = lo;
9678 value: function valueOf() {
9679 var p = this._partials;
9697 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9700 if (y == x - hi) hi = x;
9711 // `Object.defineProperties` method
9712 // https://tc39.es/ecma262/#sec-object.defineproperties
9713 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
9714 defineProperties: objectDefineProperties
9717 // `Map` constructor
9718 // https://tc39.es/ecma262/#sec-map-objects
9719 collection('Map', function (init) {
9720 return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9721 }, collectionStrong);
9724 var nativeSort = test.sort;
9727 var FAILS_ON_UNDEFINED = fails(function () {
9728 test.sort(undefined);
9731 var FAILS_ON_NULL = fails(function () {
9735 var STRICT_METHOD$2 = arrayMethodIsStrict('sort');
9737 var STABLE_SORT = !fails(function () {
9738 // feature detection can be too slow, so check engines versions
9739 if (engineV8Version) return engineV8Version < 70;
9740 if (engineFfVersion && engineFfVersion > 3) return;
9741 if (engineIsIeOrEdge) return true;
9742 if (engineWebkitVersion) return engineWebkitVersion < 603;
9745 var code, chr, value, index;
9747 // generate an array with more 512 elements (Chakra and old V8 fails only in this case)
9748 for (code = 65; code < 76; code++) {
9749 chr = String.fromCharCode(code);
9752 case 66: case 69: case 70: case 72: value = 3; break;
9753 case 68: case 71: value = 4; break;
9757 for (index = 0; index < 47; index++) {
9758 test.push({ k: chr + index, v: value });
9762 test.sort(function (a, b) { return b.v - a.v; });
9764 for (index = 0; index < test.length; index++) {
9765 chr = test[index].k.charAt(0);
9766 if (result.charAt(result.length - 1) !== chr) result += chr;
9769 return result !== 'DGBEFHACIJK';
9772 var FORCED$5 = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$2 || !STABLE_SORT;
9774 var getSortCompare = function (comparefn) {
9775 return function (x, y) {
9776 if (y === undefined) return -1;
9777 if (x === undefined) return 1;
9778 if (comparefn !== undefined) return +comparefn(x, y) || 0;
9779 return String(x) > String(y) ? 1 : -1;
9783 // `Array.prototype.sort` method
9784 // https://tc39.es/ecma262/#sec-array.prototype.sort
9785 _export({ target: 'Array', proto: true, forced: FORCED$5 }, {
9786 sort: function sort(comparefn) {
9787 if (comparefn !== undefined) aFunction(comparefn);
9789 var array = toObject(this);
9791 if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn);
9794 var arrayLength = toLength(array.length);
9795 var itemsLength, index;
9797 for (index = 0; index < arrayLength; index++) {
9798 if (index in array) items.push(array[index]);
9801 items = arraySort(items, getSortCompare(comparefn));
9802 itemsLength = items.length;
9805 while (index < itemsLength) array[index] = items[index++];
9806 while (index < arrayLength) delete array[index++];
9812 var e10 = Math.sqrt(50),
9815 function ticks (start, stop, count) {
9821 stop = +stop, start = +start, count = +count;
9822 if (start === stop && count > 0) return [start];
9823 if (reverse = stop < start) n = start, start = stop, stop = n;
9824 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9827 var r0 = Math.round(start / step),
9828 r1 = Math.round(stop / step);
9829 if (r0 * step < start) ++r0;
9830 if (r1 * step > stop) --r1;
9831 ticks = new Array(n = r1 - r0 + 1);
9834 ticks[i] = (r0 + i) * step;
9839 var _r = Math.round(start * step),
9840 _r2 = Math.round(stop * step);
9842 if (_r / step < start) ++_r;
9843 if (_r2 / step > stop) --_r2;
9844 ticks = new Array(n = _r2 - _r + 1);
9847 ticks[i] = (_r + i) / step;
9851 if (reverse) ticks.reverse();
9854 function tickIncrement(start, stop, count) {
9855 var step = (stop - start) / Math.max(0, count),
9856 power = Math.floor(Math.log(step) / Math.LN10),
9857 error = step / Math.pow(10, power);
9858 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);
9860 function tickStep(start, stop, count) {
9861 var step0 = Math.abs(stop - start) / Math.max(0, count),
9862 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9863 error = step0 / step1;
9864 if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9865 return stop < start ? -step1 : step1;
9868 function max(values, valueof) {
9871 if (valueof === undefined) {
9872 var _iterator = _createForOfIteratorHelper(values),
9876 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9877 var value = _step.value;
9879 if (value != null && (max < value || max === undefined && value >= value)) {
9891 var _iterator2 = _createForOfIteratorHelper(values),
9895 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9896 var _value = _step2.value;
9898 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9912 function min$2(values, valueof) {
9915 if (valueof === undefined) {
9916 var _iterator = _createForOfIteratorHelper(values),
9920 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9921 var value = _step.value;
9923 if (value != null && (min > value || min === undefined && value >= value)) {
9935 var _iterator2 = _createForOfIteratorHelper(values),
9939 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9940 var _value = _step2.value;
9942 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9956 // ISC license, Copyright 2018 Vladimir Agafonkin.
9958 function quickselect$2(array, k) {
9959 var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9960 var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9961 var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9963 while (right > left) {
9964 if (right - left > 600) {
9965 var n = right - left + 1;
9966 var m = k - left + 1;
9967 var z = Math.log(n);
9968 var s = 0.5 * Math.exp(2 * z / 3);
9969 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9970 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9971 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9972 quickselect$2(array, k, newLeft, newRight, compare);
9978 swap$1(array, left, k);
9979 if (compare(array[right], t) > 0) swap$1(array, left, right);
9982 swap$1(array, i, j), ++i, --j;
9984 while (compare(array[i], t) < 0) {
9988 while (compare(array[j], t) > 0) {
9993 if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right);
9994 if (j <= k) left = j + 1;
9995 if (k <= j) right = j - 1;
10001 function swap$1(array, i, j) {
10003 array[i] = array[j];
10007 function quantile(values, p, valueof) {
10008 values = Float64Array.from(numbers(values, valueof));
10009 if (!(n = values.length)) return;
10010 if ((p = +p) <= 0 || n < 2) return min$2(values);
10011 if (p >= 1) return max(values);
10014 i0 = Math.floor(i),
10015 value0 = max(quickselect$2(values, i0).subarray(0, i0 + 1)),
10016 value1 = min$2(values.subarray(i0 + 1));
10017 return value0 + (value1 - value0) * (i - i0);
10020 function d3_median (values, valueof) {
10021 return quantile(values, 0.5, valueof);
10024 var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
10026 function flatten(arrays) {
10027 var _iterator, _step, array;
10029 return regeneratorRuntime.wrap(function flatten$(_context) {
10031 switch (_context.prev = _context.next) {
10033 _iterator = _createForOfIteratorHelper(arrays);
10039 if ((_step = _iterator.n()).done) {
10044 array = _step.value;
10045 return _context.delegateYield(array, "t0", 6);
10052 _context.next = 13;
10056 _context.prev = 10;
10057 _context.t1 = _context["catch"](1);
10059 _iterator.e(_context.t1);
10062 _context.prev = 13;
10066 return _context.finish(13);
10070 return _context.stop();
10073 }, _marked$1, null, [[1, 10, 13, 16]]);
10076 function merge$4(arrays) {
10077 return Array.from(flatten(arrays));
10080 function range$1 (start, stop, step) {
10081 start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
10083 n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
10084 range = new Array(n);
10087 range[i] = start + i * step;
10093 // `SameValue` abstract operation
10094 // https://tc39.es/ecma262/#sec-samevalue
10095 // eslint-disable-next-line es/no-object-is -- safe
10096 var sameValue = Object.is || function is(x, y) {
10097 // eslint-disable-next-line no-self-compare -- NaN check
10098 return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
10101 // eslint-disable-next-line es/no-math-hypot -- required for testing
10102 var $hypot = Math.hypot;
10103 var abs$3 = Math.abs;
10104 var sqrt$1 = Math.sqrt;
10107 // https://bugs.chromium.org/p/v8/issues/detail?id=9546
10108 var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
10110 // `Math.hypot` method
10111 // https://tc39.es/ecma262/#sec-math.hypot
10112 _export({ target: 'Math', stat: true, forced: BUGGY }, {
10113 // eslint-disable-next-line no-unused-vars -- required for `.length`
10114 hypot: function hypot(value1, value2) {
10117 var aLen = arguments.length;
10121 arg = abs$3(arguments[i++]);
10124 sum = sum * div * div + 1;
10126 } else if (arg > 0) {
10131 return larg === Infinity ? Infinity : larg * sqrt$1(sum);
10135 // `Math.sign` method implementation
10136 // https://tc39.es/ecma262/#sec-math.sign
10137 // eslint-disable-next-line es/no-math-sign -- safe
10138 var mathSign = Math.sign || function sign(x) {
10139 // eslint-disable-next-line no-self-compare -- NaN check
10140 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
10143 // `Math.sign` method
10144 // https://tc39.es/ecma262/#sec-math.sign
10145 _export({ target: 'Math', stat: true }, {
10149 var epsilon$1 = 1e-6;
10150 var epsilon2$1 = 1e-12;
10152 var halfPi = pi / 2;
10153 var quarterPi = pi / 4;
10155 var degrees$1 = 180 / pi;
10156 var radians = pi / 180;
10157 var abs$2 = Math.abs;
10158 var atan = Math.atan;
10159 var atan2 = Math.atan2;
10160 var cos = Math.cos;
10161 var exp$2 = Math.exp;
10162 var log$1 = Math.log;
10163 var sin = Math.sin;
10164 var sign = Math.sign || function (x) {
10165 return x > 0 ? 1 : x < 0 ? -1 : 0;
10167 var sqrt = Math.sqrt;
10168 var tan = Math.tan;
10170 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
10173 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
10176 function noop$1() {}
10178 function streamGeometry(geometry, stream) {
10179 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
10180 streamGeometryType[geometry.type](geometry, stream);
10184 var streamObjectType = {
10185 Feature: function Feature(object, stream) {
10186 streamGeometry(object.geometry, stream);
10188 FeatureCollection: function FeatureCollection(object, stream) {
10189 var features = object.features,
10191 n = features.length;
10194 streamGeometry(features[i].geometry, stream);
10198 var streamGeometryType = {
10199 Sphere: function Sphere(object, stream) {
10202 Point: function Point(object, stream) {
10203 object = object.coordinates;
10204 stream.point(object[0], object[1], object[2]);
10206 MultiPoint: function MultiPoint(object, stream) {
10207 var coordinates = object.coordinates,
10209 n = coordinates.length;
10212 object = coordinates[i], stream.point(object[0], object[1], object[2]);
10215 LineString: function LineString(object, stream) {
10216 streamLine(object.coordinates, stream, 0);
10218 MultiLineString: function MultiLineString(object, stream) {
10219 var coordinates = object.coordinates,
10221 n = coordinates.length;
10224 streamLine(coordinates[i], stream, 0);
10227 Polygon: function Polygon(object, stream) {
10228 streamPolygon(object.coordinates, stream);
10230 MultiPolygon: function MultiPolygon(object, stream) {
10231 var coordinates = object.coordinates,
10233 n = coordinates.length;
10236 streamPolygon(coordinates[i], stream);
10239 GeometryCollection: function GeometryCollection(object, stream) {
10240 var geometries = object.geometries,
10242 n = geometries.length;
10245 streamGeometry(geometries[i], stream);
10250 function streamLine(coordinates, stream, closed) {
10252 n = coordinates.length - closed,
10254 stream.lineStart();
10257 coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
10263 function streamPolygon(coordinates, stream) {
10265 n = coordinates.length;
10266 stream.polygonStart();
10269 streamLine(coordinates[i], stream, 1);
10272 stream.polygonEnd();
10275 function d3_geoStream (object, stream) {
10276 if (object && streamObjectType.hasOwnProperty(object.type)) {
10277 streamObjectType[object.type](object, stream);
10279 streamGeometry(object, stream);
10283 var areaRingSum$1 = new Adder(); // hello?
10285 var areaSum$1 = new Adder(),
10291 var areaStream$1 = {
10295 polygonStart: function polygonStart() {
10296 areaRingSum$1 = new Adder();
10297 areaStream$1.lineStart = areaRingStart$1;
10298 areaStream$1.lineEnd = areaRingEnd$1;
10300 polygonEnd: function polygonEnd() {
10301 var areaRing = +areaRingSum$1;
10302 areaSum$1.add(areaRing < 0 ? tau + areaRing : areaRing);
10303 this.lineStart = this.lineEnd = this.point = noop$1;
10305 sphere: function sphere() {
10306 areaSum$1.add(tau);
10310 function areaRingStart$1() {
10311 areaStream$1.point = areaPointFirst$1;
10314 function areaRingEnd$1() {
10315 areaPoint$1(lambda00$1, phi00$1);
10318 function areaPointFirst$1(lambda, phi) {
10319 areaStream$1.point = areaPoint$1;
10320 lambda00$1 = lambda, phi00$1 = phi;
10321 lambda *= radians, phi *= radians;
10322 lambda0$2 = lambda, cosPhi0$1 = cos(phi = phi / 2 + quarterPi), sinPhi0$1 = sin(phi);
10325 function areaPoint$1(lambda, phi) {
10326 lambda *= radians, phi *= radians;
10327 phi = phi / 2 + quarterPi; // half the angular distance from south pole
10328 // Spherical excess E for a spherical triangle with vertices: south pole,
10329 // previous point, current point. Uses a formula derived from Cagnoli’s
10330 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10332 var dLambda = lambda - lambda0$2,
10333 sdLambda = dLambda >= 0 ? 1 : -1,
10334 adLambda = sdLambda * dLambda,
10337 k = sinPhi0$1 * sinPhi,
10338 u = cosPhi0$1 * cosPhi + k * cos(adLambda),
10339 v = k * sdLambda * sin(adLambda);
10340 areaRingSum$1.add(atan2(v, u)); // Advance the previous points.
10342 lambda0$2 = lambda, cosPhi0$1 = cosPhi, sinPhi0$1 = sinPhi;
10345 function d3_geoArea (object) {
10346 areaSum$1 = new Adder();
10347 d3_geoStream(object, areaStream$1);
10348 return areaSum$1 * 2;
10351 function spherical(cartesian) {
10352 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10354 function cartesian(spherical) {
10355 var lambda = spherical[0],
10356 phi = spherical[1],
10358 return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10360 function cartesianDot(a, b) {
10361 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10363 function cartesianCross(a, b) {
10364 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]];
10367 function cartesianAddInPlace(a, b) {
10368 a[0] += b[0], a[1] += b[1], a[2] += b[2];
10370 function cartesianScale(vector, k) {
10371 return [vector[0] * k, vector[1] * k, vector[2] * k];
10374 function cartesianNormalizeInPlace(d) {
10375 var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10376 d[0] /= l, d[1] /= l, d[2] /= l;
10379 var lambda0$1, phi0, lambda1, phi1, // bounds
10380 lambda2, // previous lambda-coordinate
10381 lambda00, phi00, // first point
10382 p0, // previous 3D point
10383 deltaSum, ranges, range;
10384 var boundsStream$1 = {
10385 point: boundsPoint$1,
10386 lineStart: boundsLineStart,
10387 lineEnd: boundsLineEnd,
10388 polygonStart: function polygonStart() {
10389 boundsStream$1.point = boundsRingPoint;
10390 boundsStream$1.lineStart = boundsRingStart;
10391 boundsStream$1.lineEnd = boundsRingEnd;
10392 deltaSum = new Adder();
10393 areaStream$1.polygonStart();
10395 polygonEnd: function polygonEnd() {
10396 areaStream$1.polygonEnd();
10397 boundsStream$1.point = boundsPoint$1;
10398 boundsStream$1.lineStart = boundsLineStart;
10399 boundsStream$1.lineEnd = boundsLineEnd;
10400 if (areaRingSum$1 < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon$1) phi1 = 90;else if (deltaSum < -epsilon$1) phi0 = -90;
10401 range[0] = lambda0$1, range[1] = lambda1;
10403 sphere: function sphere() {
10404 lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10408 function boundsPoint$1(lambda, phi) {
10409 ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10410 if (phi < phi0) phi0 = phi;
10411 if (phi > phi1) phi1 = phi;
10414 function linePoint(lambda, phi) {
10415 var p = cartesian([lambda * radians, phi * radians]);
10418 var normal = cartesianCross(p0, p),
10419 equatorial = [normal[1], -normal[0], 0],
10420 inflection = cartesianCross(equatorial, normal);
10421 cartesianNormalizeInPlace(inflection);
10422 inflection = spherical(inflection);
10423 var delta = lambda - lambda2,
10424 sign = delta > 0 ? 1 : -1,
10425 lambdai = inflection[0] * degrees$1 * sign,
10427 antimeridian = abs$2(delta) > 180;
10429 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10430 phii = inflection[1] * degrees$1;
10431 if (phii > phi1) phi1 = phii;
10432 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10433 phii = -inflection[1] * degrees$1;
10434 if (phii < phi0) phi0 = phii;
10436 if (phi < phi0) phi0 = phi;
10437 if (phi > phi1) phi1 = phi;
10440 if (antimeridian) {
10441 if (lambda < lambda2) {
10442 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10444 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10447 if (lambda1 >= lambda0$1) {
10448 if (lambda < lambda0$1) lambda0$1 = lambda;
10449 if (lambda > lambda1) lambda1 = lambda;
10451 if (lambda > lambda2) {
10452 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10454 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10459 ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10462 if (phi < phi0) phi0 = phi;
10463 if (phi > phi1) phi1 = phi;
10464 p0 = p, lambda2 = lambda;
10467 function boundsLineStart() {
10468 boundsStream$1.point = linePoint;
10471 function boundsLineEnd() {
10472 range[0] = lambda0$1, range[1] = lambda1;
10473 boundsStream$1.point = boundsPoint$1;
10477 function boundsRingPoint(lambda, phi) {
10479 var delta = lambda - lambda2;
10480 deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10482 lambda00 = lambda, phi00 = phi;
10485 areaStream$1.point(lambda, phi);
10486 linePoint(lambda, phi);
10489 function boundsRingStart() {
10490 areaStream$1.lineStart();
10493 function boundsRingEnd() {
10494 boundsRingPoint(lambda00, phi00);
10495 areaStream$1.lineEnd();
10496 if (abs$2(deltaSum) > epsilon$1) lambda0$1 = -(lambda1 = 180);
10497 range[0] = lambda0$1, range[1] = lambda1;
10499 } // Finds the left-right distance between two longitudes.
10500 // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10501 // the distance between ±180° to be 360°.
10504 function angle(lambda0, lambda1) {
10505 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10508 function rangeCompare(a, b) {
10509 return a[0] - b[0];
10512 function rangeContains(range, x) {
10513 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10516 function d3_geoBounds (feature) {
10517 var i, n, a, b, merged, deltaMax, delta;
10518 phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10520 d3_geoStream(feature, boundsStream$1); // First, sort ranges by their minimum longitudes.
10522 if (n = ranges.length) {
10523 ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10525 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10528 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10529 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10530 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10532 merged.push(a = b);
10534 } // Finally, find the largest gap between the merged ranges.
10535 // The final bounding box will be the inverse of this gap.
10538 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10540 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10544 ranges = range = null;
10545 return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10548 function compose (a, b) {
10549 function compose(x, y) {
10550 return x = a(x, y), b(x[0], x[1]);
10553 if (a.invert && b.invert) compose.invert = function (x, y) {
10554 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10559 function rotationIdentity(lambda, phi) {
10560 return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10563 rotationIdentity.invert = rotationIdentity;
10564 function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10565 return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10568 function forwardRotationLambda(deltaLambda) {
10569 return function (lambda, phi) {
10570 return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10574 function rotationLambda(deltaLambda) {
10575 var rotation = forwardRotationLambda(deltaLambda);
10576 rotation.invert = forwardRotationLambda(-deltaLambda);
10580 function rotationPhiGamma(deltaPhi, deltaGamma) {
10581 var cosDeltaPhi = cos(deltaPhi),
10582 sinDeltaPhi = sin(deltaPhi),
10583 cosDeltaGamma = cos(deltaGamma),
10584 sinDeltaGamma = sin(deltaGamma);
10586 function rotation(lambda, phi) {
10587 var cosPhi = cos(phi),
10588 x = cos(lambda) * cosPhi,
10589 y = sin(lambda) * cosPhi,
10591 k = z * cosDeltaPhi + x * sinDeltaPhi;
10592 return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10595 rotation.invert = function (lambda, phi) {
10596 var cosPhi = cos(phi),
10597 x = cos(lambda) * cosPhi,
10598 y = sin(lambda) * cosPhi,
10600 k = z * cosDeltaGamma - y * sinDeltaGamma;
10601 return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10607 function rotation (rotate) {
10608 rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10610 function forward(coordinates) {
10611 coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10612 return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10615 forward.invert = function (coordinates) {
10616 coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10617 return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10623 function circleStream(stream, radius, delta, direction, t0, t1) {
10624 if (!delta) return;
10625 var cosRadius = cos(radius),
10626 sinRadius = sin(radius),
10627 step = direction * delta;
10630 t0 = radius + direction * tau;
10631 t1 = radius - step / 2;
10633 t0 = circleRadius(cosRadius, t0);
10634 t1 = circleRadius(cosRadius, t1);
10635 if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10638 for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10639 point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10640 stream.point(point[0], point[1]);
10642 } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10644 function circleRadius(cosRadius, point) {
10645 point = cartesian(point), point[0] -= cosRadius;
10646 cartesianNormalizeInPlace(point);
10647 var radius = acos(-point[1]);
10648 return ((-point[2] < 0 ? -radius : radius) + tau - epsilon$1) % tau;
10651 function clipBuffer () {
10655 point: function point(x, y, m) {
10656 line.push([x, y, m]);
10658 lineStart: function lineStart() {
10659 lines.push(line = []);
10662 rejoin: function rejoin() {
10663 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10665 result: function result() {
10666 var result = lines;
10674 function pointEqual (a, b) {
10675 return abs$2(a[0] - b[0]) < epsilon$1 && abs$2(a[1] - b[1]) < epsilon$1;
10678 function Intersection(point, points, other, entry) {
10681 this.o = other; // another intersection
10683 this.e = entry; // is an entry?
10685 this.v = false; // visited
10687 this.n = this.p = null; // next & previous
10688 } // A generalized polygon clipping algorithm: given a polygon that has been cut
10689 // into its visible line segments, and rejoins the segments by interpolating
10690 // along the clip edge.
10693 function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10698 segments.forEach(function (segment) {
10699 if ((n = segment.length - 1) <= 0) return;
10705 if (pointEqual(p0, p1)) {
10706 if (!p0[2] && !p1[2]) {
10707 stream.lineStart();
10709 for (i = 0; i < n; ++i) {
10710 stream.point((p0 = segment[i])[0], p0[1]);
10715 } // handle degenerate cases by moving the point
10718 p1[0] += 2 * epsilon$1;
10721 subject.push(x = new Intersection(p0, segment, null, true));
10722 clip.push(x.o = new Intersection(p0, null, x, false));
10723 subject.push(x = new Intersection(p1, segment, null, false));
10724 clip.push(x.o = new Intersection(p1, null, x, true));
10726 if (!subject.length) return;
10727 clip.sort(compareIntersection);
10731 for (i = 0, n = clip.length; i < n; ++i) {
10732 clip[i].e = startInside = !startInside;
10735 var start = subject[0],
10740 // Find first unvisited intersection.
10741 var current = start,
10744 while (current.v) {
10745 if ((current = current.n) === start) return;
10748 points = current.z;
10749 stream.lineStart();
10752 current.v = current.o.v = true;
10756 for (i = 0, n = points.length; i < n; ++i) {
10757 stream.point((point = points[i])[0], point[1]);
10760 interpolate(current.x, current.n.x, 1, stream);
10763 current = current.n;
10766 points = current.p.z;
10768 for (i = points.length - 1; i >= 0; --i) {
10769 stream.point((point = points[i])[0], point[1]);
10772 interpolate(current.x, current.p.x, -1, stream);
10775 current = current.p;
10778 current = current.o;
10779 points = current.z;
10780 isSubject = !isSubject;
10781 } while (!current.v);
10787 function link(array) {
10788 if (!(n = array.length)) return;
10795 a.n = b = array[i];
10800 a.n = b = array[0];
10804 function longitude(point) {
10805 if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10808 function polygonContains (polygon, point) {
10809 var lambda = longitude(point),
10812 normal = [sin(lambda), -cos(lambda), 0],
10815 var sum = new Adder();
10816 if (sinPhi === 1) phi = halfPi + epsilon$1;else if (sinPhi === -1) phi = -halfPi - epsilon$1;
10818 for (var i = 0, n = polygon.length; i < n; ++i) {
10819 if (!(m = (ring = polygon[i]).length)) continue;
10822 point0 = ring[m - 1],
10823 lambda0 = longitude(point0),
10824 phi0 = point0[1] / 2 + quarterPi,
10825 sinPhi0 = sin(phi0),
10826 cosPhi0 = cos(phi0);
10828 for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10829 var point1 = ring[j],
10830 lambda1 = longitude(point1),
10831 phi1 = point1[1] / 2 + quarterPi,
10832 sinPhi1 = sin(phi1),
10833 cosPhi1 = cos(phi1),
10834 delta = lambda1 - lambda0,
10835 sign = delta >= 0 ? 1 : -1,
10836 absDelta = sign * delta,
10837 antimeridian = absDelta > pi,
10838 k = sinPhi0 * sinPhi1;
10839 sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10840 angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10841 // and are the latitudes smaller than the parallel (phi)?
10843 if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10844 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10845 cartesianNormalizeInPlace(arc);
10846 var intersection = cartesianCross(normal, arc);
10847 cartesianNormalizeInPlace(intersection);
10848 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10850 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10851 winding += antimeridian ^ delta >= 0 ? 1 : -1;
10855 } // First, determine whether the South pole is inside or outside:
10857 // It is inside if:
10858 // * the polygon winds around it in a clockwise direction.
10859 // * the polygon does not (cumulatively) wind around it, but has a negative
10860 // (counter-clockwise) area.
10862 // Second, count the (signed) number of times a segment crosses a lambda
10863 // from the point to the South pole. If it is zero, then the point is the
10864 // same side as the South pole.
10867 return (angle < -epsilon$1 || angle < epsilon$1 && sum < -epsilon2$1) ^ winding & 1;
10870 function clip (pointVisible, clipLine, interpolate, start) {
10871 return function (sink) {
10872 var line = clipLine(sink),
10873 ringBuffer = clipBuffer(),
10874 ringSink = clipLine(ringBuffer),
10875 polygonStarted = false,
10881 lineStart: lineStart,
10883 polygonStart: function polygonStart() {
10884 clip.point = pointRing;
10885 clip.lineStart = ringStart;
10886 clip.lineEnd = ringEnd;
10890 polygonEnd: function polygonEnd() {
10891 clip.point = point;
10892 clip.lineStart = lineStart;
10893 clip.lineEnd = lineEnd;
10894 segments = merge$4(segments);
10895 var startInside = polygonContains(polygon, start);
10897 if (segments.length) {
10898 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10899 clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10900 } else if (startInside) {
10901 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10903 interpolate(null, null, 1, sink);
10907 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10908 segments = polygon = null;
10910 sphere: function sphere() {
10911 sink.polygonStart();
10913 interpolate(null, null, 1, sink);
10919 function point(lambda, phi) {
10920 if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10923 function pointLine(lambda, phi) {
10924 line.point(lambda, phi);
10927 function lineStart() {
10928 clip.point = pointLine;
10932 function lineEnd() {
10933 clip.point = point;
10937 function pointRing(lambda, phi) {
10938 ring.push([lambda, phi]);
10939 ringSink.point(lambda, phi);
10942 function ringStart() {
10943 ringSink.lineStart();
10947 function ringEnd() {
10948 pointRing(ring[0][0], ring[0][1]);
10949 ringSink.lineEnd();
10950 var clean = ringSink.clean(),
10951 ringSegments = ringBuffer.result(),
10953 n = ringSegments.length,
10958 polygon.push(ring);
10960 if (!n) return; // No intersections.
10963 segment = ringSegments[0];
10965 if ((m = segment.length - 1) > 0) {
10966 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10969 for (i = 0; i < m; ++i) {
10970 sink.point((point = segment[i])[0], point[1]);
10977 } // Rejoin connected segments.
10978 // TODO reuse ringBuffer.rejoin()?
10981 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10982 segments.push(ringSegments.filter(validSegment));
10989 function validSegment(segment) {
10990 return segment.length > 1;
10991 } // Intersections are sorted along the clip edge. For both antimeridian cutting
10992 // and circle clipping, the same comparison is used.
10995 function compareIntersection(a, b) {
10996 return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon$1 : halfPi - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon$1 : halfPi - b[1]);
10999 var clipAntimeridian = clip(function () {
11001 }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
11002 // intersections or the line was empty; 1 - no intersections; 2 - there were
11003 // intersections, and the first and last segments should be rejoined.
11005 function clipAntimeridianLine(stream) {
11009 _clean; // no intersections
11013 lineStart: function lineStart() {
11014 stream.lineStart();
11017 point: function point(lambda1, phi1) {
11018 var sign1 = lambda1 > 0 ? pi : -pi,
11019 delta = abs$2(lambda1 - lambda0);
11021 if (abs$2(delta - pi) < epsilon$1) {
11022 // line crosses a pole
11023 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
11024 stream.point(sign0, phi0);
11026 stream.lineStart();
11027 stream.point(sign1, phi0);
11028 stream.point(lambda1, phi0);
11030 } else if (sign0 !== sign1 && delta >= pi) {
11031 // line crosses antimeridian
11032 if (abs$2(lambda0 - sign0) < epsilon$1) lambda0 -= sign0 * epsilon$1; // handle degeneracies
11034 if (abs$2(lambda1 - sign1) < epsilon$1) lambda1 -= sign1 * epsilon$1;
11035 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
11036 stream.point(sign0, phi0);
11038 stream.lineStart();
11039 stream.point(sign1, phi0);
11043 stream.point(lambda0 = lambda1, phi0 = phi1);
11046 lineEnd: function lineEnd() {
11048 lambda0 = phi0 = NaN;
11050 clean: function clean() {
11051 return 2 - _clean; // if intersections, rejoin first and last segments
11056 function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
11059 sinLambda0Lambda1 = sin(lambda0 - lambda1);
11060 return abs$2(sinLambda0Lambda1) > epsilon$1 ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2;
11063 function clipAntimeridianInterpolate(from, to, direction, stream) {
11066 if (from == null) {
11067 phi = direction * halfPi;
11068 stream.point(-pi, phi);
11069 stream.point(0, phi);
11070 stream.point(pi, phi);
11071 stream.point(pi, 0);
11072 stream.point(pi, -phi);
11073 stream.point(0, -phi);
11074 stream.point(-pi, -phi);
11075 stream.point(-pi, 0);
11076 stream.point(-pi, phi);
11077 } else if (abs$2(from[0] - to[0]) > epsilon$1) {
11078 var lambda = from[0] < to[0] ? pi : -pi;
11079 phi = direction * lambda / 2;
11080 stream.point(-lambda, phi);
11081 stream.point(0, phi);
11082 stream.point(lambda, phi);
11084 stream.point(to[0], to[1]);
11088 function clipCircle (radius) {
11089 var cr = cos(radius),
11090 delta = 6 * radians,
11091 smallRadius = cr > 0,
11092 notHemisphere = abs$2(cr) > epsilon$1; // TODO optimise for this common case
11094 function interpolate(from, to, direction, stream) {
11095 circleStream(stream, radius, delta, direction, from, to);
11098 function visible(lambda, phi) {
11099 return cos(lambda) * cos(phi) > cr;
11100 } // Takes a line and cuts into visible segments. Return values used for polygon
11101 // clipping: 0 - there were intersections or the line was empty; 1 - no
11102 // intersections 2 - there were intersections, and the first and last segments
11103 // should be rejoined.
11106 function clipLine(stream) {
11107 var point0, // previous point
11108 c0, // code for previous point
11109 v0, // visibility of previous point
11110 v00, // visibility of first point
11111 _clean; // no intersections
11115 lineStart: function lineStart() {
11119 point: function point(lambda, phi) {
11120 var point1 = [lambda, phi],
11122 v = visible(lambda, phi),
11123 c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
11124 if (!point0 && (v00 = v0 = v)) stream.lineStart();
11127 point2 = intersect(point0, point1);
11128 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
11135 // outside going in
11136 stream.lineStart();
11137 point2 = intersect(point1, point0);
11138 stream.point(point2[0], point2[1]);
11140 // inside going out
11141 point2 = intersect(point0, point1);
11142 stream.point(point2[0], point2[1], 2);
11147 } else if (notHemisphere && point0 && smallRadius ^ v) {
11148 var t; // If the codes for two points are different, or are both zero,
11149 // and there this segment intersects with the small circle.
11151 if (!(c & c0) && (t = intersect(point1, point0, true))) {
11155 stream.lineStart();
11156 stream.point(t[0][0], t[0][1]);
11157 stream.point(t[1][0], t[1][1]);
11160 stream.point(t[1][0], t[1][1]);
11162 stream.lineStart();
11163 stream.point(t[0][0], t[0][1], 3);
11168 if (v && (!point0 || !pointEqual(point0, point1))) {
11169 stream.point(point1[0], point1[1]);
11172 point0 = point1, v0 = v, c0 = c;
11174 lineEnd: function lineEnd() {
11175 if (v0) stream.lineEnd();
11178 // Rejoin first and last segments if there were intersections and the first
11179 // and last points were visible.
11180 clean: function clean() {
11181 return _clean | (v00 && v0) << 1;
11184 } // Intersects the great circle between a and b with the clip circle.
11187 function intersect(a, b, two) {
11188 var pa = cartesian(a),
11189 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
11190 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
11192 var n1 = [1, 0, 0],
11194 n2 = cartesianCross(pa, pb),
11195 n2n2 = cartesianDot(n2, n2),
11197 // cartesianDot(n1, n2),
11198 determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11200 if (!determinant) return !two && a;
11201 var c1 = cr * n2n2 / determinant,
11202 c2 = -cr * n1n2 / determinant,
11203 n1xn2 = cartesianCross(n1, n2),
11204 A = cartesianScale(n1, c1),
11205 B = cartesianScale(n2, c2);
11206 cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11209 w = cartesianDot(A, u),
11210 uu = cartesianDot(u, u),
11211 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11212 if (t2 < 0) return;
11214 q = cartesianScale(u, (-w - t) / uu);
11215 cartesianAddInPlace(q, A);
11217 if (!two) return q; // Two intersection points.
11219 var lambda0 = a[0],
11224 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11225 var delta = lambda1 - lambda0,
11226 polar = abs$2(delta - pi) < epsilon$1,
11227 meridian = polar || delta < epsilon$1;
11228 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11230 if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon$1 ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
11231 var q1 = cartesianScale(u, (-w + t) / uu);
11232 cartesianAddInPlace(q1, A);
11233 return [q, spherical(q1)];
11235 } // Generates a 4-bit vector representing the location of a point relative to
11236 // the small circle's bounding box.
11239 function code(lambda, phi) {
11240 var r = smallRadius ? radius : pi - radius,
11242 if (lambda < -r) code |= 1; // left
11243 else if (lambda > r) code |= 2; // right
11245 if (phi < -r) code |= 4; // below
11246 else if (phi > r) code |= 8; // above
11251 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11254 function clipLine (a, b, x0, y0, x1, y1) {
11265 if (!dx && r > 0) return;
11269 if (r < t0) return;
11270 if (r < t1) t1 = r;
11271 } else if (dx > 0) {
11272 if (r > t1) return;
11273 if (r > t0) t0 = r;
11277 if (!dx && r < 0) return;
11281 if (r > t1) return;
11282 if (r > t0) t0 = r;
11283 } else if (dx > 0) {
11284 if (r < t0) return;
11285 if (r < t1) t1 = r;
11289 if (!dy && r > 0) return;
11293 if (r < t0) return;
11294 if (r < t1) t1 = r;
11295 } else if (dy > 0) {
11296 if (r > t1) return;
11297 if (r > t0) t0 = r;
11301 if (!dy && r < 0) return;
11305 if (r > t1) return;
11306 if (r > t0) t0 = r;
11307 } else if (dy > 0) {
11308 if (r < t0) return;
11309 if (r < t1) t1 = r;
11312 if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11313 if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11318 clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11319 // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11321 function clipRectangle(x0, y0, x1, y1) {
11322 function visible(x, y) {
11323 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11326 function interpolate(from, to, direction, stream) {
11330 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11332 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11333 } while ((a = (a + direction + 4) % 4) !== a1);
11335 stream.point(to[0], to[1]);
11339 function corner(p, direction) {
11340 return abs$2(p[0] - x0) < epsilon$1 ? direction > 0 ? 0 : 3 : abs$2(p[0] - x1) < epsilon$1 ? direction > 0 ? 2 : 1 : abs$2(p[1] - y0) < epsilon$1 ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
11343 function compareIntersection(a, b) {
11344 return comparePoint(a.x, b.x);
11347 function comparePoint(a, b) {
11348 var ca = corner(a, 1),
11350 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];
11353 return function (stream) {
11354 var activeStream = stream,
11355 bufferStream = clipBuffer(),
11371 lineStart: lineStart,
11373 polygonStart: polygonStart,
11374 polygonEnd: polygonEnd
11377 function point(x, y) {
11378 if (visible(x, y)) activeStream.point(x, y);
11381 function polygonInside() {
11384 for (var i = 0, n = polygon.length; i < n; ++i) {
11385 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11386 a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11389 if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11391 if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11397 } // Buffer geometry within a polygon and then clip it en masse.
11400 function polygonStart() {
11401 activeStream = bufferStream, segments = [], polygon = [], clean = true;
11404 function polygonEnd() {
11405 var startInside = polygonInside(),
11406 cleanInside = clean && startInside,
11407 visible = (segments = merge$4(segments)).length;
11409 if (cleanInside || visible) {
11410 stream.polygonStart();
11413 stream.lineStart();
11414 interpolate(null, null, 1, stream);
11419 clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11422 stream.polygonEnd();
11425 activeStream = stream, segments = polygon = ring = null;
11428 function lineStart() {
11429 clipStream.point = linePoint;
11430 if (polygon) polygon.push(ring = []);
11434 } // TODO rather than special-case polygons, simply handle them separately.
11435 // Ideally, coincident intersection points should be jittered to avoid
11436 // clipping issues.
11439 function lineEnd() {
11441 linePoint(x__, y__);
11442 if (v__ && v_) bufferStream.rejoin();
11443 segments.push(bufferStream.result());
11446 clipStream.point = point;
11447 if (v_) activeStream.lineEnd();
11450 function linePoint(x, y) {
11451 var v = visible(x, y);
11452 if (polygon) ring.push([x, y]);
11455 x__ = x, y__ = y, v__ = v;
11459 activeStream.lineStart();
11460 activeStream.point(x, y);
11463 if (v && v_) activeStream.point(x, y);else {
11464 var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11465 b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11467 if (clipLine(a, b, x0, y0, x1, y1)) {
11469 activeStream.lineStart();
11470 activeStream.point(a[0], a[1]);
11473 activeStream.point(b[0], b[1]);
11474 if (!v) activeStream.lineEnd();
11477 activeStream.lineStart();
11478 activeStream.point(x, y);
11484 x_ = x, y_ = y, v_ = v;
11491 var lengthSum$1, lambda0, sinPhi0, cosPhi0;
11492 var lengthStream$1 = {
11495 lineStart: lengthLineStart,
11497 polygonStart: noop$1,
11501 function lengthLineStart() {
11502 lengthStream$1.point = lengthPointFirst$1;
11503 lengthStream$1.lineEnd = lengthLineEnd;
11506 function lengthLineEnd() {
11507 lengthStream$1.point = lengthStream$1.lineEnd = noop$1;
11510 function lengthPointFirst$1(lambda, phi) {
11511 lambda *= radians, phi *= radians;
11512 lambda0 = lambda, sinPhi0 = sin(phi), cosPhi0 = cos(phi);
11513 lengthStream$1.point = lengthPoint$1;
11516 function lengthPoint$1(lambda, phi) {
11517 lambda *= radians, phi *= radians;
11518 var sinPhi = sin(phi),
11520 delta = abs$2(lambda - lambda0),
11521 cosDelta = cos(delta),
11522 sinDelta = sin(delta),
11523 x = cosPhi * sinDelta,
11524 y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,
11525 z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;
11526 lengthSum$1.add(atan2(sqrt(x * x + y * y), z));
11527 lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;
11530 function d3_geoLength (object) {
11531 lengthSum$1 = new Adder();
11532 d3_geoStream(object, lengthStream$1);
11533 return +lengthSum$1;
11536 var identity$4 = (function (x) {
11540 var areaSum = new Adder(),
11541 areaRingSum = new Adder(),
11550 polygonStart: function polygonStart() {
11551 areaStream.lineStart = areaRingStart;
11552 areaStream.lineEnd = areaRingEnd;
11554 polygonEnd: function polygonEnd() {
11555 areaStream.lineStart = areaStream.lineEnd = areaStream.point = noop$1;
11556 areaSum.add(abs$2(areaRingSum));
11557 areaRingSum = new Adder();
11559 result: function result() {
11560 var area = areaSum / 2;
11561 areaSum = new Adder();
11566 function areaRingStart() {
11567 areaStream.point = areaPointFirst;
11570 function areaPointFirst(x, y) {
11571 areaStream.point = areaPoint;
11572 x00$2 = x0$3 = x, y00$2 = y0$3 = y;
11575 function areaPoint(x, y) {
11576 areaRingSum.add(y0$3 * x - x0$3 * y);
11577 x0$3 = x, y0$3 = y;
11580 function areaRingEnd() {
11581 areaPoint(x00$2, y00$2);
11584 var x0$2 = Infinity,
11588 var boundsStream = {
11589 point: boundsPoint,
11592 polygonStart: noop$1,
11593 polygonEnd: noop$1,
11594 result: function result() {
11595 var bounds = [[x0$2, y0$2], [x1, y1]];
11596 x1 = y1 = -(y0$2 = x0$2 = Infinity);
11601 function boundsPoint(x, y) {
11602 if (x < x0$2) x0$2 = x;
11603 if (x > x1) x1 = x;
11604 if (y < y0$2) y0$2 = y;
11605 if (y > y1) y1 = y;
11621 var centroidStream = {
11622 point: centroidPoint,
11623 lineStart: centroidLineStart,
11624 lineEnd: centroidLineEnd,
11625 polygonStart: function polygonStart() {
11626 centroidStream.lineStart = centroidRingStart;
11627 centroidStream.lineEnd = centroidRingEnd;
11629 polygonEnd: function polygonEnd() {
11630 centroidStream.point = centroidPoint;
11631 centroidStream.lineStart = centroidLineStart;
11632 centroidStream.lineEnd = centroidLineEnd;
11634 result: function result() {
11635 var centroid = Z2 ? [X2 / Z2, Y2 / Z2] : Z1 ? [X1 / Z1, Y1 / Z1] : Z0 ? [X0 / Z0, Y0 / Z0] : [NaN, NaN];
11636 X0 = Y0 = Z0 = X1 = Y1 = Z1 = X2 = Y2 = Z2 = 0;
11641 function centroidPoint(x, y) {
11647 function centroidLineStart() {
11648 centroidStream.point = centroidPointFirstLine;
11651 function centroidPointFirstLine(x, y) {
11652 centroidStream.point = centroidPointLine;
11653 centroidPoint(x0$1 = x, y0$1 = y);
11656 function centroidPointLine(x, y) {
11659 z = sqrt(dx * dx + dy * dy);
11660 X1 += z * (x0$1 + x) / 2;
11661 Y1 += z * (y0$1 + y) / 2;
11663 centroidPoint(x0$1 = x, y0$1 = y);
11666 function centroidLineEnd() {
11667 centroidStream.point = centroidPoint;
11670 function centroidRingStart() {
11671 centroidStream.point = centroidPointFirstRing;
11674 function centroidRingEnd() {
11675 centroidPointRing(x00$1, y00$1);
11678 function centroidPointFirstRing(x, y) {
11679 centroidStream.point = centroidPointRing;
11680 centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
11683 function centroidPointRing(x, y) {
11686 z = sqrt(dx * dx + dy * dy);
11687 X1 += z * (x0$1 + x) / 2;
11688 Y1 += z * (y0$1 + y) / 2;
11690 z = y0$1 * x - x0$1 * y;
11691 X2 += z * (x0$1 + x);
11692 Y2 += z * (y0$1 + y);
11694 centroidPoint(x0$1 = x, y0$1 = y);
11697 function PathContext(context) {
11698 this._context = context;
11700 PathContext.prototype = {
11702 pointRadius: function pointRadius(_) {
11703 return this._radius = _, this;
11705 polygonStart: function polygonStart() {
11708 polygonEnd: function polygonEnd() {
11711 lineStart: function lineStart() {
11714 lineEnd: function lineEnd() {
11715 if (this._line === 0) this._context.closePath();
11718 point: function point(x, y) {
11719 switch (this._point) {
11722 this._context.moveTo(x, y);
11730 this._context.lineTo(x, y);
11737 this._context.moveTo(x + this._radius, y);
11739 this._context.arc(x, y, this._radius, 0, tau);
11748 var lengthSum = new Adder(),
11754 var lengthStream = {
11756 lineStart: function lineStart() {
11757 lengthStream.point = lengthPointFirst;
11759 lineEnd: function lineEnd() {
11760 if (lengthRing) lengthPoint(x00, y00);
11761 lengthStream.point = noop$1;
11763 polygonStart: function polygonStart() {
11766 polygonEnd: function polygonEnd() {
11769 result: function result() {
11770 var length = +lengthSum;
11771 lengthSum = new Adder();
11776 function lengthPointFirst(x, y) {
11777 lengthStream.point = lengthPoint;
11778 x00 = x0 = x, y00 = y0 = y;
11781 function lengthPoint(x, y) {
11783 lengthSum.add(sqrt(x0 * x0 + y0 * y0));
11787 function PathString() {
11790 PathString.prototype = {
11792 _circle: circle(4.5),
11793 pointRadius: function pointRadius(_) {
11794 if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11797 polygonStart: function polygonStart() {
11800 polygonEnd: function polygonEnd() {
11803 lineStart: function lineStart() {
11806 lineEnd: function lineEnd() {
11807 if (this._line === 0) this._string.push("Z");
11810 point: function point(x, y) {
11811 switch (this._point) {
11814 this._string.push("M", x, ",", y);
11822 this._string.push("L", x, ",", y);
11829 if (this._circle == null) this._circle = circle(this._radius);
11831 this._string.push("M", x, ",", y, this._circle);
11837 result: function result() {
11838 if (this._string.length) {
11839 var result = this._string.join("");
11849 function circle(radius) {
11850 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11853 function d3_geoPath (projection, context) {
11854 var pointRadius = 4.5,
11858 function path(object) {
11860 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11861 d3_geoStream(object, projectionStream(contextStream));
11864 return contextStream.result();
11867 path.area = function (object) {
11868 d3_geoStream(object, projectionStream(areaStream));
11869 return areaStream.result();
11872 path.measure = function (object) {
11873 d3_geoStream(object, projectionStream(lengthStream));
11874 return lengthStream.result();
11877 path.bounds = function (object) {
11878 d3_geoStream(object, projectionStream(boundsStream));
11879 return boundsStream.result();
11882 path.centroid = function (object) {
11883 d3_geoStream(object, projectionStream(centroidStream));
11884 return centroidStream.result();
11887 path.projection = function (_) {
11888 return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
11891 path.context = function (_) {
11892 if (!arguments.length) return context;
11893 contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11894 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11898 path.pointRadius = function (_) {
11899 if (!arguments.length) return pointRadius;
11900 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11904 return path.projection(projection).context(context);
11907 function d3_geoTransform (methods) {
11909 stream: transformer$1(methods)
11912 function transformer$1(methods) {
11913 return function (stream) {
11914 var s = new TransformStream();
11916 for (var key in methods) {
11917 s[key] = methods[key];
11925 function TransformStream() {}
11927 TransformStream.prototype = {
11928 constructor: TransformStream,
11929 point: function point(x, y) {
11930 this.stream.point(x, y);
11932 sphere: function sphere() {
11933 this.stream.sphere();
11935 lineStart: function lineStart() {
11936 this.stream.lineStart();
11938 lineEnd: function lineEnd() {
11939 this.stream.lineEnd();
11941 polygonStart: function polygonStart() {
11942 this.stream.polygonStart();
11944 polygonEnd: function polygonEnd() {
11945 this.stream.polygonEnd();
11949 function fit(projection, fitBounds, object) {
11950 var clip = projection.clipExtent && projection.clipExtent();
11951 projection.scale(150).translate([0, 0]);
11952 if (clip != null) projection.clipExtent(null);
11953 d3_geoStream(object, projection.stream(boundsStream));
11954 fitBounds(boundsStream.result());
11955 if (clip != null) projection.clipExtent(clip);
11959 function fitExtent(projection, extent, object) {
11960 return fit(projection, function (b) {
11961 var w = extent[1][0] - extent[0][0],
11962 h = extent[1][1] - extent[0][1],
11963 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11964 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11965 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11966 projection.scale(150 * k).translate([x, y]);
11969 function fitSize(projection, size, object) {
11970 return fitExtent(projection, [[0, 0], size], object);
11972 function fitWidth(projection, width, object) {
11973 return fit(projection, function (b) {
11975 k = w / (b[1][0] - b[0][0]),
11976 x = (w - k * (b[1][0] + b[0][0])) / 2,
11978 projection.scale(150 * k).translate([x, y]);
11981 function fitHeight(projection, height, object) {
11982 return fit(projection, function (b) {
11984 k = h / (b[1][1] - b[0][1]),
11986 y = (h - k * (b[1][1] + b[0][1])) / 2;
11987 projection.scale(150 * k).translate([x, y]);
11992 // maximum depth of subdivision
11993 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11995 function resample (project, delta2) {
11996 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11999 function resampleNone(project) {
12000 return transformer$1({
12001 point: function point(x, y) {
12003 this.stream.point(x[0], x[1]);
12008 function resample$1(project, delta2) {
12009 function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
12012 d2 = dx * dx + dy * dy;
12014 if (d2 > 4 * delta2 && depth--) {
12018 m = sqrt(a * a + b * b + c * c),
12019 phi2 = asin(c /= m),
12020 lambda2 = abs$2(abs$2(c) - 1) < epsilon$1 || abs$2(lambda0 - lambda1) < epsilon$1 ? (lambda0 + lambda1) / 2 : atan2(b, a),
12021 p = project(lambda2, phi2),
12026 dz = dy * dx2 - dx * dy2;
12028 if (dz * dz / d2 > delta2 // perpendicular projected distance
12029 || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
12030 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
12031 // angular distance
12032 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
12033 stream.point(x2, y2);
12034 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
12039 return function (stream) {
12040 var lambda00, x00, y00, a00, b00, c00, // first point
12041 lambda0, x0, y0, a0, b0, c0; // previous point
12043 var resampleStream = {
12045 lineStart: lineStart,
12047 polygonStart: function polygonStart() {
12048 stream.polygonStart();
12049 resampleStream.lineStart = ringStart;
12051 polygonEnd: function polygonEnd() {
12052 stream.polygonEnd();
12053 resampleStream.lineStart = lineStart;
12057 function point(x, y) {
12059 stream.point(x[0], x[1]);
12062 function lineStart() {
12064 resampleStream.point = linePoint;
12065 stream.lineStart();
12068 function linePoint(lambda, phi) {
12069 var c = cartesian([lambda, phi]),
12070 p = project(lambda, phi);
12071 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);
12072 stream.point(x0, y0);
12075 function lineEnd() {
12076 resampleStream.point = point;
12080 function ringStart() {
12082 resampleStream.point = ringPoint;
12083 resampleStream.lineEnd = ringEnd;
12086 function ringPoint(lambda, phi) {
12087 linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
12088 resampleStream.point = linePoint;
12091 function ringEnd() {
12092 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
12093 resampleStream.lineEnd = lineEnd;
12097 return resampleStream;
12101 var transformRadians = transformer$1({
12102 point: function point(x, y) {
12103 this.stream.point(x * radians, y * radians);
12107 function transformRotate(rotate) {
12108 return transformer$1({
12109 point: function point(x, y) {
12110 var r = rotate(x, y);
12111 return this.stream.point(r[0], r[1]);
12116 function scaleTranslate(k, dx, dy, sx, sy) {
12117 function transform(x, y) {
12120 return [dx + k * x, dy - k * y];
12123 transform.invert = function (x, y) {
12124 return [(x - dx) / k * sx, (dy - y) / k * sy];
12130 function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
12131 if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
12132 var cosAlpha = cos(alpha),
12133 sinAlpha = sin(alpha),
12138 ci = (sinAlpha * dy - cosAlpha * dx) / k,
12139 fi = (sinAlpha * dx + cosAlpha * dy) / k;
12141 function transform(x, y) {
12144 return [a * x - b * y + dx, dy - b * x - a * y];
12147 transform.invert = function (x, y) {
12148 return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
12154 function projection(project) {
12155 return projectionMutator(function () {
12159 function projectionMutator(projectAt) {
12175 // post-rotate angle
12181 preclip = clipAntimeridian,
12187 postclip = identity$4,
12188 // post-clip extent
12193 projectRotateTransform,
12197 function projection(point) {
12198 return projectRotateTransform(point[0] * radians, point[1] * radians);
12201 function invert(point) {
12202 point = projectRotateTransform.invert(point[0], point[1]);
12203 return point && [point[0] * degrees$1, point[1] * degrees$1];
12206 projection.stream = function (stream) {
12207 return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12210 projection.preclip = function (_) {
12211 return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12214 projection.postclip = function (_) {
12215 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12218 projection.clipAngle = function (_) {
12219 return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
12222 projection.clipExtent = function (_) {
12223 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12226 projection.scale = function (_) {
12227 return arguments.length ? (k = +_, recenter()) : k;
12230 projection.translate = function (_) {
12231 return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12234 projection.center = function (_) {
12235 return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
12238 projection.rotate = function (_) {
12239 return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
12242 projection.angle = function (_) {
12243 return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1;
12246 projection.reflectX = function (_) {
12247 return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12250 projection.reflectY = function (_) {
12251 return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12254 projection.precision = function (_) {
12255 return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
12258 projection.fitExtent = function (extent, object) {
12259 return fitExtent(projection, extent, object);
12262 projection.fitSize = function (size, object) {
12263 return fitSize(projection, size, object);
12266 projection.fitWidth = function (width, object) {
12267 return fitWidth(projection, width, object);
12270 projection.fitHeight = function (height, object) {
12271 return fitHeight(projection, height, object);
12274 function recenter() {
12275 var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12276 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12277 rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12278 projectTransform = compose(project, transform);
12279 projectRotateTransform = compose(rotate, projectTransform);
12280 projectResample = resample(projectTransform, delta2);
12285 cache = cacheStream = null;
12289 return function () {
12290 project = projectAt.apply(this, arguments);
12291 projection.invert = project.invert && invert;
12296 function mercatorRaw(lambda, phi) {
12297 return [lambda, log$1(tan((halfPi + phi) / 2))];
12300 mercatorRaw.invert = function (x, y) {
12301 return [x, 2 * atan(exp$2(y)) - halfPi];
12304 function mercator () {
12305 return mercatorProjection(mercatorRaw).scale(961 / tau);
12307 function mercatorProjection(project) {
12308 var m = projection(project),
12311 translate = m.translate,
12312 clipExtent = m.clipExtent,
12318 m.scale = function (_) {
12319 return arguments.length ? (scale(_), reclip()) : scale();
12322 m.translate = function (_) {
12323 return arguments.length ? (translate(_), reclip()) : translate();
12326 m.center = function (_) {
12327 return arguments.length ? (center(_), reclip()) : center();
12330 m.clipExtent = function (_) {
12331 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]];
12334 function reclip() {
12335 var k = pi * scale(),
12336 t = m(rotation(m.rotate()).invert([0, 0]));
12337 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)]]);
12343 function d3_geoIdentity () {
12349 // scale, translate and reflect
12361 transform = transformer$1({
12362 point: function point(x, y) {
12363 var p = projection([x, y]);
12364 this.stream.point(p[0], p[1]);
12367 postclip = identity$4,
12374 cache = cacheStream = null;
12378 function projection(p) {
12383 var t = y * ca - x * sa;
12384 x = x * ca + y * sa;
12388 return [x + tx, y + ty];
12391 projection.invert = function (p) {
12396 var t = y * ca + x * sa;
12397 x = x * ca - y * sa;
12401 return [x / kx, y / ky];
12404 projection.stream = function (stream) {
12405 return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12408 projection.postclip = function (_) {
12409 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12412 projection.clipExtent = function (_) {
12413 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12416 projection.scale = function (_) {
12417 return arguments.length ? (k = +_, reset()) : k;
12420 projection.translate = function (_) {
12421 return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12424 projection.angle = function (_) {
12425 return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees$1;
12428 projection.reflectX = function (_) {
12429 return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12432 projection.reflectY = function (_) {
12433 return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12436 projection.fitExtent = function (extent, object) {
12437 return fitExtent(projection, extent, object);
12440 projection.fitSize = function (size, object) {
12441 return fitSize(projection, size, object);
12444 projection.fitWidth = function (width, object) {
12445 return fitWidth(projection, width, object);
12448 projection.fitHeight = function (height, object) {
12449 return fitHeight(projection, height, object);
12456 var TAU = 2 * Math.PI;
12457 var EQUATORIAL_RADIUS = 6356752.314245179;
12458 var POLAR_RADIUS$1 = 6378137.0;
12459 function geoLatToMeters(dLat) {
12460 return dLat * (TAU * POLAR_RADIUS$1 / 360);
12462 function geoLonToMeters(dLon, atLat) {
12463 return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12465 function geoMetersToLat(m) {
12466 return m / (TAU * POLAR_RADIUS$1 / 360);
12468 function geoMetersToLon(m, atLat) {
12469 return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12471 function geoMetersToOffset(meters, tileSize) {
12472 tileSize = tileSize || 256;
12473 return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS$1)];
12475 function geoOffsetToMeters(offset, tileSize) {
12476 tileSize = tileSize || 256;
12477 return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS$1 / tileSize];
12478 } // Equirectangular approximation of spherical distances on Earth
12480 function geoSphericalDistance(a, b) {
12481 var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12482 var y = geoLatToMeters(a[1] - b[1]);
12483 return Math.sqrt(x * x + y * y);
12486 function geoScaleToZoom(k, tileSize) {
12487 tileSize = tileSize || 256;
12488 var log2ts = Math.log(tileSize) * Math.LOG2E;
12489 return Math.log(k * TAU) / Math.LN2 - log2ts;
12492 function geoZoomToScale(z, tileSize) {
12493 tileSize = tileSize || 256;
12494 return tileSize * Math.pow(2, z) / TAU;
12495 } // returns info about the node from `nodes` closest to the given `point`
12497 function geoSphericalClosestNode(nodes, point) {
12498 var minDistance = Infinity,
12502 for (var i in nodes) {
12503 distance = geoSphericalDistance(nodes[i].loc, point);
12505 if (distance < minDistance) {
12506 minDistance = distance;
12511 if (indexOfMin !== undefined) {
12514 distance: minDistance,
12515 node: nodes[indexOfMin]
12522 function geoExtent(min, max) {
12523 if (!(this instanceof geoExtent)) {
12524 return new geoExtent(min, max);
12525 } else if (min instanceof geoExtent) {
12527 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12531 this[0] = min || [Infinity, Infinity];
12532 this[1] = max || min || [-Infinity, -Infinity];
12535 geoExtent.prototype = new Array(2);
12536 Object.assign(geoExtent.prototype, {
12537 equals: function equals(obj) {
12538 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];
12540 extend: function extend(obj) {
12541 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12542 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])]);
12544 _extend: function _extend(extent) {
12545 this[0][0] = Math.min(extent[0][0], this[0][0]);
12546 this[0][1] = Math.min(extent[0][1], this[0][1]);
12547 this[1][0] = Math.max(extent[1][0], this[1][0]);
12548 this[1][1] = Math.max(extent[1][1], this[1][1]);
12550 area: function area() {
12551 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12553 center: function center() {
12554 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12556 rectangle: function rectangle() {
12557 return [this[0][0], this[0][1], this[1][0], this[1][1]];
12559 bbox: function bbox() {
12567 polygon: function polygon() {
12568 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]]];
12570 contains: function contains(obj) {
12571 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12572 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];
12574 intersects: function intersects(obj) {
12575 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12576 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];
12578 intersection: function intersection(obj) {
12579 if (!this.intersects(obj)) return new geoExtent();
12580 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])]);
12582 percentContainedIn: function percentContainedIn(obj) {
12583 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12584 var a1 = this.intersection(obj).area();
12585 var a2 = this.area();
12587 if (a1 === Infinity || a2 === Infinity) {
12589 } else if (a1 === 0 || a2 === 0) {
12590 if (obj.contains(this)) {
12599 padByMeters: function padByMeters(meters) {
12600 var dLat = geoMetersToLat(meters);
12601 var dLon = geoMetersToLon(meters, this.center()[1]);
12602 return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12604 toParam: function toParam() {
12605 return this.rectangle().join(',');
12609 var $every = arrayIteration.every;
12612 var STRICT_METHOD$1 = arrayMethodIsStrict('every');
12614 // `Array.prototype.every` method
12615 // https://tc39.es/ecma262/#sec-array.prototype.every
12616 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$1 }, {
12617 every: function every(callbackfn /* , thisArg */) {
12618 return $every(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12622 var $reduce = arrayReduce.left;
12627 var STRICT_METHOD = arrayMethodIsStrict('reduce');
12628 // Chrome 80-82 has a critical bug
12629 // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
12630 var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
12632 // `Array.prototype.reduce` method
12633 // https://tc39.es/ecma262/#sec-array.prototype.reduce
12634 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD || CHROME_BUG }, {
12635 reduce: function reduce(callbackfn /* , initialValue */) {
12636 return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12640 function d3_polygonArea (polygon) {
12642 n = polygon.length,
12644 b = polygon[n - 1],
12650 area += a[1] * b[0] - a[0] * b[1];
12656 function d3_polygonCentroid (polygon) {
12658 n = polygon.length,
12662 b = polygon[n - 1],
12669 k += c = a[0] * b[1] - b[0] * a[1];
12670 x += (a[0] + b[0]) * c;
12671 y += (a[1] + b[1]) * c;
12674 return k *= 3, [x / k, y / k];
12677 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12678 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12679 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12680 // negative if clockwise, and zero if the points are collinear.
12681 function cross (a, b, c) {
12682 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12685 function lexicographicOrder(a, b) {
12686 return a[0] - b[0] || a[1] - b[1];
12687 } // Computes the upper convex hull per the monotone chain algorithm.
12688 // Assumes points.length >= 3, is sorted by x, unique in y.
12689 // Returns an array of indices into points in left-to-right order.
12692 function computeUpperHullIndexes(points) {
12693 var n = points.length,
12698 for (i = 2; i < n; ++i) {
12699 while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12703 indexes[size++] = i;
12706 return indexes.slice(0, size); // remove popped points
12709 function d3_polygonHull (points) {
12710 if ((n = points.length) < 3) return null;
12713 sortedPoints = new Array(n),
12714 flippedPoints = new Array(n);
12716 for (i = 0; i < n; ++i) {
12717 sortedPoints[i] = [+points[i][0], +points[i][1], i];
12720 sortedPoints.sort(lexicographicOrder);
12722 for (i = 0; i < n; ++i) {
12723 flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12726 var upperIndexes = computeUpperHullIndexes(sortedPoints),
12727 lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12729 var skipLeft = lowerIndexes[0] === upperIndexes[0],
12730 skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12731 hull = []; // Add upper hull in right-to-l order.
12732 // Then add lower hull in left-to-right order.
12734 for (i = upperIndexes.length - 1; i >= 0; --i) {
12735 hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12738 for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12739 hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12746 function geoVecEqual(a, b, epsilon) {
12748 return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12750 return a[0] === b[0] && a[1] === b[1];
12752 } // vector addition
12754 function geoVecAdd(a, b) {
12755 return [a[0] + b[0], a[1] + b[1]];
12756 } // vector subtraction
12758 function geoVecSubtract(a, b) {
12759 return [a[0] - b[0], a[1] - b[1]];
12760 } // vector scaling
12762 function geoVecScale(a, mag) {
12763 return [a[0] * mag, a[1] * mag];
12764 } // vector rounding (was: geoRoundCoordinates)
12766 function geoVecFloor(a) {
12767 return [Math.floor(a[0]), Math.floor(a[1])];
12768 } // linear interpolation
12770 function geoVecInterp(a, b, t) {
12771 return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12772 } // http://jsperf.com/id-dist-optimization
12774 function geoVecLength(a, b) {
12775 return Math.sqrt(geoVecLengthSquare(a, b));
12776 } // length of vector raised to the power two
12778 function geoVecLengthSquare(a, b) {
12780 var x = a[0] - b[0];
12781 var y = a[1] - b[1];
12782 return x * x + y * y;
12783 } // get a unit vector
12785 function geoVecNormalize(a) {
12786 var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12788 if (length !== 0) {
12789 return geoVecScale(a, 1 / length);
12793 } // Return the counterclockwise angle in the range (-pi, pi)
12794 // between the positive X axis and the line intersecting a and b.
12796 function geoVecAngle(a, b) {
12797 return Math.atan2(b[1] - a[1], b[0] - a[0]);
12800 function geoVecDot(a, b, origin) {
12801 origin = origin || [0, 0];
12802 var p = geoVecSubtract(a, origin);
12803 var q = geoVecSubtract(b, origin);
12804 return p[0] * q[0] + p[1] * q[1];
12805 } // normalized dot product
12807 function geoVecNormalizedDot(a, b, origin) {
12808 origin = origin || [0, 0];
12809 var p = geoVecNormalize(geoVecSubtract(a, origin));
12810 var q = geoVecNormalize(geoVecSubtract(b, origin));
12811 return geoVecDot(p, q);
12812 } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12813 // Returns a positive value, if OAB makes a counter-clockwise turn,
12814 // negative for clockwise turn, and zero if the points are collinear.
12816 function geoVecCross(a, b, origin) {
12817 origin = origin || [0, 0];
12818 var p = geoVecSubtract(a, origin);
12819 var q = geoVecSubtract(b, origin);
12820 return p[0] * q[1] - p[1] * q[0];
12821 } // find closest orthogonal projection of point onto points array
12823 function geoVecProject(a, points) {
12824 var min = Infinity;
12828 for (var i = 0; i < points.length - 1; i++) {
12830 var s = geoVecSubtract(points[i + 1], o);
12831 var v = geoVecSubtract(a, o);
12832 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12837 } else if (proj > 1) {
12840 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12843 var dist = geoVecLength(p, a);
12852 if (idx !== undefined) {
12863 // between the positive X axis and the line intersecting a and b.
12865 function geoAngle(a, b, projection) {
12866 return geoVecAngle(projection(a.loc), projection(b.loc));
12868 function geoEdgeEqual(a, b) {
12869 return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12870 } // Rotate all points counterclockwise around a pivot point by given angle
12872 function geoRotate(points, angle, around) {
12873 return points.map(function (point) {
12874 var radial = geoVecSubtract(point, around);
12875 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]];
12877 } // Choose the edge with the minimal distance from `point` to its orthogonal
12878 // projection onto that edge, if such a projection exists, or the distance to
12879 // the closest vertex on that edge. Returns an object with the `index` of the
12880 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12882 function geoChooseEdge(nodes, point, projection, activeID) {
12883 var dist = geoVecLength;
12884 var points = nodes.map(function (n) {
12885 return projection(n.loc);
12887 var ids = nodes.map(function (n) {
12890 var min = Infinity;
12894 for (var i = 0; i < points.length - 1; i++) {
12895 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12897 var s = geoVecSubtract(points[i + 1], o);
12898 var v = geoVecSubtract(point, o);
12899 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12904 } else if (proj > 1) {
12907 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12910 var d = dist(p, point);
12915 loc = projection.invert(p);
12919 if (idx !== undefined) {
12928 } // Test active (dragged or drawing) segments against inactive segments
12929 // This is used to test e.g. multipolygon rings that cross
12930 // `activeNodes` is the ring containing the activeID being dragged.
12931 // `inactiveNodes` is the other ring to test against
12933 function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12935 var inactives = [];
12936 var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12938 for (j = 0; j < activeNodes.length - 1; j++) {
12939 n1 = activeNodes[j];
12940 n2 = activeNodes[j + 1];
12941 segment = [n1.loc, n2.loc];
12943 if (n1.id === activeID || n2.id === activeID) {
12944 actives.push(segment);
12946 } // gather inactive segments
12949 for (j = 0; j < inactiveNodes.length - 1; j++) {
12950 n1 = inactiveNodes[j];
12951 n2 = inactiveNodes[j + 1];
12952 segment = [n1.loc, n2.loc];
12953 inactives.push(segment);
12957 for (j = 0; j < actives.length; j++) {
12958 for (k = 0; k < inactives.length; k++) {
12959 var p = actives[j];
12960 var q = inactives[k];
12961 var hit = geoLineIntersection(p, q);
12970 } // Test active (dragged or drawing) segments against inactive segments
12971 // This is used to test whether a way intersects with itself.
12973 function geoHasSelfIntersections(nodes, activeID) {
12975 var inactives = [];
12976 var j, k; // group active and passive segments along the nodes
12978 for (j = 0; j < nodes.length - 1; j++) {
12980 var n2 = nodes[j + 1];
12981 var segment = [n1.loc, n2.loc];
12983 if (n1.id === activeID || n2.id === activeID) {
12984 actives.push(segment);
12986 inactives.push(segment);
12991 for (j = 0; j < actives.length; j++) {
12992 for (k = 0; k < inactives.length; k++) {
12993 var p = actives[j];
12994 var q = inactives[k]; // skip if segments share an endpoint
12996 if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
13000 var hit = geoLineIntersection(p, q);
13003 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
13005 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
13015 } // Return the intersection point of 2 line segments.
13016 // From https://github.com/pgkelley4/line-segments-intersect
13017 // This uses the vector cross product approach described below:
13018 // http://stackoverflow.com/a/565282/786339
13020 function geoLineIntersection(a, b) {
13021 var p = [a[0][0], a[0][1]];
13022 var p2 = [a[1][0], a[1][1]];
13023 var q = [b[0][0], b[0][1]];
13024 var q2 = [b[1][0], b[1][1]];
13025 var r = geoVecSubtract(p2, p);
13026 var s = geoVecSubtract(q2, q);
13027 var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
13028 var denominator = geoVecCross(r, s);
13030 if (uNumerator && denominator) {
13031 var u = uNumerator / denominator;
13032 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
13034 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
13035 return geoVecInterp(p, p2, t);
13041 function geoPathIntersections(path1, path2) {
13042 var intersections = [];
13044 for (var i = 0; i < path1.length - 1; i++) {
13045 for (var j = 0; j < path2.length - 1; j++) {
13046 var a = [path1[i], path1[i + 1]];
13047 var b = [path2[j], path2[j + 1]];
13048 var hit = geoLineIntersection(a, b);
13051 intersections.push(hit);
13056 return intersections;
13058 function geoPathHasIntersections(path1, path2) {
13059 for (var i = 0; i < path1.length - 1; i++) {
13060 for (var j = 0; j < path2.length - 1; j++) {
13061 var a = [path1[i], path1[i + 1]];
13062 var b = [path2[j], path2[j + 1]];
13063 var hit = geoLineIntersection(a, b);
13072 } // Return whether point is contained in polygon.
13074 // `point` should be a 2-item array of coordinates.
13075 // `polygon` should be an array of 2-item arrays of coordinates.
13077 // From https://github.com/substack/point-in-polygon.
13078 // ray-casting algorithm based on
13079 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
13082 function geoPointInPolygon(point, polygon) {
13085 var inside = false;
13087 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
13088 var xi = polygon[i][0];
13089 var yi = polygon[i][1];
13090 var xj = polygon[j][0];
13091 var yj = polygon[j][1];
13092 var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
13093 if (intersect) inside = !inside;
13098 function geoPolygonContainsPolygon(outer, inner) {
13099 return inner.every(function (point) {
13100 return geoPointInPolygon(point, outer);
13103 function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
13104 function testPoints(outer, inner) {
13105 return inner.some(function (point) {
13106 return geoPointInPolygon(point, outer);
13110 return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
13111 } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
13112 // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
13114 function geoGetSmallestSurroundingRectangle(points) {
13115 var hull = d3_polygonHull(points);
13116 var centroid = d3_polygonCentroid(hull);
13117 var minArea = Infinity;
13118 var ssrExtent = [];
13122 for (var i = 0; i <= hull.length - 1; i++) {
13123 var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
13124 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
13125 var poly = geoRotate(hull, -angle, centroid);
13126 var extent = poly.reduce(function (extent, point) {
13127 return extent.extend(geoExtent(point));
13129 var area = extent.area();
13131 if (area < minArea) {
13133 ssrExtent = extent;
13141 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
13145 function geoPathLength(path) {
13148 for (var i = 0; i < path.length - 1; i++) {
13149 length += geoVecLength(path[i], path[i + 1]);
13153 } // If the given point is at the edge of the padded viewport,
13154 // return a vector that will nudge the viewport in that direction
13156 function geoViewportEdge(point, dimensions) {
13157 var pad = [80, 20, 50, 20]; // top, right, bottom, left
13162 if (point[0] > dimensions[0] - pad[1]) {
13166 if (point[0] < pad[3]) {
13170 if (point[1] > dimensions[1] - pad[2]) {
13174 if (point[1] < pad[0]) {
13186 value: function value() {}
13189 function dispatch$8() {
13190 for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
13191 if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
13195 return new Dispatch(_);
13198 function Dispatch(_) {
13202 function parseTypenames$1(typenames, types) {
13203 return typenames.trim().split(/^|\s+/).map(function (t) {
13205 i = t.indexOf(".");
13206 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13207 if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13215 Dispatch.prototype = dispatch$8.prototype = {
13216 constructor: Dispatch,
13217 on: function on(typename, callback) {
13219 T = parseTypenames$1(typename + "", _),
13222 n = T.length; // If no callback was specified, return the callback of the given type and name.
13224 if (arguments.length < 2) {
13226 if ((t = (typename = T[i]).type) && (t = get$2(_[t], typename.name))) return t;
13230 } // If a type was specified, set the callback for the given type and name.
13231 // Otherwise, if a null callback was specified, remove callbacks of the given name.
13234 if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13237 if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13238 _[t] = set$1(_[t], typename.name, null);
13244 copy: function copy() {
13249 copy[t] = _[t].slice();
13252 return new Dispatch(copy);
13254 call: function call(type, that) {
13255 if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13256 args[i] = arguments[i + 2];
13258 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13260 for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13261 t[i].value.apply(that, args);
13264 apply: function apply(type, that, args) {
13265 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13267 for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13268 t[i].value.apply(that, args);
13273 function get$2(type, name) {
13274 for (var i = 0, n = type.length, c; i < n; ++i) {
13275 if ((c = type[i]).name === name) {
13281 function set$1(type, name, callback) {
13282 for (var i = 0, n = type.length; i < n; ++i) {
13283 if (type[i].name === name) {
13284 type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
13289 if (callback != null) type.push({
13296 var xhtml = "http://www.w3.org/1999/xhtml";
13298 svg: "http://www.w3.org/2000/svg",
13300 xlink: "http://www.w3.org/1999/xlink",
13301 xml: "http://www.w3.org/XML/1998/namespace",
13302 xmlns: "http://www.w3.org/2000/xmlns/"
13305 function namespace (name) {
13306 var prefix = name += "",
13307 i = prefix.indexOf(":");
13308 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13309 return namespaces.hasOwnProperty(prefix) ? {
13310 space: namespaces[prefix],
13312 } : name; // eslint-disable-line no-prototype-builtins
13315 function creatorInherit(name) {
13316 return function () {
13317 var document = this.ownerDocument,
13318 uri = this.namespaceURI;
13319 return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13323 function creatorFixed(fullname) {
13324 return function () {
13325 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13329 function creator (name) {
13330 var fullname = namespace(name);
13331 return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13336 function selector (selector) {
13337 return selector == null ? none : function () {
13338 return this.querySelector(selector);
13342 function selection_select (select) {
13343 if (typeof select !== "function") select = selector(select);
13345 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13346 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13347 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13348 if ("__data__" in node) subnode.__data__ = node.__data__;
13349 subgroup[i] = subnode;
13354 return new Selection$1(subgroups, this._parents);
13357 function array (x) {
13358 return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13359 : Array.from(x); // Map, Set, iterable, string, or anything else
13366 function selectorAll (selector) {
13367 return selector == null ? empty : function () {
13368 return this.querySelectorAll(selector);
13372 function arrayAll(select) {
13373 return function () {
13374 var group = select.apply(this, arguments);
13375 return group == null ? [] : array(group);
13379 function selection_selectAll (select) {
13380 if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13382 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13383 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13384 if (node = group[i]) {
13385 subgroups.push(select.call(node, node.__data__, i, group));
13386 parents.push(node);
13391 return new Selection$1(subgroups, parents);
13394 var $find = arrayIteration.find;
13398 var SKIPS_HOLES$1 = true;
13400 // Shouldn't skip holes
13401 if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES$1 = false; });
13403 // `Array.prototype.find` method
13404 // https://tc39.es/ecma262/#sec-array.prototype.find
13405 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 }, {
13406 find: function find(callbackfn /* , that = undefined */) {
13407 return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13411 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
13412 addToUnscopables(FIND);
13414 function matcher (selector) {
13415 return function () {
13416 return this.matches(selector);
13419 function childMatcher(selector) {
13420 return function (node) {
13421 return node.matches(selector);
13425 var find = Array.prototype.find;
13427 function childFind(match) {
13428 return function () {
13429 return find.call(this.children, match);
13433 function childFirst() {
13434 return this.firstElementChild;
13437 function selection_selectChild (match) {
13438 return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13441 var filter = Array.prototype.filter;
13443 function children() {
13444 return this.children;
13447 function childrenFilter(match) {
13448 return function () {
13449 return filter.call(this.children, match);
13453 function selection_selectChildren (match) {
13454 return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13457 function selection_filter (match) {
13458 if (typeof match !== "function") match = matcher(match);
13460 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13461 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13462 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13463 subgroup.push(node);
13468 return new Selection$1(subgroups, this._parents);
13471 function sparse (update) {
13472 return new Array(update.length);
13475 function selection_enter () {
13476 return new Selection$1(this._enter || this._groups.map(sparse), this._parents);
13478 function EnterNode(parent, datum) {
13479 this.ownerDocument = parent.ownerDocument;
13480 this.namespaceURI = parent.namespaceURI;
13482 this._parent = parent;
13483 this.__data__ = datum;
13485 EnterNode.prototype = {
13486 constructor: EnterNode,
13487 appendChild: function appendChild(child) {
13488 return this._parent.insertBefore(child, this._next);
13490 insertBefore: function insertBefore(child, next) {
13491 return this._parent.insertBefore(child, next);
13493 querySelector: function querySelector(selector) {
13494 return this._parent.querySelector(selector);
13496 querySelectorAll: function querySelectorAll(selector) {
13497 return this._parent.querySelectorAll(selector);
13501 function constant$3 (x) {
13502 return function () {
13507 function bindIndex(parent, group, enter, update, exit, data) {
13510 groupLength = group.length,
13511 dataLength = data.length; // Put any non-null nodes that fit into update.
13512 // Put any null nodes into enter.
13513 // Put any remaining data into enter.
13515 for (; i < dataLength; ++i) {
13516 if (node = group[i]) {
13517 node.__data__ = data[i];
13520 enter[i] = new EnterNode(parent, data[i]);
13522 } // Put any non-null nodes that don’t fit into exit.
13525 for (; i < groupLength; ++i) {
13526 if (node = group[i]) {
13532 function bindKey(parent, group, enter, update, exit, data, key) {
13535 nodeByKeyValue = new Map(),
13536 groupLength = group.length,
13537 dataLength = data.length,
13538 keyValues = new Array(groupLength),
13539 keyValue; // Compute the key for each node.
13540 // If multiple nodes have the same key, the duplicates are added to exit.
13542 for (i = 0; i < groupLength; ++i) {
13543 if (node = group[i]) {
13544 keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13546 if (nodeByKeyValue.has(keyValue)) {
13549 nodeByKeyValue.set(keyValue, node);
13552 } // Compute the key for each datum.
13553 // If there a node associated with this key, join and add it to update.
13554 // If there is not (or the key is a duplicate), add it to enter.
13557 for (i = 0; i < dataLength; ++i) {
13558 keyValue = key.call(parent, data[i], i, data) + "";
13560 if (node = nodeByKeyValue.get(keyValue)) {
13562 node.__data__ = data[i];
13563 nodeByKeyValue["delete"](keyValue);
13565 enter[i] = new EnterNode(parent, data[i]);
13567 } // Add any remaining nodes that were not bound to data to exit.
13570 for (i = 0; i < groupLength; ++i) {
13571 if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13577 function datum(node) {
13578 return node.__data__;
13581 function selection_data (value, key) {
13582 if (!arguments.length) return Array.from(this, datum);
13583 var bind = key ? bindKey : bindIndex,
13584 parents = this._parents,
13585 groups = this._groups;
13586 if (typeof value !== "function") value = constant$3(value);
13588 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13589 var parent = parents[j],
13591 groupLength = group.length,
13592 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13593 dataLength = data.length,
13594 enterGroup = enter[j] = new Array(dataLength),
13595 updateGroup = update[j] = new Array(dataLength),
13596 exitGroup = exit[j] = new Array(groupLength);
13597 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13598 // appendChild can insert the materialized enter node before this node,
13599 // rather than at the end of the parent node.
13601 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13602 if (previous = enterGroup[i0]) {
13603 if (i0 >= i1) i1 = i0 + 1;
13605 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13608 previous._next = next || null;
13613 update = new Selection$1(update, parents);
13614 update._enter = enter;
13615 update._exit = exit;
13619 function selection_exit () {
13620 return new Selection$1(this._exit || this._groups.map(sparse), this._parents);
13623 function selection_join (onenter, onupdate, onexit) {
13624 var enter = this.enter(),
13626 exit = this.exit();
13627 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13628 if (onupdate != null) update = onupdate(update);
13629 if (onexit == null) exit.remove();else onexit(exit);
13630 return enter && update ? enter.merge(update).order() : update;
13633 function selection_merge (selection) {
13634 if (!(selection instanceof Selection$1)) throw new Error("invalid merge");
13636 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) {
13637 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13638 if (node = group0[i] || group1[i]) {
13644 for (; j < m0; ++j) {
13645 merges[j] = groups0[j];
13648 return new Selection$1(merges, this._parents);
13651 function selection_order () {
13652 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13653 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13654 if (node = group[i]) {
13655 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13664 function selection_sort (compare) {
13665 if (!compare) compare = ascending;
13667 function compareNode(a, b) {
13668 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13671 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13672 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13673 if (node = group[i]) {
13674 sortgroup[i] = node;
13678 sortgroup.sort(compareNode);
13681 return new Selection$1(sortgroups, this._parents).order();
13684 function ascending(a, b) {
13685 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13688 function selection_call () {
13689 var callback = arguments[0];
13690 arguments[0] = this;
13691 callback.apply(null, arguments);
13695 function selection_nodes () {
13696 return Array.from(this);
13699 function selection_node () {
13700 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13701 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13702 var node = group[i];
13703 if (node) return node;
13710 function selection_size () {
13713 var _iterator = _createForOfIteratorHelper(this),
13717 for (_iterator.s(); !(_step = _iterator.n()).done;) {
13718 var node = _step.value;
13720 } // eslint-disable-line no-unused-vars
13731 function selection_empty () {
13732 return !this.node();
13735 function selection_each (callback) {
13736 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13737 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13738 if (node = group[i]) callback.call(node, node.__data__, i, group);
13745 function attrRemove$1(name) {
13746 return function () {
13747 this.removeAttribute(name);
13751 function attrRemoveNS$1(fullname) {
13752 return function () {
13753 this.removeAttributeNS(fullname.space, fullname.local);
13757 function attrConstant$1(name, value) {
13758 return function () {
13759 this.setAttribute(name, value);
13763 function attrConstantNS$1(fullname, value) {
13764 return function () {
13765 this.setAttributeNS(fullname.space, fullname.local, value);
13769 function attrFunction$1(name, value) {
13770 return function () {
13771 var v = value.apply(this, arguments);
13772 if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13776 function attrFunctionNS$1(fullname, value) {
13777 return function () {
13778 var v = value.apply(this, arguments);
13779 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13783 function selection_attr (name, value) {
13784 var fullname = namespace(name);
13786 if (arguments.length < 2) {
13787 var node = this.node();
13788 return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13791 return this.each((value == null ? fullname.local ? attrRemoveNS$1 : attrRemove$1 : typeof value === "function" ? fullname.local ? attrFunctionNS$1 : attrFunction$1 : fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, value));
13794 function defaultView (node) {
13795 return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13796 node.document && node // node is a Window
13797 || node.defaultView; // node is a Document
13800 function styleRemove$1(name) {
13801 return function () {
13802 this.style.removeProperty(name);
13806 function styleConstant$1(name, value, priority) {
13807 return function () {
13808 this.style.setProperty(name, value, priority);
13812 function styleFunction$1(name, value, priority) {
13813 return function () {
13814 var v = value.apply(this, arguments);
13815 if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13819 function selection_style (name, value, priority) {
13820 return arguments.length > 1 ? this.each((value == null ? styleRemove$1 : typeof value === "function" ? styleFunction$1 : styleConstant$1)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13822 function styleValue(node, name) {
13823 return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13826 function propertyRemove(name) {
13827 return function () {
13832 function propertyConstant(name, value) {
13833 return function () {
13834 this[name] = value;
13838 function propertyFunction(name, value) {
13839 return function () {
13840 var v = value.apply(this, arguments);
13841 if (v == null) delete this[name];else this[name] = v;
13845 function selection_property (name, value) {
13846 return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13849 function classArray(string) {
13850 return string.trim().split(/^|\s+/);
13853 function classList(node) {
13854 return node.classList || new ClassList(node);
13857 function ClassList(node) {
13859 this._names = classArray(node.getAttribute("class") || "");
13862 ClassList.prototype = {
13863 add: function add(name) {
13864 var i = this._names.indexOf(name);
13867 this._names.push(name);
13869 this._node.setAttribute("class", this._names.join(" "));
13872 remove: function remove(name) {
13873 var i = this._names.indexOf(name);
13876 this._names.splice(i, 1);
13878 this._node.setAttribute("class", this._names.join(" "));
13881 contains: function contains(name) {
13882 return this._names.indexOf(name) >= 0;
13886 function classedAdd(node, names) {
13887 var list = classList(node),
13892 list.add(names[i]);
13896 function classedRemove(node, names) {
13897 var list = classList(node),
13902 list.remove(names[i]);
13906 function classedTrue(names) {
13907 return function () {
13908 classedAdd(this, names);
13912 function classedFalse(names) {
13913 return function () {
13914 classedRemove(this, names);
13918 function classedFunction(names, value) {
13919 return function () {
13920 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13924 function selection_classed (name, value) {
13925 var names = classArray(name + "");
13927 if (arguments.length < 2) {
13928 var list = classList(this.node()),
13933 if (!list.contains(names[i])) return false;
13939 return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13942 function textRemove() {
13943 this.textContent = "";
13946 function textConstant$1(value) {
13947 return function () {
13948 this.textContent = value;
13952 function textFunction$1(value) {
13953 return function () {
13954 var v = value.apply(this, arguments);
13955 this.textContent = v == null ? "" : v;
13959 function selection_text (value) {
13960 return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction$1 : textConstant$1)(value)) : this.node().textContent;
13963 function htmlRemove() {
13964 this.innerHTML = "";
13967 function htmlConstant(value) {
13968 return function () {
13969 this.innerHTML = value;
13973 function htmlFunction(value) {
13974 return function () {
13975 var v = value.apply(this, arguments);
13976 this.innerHTML = v == null ? "" : v;
13980 function selection_html (value) {
13981 return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13985 if (this.nextSibling) this.parentNode.appendChild(this);
13988 function selection_raise () {
13989 return this.each(raise);
13993 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13996 function selection_lower () {
13997 return this.each(lower);
14000 function selection_append (name) {
14001 var create = typeof name === "function" ? name : creator(name);
14002 return this.select(function () {
14003 return this.appendChild(create.apply(this, arguments));
14007 function constantNull() {
14011 function selection_insert (name, before) {
14012 var create = typeof name === "function" ? name : creator(name),
14013 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
14014 return this.select(function () {
14015 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
14019 function remove$7() {
14020 var parent = this.parentNode;
14021 if (parent) parent.removeChild(this);
14024 function selection_remove () {
14025 return this.each(remove$7);
14028 function selection_cloneShallow() {
14029 var clone = this.cloneNode(false),
14030 parent = this.parentNode;
14031 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14034 function selection_cloneDeep() {
14035 var clone = this.cloneNode(true),
14036 parent = this.parentNode;
14037 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14040 function selection_clone (deep) {
14041 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
14044 function selection_datum (value) {
14045 return arguments.length ? this.property("__data__", value) : this.node().__data__;
14048 function contextListener(listener) {
14049 return function (event) {
14050 listener.call(this, event, this.__data__);
14054 function parseTypenames(typenames) {
14055 return typenames.trim().split(/^|\s+/).map(function (t) {
14057 i = t.indexOf(".");
14058 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
14066 function onRemove(typename) {
14067 return function () {
14068 var on = this.__on;
14071 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
14072 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
14073 this.removeEventListener(o.type, o.listener, o.options);
14079 if (++i) on.length = i;else delete this.__on;
14083 function onAdd(typename, value, options) {
14084 return function () {
14085 var on = this.__on,
14087 listener = contextListener(value);
14088 if (on) for (var j = 0, m = on.length; j < m; ++j) {
14089 if ((o = on[j]).type === typename.type && o.name === typename.name) {
14090 this.removeEventListener(o.type, o.listener, o.options);
14091 this.addEventListener(o.type, o.listener = listener, o.options = options);
14096 this.addEventListener(typename.type, listener, options);
14098 type: typename.type,
14099 name: typename.name,
14101 listener: listener,
14104 if (!on) this.__on = [o];else on.push(o);
14108 function selection_on (typename, value, options) {
14109 var typenames = parseTypenames(typename + ""),
14111 n = typenames.length,
14114 if (arguments.length < 2) {
14115 var on = this.node().__on;
14117 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
14118 for (i = 0, o = on[j]; i < n; ++i) {
14119 if ((t = typenames[i]).type === o.type && t.name === o.name) {
14127 on = value ? onAdd : onRemove;
14129 for (i = 0; i < n; ++i) {
14130 this.each(on(typenames[i], value, options));
14136 function dispatchEvent(node, type, params) {
14137 var window = defaultView(node),
14138 event = window.CustomEvent;
14140 if (typeof event === "function") {
14141 event = new event(type, params);
14143 event = window.document.createEvent("Event");
14144 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
14147 node.dispatchEvent(event);
14150 function dispatchConstant(type, params) {
14151 return function () {
14152 return dispatchEvent(this, type, params);
14156 function dispatchFunction(type, params) {
14157 return function () {
14158 return dispatchEvent(this, type, params.apply(this, arguments));
14162 function selection_dispatch (type, params) {
14163 return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
14166 var _marked = /*#__PURE__*/regeneratorRuntime.mark(_callee);
14168 function _callee() {
14169 var groups, j, m, group, i, n, node;
14170 return regeneratorRuntime.wrap(function _callee$(_context) {
14172 switch (_context.prev = _context.next) {
14174 groups = this._groups, j = 0, m = groups.length;
14178 _context.next = 13;
14182 group = groups[j], i = 0, n = group.length;
14186 _context.next = 10;
14190 if (!(node = group[i])) {
14210 return _context.stop();
14216 var root$1 = [null];
14217 function Selection$1(groups, parents) {
14218 this._groups = groups;
14219 this._parents = parents;
14222 function selection() {
14223 return new Selection$1([[document.documentElement]], root$1);
14226 function selection_selection() {
14230 Selection$1.prototype = selection.prototype = _defineProperty({
14231 constructor: Selection$1,
14232 select: selection_select,
14233 selectAll: selection_selectAll,
14234 selectChild: selection_selectChild,
14235 selectChildren: selection_selectChildren,
14236 filter: selection_filter,
14237 data: selection_data,
14238 enter: selection_enter,
14239 exit: selection_exit,
14240 join: selection_join,
14241 merge: selection_merge,
14242 selection: selection_selection,
14243 order: selection_order,
14244 sort: selection_sort,
14245 call: selection_call,
14246 nodes: selection_nodes,
14247 node: selection_node,
14248 size: selection_size,
14249 empty: selection_empty,
14250 each: selection_each,
14251 attr: selection_attr,
14252 style: selection_style,
14253 property: selection_property,
14254 classed: selection_classed,
14255 text: selection_text,
14256 html: selection_html,
14257 raise: selection_raise,
14258 lower: selection_lower,
14259 append: selection_append,
14260 insert: selection_insert,
14261 remove: selection_remove,
14262 clone: selection_clone,
14263 datum: selection_datum,
14265 dispatch: selection_dispatch
14266 }, Symbol.iterator, _callee);
14268 function select (selector) {
14269 return typeof selector === "string" ? new Selection$1([[document.querySelector(selector)]], [document.documentElement]) : new Selection$1([[selector]], root$1);
14272 function sourceEvent (event) {
14275 while (sourceEvent = event.sourceEvent) {
14276 event = sourceEvent;
14282 function pointer (event, node) {
14283 event = sourceEvent(event);
14284 if (node === undefined) node = event.currentTarget;
14287 var svg = node.ownerSVGElement || node;
14289 if (svg.createSVGPoint) {
14290 var point = svg.createSVGPoint();
14291 point.x = event.clientX, point.y = event.clientY;
14292 point = point.matrixTransform(node.getScreenCTM().inverse());
14293 return [point.x, point.y];
14296 if (node.getBoundingClientRect) {
14297 var rect = node.getBoundingClientRect();
14298 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14302 return [event.pageX, event.pageY];
14305 function selectAll (selector) {
14306 return typeof selector === "string" ? new Selection$1([document.querySelectorAll(selector)], [document.documentElement]) : new Selection$1([selector == null ? [] : array(selector)], root$1);
14309 function nopropagation$1(event) {
14310 event.stopImmediatePropagation();
14312 function noevent$1 (event) {
14313 event.preventDefault();
14314 event.stopImmediatePropagation();
14317 function dragDisable (view) {
14318 var root = view.document.documentElement,
14319 selection = select(view).on("dragstart.drag", noevent$1, true);
14321 if ("onselectstart" in root) {
14322 selection.on("selectstart.drag", noevent$1, true);
14324 root.__noselect = root.style.MozUserSelect;
14325 root.style.MozUserSelect = "none";
14328 function yesdrag(view, noclick) {
14329 var root = view.document.documentElement,
14330 selection = select(view).on("dragstart.drag", null);
14333 selection.on("click.drag", noevent$1, true);
14334 setTimeout(function () {
14335 selection.on("click.drag", null);
14339 if ("onselectstart" in root) {
14340 selection.on("selectstart.drag", null);
14342 root.style.MozUserSelect = root.__noselect;
14343 delete root.__noselect;
14347 var constant$2 = (function (x) {
14348 return function () {
14353 function DragEvent(type, _ref) {
14354 var sourceEvent = _ref.sourceEvent,
14355 subject = _ref.subject,
14356 target = _ref.target,
14357 identifier = _ref.identifier,
14358 active = _ref.active,
14363 dispatch = _ref.dispatch;
14364 Object.defineProperties(this, {
14371 value: sourceEvent,
14421 DragEvent.prototype.on = function () {
14422 var value = this._.on.apply(this._, arguments);
14424 return value === this._ ? this : value;
14427 function defaultFilter$2(event) {
14428 return !event.ctrlKey && !event.button;
14431 function defaultContainer() {
14432 return this.parentNode;
14435 function defaultSubject(event, d) {
14436 return d == null ? {
14442 function defaultTouchable$1() {
14443 return navigator.maxTouchPoints || "ontouchstart" in this;
14446 function d3_drag () {
14447 var filter = defaultFilter$2,
14448 container = defaultContainer,
14449 subject = defaultSubject,
14450 touchable = defaultTouchable$1,
14452 listeners = dispatch$8("start", "drag", "end"),
14458 clickDistance2 = 0;
14460 function drag(selection) {
14461 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)");
14464 function mousedowned(event, d) {
14465 if (touchending || !filter.call(this, event, d)) return;
14466 var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14467 if (!gesture) return;
14468 select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14469 dragDisable(event.view);
14470 nopropagation$1(event);
14471 mousemoving = false;
14472 mousedownx = event.clientX;
14473 mousedowny = event.clientY;
14474 gesture("start", event);
14477 function mousemoved(event) {
14480 if (!mousemoving) {
14481 var dx = event.clientX - mousedownx,
14482 dy = event.clientY - mousedowny;
14483 mousemoving = dx * dx + dy * dy > clickDistance2;
14486 gestures.mouse("drag", event);
14489 function mouseupped(event) {
14490 select(event.view).on("mousemove.drag mouseup.drag", null);
14491 yesdrag(event.view, mousemoving);
14493 gestures.mouse("end", event);
14496 function touchstarted(event, d) {
14497 if (!filter.call(this, event, d)) return;
14498 var touches = event.changedTouches,
14499 c = container.call(this, event, d),
14500 n = touches.length,
14504 for (i = 0; i < n; ++i) {
14505 if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14506 nopropagation$1(event);
14507 gesture("start", event, touches[i]);
14512 function touchmoved(event) {
14513 var touches = event.changedTouches,
14514 n = touches.length,
14518 for (i = 0; i < n; ++i) {
14519 if (gesture = gestures[touches[i].identifier]) {
14521 gesture("drag", event, touches[i]);
14526 function touchended(event) {
14527 var touches = event.changedTouches,
14528 n = touches.length,
14531 if (touchending) clearTimeout(touchending);
14532 touchending = setTimeout(function () {
14533 touchending = null;
14534 }, 500); // Ghost clicks are delayed!
14536 for (i = 0; i < n; ++i) {
14537 if (gesture = gestures[touches[i].identifier]) {
14538 nopropagation$1(event);
14539 gesture("end", event, touches[i]);
14544 function beforestart(that, container, event, d, identifier, touch) {
14545 var dispatch = listeners.copy(),
14546 p = pointer(touch || event, container),
14550 if ((s = subject.call(that, new DragEvent("beforestart", {
14551 sourceEvent: event,
14553 identifier: identifier,
14560 }), d)) == null) return;
14561 dx = s.x - p[0] || 0;
14562 dy = s.y - p[1] || 0;
14563 return function gesture(type, event, touch) {
14569 gestures[identifier] = gesture, n = active++;
14573 delete gestures[identifier], --active;
14577 p = pointer(touch || event, container), n = active;
14581 dispatch.call(type, that, new DragEvent(type, {
14582 sourceEvent: event,
14585 identifier: identifier,
14596 drag.filter = function (_) {
14597 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter;
14600 drag.container = function (_) {
14601 return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container;
14604 drag.subject = function (_) {
14605 return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject;
14608 drag.touchable = function (_) {
14609 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable;
14612 drag.on = function () {
14613 var value = listeners.on.apply(listeners, arguments);
14614 return value === listeners ? drag : value;
14617 drag.clickDistance = function (_) {
14618 return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14624 var defineProperty$1 = objectDefineProperty.f;
14625 var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14632 var enforceInternalState = internalState.enforce;
14638 var MATCH$1 = wellKnownSymbol('match');
14639 var NativeRegExp = global$2.RegExp;
14640 var RegExpPrototype = NativeRegExp.prototype;
14641 // TODO: Use only propper RegExpIdentifierName
14642 var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/;
14646 // "new" should create a new object, old webkit bug
14647 var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14649 var UNSUPPORTED_Y = regexpStickyHelpers.UNSUPPORTED_Y;
14651 var BASE_FORCED = descriptors &&
14652 (!CORRECT_NEW || UNSUPPORTED_Y || regexpUnsupportedDotAll || regexpUnsupportedNcg || fails(function () {
14653 re2[MATCH$1] = false;
14654 // RegExp constructor can alter flags and IsRegExp works correct with @@match
14655 return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14658 var handleDotAll = function (string) {
14659 var length = string.length;
14662 var brackets = false;
14664 for (; index <= length; index++) {
14665 chr = string.charAt(index);
14666 if (chr === '\\') {
14667 result += chr + string.charAt(++index);
14670 if (!brackets && chr === '.') {
14671 result += '[\\s\\S]';
14675 } else if (chr === ']') {
14682 var handleNCG = function (string) {
14683 var length = string.length;
14688 var brackets = false;
14691 var groupname = '';
14693 for (; index <= length; index++) {
14694 chr = string.charAt(index);
14695 if (chr === '\\') {
14696 chr = chr + string.charAt(++index);
14697 } else if (chr === ']') {
14699 } else if (!brackets) switch (true) {
14704 if (IS_NCG.test(string.slice(index + 1))) {
14711 case chr === '>' && ncg:
14712 if (groupname === '' || has$1(names, groupname)) {
14713 throw new SyntaxError('Invalid capture group name');
14715 names[groupname] = true;
14716 named.push([groupname, groupid]);
14721 if (ncg) groupname += chr;
14722 else result += chr;
14723 } return [result, named];
14726 // `RegExp` constructor
14727 // https://tc39.es/ecma262/#sec-regexp-constructor
14728 if (isForced_1('RegExp', BASE_FORCED)) {
14729 var RegExpWrapper = function RegExp(pattern, flags) {
14730 var thisIsRegExp = this instanceof RegExpWrapper;
14731 var patternIsRegExp = isRegexp(pattern);
14732 var flagsAreUndefined = flags === undefined;
14734 var rawPattern = pattern;
14735 var rawFlags, dotAll, sticky, handled, result, state;
14737 if (!thisIsRegExp && patternIsRegExp && flagsAreUndefined && pattern.constructor === RegExpWrapper) {
14741 if (patternIsRegExp || pattern instanceof RegExpWrapper) {
14742 pattern = pattern.source;
14743 if (flagsAreUndefined) flags = 'flags' in rawPattern ? rawPattern.flags : regexpFlags.call(rawPattern);
14746 pattern = pattern === undefined ? '' : String(pattern);
14747 flags = flags === undefined ? '' : String(flags);
14748 rawPattern = pattern;
14750 if (regexpUnsupportedDotAll && 'dotAll' in re1) {
14751 dotAll = !!flags && flags.indexOf('s') > -1;
14752 if (dotAll) flags = flags.replace(/s/g, '');
14757 if (UNSUPPORTED_Y && 'sticky' in re1) {
14758 sticky = !!flags && flags.indexOf('y') > -1;
14759 if (sticky) flags = flags.replace(/y/g, '');
14762 if (regexpUnsupportedNcg) {
14763 handled = handleNCG(pattern);
14764 pattern = handled[0];
14765 groups = handled[1];
14768 result = inheritIfRequired(NativeRegExp(pattern, flags), thisIsRegExp ? this : RegExpPrototype, RegExpWrapper);
14770 if (dotAll || sticky || groups.length) {
14771 state = enforceInternalState(result);
14773 state.dotAll = true;
14774 state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags);
14776 if (sticky) state.sticky = true;
14777 if (groups.length) state.groups = groups;
14780 if (pattern !== rawPattern) try {
14781 // fails in old engines, but we have no alternatives for unsupported regex syntax
14782 createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern);
14783 } catch (error) { /* empty */ }
14788 var proxy = function (key) {
14789 key in RegExpWrapper || defineProperty$1(RegExpWrapper, key, {
14790 configurable: true,
14791 get: function () { return NativeRegExp[key]; },
14792 set: function (it) { NativeRegExp[key] = it; }
14796 for (var keys$1 = getOwnPropertyNames$1(NativeRegExp), index$1 = 0; keys$1.length > index$1;) {
14797 proxy(keys$1[index$1++]);
14800 RegExpPrototype.constructor = RegExpWrapper;
14801 RegExpWrapper.prototype = RegExpPrototype;
14802 redefine(global$2, 'RegExp', RegExpWrapper);
14805 // https://tc39.es/ecma262/#sec-get-regexp-@@species
14806 setSpecies('RegExp');
14808 function define (constructor, factory, prototype) {
14809 constructor.prototype = factory.prototype = prototype;
14810 prototype.constructor = constructor;
14812 function extend$3(parent, definition) {
14813 var prototype = Object.create(parent.prototype);
14815 for (var key in definition) {
14816 prototype[key] = definition[key];
14822 function Color() {}
14825 var _brighter = 1 / _darker;
14826 var reI = "\\s*([+-]?\\d+)\\s*",
14827 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14828 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14829 reHex = /^#([0-9a-f]{3,8})$/,
14830 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14831 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14832 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14833 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14834 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14835 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14837 aliceblue: 0xf0f8ff,
14838 antiquewhite: 0xfaebd7,
14840 aquamarine: 0x7fffd4,
14845 blanchedalmond: 0xffebcd,
14847 blueviolet: 0x8a2be2,
14849 burlywood: 0xdeb887,
14850 cadetblue: 0x5f9ea0,
14851 chartreuse: 0x7fff00,
14852 chocolate: 0xd2691e,
14854 cornflowerblue: 0x6495ed,
14855 cornsilk: 0xfff8dc,
14858 darkblue: 0x00008b,
14859 darkcyan: 0x008b8b,
14860 darkgoldenrod: 0xb8860b,
14861 darkgray: 0xa9a9a9,
14862 darkgreen: 0x006400,
14863 darkgrey: 0xa9a9a9,
14864 darkkhaki: 0xbdb76b,
14865 darkmagenta: 0x8b008b,
14866 darkolivegreen: 0x556b2f,
14867 darkorange: 0xff8c00,
14868 darkorchid: 0x9932cc,
14870 darksalmon: 0xe9967a,
14871 darkseagreen: 0x8fbc8f,
14872 darkslateblue: 0x483d8b,
14873 darkslategray: 0x2f4f4f,
14874 darkslategrey: 0x2f4f4f,
14875 darkturquoise: 0x00ced1,
14876 darkviolet: 0x9400d3,
14877 deeppink: 0xff1493,
14878 deepskyblue: 0x00bfff,
14881 dodgerblue: 0x1e90ff,
14882 firebrick: 0xb22222,
14883 floralwhite: 0xfffaf0,
14884 forestgreen: 0x228b22,
14886 gainsboro: 0xdcdcdc,
14887 ghostwhite: 0xf8f8ff,
14889 goldenrod: 0xdaa520,
14892 greenyellow: 0xadff2f,
14894 honeydew: 0xf0fff0,
14896 indianred: 0xcd5c5c,
14900 lavender: 0xe6e6fa,
14901 lavenderblush: 0xfff0f5,
14902 lawngreen: 0x7cfc00,
14903 lemonchiffon: 0xfffacd,
14904 lightblue: 0xadd8e6,
14905 lightcoral: 0xf08080,
14906 lightcyan: 0xe0ffff,
14907 lightgoldenrodyellow: 0xfafad2,
14908 lightgray: 0xd3d3d3,
14909 lightgreen: 0x90ee90,
14910 lightgrey: 0xd3d3d3,
14911 lightpink: 0xffb6c1,
14912 lightsalmon: 0xffa07a,
14913 lightseagreen: 0x20b2aa,
14914 lightskyblue: 0x87cefa,
14915 lightslategray: 0x778899,
14916 lightslategrey: 0x778899,
14917 lightsteelblue: 0xb0c4de,
14918 lightyellow: 0xffffe0,
14920 limegreen: 0x32cd32,
14924 mediumaquamarine: 0x66cdaa,
14925 mediumblue: 0x0000cd,
14926 mediumorchid: 0xba55d3,
14927 mediumpurple: 0x9370db,
14928 mediumseagreen: 0x3cb371,
14929 mediumslateblue: 0x7b68ee,
14930 mediumspringgreen: 0x00fa9a,
14931 mediumturquoise: 0x48d1cc,
14932 mediumvioletred: 0xc71585,
14933 midnightblue: 0x191970,
14934 mintcream: 0xf5fffa,
14935 mistyrose: 0xffe4e1,
14936 moccasin: 0xffe4b5,
14937 navajowhite: 0xffdead,
14941 olivedrab: 0x6b8e23,
14943 orangered: 0xff4500,
14945 palegoldenrod: 0xeee8aa,
14946 palegreen: 0x98fb98,
14947 paleturquoise: 0xafeeee,
14948 palevioletred: 0xdb7093,
14949 papayawhip: 0xffefd5,
14950 peachpuff: 0xffdab9,
14954 powderblue: 0xb0e0e6,
14956 rebeccapurple: 0x663399,
14958 rosybrown: 0xbc8f8f,
14959 royalblue: 0x4169e1,
14960 saddlebrown: 0x8b4513,
14962 sandybrown: 0xf4a460,
14963 seagreen: 0x2e8b57,
14964 seashell: 0xfff5ee,
14968 slateblue: 0x6a5acd,
14969 slategray: 0x708090,
14970 slategrey: 0x708090,
14972 springgreen: 0x00ff7f,
14973 steelblue: 0x4682b4,
14978 turquoise: 0x40e0d0,
14982 whitesmoke: 0xf5f5f5,
14984 yellowgreen: 0x9acd32
14986 define(Color, color, {
14987 copy: function copy(channels) {
14988 return Object.assign(new this.constructor(), this, channels);
14990 displayable: function displayable() {
14991 return this.rgb().displayable();
14993 hex: color_formatHex,
14994 // Deprecated! Use color.formatHex.
14995 formatHex: color_formatHex,
14996 formatHsl: color_formatHsl,
14997 formatRgb: color_formatRgb,
14998 toString: color_formatRgb
15001 function color_formatHex() {
15002 return this.rgb().formatHex();
15005 function color_formatHsl() {
15006 return hslConvert(this).formatHsl();
15009 function color_formatRgb() {
15010 return this.rgb().formatRgb();
15013 function color(format) {
15015 format = (format + "").trim().toLowerCase();
15016 return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
15017 : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
15018 : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
15019 : 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
15020 : null // invalid hex
15021 ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
15022 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
15023 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
15024 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
15025 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
15026 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
15027 : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
15028 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
15032 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
15035 function rgba(r, g, b, a) {
15036 if (a <= 0) r = g = b = NaN;
15037 return new Rgb(r, g, b, a);
15040 function rgbConvert(o) {
15041 if (!(o instanceof Color)) o = color(o);
15042 if (!o) return new Rgb();
15044 return new Rgb(o.r, o.g, o.b, o.opacity);
15046 function rgb(r, g, b, opacity) {
15047 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
15049 function Rgb(r, g, b, opacity) {
15053 this.opacity = +opacity;
15055 define(Rgb, rgb, extend$3(Color, {
15056 brighter: function brighter(k) {
15057 k = k == null ? _brighter : Math.pow(_brighter, k);
15058 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15060 darker: function darker(k) {
15061 k = k == null ? _darker : Math.pow(_darker, k);
15062 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15064 rgb: function rgb() {
15067 displayable: function displayable() {
15068 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;
15070 hex: rgb_formatHex,
15071 // Deprecated! Use color.formatHex.
15072 formatHex: rgb_formatHex,
15073 formatRgb: rgb_formatRgb,
15074 toString: rgb_formatRgb
15077 function rgb_formatHex() {
15078 return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b);
15081 function rgb_formatRgb() {
15082 var a = this.opacity;
15083 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15084 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 + ")");
15087 function hex$1(value) {
15088 value = Math.max(0, Math.min(255, Math.round(value) || 0));
15089 return (value < 16 ? "0" : "") + value.toString(16);
15092 function hsla(h, s, l, a) {
15093 if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
15094 return new Hsl(h, s, l, a);
15097 function hslConvert(o) {
15098 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
15099 if (!(o instanceof Color)) o = color(o);
15100 if (!o) return new Hsl();
15101 if (o instanceof Hsl) return o;
15106 min = Math.min(r, g, b),
15107 max = Math.max(r, g, b),
15110 l = (max + min) / 2;
15113 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;
15114 s /= l < 0.5 ? max + min : 2 - max - min;
15117 s = l > 0 && l < 1 ? 0 : h;
15120 return new Hsl(h, s, l, o.opacity);
15122 function hsl(h, s, l, opacity) {
15123 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
15126 function Hsl(h, s, l, opacity) {
15130 this.opacity = +opacity;
15133 define(Hsl, hsl, extend$3(Color, {
15134 brighter: function brighter(k) {
15135 k = k == null ? _brighter : Math.pow(_brighter, k);
15136 return new Hsl(this.h, this.s, this.l * k, this.opacity);
15138 darker: function darker(k) {
15139 k = k == null ? _darker : Math.pow(_darker, k);
15140 return new Hsl(this.h, this.s, this.l * k, this.opacity);
15142 rgb: function rgb() {
15143 var h = this.h % 360 + (this.h < 0) * 360,
15144 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
15146 m2 = l + (l < 0.5 ? l : 1 - l) * s,
15148 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);
15150 displayable: function displayable() {
15151 return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
15153 formatHsl: function formatHsl() {
15154 var a = this.opacity;
15155 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15156 return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
15159 /* From FvD 13.37, CSS Color Module Level 3 */
15161 function hsl2rgb(h, m1, m2) {
15162 return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
15165 var constant$1 = (function (x) {
15166 return function () {
15171 function linear$2(a, d) {
15172 return function (t) {
15177 function exponential(a, b, y) {
15178 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
15179 return Math.pow(a + t * b, y);
15182 function gamma(y) {
15183 return (y = +y) === 1 ? nogamma : function (a, b) {
15184 return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
15187 function nogamma(a, b) {
15189 return d ? linear$2(a, d) : constant$1(isNaN(a) ? b : a);
15192 var d3_interpolateRgb = (function rgbGamma(y) {
15193 var color = gamma(y);
15195 function rgb$1(start, end) {
15196 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
15197 g = color(start.g, end.g),
15198 b = color(start.b, end.b),
15199 opacity = nogamma(start.opacity, end.opacity);
15200 return function (t) {
15204 start.opacity = opacity(t);
15209 rgb$1.gamma = rgbGamma;
15213 function numberArray (a, b) {
15215 var n = a ? Math.min(b.length, a.length) : 0,
15218 return function (t) {
15219 for (i = 0; i < n; ++i) {
15220 c[i] = a[i] * (1 - t) + b[i] * t;
15226 function isNumberArray(x) {
15227 return ArrayBuffer.isView(x) && !(x instanceof DataView);
15230 function genericArray(a, b) {
15231 var nb = b ? b.length : 0,
15232 na = a ? Math.min(nb, a.length) : 0,
15237 for (i = 0; i < na; ++i) {
15238 x[i] = interpolate$1(a[i], b[i]);
15241 for (; i < nb; ++i) {
15245 return function (t) {
15246 for (i = 0; i < na; ++i) {
15254 function date (a, b) {
15255 var d = new Date();
15256 return a = +a, b = +b, function (t) {
15257 return d.setTime(a * (1 - t) + b * t), d;
15261 function d3_interpolateNumber (a, b) {
15262 return a = +a, b = +b, function (t) {
15263 return a * (1 - t) + b * t;
15267 function object (a, b) {
15271 if (a === null || _typeof(a) !== "object") a = {};
15272 if (b === null || _typeof(b) !== "object") b = {};
15276 i[k] = interpolate$1(a[k], b[k]);
15282 return function (t) {
15291 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
15292 reB = new RegExp(reA.source, "g");
15295 return function () {
15301 return function (t) {
15306 function interpolateString (a, b) {
15307 var bi = reA.lastIndex = reB.lastIndex = 0,
15308 // scan index for next number in b
15310 // current match in a
15312 // current match in b
15314 // string preceding current number in b, if any
15318 // string constants and placeholders
15319 q = []; // number interpolators
15320 // Coerce inputs to strings.
15322 a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15324 while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15325 if ((bs = bm.index) > bi) {
15326 // a string precedes the next number in b
15327 bs = b.slice(bi, bs);
15328 if (s[i]) s[i] += bs; // coalesce with previous string
15332 if ((am = am[0]) === (bm = bm[0])) {
15333 // numbers in a & b match
15334 if (s[i]) s[i] += bm; // coalesce with previous string
15337 // interpolate non-matching numbers
15341 x: d3_interpolateNumber(am, bm)
15345 bi = reB.lastIndex;
15346 } // Add remains of b.
15349 if (bi < b.length) {
15351 if (s[i]) s[i] += bs; // coalesce with previous string
15353 } // Special optimization for only a single match.
15354 // Otherwise, interpolate each of the numbers and rejoin the string.
15357 return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15358 for (var i = 0, o; i < b; ++i) {
15359 s[(o = q[i]).i] = o.x(t);
15366 function interpolate$1 (a, b) {
15367 var t = _typeof(b),
15370 return b == null || t === "boolean" ? constant$1(b) : (t === "number" ? d3_interpolateNumber : t === "string" ? (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString : b instanceof color ? d3_interpolateRgb : b instanceof Date ? date : isNumberArray(b) ? numberArray : Array.isArray(b) ? genericArray : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object : d3_interpolateNumber)(a, b);
15373 function interpolateRound (a, b) {
15374 return a = +a, b = +b, function (t) {
15375 return Math.round(a * (1 - t) + b * t);
15379 var degrees = 180 / Math.PI;
15388 function decompose (a, b, c, d, e, f) {
15389 var scaleX, scaleY, skewX;
15390 if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15391 if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15392 if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15393 if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15397 rotate: Math.atan2(b, a) * degrees,
15398 skewX: Math.atan(skewX) * degrees,
15405 /* eslint-disable no-undef */
15407 function parseCss(value) {
15408 var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15409 return m.isIdentity ? identity$3 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15411 function parseSvg(value) {
15412 if (value == null) return identity$3;
15413 if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15414 svgNode.setAttribute("transform", value);
15415 if (!(value = svgNode.transform.baseVal.consolidate())) return identity$3;
15416 value = value.matrix;
15417 return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15420 function interpolateTransform(parse, pxComma, pxParen, degParen) {
15422 return s.length ? s.pop() + " " : "";
15425 function translate(xa, ya, xb, yb, s, q) {
15426 if (xa !== xb || ya !== yb) {
15427 var i = s.push("translate(", null, pxComma, null, pxParen);
15430 x: d3_interpolateNumber(xa, xb)
15433 x: d3_interpolateNumber(ya, yb)
15435 } else if (xb || yb) {
15436 s.push("translate(" + xb + pxComma + yb + pxParen);
15440 function rotate(a, b, s, q) {
15442 if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15445 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15446 x: d3_interpolateNumber(a, b)
15449 s.push(pop(s) + "rotate(" + b + degParen);
15453 function skewX(a, b, s, q) {
15456 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15457 x: d3_interpolateNumber(a, b)
15460 s.push(pop(s) + "skewX(" + b + degParen);
15464 function scale(xa, ya, xb, yb, s, q) {
15465 if (xa !== xb || ya !== yb) {
15466 var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15469 x: d3_interpolateNumber(xa, xb)
15472 x: d3_interpolateNumber(ya, yb)
15474 } else if (xb !== 1 || yb !== 1) {
15475 s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15479 return function (a, b) {
15481 // string constants and placeholders
15482 q = []; // number interpolators
15484 a = parse(a), b = parse(b);
15485 translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15486 rotate(a.rotate, b.rotate, s, q);
15487 skewX(a.skewX, b.skewX, s, q);
15488 scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15489 a = b = null; // gc
15491 return function (t) {
15497 s[(o = q[i]).i] = o.x(t);
15505 var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15506 var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15508 var epsilon2 = 1e-12;
15511 return ((x = Math.exp(x)) + 1 / x) / 2;
15515 return ((x = Math.exp(x)) - 1 / x) / 2;
15519 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15522 var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15523 // p0 = [ux0, uy0, w0]
15524 // p1 = [ux1, uy1, w1]
15525 function zoom(p0, p1) {
15534 d2 = dx * dx + dy * dy,
15536 S; // Special case for u0 ≅ u1.
15538 if (d2 < epsilon2) {
15539 S = Math.log(w1 / w0) / rho;
15541 i = function i(t) {
15542 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15546 var d1 = Math.sqrt(d2),
15547 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15548 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15549 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15550 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15551 S = (r1 - r0) / rho;
15553 i = function i(t) {
15556 u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15557 return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15561 i.duration = S * 1000 * rho / Math.SQRT2;
15565 zoom.rho = function (_) {
15566 var _1 = Math.max(1e-3, +_),
15570 return zoomRho(_1, _2, _4);
15574 })(Math.SQRT2, 2, 4);
15576 function d3_quantize (interpolator, n) {
15577 var samples = new Array(n);
15579 for (var i = 0; i < n; ++i) {
15580 samples[i] = interpolator(i / (n - 1));
15586 // `Function.prototype.bind` method
15587 // https://tc39.es/ecma262/#sec-function.prototype.bind
15588 _export({ target: 'Function', proto: true }, {
15593 // is an animation frame pending?
15595 // is a timeout pending?
15597 // are any timers active?
15599 // how frequently we check for clock skew
15605 clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15606 setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15610 return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15613 function clearNow() {
15618 this._call = this._time = this._next = null;
15620 Timer.prototype = timer.prototype = {
15621 constructor: Timer,
15622 restart: function restart(callback, delay, time) {
15623 if (typeof callback !== "function") throw new TypeError("callback is not a function");
15624 time = (time == null ? now$1() : +time) + (delay == null ? 0 : +delay);
15626 if (!this._next && taskTail !== this) {
15627 if (taskTail) taskTail._next = this;else taskHead = this;
15631 this._call = callback;
15635 stop: function stop() {
15638 this._time = Infinity;
15643 function timer(callback, delay, time) {
15644 var t = new Timer();
15645 t.restart(callback, delay, time);
15648 function timerFlush() {
15649 now$1(); // Get the current time, if not already set.
15651 ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15657 if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15665 clockNow = (clockLast = clock.now()) + clockSkew;
15666 frame = timeout = 0;
15678 var now = clock.now(),
15679 delay = now - clockLast;
15680 if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15691 if (time > t1._time) time = t1._time;
15692 t0 = t1, t1 = t1._next;
15694 t2 = t1._next, t1._next = null;
15695 t1 = t0 ? t0._next = t2 : taskHead = t2;
15703 function sleep(time) {
15704 if (frame) return; // Soonest alarm already set, or will be.
15706 if (timeout) timeout = clearTimeout(timeout);
15707 var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15710 if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15711 if (interval) interval = clearInterval(interval);
15713 if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15714 frame = 1, setFrame(wake);
15718 function d3_timeout (callback, delay, time) {
15719 var t = new Timer();
15720 delay = delay == null ? 0 : +delay;
15721 t.restart(function (elapsed) {
15723 callback(elapsed + delay);
15728 var emptyOn = dispatch$8("start", "end", "cancel", "interrupt");
15729 var emptyTween = [];
15737 function schedule (node, name, id, index, group, timing) {
15738 var schedules = node.__transition;
15739 if (!schedules) node.__transition = {};else if (id in schedules) return;
15740 create$2(node, id, {
15743 // For context during callback.
15745 // For context during callback.
15749 delay: timing.delay,
15750 duration: timing.duration,
15756 function init(node, id) {
15757 var schedule = get$1(node, id);
15758 if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15761 function set(node, id) {
15762 var schedule = get$1(node, id);
15763 if (schedule.state > STARTED) throw new Error("too late; already running");
15766 function get$1(node, id) {
15767 var schedule = node.__transition;
15768 if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15772 function create$2(node, id, self) {
15773 var schedules = node.__transition,
15774 tween; // Initialize the self timer when the transition is created.
15775 // Note the actual delay is not known until the first callback!
15777 schedules[id] = self;
15778 self.timer = timer(schedule, 0, self.time);
15780 function schedule(elapsed) {
15781 self.state = SCHEDULED;
15782 self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15784 if (self.delay <= elapsed) start(elapsed - self.delay);
15787 function start(elapsed) {
15788 var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15790 if (self.state !== SCHEDULED) return stop();
15792 for (i in schedules) {
15794 if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15795 // defer starting an interrupting transition until that transition has a
15796 // chance to tick (and possibly end); see d3/d3-transition#54!
15798 if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15800 if (o.state === RUNNING) {
15803 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15804 delete schedules[i];
15805 } // Cancel any pre-empted transitions.
15806 else if (+i < id) {
15809 o.on.call("cancel", node, node.__data__, o.index, o.group);
15810 delete schedules[i];
15812 } // Defer the first tick to end of the current frame; see d3/d3#1576.
15813 // Note the transition may be canceled after start and before the first tick!
15814 // Note this must be scheduled before the start event; see d3/d3-transition#16!
15815 // Assuming this is successful, subsequent callbacks go straight to tick.
15818 d3_timeout(function () {
15819 if (self.state === STARTED) {
15820 self.state = RUNNING;
15821 self.timer.restart(tick, self.delay, self.time);
15824 }); // Dispatch the start event.
15825 // Note this must be done before the tween are initialized.
15827 self.state = STARTING;
15828 self.on.call("start", node, node.__data__, self.index, self.group);
15829 if (self.state !== STARTING) return; // interrupted
15831 self.state = STARTED; // Initialize the tween, deleting null tween.
15833 tween = new Array(n = self.tween.length);
15835 for (i = 0, j = -1; i < n; ++i) {
15836 if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15841 tween.length = j + 1;
15844 function tick(elapsed) {
15845 var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15850 tween[i].call(node, t);
15851 } // Dispatch the end event.
15854 if (self.state === ENDING) {
15855 self.on.call("end", node, node.__data__, self.index, self.group);
15861 self.state = ENDED;
15863 delete schedules[id];
15865 for (var i in schedules) {
15867 } // eslint-disable-line no-unused-vars
15870 delete node.__transition;
15874 function interrupt (node, name) {
15875 var schedules = node.__transition,
15880 if (!schedules) return;
15881 name = name == null ? null : name + "";
15883 for (i in schedules) {
15884 if ((schedule = schedules[i]).name !== name) {
15889 active = schedule.state > STARTING && schedule.state < ENDING;
15890 schedule.state = ENDED;
15891 schedule.timer.stop();
15892 schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15893 delete schedules[i];
15896 if (empty) delete node.__transition;
15899 function selection_interrupt (name) {
15900 return this.each(function () {
15901 interrupt(this, name);
15905 function tweenRemove(id, name) {
15906 var tween0, tween1;
15907 return function () {
15908 var schedule = set(this, id),
15909 tween = schedule.tween; // If this node shared tween with the previous node,
15910 // just assign the updated shared tween and we’re done!
15911 // Otherwise, copy-on-write.
15913 if (tween !== tween0) {
15914 tween1 = tween0 = tween;
15916 for (var i = 0, n = tween1.length; i < n; ++i) {
15917 if (tween1[i].name === name) {
15918 tween1 = tween1.slice();
15919 tween1.splice(i, 1);
15925 schedule.tween = tween1;
15929 function tweenFunction(id, name, value) {
15930 var tween0, tween1;
15931 if (typeof value !== "function") throw new Error();
15932 return function () {
15933 var schedule = set(this, id),
15934 tween = schedule.tween; // If this node shared tween with the previous node,
15935 // just assign the updated shared tween and we’re done!
15936 // Otherwise, copy-on-write.
15938 if (tween !== tween0) {
15939 tween1 = (tween0 = tween).slice();
15944 }, i = 0, n = tween1.length; i < n; ++i) {
15945 if (tween1[i].name === name) {
15951 if (i === n) tween1.push(t);
15954 schedule.tween = tween1;
15958 function transition_tween (name, value) {
15962 if (arguments.length < 2) {
15963 var tween = get$1(this.node(), id).tween;
15965 for (var i = 0, n = tween.length, t; i < n; ++i) {
15966 if ((t = tween[i]).name === name) {
15974 return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15976 function tweenValue(transition, name, value) {
15977 var id = transition._id;
15978 transition.each(function () {
15979 var schedule = set(this, id);
15980 (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15982 return function (node) {
15983 return get$1(node, id).value[name];
15987 function interpolate (a, b) {
15989 return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15992 function attrRemove(name) {
15993 return function () {
15994 this.removeAttribute(name);
15998 function attrRemoveNS(fullname) {
15999 return function () {
16000 this.removeAttributeNS(fullname.space, fullname.local);
16004 function attrConstant(name, interpolate, value1) {
16006 string1 = value1 + "",
16008 return function () {
16009 var string0 = this.getAttribute(name);
16010 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16014 function attrConstantNS(fullname, interpolate, value1) {
16016 string1 = value1 + "",
16018 return function () {
16019 var string0 = this.getAttributeNS(fullname.space, fullname.local);
16020 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16024 function attrFunction(name, interpolate, value) {
16025 var string00, string10, interpolate0;
16026 return function () {
16028 value1 = value(this),
16030 if (value1 == null) return void this.removeAttribute(name);
16031 string0 = this.getAttribute(name);
16032 string1 = value1 + "";
16033 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16037 function attrFunctionNS(fullname, interpolate, value) {
16038 var string00, string10, interpolate0;
16039 return function () {
16041 value1 = value(this),
16043 if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
16044 string0 = this.getAttributeNS(fullname.space, fullname.local);
16045 string1 = value1 + "";
16046 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16050 function transition_attr (name, value) {
16051 var fullname = namespace(name),
16052 i = fullname === "transform" ? interpolateTransformSvg : interpolate;
16053 return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));
16056 function attrInterpolate(name, i) {
16057 return function (t) {
16058 this.setAttribute(name, i.call(this, t));
16062 function attrInterpolateNS(fullname, i) {
16063 return function (t) {
16064 this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
16068 function attrTweenNS(fullname, value) {
16072 var i = value.apply(this, arguments);
16073 if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
16077 tween._value = value;
16081 function attrTween(name, value) {
16085 var i = value.apply(this, arguments);
16086 if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
16090 tween._value = value;
16094 function transition_attrTween (name, value) {
16095 var key = "attr." + name;
16096 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16097 if (value == null) return this.tween(key, null);
16098 if (typeof value !== "function") throw new Error();
16099 var fullname = namespace(name);
16100 return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
16103 function delayFunction(id, value) {
16104 return function () {
16105 init(this, id).delay = +value.apply(this, arguments);
16109 function delayConstant(id, value) {
16110 return value = +value, function () {
16111 init(this, id).delay = value;
16115 function transition_delay (value) {
16117 return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$1(this.node(), id).delay;
16120 function durationFunction(id, value) {
16121 return function () {
16122 set(this, id).duration = +value.apply(this, arguments);
16126 function durationConstant(id, value) {
16127 return value = +value, function () {
16128 set(this, id).duration = value;
16132 function transition_duration (value) {
16134 return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$1(this.node(), id).duration;
16137 function easeConstant(id, value) {
16138 if (typeof value !== "function") throw new Error();
16139 return function () {
16140 set(this, id).ease = value;
16144 function transition_ease (value) {
16146 return arguments.length ? this.each(easeConstant(id, value)) : get$1(this.node(), id).ease;
16149 function easeVarying(id, value) {
16150 return function () {
16151 var v = value.apply(this, arguments);
16152 if (typeof v !== "function") throw new Error();
16153 set(this, id).ease = v;
16157 function transition_easeVarying (value) {
16158 if (typeof value !== "function") throw new Error();
16159 return this.each(easeVarying(this._id, value));
16162 function transition_filter (match) {
16163 if (typeof match !== "function") match = matcher(match);
16165 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16166 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
16167 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
16168 subgroup.push(node);
16173 return new Transition(subgroups, this._parents, this._name, this._id);
16176 function transition_merge (transition) {
16177 if (transition._id !== this._id) throw new Error();
16179 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) {
16180 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
16181 if (node = group0[i] || group1[i]) {
16187 for (; j < m0; ++j) {
16188 merges[j] = groups0[j];
16191 return new Transition(merges, this._parents, this._name, this._id);
16194 function start(name) {
16195 return (name + "").trim().split(/^|\s+/).every(function (t) {
16196 var i = t.indexOf(".");
16197 if (i >= 0) t = t.slice(0, i);
16198 return !t || t === "start";
16202 function onFunction(id, name, listener) {
16205 sit = start(name) ? init : set;
16206 return function () {
16207 var schedule = sit(this, id),
16208 on = schedule.on; // If this node shared a dispatch with the previous node,
16209 // just assign the updated shared dispatch and we’re done!
16210 // Otherwise, copy-on-write.
16212 if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
16217 function transition_on (name, listener) {
16219 return arguments.length < 2 ? get$1(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
16222 function removeFunction(id) {
16223 return function () {
16224 var parent = this.parentNode;
16226 for (var i in this.__transition) {
16227 if (+i !== id) return;
16230 if (parent) parent.removeChild(this);
16234 function transition_remove () {
16235 return this.on("end.remove", removeFunction(this._id));
16238 function transition_select (select) {
16239 var name = this._name,
16241 if (typeof select !== "function") select = selector(select);
16243 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16244 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
16245 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
16246 if ("__data__" in node) subnode.__data__ = node.__data__;
16247 subgroup[i] = subnode;
16248 schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
16253 return new Transition(subgroups, this._parents, name, id);
16256 function transition_selectAll (select) {
16257 var name = this._name,
16259 if (typeof select !== "function") select = selectorAll(select);
16261 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
16262 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16263 if (node = group[i]) {
16264 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
16265 if (child = children[k]) {
16266 schedule(child, name, id, k, children, inherit);
16270 subgroups.push(children);
16271 parents.push(node);
16276 return new Transition(subgroups, parents, name, id);
16279 var Selection = selection.prototype.constructor;
16280 function transition_selection () {
16281 return new Selection(this._groups, this._parents);
16284 function styleNull(name, interpolate) {
16285 var string00, string10, interpolate0;
16286 return function () {
16287 var string0 = styleValue(this, name),
16288 string1 = (this.style.removeProperty(name), styleValue(this, name));
16289 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
16293 function styleRemove(name) {
16294 return function () {
16295 this.style.removeProperty(name);
16299 function styleConstant(name, interpolate, value1) {
16301 string1 = value1 + "",
16303 return function () {
16304 var string0 = styleValue(this, name);
16305 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16309 function styleFunction(name, interpolate, value) {
16310 var string00, string10, interpolate0;
16311 return function () {
16312 var string0 = styleValue(this, name),
16313 value1 = value(this),
16314 string1 = value1 + "";
16315 if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16316 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16320 function styleMaybeRemove(id, name) {
16324 key = "style." + name,
16325 event = "end." + key,
16327 return function () {
16328 var schedule = set(this, id),
16330 listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined; // If this node shared a dispatch with the previous node,
16331 // just assign the updated shared dispatch and we’re done!
16332 // Otherwise, copy-on-write.
16334 if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16339 function transition_style (name, value, priority) {
16340 var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
16341 return value == null ? this.styleTween(name, styleNull(name, i)).on("end.style." + name, styleRemove(name)) : typeof value === "function" ? this.styleTween(name, styleFunction(name, i, tweenValue(this, "style." + name, value))).each(styleMaybeRemove(this._id, name)) : this.styleTween(name, styleConstant(name, i, value), priority).on("end.style." + name, null);
16344 function styleInterpolate(name, i, priority) {
16345 return function (t) {
16346 this.style.setProperty(name, i.call(this, t), priority);
16350 function styleTween(name, value, priority) {
16354 var i = value.apply(this, arguments);
16355 if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16359 tween._value = value;
16363 function transition_styleTween (name, value, priority) {
16364 var key = "style." + (name += "");
16365 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16366 if (value == null) return this.tween(key, null);
16367 if (typeof value !== "function") throw new Error();
16368 return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16371 function textConstant(value) {
16372 return function () {
16373 this.textContent = value;
16377 function textFunction(value) {
16378 return function () {
16379 var value1 = value(this);
16380 this.textContent = value1 == null ? "" : value1;
16384 function transition_text (value) {
16385 return this.tween("text", typeof value === "function" ? textFunction(tweenValue(this, "text", value)) : textConstant(value == null ? "" : value + ""));
16388 function textInterpolate(i) {
16389 return function (t) {
16390 this.textContent = i.call(this, t);
16394 function textTween(value) {
16398 var i = value.apply(this, arguments);
16399 if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16403 tween._value = value;
16407 function transition_textTween (value) {
16409 if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16410 if (value == null) return this.tween(key, null);
16411 if (typeof value !== "function") throw new Error();
16412 return this.tween(key, textTween(value));
16415 function transition_transition () {
16416 var name = this._name,
16420 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16421 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16422 if (node = group[i]) {
16423 var inherit = get$1(node, id0);
16424 schedule(node, name, id1, i, group, {
16425 time: inherit.time + inherit.delay + inherit.duration,
16427 duration: inherit.duration,
16434 return new Transition(groups, this._parents, name, id1);
16437 function transition_end () {
16442 size = that.size();
16443 return new Promise(function (resolve, reject) {
16448 value: function value() {
16449 if (--size === 0) resolve();
16452 that.each(function () {
16453 var schedule = set(this, id),
16454 on = schedule.on; // If this node shared a dispatch with the previous node,
16455 // just assign the updated shared dispatch and we’re done!
16456 // Otherwise, copy-on-write.
16459 on1 = (on0 = on).copy();
16461 on1._.cancel.push(cancel);
16463 on1._.interrupt.push(cancel);
16465 on1._.end.push(end);
16469 }); // The selection was empty, resolve end immediately
16471 if (size === 0) resolve();
16476 function Transition(groups, parents, name, id) {
16477 this._groups = groups;
16478 this._parents = parents;
16485 var selection_prototype = selection.prototype;
16486 Transition.prototype = _defineProperty({
16487 constructor: Transition,
16488 select: transition_select,
16489 selectAll: transition_selectAll,
16490 filter: transition_filter,
16491 merge: transition_merge,
16492 selection: transition_selection,
16493 transition: transition_transition,
16494 call: selection_prototype.call,
16495 nodes: selection_prototype.nodes,
16496 node: selection_prototype.node,
16497 size: selection_prototype.size,
16498 empty: selection_prototype.empty,
16499 each: selection_prototype.each,
16501 attr: transition_attr,
16502 attrTween: transition_attrTween,
16503 style: transition_style,
16504 styleTween: transition_styleTween,
16505 text: transition_text,
16506 textTween: transition_textTween,
16507 remove: transition_remove,
16508 tween: transition_tween,
16509 delay: transition_delay,
16510 duration: transition_duration,
16511 ease: transition_ease,
16512 easeVarying: transition_easeVarying,
16513 end: transition_end
16514 }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16516 var linear$1 = function linear(t) {
16520 function cubicInOut(t) {
16521 return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16524 var defaultTiming = {
16532 function inherit(node, id) {
16535 while (!(timing = node.__transition) || !(timing = timing[id])) {
16536 if (!(node = node.parentNode)) {
16537 throw new Error("transition ".concat(id, " not found"));
16544 function selection_transition (name) {
16547 if (name instanceof Transition) {
16548 id = name._id, name = name._name;
16550 id = newId(), (timing = defaultTiming).time = now$1(), name = name == null ? null : name + "";
16553 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16554 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16555 if (node = group[i]) {
16556 schedule(node, name, id, i, group, timing || inherit(node, id));
16561 return new Transition(groups, this._parents, name, id);
16564 selection.prototype.interrupt = selection_interrupt;
16565 selection.prototype.transition = selection_transition;
16567 var constant = (function (x) {
16568 return function () {
16573 function ZoomEvent(type, _ref) {
16574 var sourceEvent = _ref.sourceEvent,
16575 target = _ref.target,
16576 transform = _ref.transform,
16577 dispatch = _ref.dispatch;
16578 Object.defineProperties(this, {
16585 value: sourceEvent,
16605 function Transform(k, x, y) {
16610 Transform.prototype = {
16611 constructor: Transform,
16612 scale: function scale(k) {
16613 return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16615 translate: function translate(x, y) {
16616 return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16618 apply: function apply(point) {
16619 return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16621 applyX: function applyX(x) {
16622 return x * this.k + this.x;
16624 applyY: function applyY(y) {
16625 return y * this.k + this.y;
16627 invert: function invert(location) {
16628 return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16630 invertX: function invertX(x) {
16631 return (x - this.x) / this.k;
16633 invertY: function invertY(y) {
16634 return (y - this.y) / this.k;
16636 rescaleX: function rescaleX(x) {
16637 return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16639 rescaleY: function rescaleY(y) {
16640 return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16642 toString: function toString() {
16643 return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16646 var identity$2 = new Transform(1, 0, 0);
16648 function nopropagation(event) {
16649 event.stopImmediatePropagation();
16651 function noevent (event) {
16652 event.preventDefault();
16653 event.stopImmediatePropagation();
16656 // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16658 function defaultFilter$1(event) {
16659 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16662 function defaultExtent$1() {
16665 if (e instanceof SVGElement) {
16666 e = e.ownerSVGElement || e;
16668 if (e.hasAttribute("viewBox")) {
16669 e = e.viewBox.baseVal;
16670 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16673 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16676 return [[0, 0], [e.clientWidth, e.clientHeight]];
16679 function defaultTransform() {
16680 return this.__zoom || identity$2;
16683 function defaultWheelDelta$1(event) {
16684 return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16687 function defaultTouchable() {
16688 return navigator.maxTouchPoints || "ontouchstart" in this;
16691 function defaultConstrain$1(transform, extent, translateExtent) {
16692 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16693 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16694 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16695 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16696 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));
16699 function d3_zoom () {
16700 var filter = defaultFilter$1,
16701 extent = defaultExtent$1,
16702 constrain = defaultConstrain$1,
16703 wheelDelta = defaultWheelDelta$1,
16704 touchable = defaultTouchable,
16705 scaleExtent = [0, Infinity],
16706 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16708 interpolate = interpolateZoom,
16709 listeners = dispatch$8("start", "zoom", "end"),
16715 clickDistance2 = 0,
16718 function zoom(selection) {
16719 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)");
16722 zoom.transform = function (collection, transform, point, event) {
16723 var selection = collection.selection ? collection.selection() : collection;
16724 selection.property("__zoom", defaultTransform);
16726 if (collection !== selection) {
16727 schedule(collection, transform, point, event);
16729 selection.interrupt().each(function () {
16730 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16735 zoom.scaleBy = function (selection, k, p, event) {
16736 zoom.scaleTo(selection, function () {
16737 var k0 = this.__zoom.k,
16738 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16743 zoom.scaleTo = function (selection, k, p, event) {
16744 zoom.transform(selection, function () {
16745 var e = extent.apply(this, arguments),
16747 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16748 p1 = t0.invert(p0),
16749 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16750 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16754 zoom.translateBy = function (selection, x, y, event) {
16755 zoom.transform(selection, function () {
16756 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);
16760 zoom.translateTo = function (selection, x, y, p, event) {
16761 zoom.transform(selection, function () {
16762 var e = extent.apply(this, arguments),
16764 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16765 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);
16769 function scale(transform, k) {
16770 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16771 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16774 function translate(transform, p0, p1) {
16775 var x = p0[0] - p1[0] * transform.k,
16776 y = p0[1] - p1[1] * transform.k;
16777 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16780 function centroid(extent) {
16781 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16784 function schedule(transition, transform, point, event) {
16785 transition.on("start.zoom", function () {
16786 gesture(this, arguments).event(event).start();
16787 }).on("interrupt.zoom end.zoom", function () {
16788 gesture(this, arguments).event(event).end();
16789 }).tween("zoom", function () {
16792 g = gesture(that, args).event(event),
16793 e = extent.apply(that, args),
16794 p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16795 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16797 b = typeof transform === "function" ? transform.apply(that, args) : transform,
16798 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16799 return function (t) {
16800 if (t === 1) t = b; // Avoid rounding error on end.
16804 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16811 function gesture(that, args, clean) {
16812 return !clean && that.__zooming || new Gesture(that, args);
16815 function Gesture(that, args) {
16819 this.sourceEvent = null;
16820 this.extent = extent.apply(that, args);
16824 Gesture.prototype = {
16825 event: function event(_event) {
16826 if (_event) this.sourceEvent = _event;
16829 start: function start() {
16830 if (++this.active === 1) {
16831 this.that.__zooming = this;
16832 this.emit("start");
16837 zoom: function zoom(key, transform) {
16838 if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16839 if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16840 if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16841 this.that.__zoom = transform;
16845 end: function end() {
16846 if (--this.active === 0) {
16847 delete this.that.__zooming;
16853 emit: function emit(type) {
16854 var d = select(this.that).datum();
16855 listeners.call(type, this.that, new ZoomEvent(type, {
16856 sourceEvent: this.sourceEvent,
16859 transform: this.that.__zoom,
16860 dispatch: listeners
16865 function wheeled(event) {
16866 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16867 args[_key - 1] = arguments[_key];
16870 if (!filter.apply(this, arguments)) return;
16871 var g = gesture(this, args).event(event),
16873 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16874 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16875 // If there were recent wheel events, reset the wheel idle timeout.
16878 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16879 g.mouse[1] = t.invert(g.mouse[0] = p);
16882 clearTimeout(g.wheel);
16883 } // If this wheel event won’t trigger a transform change, ignore it.
16884 else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16886 g.mouse = [p, t.invert(p)];
16892 g.wheel = setTimeout(wheelidled, wheelDelay);
16893 g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16895 function wheelidled() {
16901 function mousedowned(event) {
16902 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16903 args[_key2 - 1] = arguments[_key2];
16906 if (touchending || !filter.apply(this, arguments)) return;
16907 var g = gesture(this, args, true).event(event),
16908 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16909 p = pointer(event, currentTarget),
16910 currentTarget = event.currentTarget,
16911 x0 = event.clientX,
16912 y0 = event.clientY;
16913 dragDisable(event.view);
16914 nopropagation(event);
16915 g.mouse = [p, this.__zoom.invert(p)];
16919 function mousemoved(event) {
16923 var dx = event.clientX - x0,
16924 dy = event.clientY - y0;
16925 g.moved = dx * dx + dy * dy > clickDistance2;
16928 g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16931 function mouseupped(event) {
16932 v.on("mousemove.zoom mouseup.zoom", null);
16933 yesdrag(event.view, g.moved);
16935 g.event(event).end();
16939 function dblclicked(event) {
16940 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16941 args[_key3 - 1] = arguments[_key3];
16944 if (!filter.apply(this, arguments)) return;
16945 var t0 = this.__zoom,
16946 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16947 p1 = t0.invert(p0),
16948 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16949 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16951 if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16954 function touchstarted(event) {
16955 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16956 args[_key4 - 1] = arguments[_key4];
16959 if (!filter.apply(this, arguments)) return;
16960 var touches = event.touches,
16961 n = touches.length,
16962 g = gesture(this, args, event.changedTouches.length === n).event(event),
16967 nopropagation(event);
16969 for (i = 0; i < n; ++i) {
16970 t = touches[i], p = pointer(t, this);
16971 p = [p, this.__zoom.invert(p), t.identifier];
16972 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;
16975 if (touchstarting) touchstarting = clearTimeout(touchstarting);
16978 if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16979 touchstarting = null;
16986 function touchmoved(event) {
16987 if (!this.__zooming) return;
16989 for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16990 args[_key5 - 1] = arguments[_key5];
16993 var g = gesture(this, args).event(event),
16994 touches = event.changedTouches,
16995 n = touches.length,
17002 for (i = 0; i < n; ++i) {
17003 t = touches[i], p = pointer(t, this);
17004 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;
17010 var p0 = g.touch0[0],
17014 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
17015 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
17016 t = scale(t, Math.sqrt(dp / dl));
17017 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
17018 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
17019 } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
17021 g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
17024 function touchended(event) {
17025 for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
17026 args[_key6 - 1] = arguments[_key6];
17029 if (!this.__zooming) return;
17030 var g = gesture(this, args).event(event),
17031 touches = event.changedTouches,
17032 n = touches.length,
17035 nopropagation(event);
17036 if (touchending) clearTimeout(touchending);
17037 touchending = setTimeout(function () {
17038 touchending = null;
17041 for (i = 0; i < n; ++i) {
17043 if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
17046 if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
17047 if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
17048 g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
17050 if (g.taps === 2) {
17051 t = pointer(t, this);
17053 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
17054 var p = select(this).on("dblclick.zoom");
17055 if (p) p.apply(this, arguments);
17061 zoom.wheelDelta = function (_) {
17062 return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant(+_), zoom) : wheelDelta;
17065 zoom.filter = function (_) {
17066 return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), zoom) : filter;
17069 zoom.touchable = function (_) {
17070 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable;
17073 zoom.extent = function (_) {
17074 return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
17077 zoom.scaleExtent = function (_) {
17078 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
17081 zoom.translateExtent = function (_) {
17082 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]]];
17085 zoom.constrain = function (_) {
17086 return arguments.length ? (constrain = _, zoom) : constrain;
17089 zoom.duration = function (_) {
17090 return arguments.length ? (duration = +_, zoom) : duration;
17093 zoom.interpolate = function (_) {
17094 return arguments.length ? (interpolate = _, zoom) : interpolate;
17097 zoom.on = function () {
17098 var value = listeners.on.apply(listeners, arguments);
17099 return value === listeners ? zoom : value;
17102 zoom.clickDistance = function (_) {
17103 return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
17106 zoom.tapDistance = function (_) {
17107 return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
17114 Bypasses features of D3's default projection stream pipeline that are unnecessary:
17115 * Antimeridian clipping
17116 * Spherical rotation
17120 function geoRawMercator() {
17121 var project = mercatorRaw;
17122 var k = 512 / Math.PI; // scale
17125 var y = 0; // translate
17127 var clipExtent = [[0, 0], [0, 0]];
17129 function projection(point) {
17130 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
17131 return [point[0] * k + x, y - point[1] * k];
17134 projection.invert = function (point) {
17135 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
17136 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
17139 projection.scale = function (_) {
17140 if (!arguments.length) return k;
17145 projection.translate = function (_) {
17146 if (!arguments.length) return [x, y];
17152 projection.clipExtent = function (_) {
17153 if (!arguments.length) return clipExtent;
17158 projection.transform = function (obj) {
17159 if (!arguments.length) return identity$2.translate(x, y).scale(k);
17166 projection.stream = d3_geoTransform({
17167 point: function point(x, y) {
17168 var vec = projection([x, y]);
17169 this.stream.point(vec[0], vec[1]);
17175 function geoOrthoNormalizedDotProduct(a, b, origin) {
17176 if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
17177 return 1; // coincident points, treat as straight and try to remove
17180 return geoVecNormalizedDot(a, b, origin);
17183 function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
17184 var val = Math.abs(dotp);
17186 if (val < epsilon) {
17187 return 0; // already orthogonal
17188 } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
17189 return 0; // straight angle, which is okay in this case
17190 } else if (val < lowerThreshold || val > upperThreshold) {
17191 return dotp; // can be adjusted
17193 return null; // ignore vertex
17197 function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
17199 var first = isClosed ? 0 : 1;
17200 var last = isClosed ? points.length : points.length - 1;
17201 var coords = points.map(function (p) {
17204 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17205 var upperThreshold = Math.cos(threshold * Math.PI / 180);
17207 for (var i = first; i < last; i++) {
17208 var a = coords[(i - 1 + coords.length) % coords.length];
17209 var origin = coords[i];
17210 var b = coords[(i + 1) % coords.length];
17211 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
17212 if (dotp === null) continue; // ignore vertex
17214 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
17218 } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
17220 function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
17221 var max = -Infinity;
17222 var first = isClosed ? 0 : 1;
17223 var last = isClosed ? coords.length : coords.length - 1;
17225 for (var i = first; i < last; i++) {
17226 var a = coords[(i - 1 + coords.length) % coords.length];
17227 var origin = coords[i];
17228 var b = coords[(i + 1) % coords.length];
17229 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
17230 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
17231 if (angle > 45) angle = 90 - angle;
17232 if (angle >= lessThan) continue;
17233 if (angle > max) max = angle;
17236 if (max === -Infinity) return null;
17238 } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
17240 function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
17242 var first = isClosed ? 0 : 1;
17243 var last = isClosed ? coords.length : coords.length - 1;
17244 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17245 var upperThreshold = Math.cos(threshold * Math.PI / 180);
17247 for (var i = first; i < last; i++) {
17248 var a = coords[(i - 1 + coords.length) % coords.length];
17249 var origin = coords[i];
17250 var b = coords[(i + 1) % coords.length];
17251 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
17252 if (dotp === null) continue; // ignore vertex
17254 if (Math.abs(dotp) > 0) return 1; // something to do
17256 score = 0; // already square
17262 var onFreeze = internalMetadata.onFreeze;
17264 // eslint-disable-next-line es/no-object-freeze -- safe
17265 var $freeze = Object.freeze;
17266 var FAILS_ON_PRIMITIVES = fails(function () { $freeze(1); });
17268 // `Object.freeze` method
17269 // https://tc39.es/ecma262/#sec-object.freeze
17270 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES, sham: !freezing }, {
17271 freeze: function freeze(it) {
17272 return $freeze && isObject$4(it) ? $freeze(onFreeze(it)) : it;
17276 // Returns true if a and b have the same elements at the same indices.
17277 function utilArrayIdentical(a, b) {
17278 // an array is always identical to itself
17279 if (a === b) return true;
17281 if (i !== b.length) return false;
17284 if (a[i] !== b[i]) return false;
17288 } // http://2ality.com/2015/01/es6-set-operations.html
17289 // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
17290 // This operation is also sometimes called minus (-).
17291 // var a = [1,2,3];
17292 // var b = [4,3,2];
17293 // utilArrayDifference(a, b)
17295 // utilArrayDifference(b, a)
17298 function utilArrayDifference(a, b) {
17299 var other = new Set(b);
17300 return Array.from(new Set(a)).filter(function (v) {
17301 return !other.has(v);
17303 } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
17304 // var a = [1,2,3];
17305 // var b = [4,3,2];
17306 // utilArrayIntersection(a, b)
17309 function utilArrayIntersection(a, b) {
17310 var other = new Set(b);
17311 return Array.from(new Set(a)).filter(function (v) {
17312 return other.has(v);
17314 } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17315 // var a = [1,2,3];
17316 // var b = [4,3,2];
17317 // utilArrayUnion(a, b)
17320 function utilArrayUnion(a, b) {
17321 var result = new Set(a);
17322 b.forEach(function (v) {
17325 return Array.from(result);
17326 } // Returns an Array with all the duplicates removed
17327 // var a = [1,1,2,3,3];
17328 // utilArrayUniq(a)
17331 function utilArrayUniq(a) {
17332 return Array.from(new Set(a));
17333 } // Splits array into chunks of given chunk size
17334 // var a = [1,2,3,4,5,6,7];
17335 // utilArrayChunk(a, 3);
17336 // [[1,2,3],[4,5,6],[7]];
17338 function utilArrayChunk(a, chunkSize) {
17339 if (!chunkSize || chunkSize < 0) return [a.slice()];
17340 var result = new Array(Math.ceil(a.length / chunkSize));
17341 return Array.from(result, function (item, i) {
17342 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17344 } // Flattens two level array into a single level
17345 // var a = [[1,2,3],[4,5,6],[7]];
17346 // utilArrayFlatten(a);
17347 // [1,2,3,4,5,6,7];
17349 function utilArrayFlatten(a) {
17350 return a.reduce(function (acc, val) {
17351 return acc.concat(val);
17353 } // Groups the items of the Array according to the given key
17354 // `key` can be passed as a property or as a key function
17357 // { type: 'Dog', name: 'Spot' },
17358 // { type: 'Cat', name: 'Tiger' },
17359 // { type: 'Dog', name: 'Rover' },
17360 // { type: 'Cat', name: 'Leo' }
17363 // utilArrayGroupBy(pets, 'type')
17365 // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17366 // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17369 // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17371 // 3: [{type: 'Cat', name: 'Leo'}],
17372 // 4: [{type: 'Dog', name: 'Spot'}],
17373 // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17376 function utilArrayGroupBy(a, key) {
17377 return a.reduce(function (acc, item) {
17378 var group = typeof key === 'function' ? key(item) : item[key];
17379 (acc[group] = acc[group] || []).push(item);
17382 } // Returns an Array with all the duplicates removed
17383 // where uniqueness determined by the given key
17384 // `key` can be passed as a property or as a key function
17387 // { type: 'Dog', name: 'Spot' },
17388 // { type: 'Cat', name: 'Tiger' },
17389 // { type: 'Dog', name: 'Rover' },
17390 // { type: 'Cat', name: 'Leo' }
17393 // utilArrayUniqBy(pets, 'type')
17395 // { type: 'Dog', name: 'Spot' },
17396 // { type: 'Cat', name: 'Tiger' }
17399 // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17401 // { type: 'Dog', name: 'Spot' },
17402 // { type: 'Cat', name: 'Tiger' },
17403 // { type: 'Cat', name: 'Leo' }
17406 function utilArrayUniqBy(a, key) {
17407 var seen = new Set();
17408 return a.reduce(function (acc, item) {
17409 var val = typeof key === 'function' ? key(item) : item[key];
17411 if (val && !seen.has(val)) {
17421 fixRegexpWellKnownSymbolLogic('match', function (MATCH, nativeMatch, maybeCallNative) {
17423 // `String.prototype.match` method
17424 // https://tc39.es/ecma262/#sec-string.prototype.match
17425 function match(regexp) {
17426 var O = requireObjectCoercible(this);
17427 var matcher = regexp == undefined ? undefined : regexp[MATCH];
17428 return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17430 // `RegExp.prototype[@@match]` method
17431 // https://tc39.es/ecma262/#sec-regexp.prototype-@@match
17432 function (string) {
17433 var res = maybeCallNative(nativeMatch, this, string);
17434 if (res.done) return res.value;
17436 var rx = anObject(this);
17437 var S = String(string);
17439 if (!rx.global) return regexpExecAbstract(rx, S);
17441 var fullUnicode = rx.unicode;
17446 while ((result = regexpExecAbstract(rx, S)) !== null) {
17447 var matchStr = String(result[0]);
17449 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17452 return n === 0 ? null : A;
17457 var remove$6 = removeDiacritics;
17458 var replacementList = [{
17466 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"
17472 chars: "\xC6\u01FC\u01E2"
17481 chars: "\uA738\uA73A"
17487 chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17490 chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17493 chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17499 chars: "\u01F1\u01C4"
17502 chars: "\u01F2\u01C5"
17505 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"
17508 chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17511 chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17514 chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17517 chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17520 chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17523 chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17526 chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17535 chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17538 chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17547 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"
17562 chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17565 chars: "\u24C6\uFF31\uA756\uA758\u024A"
17568 chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17571 chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17574 chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17583 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"
17586 chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17592 chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17595 chars: "\u24CD\uFF38\u1E8A\u1E8C"
17598 chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17601 chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17604 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"
17610 chars: "\xE6\u01FD\u01E3"
17619 chars: "\uA739\uA73B"
17625 chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17628 chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17631 chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17637 chars: "\u01F3\u01C6"
17640 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"
17643 chars: "\u24D5\uFF46\u1E1F\u0192"
17661 chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17664 chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17670 chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17673 chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17676 chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17679 chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17685 chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17688 chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17694 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"
17709 chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17712 chars: "\u24E0\uFF51\u024B\uA757\uA759"
17715 chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17718 chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17724 chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17733 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"
17736 chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17742 chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17745 chars: "\u24E7\uFF58\u1E8B\u1E8D"
17748 chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17751 chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17753 var diacriticsMap = {};
17755 for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) {
17756 var chars = replacementList[i$1].chars;
17758 for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17759 diacriticsMap[chars[j$1]] = replacementList[i$1].base;
17763 function removeDiacritics(str) {
17764 return str.replace(/[^\u0000-\u007e]/g, function (c) {
17765 return diacriticsMap[c] || c;
17769 var replacementList_1 = replacementList;
17770 var diacriticsMap_1 = diacriticsMap;
17773 replacementList: replacementList_1,
17774 diacriticsMap: diacriticsMap_1
17777 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
17780 function isArabic(_char) {
17781 if (_char.length > 1) {
17782 // allow the newer chars?
17783 throw new Error('isArabic works on only one-character strings');
17786 var code = _char.charCodeAt(0);
17788 for (var i = 0; i < arabicBlocks.length; i++) {
17789 var block = arabicBlocks[i];
17791 if (code >= block[0] && code <= block[1]) {
17799 var isArabic_2 = isArabic;
17801 function isMath(_char2) {
17802 if (_char2.length > 2) {
17803 // allow the newer chars?
17804 throw new Error('isMath works on only one-character strings');
17807 var code = _char2.charCodeAt(0);
17809 return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17812 var isMath_1 = isMath;
17813 var isArabic_1 = /*#__PURE__*/Object.defineProperty({
17814 isArabic: isArabic_2,
17820 var arabicReference = {
17822 "normal": ["\u0627"],
17824 "normal": ["\u0627\u0653", "\u0622"],
17825 "isolated": "\uFE81",
17829 "normal": ["\u0627\u0654", "\u0623"],
17830 "isolated": "\uFE83",
17834 "normal": ["\u0627\u0655", "\u0625"],
17835 "isolated": "\uFE87",
17839 "normal": "\u0671",
17840 "isolated": "\uFB50",
17843 "wavy_hamza_above": ["\u0672"],
17844 "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17845 "high_hamza": ["\u0675", "\u0627\u0674"],
17846 "indic_two_above": ["\u0773"],
17847 "indic_three_above": ["\u0774"],
17849 "normal": ["\u0627\u064B"],
17851 "isolated": "\uFD3D"
17853 "isolated": "\uFE8D",
17857 "normal": ["\u0628"],
17858 "dotless": ["\u066E"],
17859 "three_dots_horizontally_below": ["\u0750"],
17860 "dot_below_three_dots_above": ["\u0751"],
17861 "three_dots_pointing_upwards_below": ["\u0752"],
17862 "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17863 "two_dots_below_dot_above": ["\u0754"],
17864 "inverted_small_v_below": ["\u0755"],
17865 "small_v": ["\u0756"],
17866 "small_v_below": ["\u08A0"],
17867 "hamza_above": ["\u08A1"],
17868 "small_meem_above": ["\u08B6"],
17869 "isolated": "\uFE8F",
17871 "initial": "\uFE91",
17875 "normal": ["\u0629"],
17876 "isolated": "\uFE93",
17880 "normal": ["\u062A"],
17881 "ring": ["\u067C"],
17882 "three_dots_above_downwards": ["\u067D"],
17883 "small_teh_above": ["\u08B8"],
17884 "isolated": "\uFE95",
17886 "initial": "\uFE97",
17890 "normal": ["\u062B"],
17891 "isolated": "\uFE99",
17893 "initial": "\uFE9B",
17897 "normal": ["\u062C"],
17898 "two_dots_above": ["\u08A2"],
17899 "isolated": "\uFE9D",
17901 "initial": "\uFE9F",
17905 "normal": ["\u062D"],
17906 "hamza_above": ["\u0681"],
17907 "two_dots_vertical_above": ["\u0682"],
17908 "three_dots_above": ["\u0685"],
17909 "two_dots_above": ["\u0757"],
17910 "three_dots_pointing_upwards_below": ["\u0758"],
17911 "small_tah_below": ["\u076E"],
17912 "small_tah_two_dots": ["\u076F"],
17913 "small_tah_above": ["\u0772"],
17914 "indic_four_below": ["\u077C"],
17915 "isolated": "\uFEA1",
17917 "initial": "\uFEA3",
17921 "normal": ["\u062E"],
17922 "isolated": "\uFEA5",
17924 "initial": "\uFEA7",
17928 "normal": ["\u062F"],
17929 "ring": ["\u0689"],
17930 "dot_below": ["\u068A"],
17931 "dot_below_small_tah": ["\u068B"],
17932 "three_dots_above_downwards": ["\u068F"],
17933 "four_dots_above": ["\u0690"],
17934 "inverted_v": ["\u06EE"],
17935 "two_dots_vertically_below_small_tah": ["\u0759"],
17936 "inverted_small_v_below": ["\u075A"],
17937 "three_dots_below": ["\u08AE"],
17938 "isolated": "\uFEA9",
17942 "normal": ["\u0630"],
17943 "isolated": "\uFEAB",
17947 "normal": ["\u0631"],
17948 "small_v": ["\u0692"],
17949 "ring": ["\u0693"],
17950 "dot_below": ["\u0694"],
17951 "small_v_below": ["\u0695"],
17952 "dot_below_dot_above": ["\u0696"],
17953 "two_dots_above": ["\u0697"],
17954 "four_dots_above": ["\u0699"],
17955 "inverted_v": ["\u06EF"],
17956 "stroke": ["\u075B"],
17957 "two_dots_vertically_above": ["\u076B"],
17958 "hamza_above": ["\u076C"],
17959 "small_tah_two_dots": ["\u0771"],
17960 "loop": ["\u08AA"],
17961 "small_noon_above": ["\u08B9"],
17962 "isolated": "\uFEAD",
17966 "normal": ["\u0632"],
17967 "inverted_v_above": ["\u08B2"],
17968 "isolated": "\uFEAF",
17972 "normal": ["\u0633"],
17973 "dot_below_dot_above": ["\u069A"],
17974 "three_dots_below": ["\u069B"],
17975 "three_dots_below_three_dots_above": ["\u069C"],
17976 "four_dots_above": ["\u075C"],
17977 "two_dots_vertically_above": ["\u076D"],
17978 "small_tah_two_dots": ["\u0770"],
17979 "indic_four_above": ["\u077D"],
17980 "inverted_v": ["\u077E"],
17981 "isolated": "\uFEB1",
17983 "initial": "\uFEB3",
17987 "normal": ["\u0634"],
17988 "dot_below": ["\u06FA"],
17989 "isolated": "\uFEB5",
17991 "initial": "\uFEB7",
17995 "normal": ["\u0635"],
17996 "two_dots_below": ["\u069D"],
17997 "three_dots_above": ["\u069E"],
17998 "three_dots_below": ["\u08AF"],
17999 "isolated": "\uFEB9",
18001 "initial": "\uFEBB",
18005 "normal": ["\u0636"],
18006 "dot_below": ["\u06FB"],
18007 "isolated": "\uFEBD",
18009 "initial": "\uFEBF",
18013 "normal": ["\u0637"],
18014 "three_dots_above": ["\u069F"],
18015 "two_dots_above": ["\u08A3"],
18016 "isolated": "\uFEC1",
18018 "initial": "\uFEC3",
18022 "normal": ["\u0638"],
18023 "isolated": "\uFEC5",
18025 "initial": "\uFEC7",
18029 "normal": ["\u0639"],
18030 "three_dots_above": ["\u06A0"],
18031 "two_dots_above": ["\u075D"],
18032 "three_dots_pointing_downwards_above": ["\u075E"],
18033 "two_dots_vertically_above": ["\u075F"],
18034 "three_dots_below": ["\u08B3"],
18035 "isolated": "\uFEC9",
18037 "initial": "\uFECB",
18041 "normal": ["\u063A"],
18042 "dot_below": ["\u06FC"],
18043 "isolated": "\uFECD",
18045 "initial": "\uFECF",
18049 "normal": ["\u0641"],
18050 "dotless": ["\u06A1"],
18051 "dot_moved_below": ["\u06A2"],
18052 "dot_below": ["\u06A3"],
18053 "three_dots_below": ["\u06A5"],
18054 "two_dots_below": ["\u0760"],
18055 "three_dots_pointing_upwards_below": ["\u0761"],
18056 "dot_below_three_dots_above": ["\u08A4"],
18057 "isolated": "\uFED1",
18059 "initial": "\uFED3",
18063 "normal": ["\u0642"],
18064 "dotless": ["\u066F"],
18065 "dot_above": ["\u06A7"],
18066 "three_dots_above": ["\u06A8"],
18067 "dot_below": ["\u08A5"],
18068 "isolated": "\uFED5",
18070 "initial": "\uFED7",
18074 "normal": ["\u0643"],
18075 "swash": ["\u06AA"],
18076 "ring": ["\u06AB"],
18077 "dot_above": ["\u06AC"],
18078 "three_dots_below": ["\u06AE"],
18079 "two_dots_above": ["\u077F"],
18080 "dot_below": ["\u08B4"],
18081 "isolated": "\uFED9",
18083 "initial": "\uFEDB",
18087 "normal": ["\u0644"],
18088 "small_v": ["\u06B5"],
18089 "dot_above": ["\u06B6"],
18090 "three_dots_above": ["\u06B7"],
18091 "three_dots_below": ["\u06B8"],
18093 "double_bar": ["\u08A6"],
18094 "isolated": "\uFEDD",
18096 "initial": "\uFEDF",
18100 "normal": ["\u0645"],
18101 "dot_above": ["\u0765"],
18102 "dot_below": ["\u0766"],
18103 "three_dots_above": ["\u08A7"],
18104 "isolated": "\uFEE1",
18106 "initial": "\uFEE3",
18110 "normal": ["\u0646"],
18111 "dot_below": ["\u06B9"],
18112 "ring": ["\u06BC"],
18113 "three_dots_above": ["\u06BD"],
18114 "two_dots_below": ["\u0767"],
18115 "small_tah": ["\u0768"],
18116 "small_v": ["\u0769"],
18117 "isolated": "\uFEE5",
18119 "initial": "\uFEE7",
18123 "normal": ["\u0647"],
18124 "isolated": "\uFEE9",
18126 "initial": "\uFEEB",
18130 "normal": ["\u0648"],
18132 "normal": ["\u0624", "\u0648\u0654"],
18133 "isolated": "\uFE85",
18136 "high_hamza": ["\u0676", "\u0648\u0674"],
18137 "ring": ["\u06C4"],
18138 "two_dots_above": ["\u06CA"],
18139 "dot_above": ["\u06CF"],
18140 "indic_two_above": ["\u0778"],
18141 "indic_three_above": ["\u0779"],
18142 "dot_within": ["\u08AB"],
18143 "isolated": "\uFEED",
18147 "normal": ["\u0649"],
18148 "hamza_above": ["\u0626", "\u064A\u0654"],
18149 "initial": "\uFBE8",
18150 "medial": "\uFBE9",
18151 "isolated": "\uFEEF",
18155 "normal": ["\u064A"],
18157 "normal": ["\u0626", "\u0649\u0654"],
18158 "isolated": "\uFE89",
18160 "initial": "\uFE8B",
18163 "two_dots_below_hamza_above": ["\u08A8"],
18164 "high_hamza": ["\u0678", "\u064A\u0674"],
18165 "tail": ["\u06CD"],
18166 "small_v": ["\u06CE"],
18167 "three_dots_below": ["\u06D1"],
18168 "two_dots_below_dot_above": ["\u08A9"],
18169 "two_dots_below_small_noon_above": ["\u08BA"],
18170 "isolated": "\uFEF1",
18172 "initial": "\uFEF3",
18176 "normal": ["\u0679"],
18177 "isolated": "\uFB66",
18179 "initial": "\uFB68",
18183 "normal": ["\u067A"],
18184 "isolated": "\uFB5E",
18186 "initial": "\uFB60",
18190 "normal": ["\u067B"],
18191 "isolated": "\uFB52",
18193 "initial": "\uFB54",
18197 "normal": ["\u067E"],
18198 "small_meem_above": ["\u08B7"],
18199 "isolated": "\uFB56",
18201 "initial": "\uFB58",
18205 "normal": ["\u067F"],
18206 "isolated": "\uFB62",
18208 "initial": "\uFB64",
18212 "normal": ["\u0680"],
18213 "isolated": "\uFB5A",
18215 "initial": "\uFB5C",
18219 "normal": ["\u0683"],
18220 "isolated": "\uFB76",
18222 "initial": "\uFB78",
18226 "normal": ["\u0684"],
18227 "isolated": "\uFB72",
18229 "initial": "\uFB74",
18233 "normal": ["\u0686"],
18234 "dot_above": ["\u06BF"],
18235 "isolated": "\uFB7A",
18237 "initial": "\uFB7C",
18241 "normal": ["\u0687"],
18242 "isolated": "\uFB7E",
18244 "initial": "\uFB80",
18248 "normal": ["\u0688"],
18249 "isolated": "\uFB88",
18253 "normal": ["\u068C"],
18254 "isolated": "\uFB84",
18258 "normal": ["\u068D"],
18259 "isolated": "\uFB82",
18263 "normal": ["\u068F", "\u068E"],
18264 "isolated": "\uFB86",
18268 "normal": ["\u0691"],
18269 "isolated": "\uFB8C",
18273 "normal": ["\u0698"],
18274 "isolated": "\uFB8A",
18278 "normal": ["\u06A4"],
18279 "isolated": "\uFB6A",
18281 "initial": "\uFB6C",
18285 "normal": ["\u06A6"],
18286 "isolated": "\uFB6E",
18288 "initial": "\uFB70",
18292 "normal": ["\u06A9"],
18293 "dot_above": ["\u0762"],
18294 "three_dots_above": ["\u0763"],
18295 "three_dots_pointing_upwards_below": ["\u0764"],
18296 "isolated": "\uFB8E",
18298 "initial": "\uFB90",
18302 "normal": ["\u06AD"],
18303 "isolated": "\uFBD3",
18305 "initial": "\uFBD5",
18309 "normal": ["\u06AF"],
18310 "ring": ["\u06B0"],
18311 "two_dots_below": ["\u06B2"],
18312 "three_dots_above": ["\u06B4"],
18313 "inverted_stroke": ["\u08B0"],
18314 "isolated": "\uFB92",
18316 "initial": "\uFB94",
18320 "normal": ["\u06B1"],
18321 "isolated": "\uFB9A",
18323 "initial": "\uFB9C",
18327 "normal": ["\u06B3"],
18328 "isolated": "\uFB96",
18330 "initial": "\uFB98",
18334 "normal": ["\u06BA"],
18335 "isolated": "\uFB9E",
18339 "normal": ["\u06BB"],
18340 "isolated": "\uFBA0",
18342 "initial": "\uFBA2",
18345 "heh doachashmee": {
18346 "normal": ["\u06BE"],
18347 "isolated": "\uFBAA",
18349 "initial": "\uFBAC",
18353 "normal": ["\u06C1"],
18354 "hamza_above": ["\u06C1\u0654", "\u06C2"],
18355 "isolated": "\uFBA6",
18357 "initial": "\uFBA8",
18360 "teh marbuta goal": {
18361 "normal": ["\u06C3"]
18364 "normal": ["\u06C5"],
18365 "isolated": "\uFBE0",
18369 "normal": ["\u06C6"],
18370 "isolated": "\uFBD9",
18374 "normal": ["\u06C7"],
18376 "normal": ["\u0677", "\u06C7\u0674"],
18377 "isolated": "\uFBDD"
18379 "isolated": "\uFBD7",
18383 "normal": ["\u06C8"],
18384 "isolated": "\uFBDB",
18388 "normal": ["\u06C9"],
18389 "isolated": "\uFBE2",
18393 "normal": ["\u06CB"],
18394 "isolated": "\uFBDE",
18398 "normal": ["\u06CC"],
18399 "indic_two_above": ["\u0775"],
18400 "indic_three_above": ["\u0776"],
18401 "indic_four_above": ["\u0777"],
18402 "isolated": "\uFBFC",
18404 "initial": "\uFBFE",
18408 "normal": ["\u06D0"],
18409 "isolated": "\uFBE4",
18411 "initial": "\uFBE6",
18415 "normal": ["\u06D2"],
18417 "normal": ["\u06D2\u0654", "\u06D3"],
18418 "isolated": "\uFBB0",
18421 "indic_two_above": ["\u077A"],
18422 "indic_three_above": ["\u077B"],
18423 "isolated": "\uFBAE",
18427 "normal": ["\u06D5"],
18428 "isolated": "\u06D5",
18431 "normal": ["\u06C0", "\u06D5\u0654"],
18432 "isolated": "\uFBA4",
18437 "normal": ["\u08AC"]
18440 "normal": ["\u08AD"]
18443 "normal": ["\u08B1"]
18446 "normal": ["\u08BB"]
18449 "normal": ["\u08BC"]
18452 "normal": ["\u08BD"]
18455 var _default$3 = arabicReference;
18456 var unicodeArabic = /*#__PURE__*/Object.defineProperty({
18457 "default": _default$3
18462 var ligatureReference = {
18464 "isolated": "\uFBEA",
18468 "isolated": "\uFBEC",
18472 "isolated": "\uFBEE",
18476 "isolated": "\uFBF0",
18480 "isolated": "\uFBF2",
18484 "isolated": "\uFBF4",
18488 "isolated": "\uFBF6",
18490 "initial": "\uFBF8"
18493 "uighur_kirghiz": {
18494 "isolated": "\uFBF9",
18496 "initial": "\uFBFB"
18498 "isolated": "\uFC03",
18502 "isolated": "\uFC00",
18503 "initial": "\uFC97"
18506 "isolated": "\uFC01",
18507 "initial": "\uFC98"
18510 "isolated": "\uFC02",
18512 "initial": "\uFC9A",
18516 "isolated": "\uFC04",
18520 "isolated": "\uFC05",
18521 "initial": "\uFC9C"
18524 "isolated": "\uFC06",
18525 "initial": "\uFC9D"
18528 "isolated": "\uFC07",
18529 "initial": "\uFC9E"
18532 "isolated": "\uFC08",
18534 "initial": "\uFC9F",
18538 "isolated": "\uFC09",
18542 "isolated": "\uFC0A",
18546 "isolated": "\uFC0B",
18547 "initial": "\uFCA1"
18550 "isolated": "\uFC0C",
18551 "initial": "\uFCA2"
18554 "isolated": "\uFC0D",
18555 "initial": "\uFCA3"
18558 "isolated": "\uFC0E",
18560 "initial": "\uFCA4",
18564 "isolated": "\uFC0F",
18568 "isolated": "\uFC10",
18572 "isolated": "\uFC11"
18575 "isolated": "\uFC12",
18577 "initial": "\uFCA6",
18581 "isolated": "\uFC13",
18585 "isolated": "\uFC14"
18588 "isolated": "\uFC15",
18589 "initial": "\uFCA7"
18592 "isolated": "\uFC16",
18593 "initial": "\uFCA8"
18596 "isolated": "\uFC17",
18597 "initial": "\uFCA9"
18600 "isolated": "\uFC18",
18601 "initial": "\uFCAA"
18604 "isolated": "\uFC19",
18605 "initial": "\uFCAB"
18608 "isolated": "\uFC1A"
18611 "isolated": "\uFC1B",
18612 "initial": "\uFCAC"
18615 "isolated": "\uFC1C",
18616 "initial": "\uFCAD",
18620 "isolated": "\uFC1D",
18621 "initial": "\uFCAE",
18625 "isolated": "\uFC1E",
18626 "initial": "\uFCAF",
18630 "isolated": "\uFC1F",
18631 "initial": "\uFCB0",
18635 "isolated": "\uFC20",
18636 "initial": "\uFCB1"
18639 "isolated": "\uFC21",
18640 "initial": "\uFCB3"
18643 "isolated": "\uFC22",
18644 "initial": "\uFCB4"
18647 "isolated": "\uFC23",
18648 "initial": "\uFCB5"
18651 "isolated": "\uFC24",
18652 "initial": "\uFCB6"
18655 "isolated": "\uFC25",
18656 "initial": "\uFCB7"
18659 "isolated": "\uFC26",
18660 "initial": "\uFCB8"
18663 "isolated": "\uFC27",
18664 "initial": "\uFD33",
18668 "isolated": "\uFC28",
18669 "initial": "\uFCB9",
18673 "isolated": "\uFC29",
18674 "initial": "\uFCBA"
18677 "isolated": "\uFC2A",
18678 "initial": "\uFCBB"
18681 "isolated": "\uFC2B",
18682 "initial": "\uFCBC"
18685 "isolated": "\uFC2C",
18686 "initial": "\uFCBD"
18689 "isolated": "\uFC2D",
18690 "initial": "\uFCBE"
18693 "isolated": "\uFC2E",
18694 "initial": "\uFCBF"
18697 "isolated": "\uFC2F",
18698 "initial": "\uFCC0"
18701 "isolated": "\uFC30",
18702 "initial": "\uFCC1"
18705 "isolated": "\uFC31",
18709 "isolated": "\uFC32",
18713 "isolated": "\uFC33",
18714 "initial": "\uFCC2"
18717 "isolated": "\uFC34",
18718 "initial": "\uFCC3"
18721 "isolated": "\uFC35",
18725 "isolated": "\uFC36",
18729 "isolated": "\uFC37",
18733 "isolated": "\uFC38",
18734 "initial": "\uFCC4"
18737 "isolated": "\uFC39",
18738 "initial": "\uFCC5"
18741 "isolated": "\uFC3A",
18742 "initial": "\uFCC6"
18745 "isolated": "\uFC3B",
18747 "initial": "\uFCC7",
18751 "isolated": "\uFC3C",
18753 "initial": "\uFCC8",
18757 "isolated": "\uFC3D",
18761 "isolated": "\uFC3E",
18765 "isolated": "\uFC3F",
18766 "initial": "\uFCC9"
18769 "isolated": "\uFC40",
18770 "initial": "\uFCCA"
18773 "isolated": "\uFC41",
18774 "initial": "\uFCCB"
18777 "isolated": "\uFC42",
18779 "initial": "\uFCCC",
18783 "isolated": "\uFC43",
18787 "isolated": "\uFC44",
18791 "isolated": "\uFC45",
18792 "initial": "\uFCCE"
18795 "isolated": "\uFC46",
18796 "initial": "\uFCCF"
18799 "isolated": "\uFC47",
18800 "initial": "\uFCD0"
18803 "isolated": "\uFC48",
18805 "initial": "\uFCD1"
18808 "isolated": "\uFC49"
18811 "isolated": "\uFC4A"
18814 "isolated": "\uFC4B",
18815 "initial": "\uFCD2"
18818 "isolated": "\uFC4C",
18819 "initial": "\uFCD3"
18822 "isolated": "\uFC4D",
18823 "initial": "\uFCD4"
18826 "isolated": "\uFC4E",
18828 "initial": "\uFCD5",
18832 "isolated": "\uFC4F",
18836 "isolated": "\uFC50",
18840 "isolated": "\uFC51",
18841 "initial": "\uFCD7"
18844 "isolated": "\uFC52",
18845 "initial": "\uFCD8"
18848 "isolated": "\uFC53"
18851 "isolated": "\uFC54"
18854 "isolated": "\uFC55",
18855 "initial": "\uFCDA"
18858 "isolated": "\uFC56",
18859 "initial": "\uFCDB"
18862 "isolated": "\uFC57",
18863 "initial": "\uFCDC"
18866 "isolated": "\uFC58",
18868 "initial": "\uFCDD",
18872 "isolated": "\uFC59",
18876 "isolated": "\uFC5A",
18880 "isolated": "\uFC5B"
18883 "isolated": "\uFC5C"
18886 "isolated": "\uFC5D",
18890 "isolated": "\uFC5E"
18893 "isolated": "\uFC5F"
18896 "isolated": "\uFC60"
18899 "isolated": "\uFC61"
18902 "isolated": "\uFC62"
18905 "isolated": "\uFC63"
18968 "initial": "\uFC99"
18971 "initial": "\uFC9B",
18975 "initial": "\uFCA0",
18979 "initial": "\uFCA5",
18983 "initial": "\uFCB2"
18986 "initial": "\uFCCD"
18989 "initial": "\uFCD6",
18993 "initial": "\uFCD9"
18996 "initial": "\uFCDE",
19003 "medial": "\uFCE8",
19004 "initial": "\uFD31"
19007 "medial": "\uFCE9",
19008 "isolated": "\uFD0C",
19010 "initial": "\uFD30"
19013 "medial": "\uFCEA",
19014 "initial": "\uFD32"
19016 "\u0640\u064E\u0651": {
19019 "\u0640\u064F\u0651": {
19022 "\u0640\u0650\u0651": {
19026 "isolated": "\uFCF5",
19030 "isolated": "\uFCF6",
19034 "isolated": "\uFCF7",
19038 "isolated": "\uFCF8",
19042 "isolated": "\uFCF9",
19046 "isolated": "\uFCFA",
19050 "isolated": "\uFCFB"
19053 "isolated": "\uFCFC",
19057 "isolated": "\uFCFD",
19061 "isolated": "\uFCFE",
19065 "isolated": "\uFCFF",
19069 "isolated": "\uFD00",
19073 "isolated": "\uFD01",
19077 "isolated": "\uFD02",
19081 "isolated": "\uFD03",
19085 "isolated": "\uFD04",
19089 "isolated": "\uFD05",
19093 "isolated": "\uFD06",
19097 "isolated": "\uFD07",
19101 "isolated": "\uFD08",
19105 "isolated": "\uFD09",
19107 "initial": "\uFD2D",
19111 "isolated": "\uFD0A",
19113 "initial": "\uFD2E",
19117 "isolated": "\uFD0B",
19119 "initial": "\uFD2F",
19123 "isolated": "\uFD0D",
19127 "isolated": "\uFD0E",
19131 "isolated": "\uFD0F",
19135 "isolated": "\uFD10",
19141 "\u062A\u062C\u0645": {
19142 "initial": "\uFD50"
19144 "\u062A\u062D\u062C": {
19146 "initial": "\uFD52"
19148 "\u062A\u062D\u0645": {
19149 "initial": "\uFD53"
19151 "\u062A\u062E\u0645": {
19152 "initial": "\uFD54"
19154 "\u062A\u0645\u062C": {
19155 "initial": "\uFD55"
19157 "\u062A\u0645\u062D": {
19158 "initial": "\uFD56"
19160 "\u062A\u0645\u062E": {
19161 "initial": "\uFD57"
19163 "\u062C\u0645\u062D": {
19165 "initial": "\uFD59"
19167 "\u062D\u0645\u064A": {
19170 "\u062D\u0645\u0649": {
19173 "\u0633\u062D\u062C": {
19174 "initial": "\uFD5C"
19176 "\u0633\u062C\u062D": {
19177 "initial": "\uFD5D"
19179 "\u0633\u062C\u0649": {
19182 "\u0633\u0645\u062D": {
19184 "initial": "\uFD60"
19186 "\u0633\u0645\u062C": {
19187 "initial": "\uFD61"
19189 "\u0633\u0645\u0645": {
19191 "initial": "\uFD63"
19193 "\u0635\u062D\u062D": {
19195 "initial": "\uFD65"
19197 "\u0635\u0645\u0645": {
19199 "initial": "\uFDC5"
19201 "\u0634\u062D\u0645": {
19203 "initial": "\uFD68"
19205 "\u0634\u062C\u064A": {
19208 "\u0634\u0645\u062E": {
19210 "initial": "\uFD6B"
19212 "\u0634\u0645\u0645": {
19214 "initial": "\uFD6D"
19216 "\u0636\u062D\u0649": {
19219 "\u0636\u062E\u0645": {
19221 "initial": "\uFD70"
19223 "\u0636\u0645\u062D": {
19226 "\u0637\u0645\u062D": {
19227 "initial": "\uFD72"
19229 "\u0637\u0645\u0645": {
19230 "initial": "\uFD73"
19232 "\u0637\u0645\u064A": {
19235 "\u0639\u062C\u0645": {
19237 "initial": "\uFDC4"
19239 "\u0639\u0645\u0645": {
19241 "initial": "\uFD77"
19243 "\u0639\u0645\u0649": {
19246 "\u063A\u0645\u0645": {
19249 "\u063A\u0645\u064A": {
19252 "\u063A\u0645\u0649": {
19255 "\u0641\u062E\u0645": {
19257 "initial": "\uFD7D"
19259 "\u0642\u0645\u062D": {
19261 "initial": "\uFDB4"
19263 "\u0642\u0645\u0645": {
19266 "\u0644\u062D\u0645": {
19268 "initial": "\uFDB5"
19270 "\u0644\u062D\u064A": {
19273 "\u0644\u062D\u0649": {
19276 "\u0644\u062C\u062C": {
19277 "initial": "\uFD83",
19280 "\u0644\u062E\u0645": {
19282 "initial": "\uFD86"
19284 "\u0644\u0645\u062D": {
19286 "initial": "\uFD88"
19288 "\u0645\u062D\u062C": {
19289 "initial": "\uFD89"
19291 "\u0645\u062D\u0645": {
19292 "initial": "\uFD8A"
19294 "\u0645\u062D\u064A": {
19297 "\u0645\u062C\u062D": {
19298 "initial": "\uFD8C"
19300 "\u0645\u062C\u0645": {
19301 "initial": "\uFD8D"
19303 "\u0645\u062E\u062C": {
19304 "initial": "\uFD8E"
19306 "\u0645\u062E\u0645": {
19307 "initial": "\uFD8F"
19309 "\u0645\u062C\u062E": {
19310 "initial": "\uFD92"
19312 "\u0647\u0645\u062C": {
19313 "initial": "\uFD93"
19315 "\u0647\u0645\u0645": {
19316 "initial": "\uFD94"
19318 "\u0646\u062D\u0645": {
19319 "initial": "\uFD95"
19321 "\u0646\u062D\u0649": {
19324 "\u0646\u062C\u0645": {
19326 "initial": "\uFD98"
19328 "\u0646\u062C\u0649": {
19331 "\u0646\u0645\u064A": {
19334 "\u0646\u0645\u0649": {
19337 "\u064A\u0645\u0645": {
19339 "initial": "\uFD9D"
19341 "\u0628\u062E\u064A": {
19344 "\u062A\u062C\u064A": {
19347 "\u062A\u062C\u0649": {
19350 "\u062A\u062E\u064A": {
19353 "\u062A\u062E\u0649": {
19356 "\u062A\u0645\u064A": {
19359 "\u062A\u0645\u0649": {
19362 "\u062C\u0645\u064A": {
19365 "\u062C\u062D\u0649": {
19368 "\u062C\u0645\u0649": {
19371 "\u0633\u062E\u0649": {
19374 "\u0635\u062D\u064A": {
19377 "\u0634\u062D\u064A": {
19380 "\u0636\u062D\u064A": {
19383 "\u0644\u062C\u064A": {
19386 "\u0644\u0645\u064A": {
19389 "\u064A\u062D\u064A": {
19392 "\u064A\u062C\u064A": {
19395 "\u064A\u0645\u064A": {
19398 "\u0645\u0645\u064A": {
19401 "\u0642\u0645\u064A": {
19404 "\u0646\u062D\u064A": {
19407 "\u0639\u0645\u064A": {
19410 "\u0643\u0645\u064A": {
19413 "\u0646\u062C\u062D": {
19414 "initial": "\uFDB8",
19417 "\u0645\u062E\u064A": {
19420 "\u0644\u062C\u0645": {
19421 "initial": "\uFDBA",
19424 "\u0643\u0645\u0645": {
19426 "initial": "\uFDC3"
19428 "\u062C\u062D\u064A": {
19431 "\u062D\u062C\u064A": {
19434 "\u0645\u062C\u064A": {
19437 "\u0641\u0645\u064A": {
19440 "\u0628\u062D\u064A": {
19443 "\u0633\u062E\u064A": {
19446 "\u0646\u062C\u064A": {
19450 "isolated": "\uFEF5",
19454 "isolated": "\uFEF7",
19458 "isolated": "\uFEF9",
19462 "isolated": "\uFEFB",
19466 "\u0635\u0644\u06D2": "\uFDF0",
19467 "\u0642\u0644\u06D2": "\uFDF1",
19468 "\u0627\u0644\u0644\u0647": "\uFDF2",
19469 "\u0627\u0643\u0628\u0631": "\uFDF3",
19470 "\u0645\u062D\u0645\u062F": "\uFDF4",
19471 "\u0635\u0644\u0639\u0645": "\uFDF5",
19472 "\u0631\u0633\u0648\u0644": "\uFDF6",
19473 "\u0639\u0644\u064A\u0647": "\uFDF7",
19474 "\u0648\u0633\u0644\u0645": "\uFDF8",
19475 "\u0635\u0644\u0649": "\uFDF9",
19476 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19477 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19478 "\u0631\u06CC\u0627\u0644": "\uFDFC"
19481 var _default$2 = ligatureReference;
19482 var unicodeLigatures = /*#__PURE__*/Object.defineProperty({
19483 "default": _default$2
19488 var reference = createCommonjsModule(function (module, exports) {
19490 Object.defineProperty(exports, "__esModule", {
19493 var letterList = Object.keys(unicodeArabic["default"]);
19494 exports.letterList = letterList;
19495 var ligatureList = Object.keys(unicodeLigatures["default"]);
19496 exports.ligatureList = ligatureList;
19497 var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19498 exports.ligatureWordList = ligatureWordList;
19499 var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19500 exports.lams = lams;
19501 var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19502 exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19503 // console.log('-');
19504 // for (var a = 0; a < alefs.length; a++) {
19505 // console.log(a + ': ' + lams[l] + alefs[a]);
19509 var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19510 exports.tashkeel = tashkeel;
19512 function addToTashkeel(start, finish) {
19513 for (var i = start; i <= finish; i++) {
19514 exports.tashkeel = tashkeel += String.fromCharCode(i);
19518 addToTashkeel(0x0610, 0x061A);
19519 addToTashkeel(0x064B, 0x065F);
19520 addToTashkeel(0x06D6, 0x06DC);
19521 addToTashkeel(0x06E0, 0x06E4);
19522 addToTashkeel(0x06EA, 0x06ED);
19523 addToTashkeel(0x08D3, 0x08E1);
19524 addToTashkeel(0x08E3, 0x08FF);
19525 addToTashkeel(0xFE70, 0xFE7F);
19526 var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19527 exports.lineBreakers = lineBreakers;
19529 function addToLineBreakers(start, finish) {
19530 for (var i = start; i <= finish; i++) {
19531 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19535 addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19537 addToLineBreakers(0x0621, 0x0625);
19538 addToLineBreakers(0x062F, 0x0632);
19539 addToLineBreakers(0x0660, 0x066D); // numerals, math
19541 addToLineBreakers(0x0671, 0x0677);
19542 addToLineBreakers(0x0688, 0x0699);
19543 addToLineBreakers(0x06C3, 0x06CB);
19544 addToLineBreakers(0x06D2, 0x06F9);
19545 addToLineBreakers(0x0759, 0x075B);
19546 addToLineBreakers(0x08AA, 0x08AE);
19547 addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19548 // Presentation Forms A includes diacritics but they are meant to stand alone
19550 addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19553 addToLineBreakers(0x10E60, 0x10E7F);
19554 addToLineBreakers(0x1EC70, 0x1ECBF);
19555 addToLineBreakers(0x1EE00, 0x1EEFF);
19558 function GlyphSplitter(word) {
19560 var lastLetter = '';
19561 word.split('').forEach(function (letter) {
19562 if (isArabic_1.isArabic(letter)) {
19563 if (reference.tashkeel.indexOf(letter) > -1) {
19564 letters[letters.length - 1] += letter;
19565 } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19567 letters[letters.length - 1] += letter;
19569 letters.push(letter);
19572 letters.push(letter);
19575 if (reference.tashkeel.indexOf(letter) === -1) {
19576 lastLetter = letter;
19582 var GlyphSplitter_2 = GlyphSplitter;
19583 var GlyphSplitter_1 = /*#__PURE__*/Object.defineProperty({
19584 GlyphSplitter: GlyphSplitter_2
19589 function BaselineSplitter(word) {
19591 var lastLetter = '';
19592 word.split('').forEach(function (letter) {
19593 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19594 if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19595 letters[letters.length - 1] += letter;
19596 } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19597 letters.push(letter);
19599 letters[letters.length - 1] += letter;
19602 letters.push(letter);
19605 if (reference.tashkeel.indexOf(letter) === -1) {
19606 // don't allow tashkeel to hide line break
19607 lastLetter = letter;
19613 var BaselineSplitter_2 = BaselineSplitter;
19614 var BaselineSplitter_1 = /*#__PURE__*/Object.defineProperty({
19615 BaselineSplitter: BaselineSplitter_2
19620 function Normal(word, breakPresentationForm) {
19621 // default is to turn initial/isolated/medial/final presentation form to generic
19622 if (typeof breakPresentationForm === 'undefined') {
19623 breakPresentationForm = true;
19626 var returnable = '';
19627 word.split('').forEach(function (letter) {
19628 if (!isArabic_1.isArabic(letter)) {
19629 returnable += letter;
19633 for (var w = 0; w < reference.letterList.length; w++) {
19634 // ok so we are checking this potential lettertron
19635 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19636 var versions = Object.keys(letterForms);
19638 for (var v = 0; v < versions.length; v++) {
19639 var localVersion = letterForms[versions[v]];
19641 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19642 // look at this embedded object
19643 var embeddedForms = Object.keys(localVersion);
19645 for (var ef = 0; ef < embeddedForms.length; ef++) {
19646 var form = localVersion[embeddedForms[ef]];
19648 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19650 // console.log('embedded match');
19651 if (form === letter) {
19653 if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19654 // replace presentation form
19655 // console.log('keeping normal form of the letter');
19656 if (_typeof(localVersion['normal']) === 'object') {
19657 returnable += localVersion['normal'][0];
19659 returnable += localVersion['normal'];
19663 } // console.log('keeping this letter');
19666 returnable += letter;
19668 } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19670 returnable += form[0]; // console.log('added the first letter from the same array');
19676 } else if (localVersion === letter) {
19678 if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19679 // replace presentation form
19680 // console.log('keeping normal form of the letter');
19681 if (_typeof(letterForms['normal']) === 'object') {
19682 returnable += letterForms['normal'][0];
19684 returnable += letterForms['normal'];
19688 } // console.log('keeping this letter');
19691 returnable += letter;
19693 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19695 returnable += localVersion[0]; // console.log('added the first letter from the same array');
19703 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19704 var normalForm = reference.ligatureList[v2];
19706 if (normalForm !== 'words') {
19707 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19709 for (var f = 0; f < ligForms.length; f++) {
19710 if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19711 returnable += normalForm;
19716 } // try words ligatures
19719 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19720 var _normalForm = reference.ligatureWordList[v3];
19722 if (unicodeLigatures["default"].words[_normalForm] === letter) {
19723 returnable += _normalForm;
19728 returnable += letter; // console.log('kept the letter')
19733 var Normal_1 = Normal;
19734 var Normalization = /*#__PURE__*/Object.defineProperty({
19740 function CharShaper(letter, form) {
19741 if (!isArabic_1.isArabic(letter)) {
19743 throw new Error('Not Arabic');
19746 if (letter === "\u0621") {
19751 for (var w = 0; w < reference.letterList.length; w++) {
19752 // ok so we are checking this potential lettertron
19753 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19754 var versions = Object.keys(letterForms);
19756 for (var v = 0; v < versions.length; v++) {
19757 var localVersion = letterForms[versions[v]];
19759 if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19760 if (versions.indexOf(form) > -1) {
19761 return letterForms[form];
19763 } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19765 var embeddedVersions = Object.keys(localVersion);
19767 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19768 if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19769 if (embeddedVersions.indexOf(form) > -1) {
19770 return localVersion[form];
19779 var CharShaper_2 = CharShaper;
19780 var CharShaper_1 = /*#__PURE__*/Object.defineProperty({
19781 CharShaper: CharShaper_2
19786 function WordShaper$1(word) {
19787 var state = 'initial';
19790 for (var w = 0; w < word.length; w++) {
19791 var nextLetter = ' ';
19793 for (var nxw = w + 1; nxw < word.length; nxw++) {
19794 if (!isArabic_1.isArabic(word[nxw])) {
19798 if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19799 nextLetter = word[nxw];
19804 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19805 // space or other non-Arabic
19808 } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19809 // tashkeel - add without changing state
19811 } else if (nextLetter === ' ' || // last Arabic letter in this word
19812 reference.lineBreakers.indexOf(word[w]) > -1) {
19813 // the current letter is known to break lines
19814 output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19816 } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19817 // LA letters - advance an additional letter after this
19818 output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19820 while (word[w] !== nextLetter) {
19826 output += CharShaper_1.CharShaper(word[w], state);
19834 var WordShaper_2 = WordShaper$1;
19835 var WordShaper_1 = /*#__PURE__*/Object.defineProperty({
19836 WordShaper: WordShaper_2
19841 function ParentLetter(letter) {
19842 if (!isArabic_1.isArabic(letter)) {
19843 throw new Error('Not an Arabic letter');
19846 for (var w = 0; w < reference.letterList.length; w++) {
19847 // ok so we are checking this potential lettertron
19848 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19849 var versions = Object.keys(letterForms);
19851 for (var v = 0; v < versions.length; v++) {
19852 var localVersion = letterForms[versions[v]];
19854 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19855 // look at this embedded object
19856 var embeddedForms = Object.keys(localVersion);
19858 for (var ef = 0; ef < embeddedForms.length; ef++) {
19859 var form = localVersion[embeddedForms[ef]];
19861 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19863 return localVersion;
19866 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19868 return letterForms;
19876 var ParentLetter_2 = ParentLetter;
19878 function GrandparentLetter(letter) {
19879 if (!isArabic_1.isArabic(letter)) {
19880 throw new Error('Not an Arabic letter');
19883 for (var w = 0; w < reference.letterList.length; w++) {
19884 // ok so we are checking this potential lettertron
19885 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19886 var versions = Object.keys(letterForms);
19888 for (var v = 0; v < versions.length; v++) {
19889 var localVersion = letterForms[versions[v]];
19891 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19892 // look at this embedded object
19893 var embeddedForms = Object.keys(localVersion);
19895 for (var ef = 0; ef < embeddedForms.length; ef++) {
19896 var form = localVersion[embeddedForms[ef]];
19898 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19900 return letterForms;
19903 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19905 return letterForms;
19913 var GrandparentLetter_1 = GrandparentLetter;
19914 var ParentLetter_1 = /*#__PURE__*/Object.defineProperty({
19915 ParentLetter: ParentLetter_2,
19916 GrandparentLetter: GrandparentLetter_1
19921 isArabic_1.isArabic;
19922 GlyphSplitter_1.GlyphSplitter;
19923 BaselineSplitter_1.BaselineSplitter;
19924 Normalization.Normal;
19925 CharShaper_1.CharShaper;
19926 var WordShaper = WordShaper_1.WordShaper;
19927 ParentLetter_1.ParentLetter;
19928 ParentLetter_1.GrandparentLetter;
19930 var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19931 function fixRTLTextForSvg(inputText) {
19934 var arabicRegex = /[\u0600-\u06FF]/g;
19935 var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19936 var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19937 var thaanaVowel = /[\u07A6-\u07B0]/;
19938 var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19940 if (arabicRegex.test(inputText)) {
19941 inputText = WordShaper(inputText);
19944 for (var n = 0; n < inputText.length; n++) {
19945 var c = inputText[n];
19947 if (arabicMath.test(c)) {
19948 // Arabic numbers go LTR
19949 ret += rtlBuffer.reverse().join('');
19952 if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19953 ret += rtlBuffer.reverse().join('');
19957 if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19958 rtlBuffer[rtlBuffer.length - 1] += c;
19959 } else if (rtlRegex.test(c) // include Arabic presentation forms
19960 || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19962 } else if (c === ' ' && rtlBuffer.length) {
19963 // whitespace within RTL text
19964 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19966 // non-RTL character
19967 ret += rtlBuffer.reverse().join('') + c;
19973 ret += rtlBuffer.reverse().join('');
19977 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19979 // `Object.{ entries, values }` methods implementation
19980 var createMethod$1 = function (TO_ENTRIES) {
19981 return function (it) {
19982 var O = toIndexedObject(it);
19983 var keys = objectKeys(O);
19984 var length = keys.length;
19988 while (length > i) {
19990 if (!descriptors || propertyIsEnumerable.call(O, key)) {
19991 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
19998 var objectToArray = {
19999 // `Object.entries` method
20000 // https://tc39.es/ecma262/#sec-object.entries
20001 entries: createMethod$1(true),
20002 // `Object.values` method
20003 // https://tc39.es/ecma262/#sec-object.values
20004 values: createMethod$1(false)
20007 var $values = objectToArray.values;
20009 // `Object.values` method
20010 // https://tc39.es/ecma262/#sec-object.values
20011 _export({ target: 'Object', stat: true }, {
20012 values: function values(O) {
20017 // https://github.com/openstreetmap/iD/issues/772
20018 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20022 _storage = localStorage;
20023 } catch (e) {} // eslint-disable-line no-empty
20026 _storage = _storage || function () {
20029 getItem: function getItem(k) {
20032 setItem: function setItem(k, v) {
20035 removeItem: function removeItem(k) {
20036 return delete s[k];
20040 // corePreferences is an interface for persisting basic key-value strings
20041 // within and between iD sessions on the same site.
20045 function corePreferences(k, v) {
20047 if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
20049 /* eslint-disable no-console */
20050 if (typeof console !== 'undefined') {
20051 console.error('localStorage quota exceeded');
20053 /* eslint-enable no-console */
20058 var vparse = createCommonjsModule(function (module) {
20059 (function (window) {
20061 function parseVersion(v) {
20062 var m = v.replace(/[^0-9.]/g, '').match(/[0-9]*\.|[0-9]+/g) || [];
20069 v.isEmpty = !v.major && !v.minor && !v.patch && !v.build;
20070 v.parsed = [v.major, v.minor, v.patch, v.build];
20071 v.text = v.parsed.join('.');
20072 v.compare = compare;
20076 function compare(v) {
20077 if (typeof v === 'string') {
20078 v = parseVersion(v);
20081 for (var i = 0; i < 4; i++) {
20082 if (this.parsed[i] !== v.parsed[i]) {
20083 return this.parsed[i] > v.parsed[i] ? 1 : -1;
20089 /* istanbul ignore next */
20092 if (module && 'object' === 'object') {
20093 module.exports = parseVersion;
20095 window.parseVersion = parseVersion;
20097 })(commonjsGlobal);
20101 var version = "2.20.0";
20102 var description = "A friendly editor for OpenStreetMap";
20103 var main = "dist/iD.min.js";
20104 var repository = "github:openstreetmap/iD";
20105 var homepage = "https://github.com/openstreetmap/iD";
20106 var bugs = "https://github.com/openstreetmap/iD/issues";
20107 var keywords = ["editor","openstreetmap"];
20108 var license = "ISC";
20109 var scripts = {all:"npm-run-all -s clean build build:legacy dist",build:"npm-run-all -s build:css build:data build:dev","build:css":"node scripts/build_css.js","build:data":"shx mkdir -p dist/data && node scripts/build_data.js","build:dev":"rollup --config config/rollup.config.dev.js","build:legacy":"rollup --config config/rollup.config.legacy.js","build:stats":"rollup --config config/rollup.config.stats.js",clean:"shx rm -f dist/*.js dist/*.map dist/*.css dist/img/*.svg",dist:"npm-run-all -p dist:**","dist:mapillary":"shx mkdir -p dist/mapillary-js && shx cp -R node_modules/mapillary-js/dist/* dist/mapillary-js/","dist:pannellum":"shx mkdir -p dist/pannellum-streetside && shx cp -R node_modules/pannellum/build/* dist/pannellum-streetside/","dist:min:iD":"uglifyjs dist/iD.legacy.js --compress --mangle --output dist/iD.min.js","dist:svg:iD":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"iD-%s\" --symbol-sprite dist/img/iD-sprite.svg \"svg/iD-sprite/**/*.svg\"","dist:svg:community":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"community-%s\" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg","dist:svg:fa":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg","dist:svg:maki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"maki-%s\" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg","dist:svg:mapillary:signs":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg","dist:svg:mapillary:objects":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-object-sprite.svg node_modules/mapillary_sprite_source/package_objects/*.svg","dist:svg:temaki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"temaki-%s\" --symbol-sprite dist/img/temaki-sprite.svg node_modules/@ideditor/temaki/icons/*.svg",imagery:"node scripts/update_imagery.js",lint:"eslint scripts test/spec modules","lint:fix":"eslint scripts test/spec modules --fix",start:"npm-run-all -s build start:server",quickstart:"npm-run-all -s build:dev start:server","start:server":"node scripts/server.js",test:"npm-run-all -s lint build:css build:data build:legacy test:spec","test:spec":"phantomjs --web-security=no node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html spec",translations:"node scripts/update_locales.js"};
20110 var dependencies = {"@ideditor/country-coder":"~5.0.3","@ideditor/location-conflation":"~1.0.2","@mapbox/geojson-area":"^0.2.2","@mapbox/sexagesimal":"1.2.0","@mapbox/togeojson":"0.16.0","@mapbox/vector-tile":"^1.3.1","@turf/bbox-clip":"^6.0.0","abortcontroller-polyfill":"^1.4.0","aes-js":"^3.1.2","alif-toolkit":"^1.2.9","core-js":"^3.6.5",diacritics:"1.3.0","fast-deep-equal":"~3.1.1","fast-json-stable-stringify":"2.1.0","lodash-es":"~4.17.15",marked:"~2.0.0","node-diff3":"2.1.0","osm-auth":"1.1.0",pannellum:"2.5.6","polygon-clipping":"~0.15.1",rbush:"3.0.1","whatwg-fetch":"^3.4.1","which-polygon":"2.2.0"};
20111 var devDependencies = {"@babel/core":"^7.11.6","@babel/preset-env":"^7.11.5","@fortawesome/fontawesome-svg-core":"^1.2.32","@fortawesome/free-brands-svg-icons":"~5.15.1","@fortawesome/free-regular-svg-icons":"~5.15.1","@fortawesome/free-solid-svg-icons":"~5.15.1","@ideditor/temaki":"~4.4.0","@mapbox/maki":"^6.0.0","@rollup/plugin-babel":"^5.2.1","@rollup/plugin-commonjs":"^17.0.0","@rollup/plugin-json":"^4.0.1","@rollup/plugin-node-resolve":"~11.2.0",autoprefixer:"^10.0.1",btoa:"^1.2.1",chai:"^4.1.0","cldr-core":"37.0.0","cldr-localenames-full":"37.0.0",colors:"^1.1.2","concat-files":"^0.1.1",d3:"~6.6.0","editor-layer-index":"github:osmlab/editor-layer-index#gh-pages",eslint:"^7.1.0",gaze:"^1.1.3",glob:"^7.1.0",happen:"^0.3.1","js-yaml":"^4.0.0","json-stringify-pretty-compact":"^3.0.0",mapillary_sprite_source:"^1.8.0","mapillary-js":"4.0.0",minimist:"^1.2.3",mocha:"^7.0.1","mocha-phantomjs-core":"^2.1.0","name-suggestion-index":"~6.0","node-fetch":"^2.6.1","npm-run-all":"^4.0.0","object-inspect":"1.10.3","osm-community-index":"~5.1.0","phantomjs-prebuilt":"~2.1.16",postcss:"^8.1.1","postcss-selector-prepend":"^0.5.0",rollup:"~2.52.8","rollup-plugin-includepaths":"~0.2.3","rollup-plugin-progress":"^1.1.1","rollup-plugin-visualizer":"~4.2.0",shelljs:"^0.8.0",shx:"^0.3.0",sinon:"7.5.0","sinon-chai":"^3.3.0",smash:"0.0","static-server":"^2.2.1","svg-sprite":"1.5.0","uglify-js":"~3.13.0",vparse:"~1.1.0"};
20112 var engines = {node:">=10"};
20113 var browserslist = ["> 0.2%, last 6 major versions, Firefox ESR, IE 11, maintained node versions"];
20114 var packageJSON = {
20117 description: description,
20119 repository: repository,
20120 homepage: homepage,
20122 keywords: keywords,
20125 dependencies: dependencies,
20126 devDependencies: devDependencies,
20128 browserslist: browserslist
20131 var _mainFileFetcher = coreFileFetcher(); // singleton
20132 // coreFileFetcher asynchronously fetches data from JSON files
20135 function coreFileFetcher() {
20136 var ociVersion = packageJSON.devDependencies['osm-community-index'];
20137 var v = vparse(ociVersion);
20138 var vMinor = "".concat(v.major, ".").concat(v.minor);
20140 var _inflight = {};
20142 'address_formats': 'data/address_formats.min.json',
20143 'deprecated': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/deprecated.min.json',
20144 'discarded': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/discarded.min.json',
20145 'imagery': 'data/imagery.min.json',
20146 'intro_graph': 'data/intro_graph.min.json',
20147 'keepRight': 'data/keepRight.min.json',
20148 'languages': 'data/languages.min.json',
20149 'locales': 'locales/index.min.json',
20150 'oci_defaults': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/defaults.min.json"),
20151 'oci_features': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/featureCollection.min.json"),
20152 'oci_resources': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/resources.min.json"),
20153 'preset_categories': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_categories.min.json',
20154 'preset_defaults': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_defaults.min.json',
20155 'preset_fields': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/fields.min.json',
20156 'preset_presets': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/presets.min.json',
20157 'phone_formats': 'data/phone_formats.min.json',
20158 'qa_data': 'data/qa_data.min.json',
20159 'shortcuts': 'data/shortcuts.min.json',
20160 'territory_languages': 'data/territory_languages.min.json',
20161 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
20163 var _cachedData = {}; // expose the cache; useful for tests
20165 _this.cache = function () {
20166 return _cachedData;
20167 }; // Returns a Promise to fetch data
20168 // (resolved with the data if we have it already)
20171 _this.get = function (which) {
20172 if (_cachedData[which]) {
20173 return Promise.resolve(_cachedData[which]);
20176 var file = _fileMap[which];
20178 var url = file && _this.asset(file);
20181 return Promise.reject("Unknown data file for \"".concat(which, "\""));
20184 var prom = _inflight[url];
20187 _inflight[url] = prom = utilFetchJson(url).then(function (result) {
20188 delete _inflight[url];
20191 throw new Error("No data loaded for \"".concat(which, "\""));
20194 _cachedData[which] = result;
20196 })["catch"](function (err) {
20197 delete _inflight[url];
20203 }; // Accessor for the file map
20206 _this.fileMap = function (val) {
20207 if (!arguments.length) return _fileMap;
20212 var _assetPath = '';
20214 _this.assetPath = function (val) {
20215 if (!arguments.length) return _assetPath;
20220 var _assetMap = {};
20222 _this.assetMap = function (val) {
20223 if (!arguments.length) return _assetMap;
20228 _this.asset = function (val) {
20229 if (/^http(s)?:\/\//i.test(val)) return val;
20230 var filename = _assetPath + val;
20231 return _assetMap[filename] || filename;
20237 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
20238 var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
20239 var defineProperty = objectDefineProperty.f;
20240 var trim$2 = stringTrim.trim;
20242 var NUMBER = 'Number';
20243 var NativeNumber = global$2[NUMBER];
20244 var NumberPrototype = NativeNumber.prototype;
20246 // Opera ~12 has broken Object#toString
20247 var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20249 // `ToNumber` abstract operation
20250 // https://tc39.es/ecma262/#sec-tonumber
20251 var toNumber$1 = function (argument) {
20252 var it = toPrimitive(argument, false);
20253 var first, third, radix, maxCode, digits, length, index, code;
20254 if (typeof it == 'string' && it.length > 2) {
20256 first = it.charCodeAt(0);
20257 if (first === 43 || first === 45) {
20258 third = it.charCodeAt(2);
20259 if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20260 } else if (first === 48) {
20261 switch (it.charCodeAt(1)) {
20262 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20263 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20264 default: return +it;
20266 digits = it.slice(2);
20267 length = digits.length;
20268 for (index = 0; index < length; index++) {
20269 code = digits.charCodeAt(index);
20270 // parseInt parses a string to a first unavailable symbol
20271 // but ToNumber should return NaN if a string contains unavailable symbols
20272 if (code < 48 || code > maxCode) return NaN;
20273 } return parseInt(digits, radix);
20278 // `Number` constructor
20279 // https://tc39.es/ecma262/#sec-number-constructor
20280 if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20281 var NumberWrapper = function Number(value) {
20282 var it = arguments.length < 1 ? 0 : value;
20284 return dummy instanceof NumberWrapper
20285 // check on 1..constructor(foo) case
20286 && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20287 ? inheritIfRequired(new NativeNumber(toNumber$1(it)), dummy, NumberWrapper) : toNumber$1(it);
20289 for (var keys = descriptors ? getOwnPropertyNames(NativeNumber) : (
20291 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20292 // ES2015 (in case, if modules with ES2015 Number statics required before):
20293 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20294 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,' +
20297 ).split(','), j = 0, key; keys.length > j; j++) {
20298 if (has$1(NativeNumber, key = keys[j]) && !has$1(NumberWrapper, key)) {
20299 defineProperty(NumberWrapper, key, getOwnPropertyDescriptor$2(NativeNumber, key));
20302 NumberWrapper.prototype = NumberPrototype;
20303 NumberPrototype.constructor = NumberWrapper;
20304 redefine(global$2, NUMBER, NumberWrapper);
20307 // `thisNumberValue` abstract operation
20308 // https://tc39.es/ecma262/#sec-thisnumbervalue
20309 var thisNumberValue = function (value) {
20310 if (typeof value != 'number' && classofRaw(value) != 'Number') {
20311 throw TypeError('Incorrect invocation');
20316 // `String.prototype.repeat` method implementation
20317 // https://tc39.es/ecma262/#sec-string.prototype.repeat
20318 var stringRepeat = function repeat(count) {
20319 var str = String(requireObjectCoercible(this));
20321 var n = toInteger(count);
20322 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
20323 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
20327 var nativeToFixed = 1.0.toFixed;
20328 var floor = Math.floor;
20330 var pow = function (x, n, acc) {
20331 return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc);
20334 var log = function (x) {
20337 while (x2 >= 4096) {
20347 var multiply = function (data, n, c) {
20350 while (++index < 6) {
20351 c2 += n * data[index];
20352 data[index] = c2 % 1e7;
20353 c2 = floor(c2 / 1e7);
20357 var divide = function (data, n) {
20360 while (--index >= 0) {
20362 data[index] = floor(c / n);
20367 var dataToString = function (data) {
20370 while (--index >= 0) {
20371 if (s !== '' || index === 0 || data[index] !== 0) {
20372 var t = String(data[index]);
20373 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
20378 var FORCED$4 = nativeToFixed && (
20379 0.00008.toFixed(3) !== '0.000' ||
20380 0.9.toFixed(0) !== '1' ||
20381 1.255.toFixed(2) !== '1.25' ||
20382 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
20383 ) || !fails(function () {
20384 // V8 ~ Android 4.3-
20385 nativeToFixed.call({});
20388 // `Number.prototype.toFixed` method
20389 // https://tc39.es/ecma262/#sec-number.prototype.tofixed
20390 _export({ target: 'Number', proto: true, forced: FORCED$4 }, {
20391 toFixed: function toFixed(fractionDigits) {
20392 var number = thisNumberValue(this);
20393 var fractDigits = toInteger(fractionDigits);
20394 var data = [0, 0, 0, 0, 0, 0];
20399 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
20400 // eslint-disable-next-line no-self-compare -- NaN check
20401 if (number != number) return 'NaN';
20402 if (number <= -1e21 || number >= 1e21) return String(number);
20407 if (number > 1e-21) {
20408 e = log(number * pow(2, 69, 1)) - 69;
20409 z = e < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1);
20410 z *= 0x10000000000000;
20413 multiply(data, 0, z);
20416 multiply(data, 1e7, 0);
20419 multiply(data, pow(10, j, 1), 0);
20422 divide(data, 1 << 23);
20425 divide(data, 1 << j);
20426 multiply(data, 1, 1);
20428 result = dataToString(data);
20430 multiply(data, 0, z);
20431 multiply(data, 1 << -e, 0);
20432 result = dataToString(data) + stringRepeat.call('0', fractDigits);
20435 if (fractDigits > 0) {
20437 result = sign + (k <= fractDigits
20438 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
20439 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
20441 result = sign + result;
20446 var globalIsFinite = global$2.isFinite;
20448 // `Number.isFinite` method
20449 // https://tc39.es/ecma262/#sec-number.isfinite
20450 // eslint-disable-next-line es/no-number-isfinite -- safe
20451 var numberIsFinite = Number.isFinite || function isFinite(it) {
20452 return typeof it == 'number' && globalIsFinite(it);
20455 // `Number.isFinite` method
20456 // https://tc39.es/ecma262/#sec-number.isfinite
20457 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
20459 var fromCharCode = String.fromCharCode;
20460 // eslint-disable-next-line es/no-string-fromcodepoint -- required for testing
20461 var $fromCodePoint = String.fromCodePoint;
20463 // length should be 1, old FF problem
20464 var INCORRECT_LENGTH = !!$fromCodePoint && $fromCodePoint.length != 1;
20466 // `String.fromCodePoint` method
20467 // https://tc39.es/ecma262/#sec-string.fromcodepoint
20468 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
20469 // eslint-disable-next-line no-unused-vars -- required for `.length`
20470 fromCodePoint: function fromCodePoint(x) {
20472 var length = arguments.length;
20475 while (length > i) {
20476 code = +arguments[i++];
20477 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
20478 elements.push(code < 0x10000
20479 ? fromCharCode(code)
20480 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
20482 } return elements.join('');
20487 fixRegexpWellKnownSymbolLogic('search', function (SEARCH, nativeSearch, maybeCallNative) {
20489 // `String.prototype.search` method
20490 // https://tc39.es/ecma262/#sec-string.prototype.search
20491 function search(regexp) {
20492 var O = requireObjectCoercible(this);
20493 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
20494 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
20496 // `RegExp.prototype[@@search]` method
20497 // https://tc39.es/ecma262/#sec-regexp.prototype-@@search
20498 function (string) {
20499 var res = maybeCallNative(nativeSearch, this, string);
20500 if (res.done) return res.value;
20502 var rx = anObject(this);
20503 var S = String(string);
20505 var previousLastIndex = rx.lastIndex;
20506 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
20507 var result = regexpExecAbstract(rx, S);
20508 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
20509 return result === null ? -1 : result.index;
20514 var quickselect$1 = createCommonjsModule(function (module, exports) {
20515 (function (global, factory) {
20516 module.exports = factory() ;
20517 })(commonjsGlobal, function () {
20519 function quickselect(arr, k, left, right, compare) {
20520 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
20523 function quickselectStep(arr, k, left, right, compare) {
20524 while (right > left) {
20525 if (right - left > 600) {
20526 var n = right - left + 1;
20527 var m = k - left + 1;
20528 var z = Math.log(n);
20529 var s = 0.5 * Math.exp(2 * z / 3);
20530 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
20531 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
20532 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
20533 quickselectStep(arr, k, newLeft, newRight, compare);
20539 swap(arr, left, k);
20540 if (compare(arr[right], t) > 0) swap(arr, left, right);
20547 while (compare(arr[i], t) < 0) {
20551 while (compare(arr[j], t) > 0) {
20556 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
20558 swap(arr, j, right);
20560 if (j <= k) left = j + 1;
20561 if (k <= j) right = j - 1;
20565 function swap(arr, i, j) {
20571 function defaultCompare(a, b) {
20572 return a < b ? -1 : a > b ? 1 : 0;
20575 return quickselect;
20579 var rbush_1 = rbush;
20580 var _default$1 = rbush;
20582 function rbush(maxEntries, format) {
20583 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
20585 this._maxEntries = Math.max(4, maxEntries || 9);
20586 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
20589 this._initFormat(format);
20595 rbush.prototype = {
20596 all: function all() {
20597 return this._all(this.data, []);
20599 search: function search(bbox) {
20600 var node = this.data,
20602 toBBox = this.toBBox;
20603 if (!intersects$1(bbox, node)) return result;
20604 var nodesToSearch = [],
20611 for (i = 0, len = node.children.length; i < len; i++) {
20612 child = node.children[i];
20613 childBBox = node.leaf ? toBBox(child) : child;
20615 if (intersects$1(bbox, childBBox)) {
20616 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
20620 node = nodesToSearch.pop();
20625 collides: function collides(bbox) {
20626 var node = this.data,
20627 toBBox = this.toBBox;
20628 if (!intersects$1(bbox, node)) return false;
20629 var nodesToSearch = [],
20636 for (i = 0, len = node.children.length; i < len; i++) {
20637 child = node.children[i];
20638 childBBox = node.leaf ? toBBox(child) : child;
20640 if (intersects$1(bbox, childBBox)) {
20641 if (node.leaf || contains$1(bbox, childBBox)) return true;
20642 nodesToSearch.push(child);
20646 node = nodesToSearch.pop();
20651 load: function load(data) {
20652 if (!(data && data.length)) return this;
20654 if (data.length < this._minEntries) {
20655 for (var i = 0, len = data.length; i < len; i++) {
20656 this.insert(data[i]);
20660 } // recursively build the tree with the given data from scratch using OMT algorithm
20663 var node = this._build(data.slice(), 0, data.length - 1, 0);
20665 if (!this.data.children.length) {
20666 // save as is if tree is empty
20668 } else if (this.data.height === node.height) {
20669 // split root if trees have the same height
20670 this._splitRoot(this.data, node);
20672 if (this.data.height < node.height) {
20673 // swap trees if inserted one is bigger
20674 var tmpNode = this.data;
20677 } // insert the small tree into the large tree at appropriate level
20680 this._insert(node, this.data.height - node.height - 1, true);
20685 insert: function insert(item) {
20686 if (item) this._insert(item, this.data.height - 1);
20689 clear: function clear() {
20690 this.data = createNode$1([]);
20693 remove: function remove(item, equalsFn) {
20694 if (!item) return this;
20695 var node = this.data,
20696 bbox = this.toBBox(item),
20702 goingUp; // depth-first iterative tree traversal
20704 while (node || path.length) {
20708 parent = path[path.length - 1];
20714 // check current node
20715 index = findItem$1(item, node.children, equalsFn);
20717 if (index !== -1) {
20718 // item found, remove the item and condense tree upwards
20719 node.children.splice(index, 1);
20722 this._condense(path);
20728 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
20734 node = node.children[0];
20735 } else if (parent) {
20738 node = parent.children[i];
20740 } else node = null; // nothing found
20746 toBBox: function toBBox(item) {
20749 compareMinX: compareNodeMinX$1,
20750 compareMinY: compareNodeMinY$1,
20751 toJSON: function toJSON() {
20754 fromJSON: function fromJSON(data) {
20758 _all: function _all(node, result) {
20759 var nodesToSearch = [];
20762 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
20763 node = nodesToSearch.pop();
20768 _build: function _build(items, left, right, height) {
20769 var N = right - left + 1,
20770 M = this._maxEntries,
20774 // reached leaf level; return leaf
20775 node = createNode$1(items.slice(left, right + 1));
20776 calcBBox$1(node, this.toBBox);
20781 // target height of the bulk-loaded tree
20782 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
20784 M = Math.ceil(N / Math.pow(M, height - 1));
20787 node = createNode$1([]);
20789 node.height = height; // split the items into M mostly square tiles
20791 var N2 = Math.ceil(N / M),
20792 N1 = N2 * Math.ceil(Math.sqrt(M)),
20797 multiSelect$1(items, left, right, N1, this.compareMinX);
20799 for (i = left; i <= right; i += N1) {
20800 right2 = Math.min(i + N1 - 1, right);
20801 multiSelect$1(items, i, right2, N2, this.compareMinY);
20803 for (j = i; j <= right2; j += N2) {
20804 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
20806 node.children.push(this._build(items, j, right3, height - 1));
20810 calcBBox$1(node, this.toBBox);
20813 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
20814 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
20818 if (node.leaf || path.length - 1 === level) break;
20819 minArea = minEnlargement = Infinity;
20821 for (i = 0, len = node.children.length; i < len; i++) {
20822 child = node.children[i];
20823 area = bboxArea$1(child);
20824 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
20826 if (enlargement < minEnlargement) {
20827 minEnlargement = enlargement;
20828 minArea = area < minArea ? area : minArea;
20829 targetNode = child;
20830 } else if (enlargement === minEnlargement) {
20831 // otherwise choose one with the smallest area
20832 if (area < minArea) {
20834 targetNode = child;
20839 node = targetNode || node.children[0];
20844 _insert: function _insert(item, level, isNode) {
20845 var toBBox = this.toBBox,
20846 bbox = isNode ? item : toBBox(item),
20847 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
20849 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
20852 node.children.push(item);
20853 extend$2(node, bbox); // split on node overflow; propagate upwards if necessary
20855 while (level >= 0) {
20856 if (insertPath[level].children.length > this._maxEntries) {
20857 this._split(insertPath, level);
20861 } // adjust bboxes along the insertion path
20864 this._adjustParentBBoxes(bbox, insertPath, level);
20866 // split overflowed node into two
20867 _split: function _split(insertPath, level) {
20868 var node = insertPath[level],
20869 M = node.children.length,
20870 m = this._minEntries;
20872 this._chooseSplitAxis(node, m, M);
20874 var splitIndex = this._chooseSplitIndex(node, m, M);
20876 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
20877 newNode.height = node.height;
20878 newNode.leaf = node.leaf;
20879 calcBBox$1(node, this.toBBox);
20880 calcBBox$1(newNode, this.toBBox);
20881 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
20883 _splitRoot: function _splitRoot(node, newNode) {
20885 this.data = createNode$1([node, newNode]);
20886 this.data.height = node.height + 1;
20887 this.data.leaf = false;
20888 calcBBox$1(this.data, this.toBBox);
20890 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
20891 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
20892 minOverlap = minArea = Infinity;
20894 for (i = m; i <= M - m; i++) {
20895 bbox1 = distBBox$1(node, 0, i, this.toBBox);
20896 bbox2 = distBBox$1(node, i, M, this.toBBox);
20897 overlap = intersectionArea$1(bbox1, bbox2);
20898 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
20900 if (overlap < minOverlap) {
20901 minOverlap = overlap;
20903 minArea = area < minArea ? area : minArea;
20904 } else if (overlap === minOverlap) {
20905 // otherwise choose distribution with minimum area
20906 if (area < minArea) {
20915 // sorts node children by the best axis for split
20916 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
20917 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
20918 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
20919 xMargin = this._allDistMargin(node, m, M, compareMinX),
20920 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
20921 // otherwise it's already sorted by minY
20924 if (xMargin < yMargin) node.children.sort(compareMinX);
20926 // total margin of all possible split distributions where each node is at least m full
20927 _allDistMargin: function _allDistMargin(node, m, M, compare) {
20928 node.children.sort(compare);
20929 var toBBox = this.toBBox,
20930 leftBBox = distBBox$1(node, 0, m, toBBox),
20931 rightBBox = distBBox$1(node, M - m, M, toBBox),
20932 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
20936 for (i = m; i < M - m; i++) {
20937 child = node.children[i];
20938 extend$2(leftBBox, node.leaf ? toBBox(child) : child);
20939 margin += bboxMargin$1(leftBBox);
20942 for (i = M - m - 1; i >= m; i--) {
20943 child = node.children[i];
20944 extend$2(rightBBox, node.leaf ? toBBox(child) : child);
20945 margin += bboxMargin$1(rightBBox);
20950 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
20951 // adjust bboxes along the given tree path
20952 for (var i = level; i >= 0; i--) {
20953 extend$2(path[i], bbox);
20956 _condense: function _condense(path) {
20957 // go through the path, removing empty nodes and updating bboxes
20958 for (var i = path.length - 1, siblings; i >= 0; i--) {
20959 if (path[i].children.length === 0) {
20961 siblings = path[i - 1].children;
20962 siblings.splice(siblings.indexOf(path[i]), 1);
20963 } else this.clear();
20964 } else calcBBox$1(path[i], this.toBBox);
20967 _initFormat: function _initFormat(format) {
20968 // data format (minX, minY, maxX, maxY accessors)
20969 // uses eval-type function compilation instead of just accepting a toBBox function
20970 // because the algorithms are very sensitive to sorting functions performance,
20971 // so they should be dead simple and without inner calls
20972 var compareArr = ['return a', ' - b', ';'];
20973 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
20974 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
20975 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
20979 function findItem$1(item, items, equalsFn) {
20980 if (!equalsFn) return items.indexOf(item);
20982 for (var i = 0; i < items.length; i++) {
20983 if (equalsFn(item, items[i])) return i;
20987 } // calculate node's bbox from bboxes of its children
20990 function calcBBox$1(node, toBBox) {
20991 distBBox$1(node, 0, node.children.length, toBBox, node);
20992 } // min bounding rectangle of node children from k to p-1
20995 function distBBox$1(node, k, p, toBBox, destNode) {
20996 if (!destNode) destNode = createNode$1(null);
20997 destNode.minX = Infinity;
20998 destNode.minY = Infinity;
20999 destNode.maxX = -Infinity;
21000 destNode.maxY = -Infinity;
21002 for (var i = k, child; i < p; i++) {
21003 child = node.children[i];
21004 extend$2(destNode, node.leaf ? toBBox(child) : child);
21010 function extend$2(a, b) {
21011 a.minX = Math.min(a.minX, b.minX);
21012 a.minY = Math.min(a.minY, b.minY);
21013 a.maxX = Math.max(a.maxX, b.maxX);
21014 a.maxY = Math.max(a.maxY, b.maxY);
21018 function compareNodeMinX$1(a, b) {
21019 return a.minX - b.minX;
21022 function compareNodeMinY$1(a, b) {
21023 return a.minY - b.minY;
21026 function bboxArea$1(a) {
21027 return (a.maxX - a.minX) * (a.maxY - a.minY);
21030 function bboxMargin$1(a) {
21031 return a.maxX - a.minX + (a.maxY - a.minY);
21034 function enlargedArea$1(a, b) {
21035 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));
21038 function intersectionArea$1(a, b) {
21039 var minX = Math.max(a.minX, b.minX),
21040 minY = Math.max(a.minY, b.minY),
21041 maxX = Math.min(a.maxX, b.maxX),
21042 maxY = Math.min(a.maxY, b.maxY);
21043 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
21046 function contains$1(a, b) {
21047 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
21050 function intersects$1(a, b) {
21051 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
21054 function createNode$1(children) {
21056 children: children,
21064 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
21065 // combines selection algorithm with binary divide & conquer approach
21068 function multiSelect$1(arr, left, right, n, compare) {
21069 var stack = [left, right],
21072 while (stack.length) {
21073 right = stack.pop();
21074 left = stack.pop();
21075 if (right - left <= n) continue;
21076 mid = left + Math.ceil((right - left) / n / 2) * n;
21077 quickselect$1(arr, mid, left, right, compare);
21078 stack.push(left, mid, mid, right);
21081 rbush_1["default"] = _default$1;
21083 var lineclip_1 = lineclip$1;
21084 lineclip$1.polyline = lineclip$1;
21085 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
21086 // handle polylines rather than just segments
21088 function lineclip$1(points, bbox, result) {
21089 var len = points.length,
21090 codeA = bitCode$1(points[0], bbox),
21097 if (!result) result = [];
21099 for (i = 1; i < len; i++) {
21102 codeB = lastCode = bitCode$1(b, bbox);
21105 if (!(codeA | codeB)) {
21109 if (codeB !== lastCode) {
21110 // segment went outside
21114 // start a new line
21118 } else if (i === len - 1) {
21123 } else if (codeA & codeB) {
21126 } else if (codeA) {
21127 // a outside, intersect with clip edge
21128 a = intersect$1(a, b, codeA, bbox);
21129 codeA = bitCode$1(a, bbox);
21132 b = intersect$1(a, b, codeB, bbox);
21133 codeB = bitCode$1(b, bbox);
21140 if (part.length) result.push(part);
21142 } // Sutherland-Hodgeman polygon clipping algorithm
21145 function polygonclip$1(points, bbox) {
21146 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
21148 for (edge = 1; edge <= 8; edge *= 2) {
21150 prev = points[points.length - 1];
21151 prevInside = !(bitCode$1(prev, bbox) & edge);
21153 for (i = 0; i < points.length; i++) {
21155 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
21157 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
21158 if (inside) result.push(p); // add a point if it's inside
21161 prevInside = inside;
21165 if (!points.length) break;
21169 } // intersect a segment against one of the 4 lines that make up the bbox
21172 function intersect$1(a, b, edge, bbox) {
21173 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
21174 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
21175 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
21176 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
21178 } // bit code reflects the point position relative to the bbox:
21180 // top 1001 1000 1010
21181 // mid 0001 0000 0010
21182 // bottom 0101 0100 0110
21185 function bitCode$1(p, bbox) {
21187 if (p[0] < bbox[0]) code |= 1; // left
21188 else if (p[0] > bbox[2]) code |= 2; // right
21190 if (p[1] < bbox[1]) code |= 4; // bottom
21191 else if (p[1] > bbox[3]) code |= 8; // top
21196 var whichPolygon_1 = whichPolygon;
21198 function whichPolygon(data) {
21201 for (var i = 0; i < data.features.length; i++) {
21202 var feature = data.features[i];
21203 var coords = feature.geometry.coordinates;
21205 if (feature.geometry.type === 'Polygon') {
21206 bboxes.push(treeItem(coords, feature.properties));
21207 } else if (feature.geometry.type === 'MultiPolygon') {
21208 for (var j = 0; j < coords.length; j++) {
21209 bboxes.push(treeItem(coords[j], feature.properties));
21214 var tree = rbush_1().load(bboxes);
21216 function query(p, multi) {
21218 result = tree.search({
21225 for (var i = 0; i < result.length; i++) {
21226 if (insidePolygon(result[i].coords, p)) {
21227 if (multi) output.push(result[i].props);else return result[i].props;
21231 return multi && output.length ? output : null;
21236 query.bbox = function queryBBox(bbox) {
21238 var result = tree.search({
21245 for (var i = 0; i < result.length; i++) {
21246 if (polygonIntersectsBBox(result[i].coords, bbox)) {
21247 output.push(result[i].props);
21257 function polygonIntersectsBBox(polygon, bbox) {
21258 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
21259 if (insidePolygon(polygon, bboxCenter)) return true;
21261 for (var i = 0; i < polygon.length; i++) {
21262 if (lineclip_1(polygon[i], bbox).length > 0) return true;
21266 } // ray casting algorithm for detecting if point is in polygon
21269 function insidePolygon(rings, p) {
21270 var inside = false;
21272 for (var i = 0, len = rings.length; i < len; i++) {
21273 var ring = rings[i];
21275 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
21276 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
21283 function rayIntersect(p, p1, p2) {
21284 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];
21287 function treeItem(coords, props) {
21297 for (var i = 0; i < coords[0].length; i++) {
21298 var p = coords[0][i];
21299 item.minX = Math.min(item.minX, p[0]);
21300 item.minY = Math.min(item.minY, p[1]);
21301 item.maxX = Math.max(item.maxX, p[0]);
21302 item.maxY = Math.max(item.maxY, p[1]);
21308 var type = "FeatureCollection";
21314 aliases: ["GB-ENG"],
21316 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21318 roadSpeedUnit: "mph",
21319 roadHeightUnit: "ft",
21320 callingCodes: ["44"]
21323 type: "MultiPolygon",
21324 coordinates: [[[[-6.03913, 51.13217], [-7.74976, 48.64773], [1.17405, 50.74239], [2.18458, 51.52087], [2.56575, 51.85301], [0.792, 57.56437], [-2.30613, 55.62698], [-2.17058, 55.45916], [-2.6095, 55.28488], [-2.63532, 55.19452], [-3.02906, 55.04606], [-3.09361, 54.94924], [-3.38407, 54.94278], [-4.1819, 54.57861], [-3.5082, 53.54318], [-3.08228, 53.25526], [-3.03675, 53.25092], [-2.92329, 53.19383], [-2.92022, 53.17685], [-2.98598, 53.15589], [-2.90649, 53.10964], [-2.87469, 53.12337], [-2.89131, 53.09374], [-2.83133, 52.99184], [-2.7251, 52.98389], [-2.72221, 52.92969], [-2.80549, 52.89428], [-2.85897, 52.94487], [-2.92401, 52.93836], [-2.97243, 52.9651], [-3.13576, 52.895], [-3.15744, 52.84947], [-3.16105, 52.79599], [-3.08734, 52.77504], [-3.01001, 52.76636], [-2.95581, 52.71794], [-3.01724, 52.72083], [-3.04398, 52.65435], [-3.13648, 52.58208], [-3.12926, 52.5286], [-3.09746, 52.53077], [-3.08662, 52.54811], [-3.00929, 52.57774], [-2.99701, 52.551], [-3.03603, 52.49969], [-3.13359, 52.49174], [-3.22971, 52.45344], [-3.22754, 52.42526], [-3.04687, 52.34504], [-2.95364, 52.3501], [-2.99701, 52.323], [-3.00785, 52.2753], [-3.09289, 52.20546], [-3.12638, 52.08114], [-2.97111, 51.90456], [-2.8818, 51.93196], [-2.78742, 51.88833], [-2.74277, 51.84367], [-2.66234, 51.83555], [-2.66336, 51.59504], [-3.20563, 51.31615], [-6.03913, 51.13217]]]]
21330 nameEn: "Scotland",
21331 aliases: ["GB-SCT"],
21333 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21335 roadSpeedUnit: "mph",
21336 roadHeightUnit: "ft",
21337 callingCodes: ["44"]
21340 type: "MultiPolygon",
21341 coordinates: [[[[0.792, 57.56437], [-0.3751, 61.32236], [-14.78497, 57.60709], [-6.82333, 55.83103], [-4.69044, 54.3629], [-3.38407, 54.94278], [-3.09361, 54.94924], [-3.02906, 55.04606], [-2.63532, 55.19452], [-2.6095, 55.28488], [-2.17058, 55.45916], [-2.30613, 55.62698], [0.792, 57.56437]]]]
21348 aliases: ["GB-WLS"],
21350 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21352 roadSpeedUnit: "mph",
21353 roadHeightUnit: "ft",
21354 callingCodes: ["44"]
21357 type: "MultiPolygon",
21358 coordinates: [[[[-3.5082, 53.54318], [-5.37267, 53.63269], [-6.03913, 51.13217], [-3.20563, 51.31615], [-2.66336, 51.59504], [-2.66234, 51.83555], [-2.74277, 51.84367], [-2.78742, 51.88833], [-2.8818, 51.93196], [-2.97111, 51.90456], [-3.12638, 52.08114], [-3.09289, 52.20546], [-3.00785, 52.2753], [-2.99701, 52.323], [-2.95364, 52.3501], [-3.04687, 52.34504], [-3.22754, 52.42526], [-3.22971, 52.45344], [-3.13359, 52.49174], [-3.03603, 52.49969], [-2.99701, 52.551], [-3.00929, 52.57774], [-3.08662, 52.54811], [-3.09746, 52.53077], [-3.12926, 52.5286], [-3.13648, 52.58208], [-3.04398, 52.65435], [-3.01724, 52.72083], [-2.95581, 52.71794], [-3.01001, 52.76636], [-3.08734, 52.77504], [-3.16105, 52.79599], [-3.15744, 52.84947], [-3.13576, 52.895], [-2.97243, 52.9651], [-2.92401, 52.93836], [-2.85897, 52.94487], [-2.80549, 52.89428], [-2.72221, 52.92969], [-2.7251, 52.98389], [-2.83133, 52.99184], [-2.89131, 53.09374], [-2.87469, 53.12337], [-2.90649, 53.10964], [-2.98598, 53.15589], [-2.92022, 53.17685], [-2.92329, 53.19383], [-3.03675, 53.25092], [-3.08228, 53.25526], [-3.5082, 53.54318]]]]
21364 nameEn: "Northern Ireland",
21365 aliases: ["GB-NIR"],
21367 groups: ["Q22890", "Q3336843", "154", "150", "UN"],
21369 roadSpeedUnit: "mph",
21370 roadHeightUnit: "ft",
21371 callingCodes: ["44"]
21374 type: "MultiPolygon",
21375 coordinates: [[[[-6.34755, 55.49206], [-7.2471, 55.06933], [-7.34464, 55.04688], [-7.4033, 55.00391], [-7.40004, 54.94498], [-7.44404, 54.9403], [-7.4473, 54.87003], [-7.47626, 54.83084], [-7.54508, 54.79401], [-7.54671, 54.74606], [-7.64449, 54.75265], [-7.75041, 54.7103], [-7.83352, 54.73854], [-7.93293, 54.66603], [-7.70315, 54.62077], [-7.8596, 54.53671], [-7.99812, 54.54427], [-8.04538, 54.48941], [-8.179, 54.46763], [-8.04555, 54.36292], [-7.87101, 54.29299], [-7.8596, 54.21779], [-7.81397, 54.20159], [-7.69501, 54.20731], [-7.55812, 54.12239], [-7.4799, 54.12239], [-7.44567, 54.1539], [-7.32834, 54.11475], [-7.30553, 54.11869], [-7.34005, 54.14698], [-7.29157, 54.17191], [-7.28017, 54.16714], [-7.29687, 54.1354], [-7.29493, 54.12013], [-7.26316, 54.13863], [-7.25012, 54.20063], [-7.14908, 54.22732], [-7.19145, 54.31296], [-7.02034, 54.4212], [-6.87775, 54.34682], [-6.85179, 54.29176], [-6.81583, 54.22791], [-6.74575, 54.18788], [-6.70175, 54.20218], [-6.6382, 54.17071], [-6.66264, 54.0666], [-6.62842, 54.03503], [-6.47849, 54.06947], [-6.36605, 54.07234], [-6.36279, 54.11248], [-6.32694, 54.09337], [-6.29003, 54.11278], [-6.26218, 54.09785], [-5.83481, 53.87749], [-4.69044, 54.3629], [-6.34755, 55.49206]]]]
21383 groups: ["EU", "154", "150", "UN"],
21384 callingCodes: ["45"]
21387 type: "MultiPolygon",
21388 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]]]]
21394 nameEn: "Netherlands",
21396 groups: ["EU", "155", "150", "UN"],
21397 callingCodes: ["31"]
21400 type: "MultiPolygon",
21401 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]]]]
21408 aliases: ["US-HI"],
21410 groups: ["Q35657", "061", "009", "UN"],
21411 roadSpeedUnit: "mph",
21412 roadHeightUnit: "ft",
21413 callingCodes: ["1"]
21416 type: "MultiPolygon",
21417 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]]]]
21424 aliases: ["US-AK"],
21426 groups: ["Q35657", "021", "003", "019", "UN"],
21427 roadSpeedUnit: "mph",
21428 roadHeightUnit: "ft",
21429 callingCodes: ["1"]
21432 type: "MultiPolygon",
21433 coordinates: [[[[169.34848, 52.47228], [180, 51.0171], [179.84401, 55.10087], [169.34848, 52.47228]]], [[[-168.95635, 65.98512], [-169.03888, 65.48473], [-172.76104, 63.77445], [-179.55295, 57.62081], [-179.55295, 50.81807], [-133.92876, 54.62289], [-130.61931, 54.70835], [-130.64499, 54.76912], [-130.44184, 54.85377], [-130.27203, 54.97174], [-130.18765, 55.07744], [-130.08035, 55.21556], [-129.97513, 55.28029], [-130.15373, 55.74895], [-130.00857, 55.91344], [-130.00093, 56.00325], [-130.10173, 56.12178], [-130.33965, 56.10849], [-130.77769, 56.36185], [-131.8271, 56.62247], [-133.38523, 58.42773], [-133.84645, 58.73543], [-134.27175, 58.8634], [-134.48059, 59.13231], [-134.55699, 59.1297], [-134.7047, 59.2458], [-135.00267, 59.28745], [-135.03069, 59.56208], [-135.48007, 59.79937], [-136.31566, 59.59083], [-136.22381, 59.55526], [-136.33727, 59.44466], [-136.47323, 59.46617], [-136.52365, 59.16752], [-136.82619, 59.16198], [-137.4925, 58.89415], [-137.60623, 59.24465], [-138.62145, 59.76431], [-138.71149, 59.90728], [-139.05365, 59.99655], [-139.20603, 60.08896], [-139.05831, 60.35205], [-139.68991, 60.33693], [-139.98024, 60.18027], [-140.45648, 60.30919], [-140.5227, 60.22077], [-141.00116, 60.30648], [-140.97446, 84.39275], [-168.25765, 71.99091], [-168.95635, 65.98512]]]]
21440 aliases: ["ID-SM"],
21442 groups: ["035", "142", "UN"],
21444 callingCodes: ["62"]
21447 type: "MultiPolygon",
21448 coordinates: [[[[109.82788, 2.86812], [110.90339, 7.52694], [105.01437, 3.24936], [104.56723, 1.44271], [104.34728, 1.33529], [104.12282, 1.27714], [104.03085, 1.26954], [103.74084, 1.12902], [103.66049, 1.18825], [103.56591, 1.19719], [103.03657, 1.30383], [96.11174, 6.69841], [74.28481, -3.17525], [102.92489, -8.17146], [106.32259, -5.50116], [106.38511, -5.16715], [109.17017, -4.07401], [109.3962, -2.07276], [108.50935, -2.01066], [107.94791, 1.06924], [109.82788, 2.86812]]]]
21455 aliases: ["ID-JW"],
21457 groups: ["035", "142", "UN"],
21459 callingCodes: ["62"]
21462 type: "MultiPolygon",
21463 coordinates: [[[[109.17017, -4.07401], [106.38511, -5.16715], [106.32259, -5.50116], [102.92489, -8.17146], [116.22542, -10.49172], [114.39575, -8.2889], [114.42235, -8.09762], [114.92859, -7.49253], [116.33992, -7.56171], [116.58433, -5.30385], [109.17017, -4.07401]]]]
21469 nameEn: "Kalimantan",
21470 aliases: ["ID-KA"],
21472 groups: ["Q36117", "035", "142", "UN"],
21474 callingCodes: ["62"]
21477 type: "MultiPolygon",
21478 coordinates: [[[[120.02464, 2.83703], [118.06469, 4.16638], [117.67641, 4.16535], [117.47313, 4.18857], [117.25801, 4.35108], [115.90217, 4.37708], [115.58276, 3.93499], [115.53713, 3.14776], [115.11343, 2.82879], [115.1721, 2.49671], [114.80706, 2.21665], [114.80706, 1.92351], [114.57892, 1.5], [114.03788, 1.44787], [113.64677, 1.23933], [113.01448, 1.42832], [113.021, 1.57819], [112.48648, 1.56516], [112.2127, 1.44135], [112.15679, 1.17004], [111.94553, 1.12016], [111.82846, 0.99349], [111.55434, 0.97864], [111.22979, 1.08326], [110.62374, 0.873], [110.49182, 0.88088], [110.35354, 0.98869], [109.66397, 1.60425], [109.66397, 1.79972], [109.57923, 1.80624], [109.53794, 1.91771], [109.62558, 1.99182], [109.82788, 2.86812], [107.94791, 1.06924], [108.50935, -2.01066], [109.3962, -2.07276], [109.17017, -4.07401], [116.58433, -5.30385], [120.02464, 2.83703]]]]
21484 nameEn: "Lesser Sunda Islands",
21485 aliases: ["ID-NU"],
21487 groups: ["035", "142", "UN"],
21489 callingCodes: ["62"]
21492 type: "MultiPolygon",
21493 coordinates: [[[[116.96967, -8.01483], [114.92859, -7.49253], [114.42235, -8.09762], [114.39575, -8.2889], [116.22542, -10.49172], [122.14954, -11.52517], [125.68138, -9.85176], [125.09025, -9.46406], [124.97892, -9.19281], [125.04044, -9.17093], [125.09434, -9.19669], [125.18907, -9.16434], [125.18632, -9.03142], [125.11764, -8.96359], [124.97742, -9.08128], [124.94011, -8.85617], [124.46701, -9.13002], [124.45971, -9.30263], [124.38554, -9.3582], [124.35258, -9.43002], [124.3535, -9.48493], [124.28115, -9.50453], [124.28115, -9.42189], [124.21247, -9.36904], [124.14517, -9.42324], [124.10539, -9.41206], [124.04286, -9.34243], [124.04628, -9.22671], [124.33472, -9.11416], [124.92337, -8.75859], [125.87688, -7.49892], [116.96967, -8.01483]]]]
21499 nameEn: "Sulawesi",
21500 aliases: ["ID-SL"],
21502 groups: ["035", "142", "UN"],
21504 callingCodes: ["62"]
21507 type: "MultiPolygon",
21508 coordinates: [[[[128.34321, 3.90322], [126.69413, 6.02692], [119.56457, 0.90759], [116.58433, -5.30385], [116.33992, -7.56171], [116.96967, -8.01483], [125.87688, -7.49892], [123.78965, -0.86805], [128.34321, 3.90322]]]]
21514 nameEn: "Maluku Islands",
21515 aliases: ["ID-ML"],
21517 groups: ["035", "142", "UN"],
21519 callingCodes: ["62"]
21522 type: "MultiPolygon",
21523 coordinates: [[[[129.63187, 2.21409], [128.34321, 3.90322], [123.78965, -0.86805], [125.87688, -7.49892], [125.58506, -7.95311], [125.87691, -8.31789], [127.42116, -8.22471], [127.55165, -9.05052], [135.49042, -9.2276], [135.35517, -5.01442], [132.8312, -4.70282], [130.8468, -2.61103], [128.40647, -2.30349], [129.71519, -0.24692], [129.63187, 2.21409]]]]
21529 nameEn: "Western New Guinea",
21530 aliases: ["ID-PP"],
21532 groups: ["035", "142", "UN"],
21534 callingCodes: ["62"]
21537 type: "MultiPolygon",
21538 coordinates: [[[[135.49042, -9.2276], [141.01842, -9.35091], [141.01763, -6.90181], [140.90448, -6.85033], [140.85295, -6.72996], [140.99813, -6.3233], [141.02352, 0.08993], [129.63187, 2.21409], [129.71519, -0.24692], [128.40647, -2.30349], [130.8468, -2.61103], [132.8312, -4.70282], [135.35517, -5.01442], [135.49042, -9.2276]]]]
21544 nameEn: "Balearic Islands",
21545 aliases: ["ES-IB"],
21547 groups: ["EU", "039", "150", "UN"],
21548 callingCodes: ["34 971"]
21551 type: "MultiPolygon",
21552 coordinates: [[[[-2.27707, 35.35051], [5.10072, 39.89531], [3.75438, 42.33445], [-2.27707, 35.35051]]]]
21559 aliases: ["ES-CE"],
21561 groups: ["EA", "EU", "015", "002", "UN"],
21562 level: "subterritory",
21563 callingCodes: ["34"]
21566 type: "MultiPolygon",
21567 coordinates: [[[[-5.38491, 35.92591], [-5.37338, 35.88417], [-5.35844, 35.87375], [-5.34379, 35.8711], [-5.21179, 35.90091], [-5.38491, 35.92591]]]]
21574 aliases: ["ES-ML"],
21576 groups: ["EA", "EU", "015", "002", "UN"],
21577 level: "subterritory",
21578 callingCodes: ["34"]
21581 type: "MultiPolygon",
21582 coordinates: [[[[-2.91909, 35.33927], [-2.96038, 35.31609], [-2.96648, 35.30475], [-2.96978, 35.29459], [-2.97035, 35.28852], [-2.96507, 35.28801], [-2.96826, 35.28296], [-2.96516, 35.27967], [-2.95431, 35.2728], [-2.95065, 35.26576], [-2.93893, 35.26737], [-2.92272, 35.27509], [-2.91909, 35.33927]]]]
21590 groups: ["151", "150", "UN"],
21591 level: "subterritory",
21592 callingCodes: ["7"]
21595 type: "MultiPolygon",
21596 coordinates: [[[[33.5, 44], [36.4883, 45.0488], [36.475, 45.2411], [36.5049, 45.3136], [36.6545, 45.3417], [36.6645, 45.4514], [35.0498, 45.7683], [34.9601, 45.7563], [34.7991, 45.8101], [34.8015, 45.9005], [34.7548, 45.907], [34.6668, 45.9714], [34.6086, 45.9935], [34.5589, 45.9935], [34.5201, 45.951], [34.4873, 45.9427], [34.4415, 45.9599], [34.4122, 46.0025], [34.3391, 46.0611], [34.2511, 46.0532], [34.181, 46.068], [34.1293, 46.1049], [34.0731, 46.1177], [34.0527, 46.1084], [33.9155, 46.1594], [33.8523, 46.1986], [33.7972, 46.2048], [33.7405, 46.1855], [33.646, 46.2303], [33.6152, 46.2261], [33.6385, 46.1415], [33.6147, 46.1356], [33.5732, 46.1032], [33.5909, 46.0601], [33.5597, 46.0307], [31.5, 45.5], [33.5, 44]]]]
21601 wikidata: "Q12837",
21603 level: "sharedLandform"
21609 wikidata: "Q14056",
21610 nameEn: "Jan Mayen",
21611 aliases: ["NO-22"],
21613 groups: ["SJ", "154", "150", "UN"],
21614 level: "subterritory"
21617 type: "MultiPolygon",
21618 coordinates: [[[[-9.18243, 72.23144], [-10.71459, 70.09565], [-5.93364, 70.76368], [-9.18243, 72.23144]]]]
21623 wikidata: "Q19188",
21624 nameEn: "Mainland China",
21626 groups: ["030", "142", "UN"],
21627 callingCodes: ["86"]
21630 type: "MultiPolygon",
21631 coordinates: [[[[125.6131, 53.07229], [125.17522, 53.20225], [124.46078, 53.21881], [123.86158, 53.49391], [123.26989, 53.54843], [122.85966, 53.47395], [122.35063, 53.49565], [121.39213, 53.31888], [120.85633, 53.28499], [120.0451, 52.7359], [120.04049, 52.58773], [120.46454, 52.63811], [120.71673, 52.54099], [120.61346, 52.32447], [120.77337, 52.20805], [120.65907, 51.93544], [120.10963, 51.671], [119.13553, 50.37412], [119.38598, 50.35162], [119.27996, 50.13348], [119.11003, 50.00276], [118.61623, 49.93809], [117.82343, 49.52696], [117.48208, 49.62324], [117.27597, 49.62544], [116.71193, 49.83813], [116.03781, 48.87014], [116.06565, 48.81716], [115.78876, 48.51781], [115.811, 48.25699], [115.52082, 48.15367], [115.57128, 47.91988], [115.94296, 47.67741], [116.21879, 47.88505], [116.4465, 47.83662], [116.67405, 47.89039], [116.9723, 47.87285], [117.37875, 47.63627], [117.50181, 47.77216], [117.80196, 48.01661], [118.03676, 48.00982], [118.11009, 48.04], [118.22677, 48.03853], [118.29654, 48.00246], [118.55766, 47.99277], [118.7564, 47.76947], [119.12343, 47.66458], [119.13995, 47.53997], [119.35892, 47.48104], [119.31964, 47.42617], [119.54918, 47.29505], [119.56019, 47.24874], [119.62403, 47.24575], [119.71209, 47.19192], [119.85518, 46.92196], [119.91242, 46.90091], [119.89261, 46.66423], [119.80455, 46.67631], [119.77373, 46.62947], [119.68127, 46.59015], [119.65265, 46.62342], [119.42827, 46.63783], [119.32827, 46.61433], [119.24978, 46.64761], [119.10448, 46.65516], [119.00541, 46.74273], [118.92616, 46.72765], [118.89974, 46.77139], [118.8337, 46.77742], [118.78747, 46.68689], [118.30534, 46.73519], [117.69554, 46.50991], [117.60748, 46.59771], [117.41782, 46.57862], [117.36609, 46.36335], [116.83166, 46.38637], [116.75551, 46.33083], [116.58612, 46.30211], [116.26678, 45.96479], [116.24012, 45.8778], [116.27366, 45.78637], [116.16989, 45.68603], [115.60329, 45.44717], [114.94546, 45.37377], [114.74612, 45.43585], [114.54801, 45.38337], [114.5166, 45.27189], [113.70918, 44.72891], [112.74662, 44.86297], [112.4164, 45.06858], [111.98695, 45.09074], [111.76275, 44.98032], [111.40498, 44.3461], [111.96289, 43.81596], [111.93776, 43.68709], [111.79758, 43.6637], [111.59087, 43.51207], [111.0149, 43.3289], [110.4327, 42.78293], [110.08401, 42.6411], [109.89402, 42.63111], [109.452, 42.44842], [109.00679, 42.45302], [108.84489, 42.40246], [107.57258, 42.40898], [107.49681, 42.46221], [107.29755, 42.41395], [107.24774, 42.36107], [106.76517, 42.28741], [105.0123, 41.63188], [104.51667, 41.66113], [104.52258, 41.8706], [103.92804, 41.78246], [102.72403, 42.14675], [102.07645, 42.22519], [101.80515, 42.50074], [100.84979, 42.67087], [100.33297, 42.68231], [99.50671, 42.56535], [97.1777, 42.7964], [96.37926, 42.72055], [96.35658, 42.90363], [95.89543, 43.2528], [95.52594, 43.99353], [95.32891, 44.02407], [95.39772, 44.2805], [95.01191, 44.25274], [94.71959, 44.35284], [94.10003, 44.71016], [93.51161, 44.95964], [91.64048, 45.07408], [90.89169, 45.19667], [90.65114, 45.49314], [90.70907, 45.73437], [91.03026, 46.04194], [90.99672, 46.14207], [90.89639, 46.30711], [91.07696, 46.57315], [91.0147, 46.58171], [91.03649, 46.72916], [90.84035, 46.99525], [90.76108, 46.99399], [90.48542, 47.30438], [90.48854, 47.41826], [90.33598, 47.68303], [90.10871, 47.7375], [90.06512, 47.88177], [89.76624, 47.82745], [89.55453, 48.0423], [89.0711, 47.98528], [88.93186, 48.10263], [88.8011, 48.11302], [88.58316, 48.21893], [88.58939, 48.34531], [87.96361, 48.58478], [88.0788, 48.71436], [87.73822, 48.89582], [87.88171, 48.95853], [87.81333, 49.17354], [87.48983, 49.13794], [87.478, 49.07403], [87.28386, 49.11626], [86.87238, 49.12432], [86.73568, 48.99918], [86.75343, 48.70331], [86.38069, 48.46064], [85.73581, 48.3939], [85.5169, 48.05493], [85.61067, 47.49753], [85.69696, 47.2898], [85.54294, 47.06171], [85.22443, 47.04816], [84.93995, 46.87399], [84.73077, 47.01394], [83.92184, 46.98912], [83.04622, 47.19053], [82.21792, 45.56619], [82.58474, 45.40027], [82.51374, 45.1755], [81.73278, 45.3504], [80.11169, 45.03352], [79.8987, 44.89957], [80.38384, 44.63073], [80.40229, 44.23319], [80.40031, 44.10986], [80.75156, 43.44948], [80.69718, 43.32589], [80.77771, 43.30065], [80.78817, 43.14235], [80.62913, 43.141], [80.3735, 43.01557], [80.58999, 42.9011], [80.38169, 42.83142], [80.26886, 42.8366], [80.16892, 42.61137], [80.26841, 42.23797], [80.17807, 42.21166], [80.17842, 42.03211], [79.92977, 42.04113], [78.3732, 41.39603], [78.15757, 41.38565], [78.12873, 41.23091], [77.81287, 41.14307], [77.76206, 41.01574], [77.52723, 41.00227], [77.3693, 41.0375], [77.28004, 41.0033], [76.99302, 41.0696], [76.75681, 40.95354], [76.5261, 40.46114], [76.33659, 40.3482], [75.96168, 40.38064], [75.91361, 40.2948], [75.69663, 40.28642], [75.5854, 40.66874], [75.22834, 40.45382], [75.08243, 40.43945], [74.82013, 40.52197], [74.78168, 40.44886], [74.85996, 40.32857], [74.69875, 40.34668], [74.35063, 40.09742], [74.25533, 40.13191], [73.97049, 40.04378], [73.83006, 39.76136], [73.9051, 39.75073], [73.92354, 39.69565], [73.94683, 39.60733], [73.87018, 39.47879], [73.59831, 39.46425], [73.59241, 39.40843], [73.5004, 39.38402], [73.55396, 39.3543], [73.54572, 39.27567], [73.60638, 39.24534], [73.75823, 39.023], [73.81728, 39.04007], [73.82964, 38.91517], [73.7445, 38.93867], [73.7033, 38.84782], [73.80656, 38.66449], [73.79806, 38.61106], [73.97933, 38.52945], [74.17022, 38.65504], [74.51217, 38.47034], [74.69619, 38.42947], [74.69894, 38.22155], [74.80331, 38.19889], [74.82665, 38.07359], [74.9063, 38.03033], [74.92416, 37.83428], [75.00935, 37.77486], [74.8912, 37.67576], [74.94338, 37.55501], [75.06011, 37.52779], [75.15899, 37.41443], [75.09719, 37.37297], [75.12328, 37.31839], [74.88887, 37.23275], [74.80605, 37.21565], [74.49981, 37.24518], [74.56453, 37.03023], [75.13839, 37.02622], [75.40481, 36.95382], [75.45562, 36.71971], [75.72737, 36.7529], [75.92391, 36.56986], [76.0324, 36.41198], [76.00906, 36.17511], [75.93028, 36.13136], [76.15325, 35.9264], [76.14913, 35.82848], [76.33453, 35.84296], [76.50961, 35.8908], [76.77323, 35.66062], [76.84539, 35.67356], [76.96624, 35.5932], [77.44277, 35.46132], [77.70232, 35.46244], [77.80532, 35.52058], [78.11664, 35.48022], [78.03466, 35.3785], [78.00033, 35.23954], [78.22692, 34.88771], [78.18435, 34.7998], [78.27781, 34.61484], [78.54964, 34.57283], [78.56475, 34.50835], [78.74465, 34.45174], [79.05364, 34.32482], [78.99802, 34.3027], [78.91769, 34.15452], [78.66225, 34.08858], [78.65657, 34.03195], [78.73367, 34.01121], [78.77349, 33.73871], [78.67599, 33.66445], [78.73636, 33.56521], [79.15252, 33.17156], [79.14016, 33.02545], [79.46562, 32.69668], [79.26768, 32.53277], [79.13174, 32.47766], [79.0979, 32.38051], [78.99322, 32.37948], [78.96713, 32.33655], [78.7831, 32.46873], [78.73916, 32.69438], [78.38897, 32.53938], [78.4645, 32.45367], [78.49609, 32.2762], [78.68754, 32.10256], [78.74404, 32.00384], [78.78036, 31.99478], [78.69933, 31.78723], [78.84516, 31.60631], [78.71032, 31.50197], [78.77898, 31.31209], [78.89344, 31.30481], [79.01931, 31.42817], [79.14016, 31.43403], [79.30694, 31.17357], [79.59884, 30.93943], [79.93255, 30.88288], [80.20721, 30.58541], [80.54504, 30.44936], [80.83343, 30.32023], [81.03953, 30.20059], [81.12842, 30.01395], [81.24362, 30.0126], [81.29032, 30.08806], [81.2623, 30.14596], [81.33355, 30.15303], [81.39928, 30.21862], [81.41018, 30.42153], [81.5459, 30.37688], [81.62033, 30.44703], [81.99082, 30.33423], [82.10135, 30.35439], [82.10757, 30.23745], [82.19475, 30.16884], [82.16984, 30.0692], [82.38622, 30.02608], [82.5341, 29.9735], [82.73024, 29.81695], [83.07116, 29.61957], [83.28131, 29.56813], [83.44787, 29.30513], [83.63156, 29.16249], [83.82303, 29.30513], [83.97559, 29.33091], [84.18107, 29.23451], [84.24801, 29.02783], [84.2231, 28.89571], [84.47528, 28.74023], [84.62317, 28.73887], [84.85511, 28.58041], [85.06059, 28.68562], [85.19135, 28.62825], [85.18668, 28.54076], [85.10729, 28.34092], [85.38127, 28.28336], [85.4233, 28.32996], [85.59765, 28.30529], [85.60854, 28.25045], [85.69105, 28.38475], [85.71907, 28.38064], [85.74864, 28.23126], [85.84672, 28.18187], [85.90743, 28.05144], [85.97813, 27.99023], [85.94946, 27.9401], [86.06309, 27.90021], [86.12069, 27.93047], [86.08333, 28.02121], [86.088, 28.09264], [86.18607, 28.17364], [86.22966, 27.9786], [86.42736, 27.91122], [86.51609, 27.96623], [86.56265, 28.09569], [86.74181, 28.10638], [86.75582, 28.04182], [87.03757, 27.94835], [87.11696, 27.84104], [87.56996, 27.84517], [87.72718, 27.80938], [87.82681, 27.95248], [88.13378, 27.88015], [88.1278, 27.95417], [88.25332, 27.9478], [88.54858, 28.06057], [88.63235, 28.12356], [88.83559, 28.01936], [88.88091, 27.85192], [88.77517, 27.45415], [88.82981, 27.38814], [88.91901, 27.32483], [88.93678, 27.33777], [88.96947, 27.30319], [89.00216, 27.32532], [88.95355, 27.4106], [88.97213, 27.51671], [89.0582, 27.60985], [89.12825, 27.62502], [89.59525, 28.16433], [89.79762, 28.23979], [90.13387, 28.19178], [90.58842, 28.02838], [90.69894, 28.07784], [91.20019, 27.98715], [91.25779, 28.07509], [91.46327, 28.0064], [91.48973, 27.93903], [91.5629, 27.84823], [91.6469, 27.76358], [91.84722, 27.76325], [91.87057, 27.7195], [92.27432, 27.89077], [92.32101, 27.79363], [92.42538, 27.80092], [92.7275, 27.98662], [92.73025, 28.05814], [92.65472, 28.07632], [92.67486, 28.15018], [92.93075, 28.25671], [93.14635, 28.37035], [93.18069, 28.50319], [93.44621, 28.67189], [93.72797, 28.68821], [94.35897, 29.01965], [94.2752, 29.11687], [94.69318, 29.31739], [94.81353, 29.17804], [95.0978, 29.14446], [95.11291, 29.09527], [95.2214, 29.10727], [95.26122, 29.07727], [95.3038, 29.13847], [95.41091, 29.13007], [95.50842, 29.13487], [95.72086, 29.20797], [95.75149, 29.32063], [95.84899, 29.31464], [96.05361, 29.38167], [96.31316, 29.18643], [96.18682, 29.11087], [96.20467, 29.02325], [96.3626, 29.10607], [96.61391, 28.72742], [96.40929, 28.51526], [96.48895, 28.42955], [96.6455, 28.61657], [96.85561, 28.4875], [96.88445, 28.39452], [96.98882, 28.32564], [97.1289, 28.3619], [97.34547, 28.21385], [97.41729, 28.29783], [97.47085, 28.2688], [97.50518, 28.49716], [97.56835, 28.55628], [97.70705, 28.5056], [97.79632, 28.33168], [97.90069, 28.3776], [98.15337, 28.12114], [98.13964, 27.9478], [98.32641, 27.51385], [98.42529, 27.55404], [98.43353, 27.67086], [98.69582, 27.56499], [98.7333, 26.85615], [98.77547, 26.61994], [98.72741, 26.36183], [98.67797, 26.24487], [98.7329, 26.17218], [98.66884, 26.09165], [98.63128, 26.15492], [98.57085, 26.11547], [98.60763, 26.01512], [98.70818, 25.86241], [98.63128, 25.79937], [98.54064, 25.85129], [98.40606, 25.61129], [98.31268, 25.55307], [98.25774, 25.6051], [98.16848, 25.62739], [98.18084, 25.56298], [98.12591, 25.50722], [98.14925, 25.41547], [97.92541, 25.20815], [97.83614, 25.2715], [97.77023, 25.11492], [97.72216, 25.08508], [97.72903, 24.91332], [97.79949, 24.85655], [97.76481, 24.8289], [97.73127, 24.83015], [97.70181, 24.84557], [97.64354, 24.79171], [97.56648, 24.76475], [97.56383, 24.75535], [97.5542, 24.74943], [97.54675, 24.74202], [97.56525, 24.72838], [97.56286, 24.54535], [97.52757, 24.43748], [97.60029, 24.4401], [97.66998, 24.45288], [97.7098, 24.35658], [97.65624, 24.33781], [97.66723, 24.30027], [97.71941, 24.29652], [97.76799, 24.26365], [97.72998, 24.2302], [97.72799, 24.18883], [97.75305, 24.16902], [97.72903, 24.12606], [97.62363, 24.00506], [97.5247, 23.94032], [97.64667, 23.84574], [97.72302, 23.89288], [97.79456, 23.94836], [97.79416, 23.95663], [97.84328, 23.97603], [97.86545, 23.97723], [97.88811, 23.97446], [97.8955, 23.97758], [97.89676, 23.97931], [97.89683, 23.98389], [97.88814, 23.98605], [97.88414, 23.99405], [97.88616, 24.00463], [97.90998, 24.02094], [97.93951, 24.01953], [97.98691, 24.03897], [97.99583, 24.04932], [98.04709, 24.07616], [98.05302, 24.07408], [98.05671, 24.07961], [98.0607, 24.07812], [98.06703, 24.08028], [98.07806, 24.07988], [98.20666, 24.11406], [98.54476, 24.13119], [98.59256, 24.08371], [98.85319, 24.13042], [98.87998, 24.15624], [98.89632, 24.10612], [98.67797, 23.9644], [98.68209, 23.80492], [98.79607, 23.77947], [98.82933, 23.72921], [98.81775, 23.694], [98.88396, 23.59555], [98.80294, 23.5345], [98.82877, 23.47908], [98.87683, 23.48995], [98.92104, 23.36946], [98.87573, 23.33038], [98.93958, 23.31414], [98.92515, 23.29535], [98.88597, 23.18656], [99.05975, 23.16382], [99.04601, 23.12215], [99.25741, 23.09025], [99.34127, 23.13099], [99.52214, 23.08218], [99.54218, 22.90014], [99.43537, 22.94086], [99.45654, 22.85726], [99.31243, 22.73893], [99.38247, 22.57544], [99.37972, 22.50188], [99.28771, 22.4105], [99.17318, 22.18025], [99.19176, 22.16983], [99.1552, 22.15874], [99.33166, 22.09656], [99.47585, 22.13345], [99.85351, 22.04183], [99.96612, 22.05965], [99.99084, 21.97053], [99.94003, 21.82782], [99.98654, 21.71064], [100.04956, 21.66843], [100.12679, 21.70539], [100.17486, 21.65306], [100.10757, 21.59945], [100.12542, 21.50365], [100.1625, 21.48704], [100.18447, 21.51898], [100.25863, 21.47043], [100.35201, 21.53176], [100.42892, 21.54325], [100.4811, 21.46148], [100.57861, 21.45637], [100.72143, 21.51898], [100.87265, 21.67396], [101.11744, 21.77659], [101.15156, 21.56129], [101.2124, 21.56422], [101.19349, 21.41959], [101.26912, 21.36482], [101.2229, 21.23271], [101.29326, 21.17254], [101.54563, 21.25668], [101.6068, 21.23329], [101.59491, 21.18621], [101.60886, 21.17947], [101.66977, 21.20004], [101.70548, 21.14911], [101.7622, 21.14813], [101.79266, 21.19025], [101.76745, 21.21571], [101.83887, 21.20983], [101.84412, 21.25291], [101.74014, 21.30967], [101.74224, 21.48276], [101.7727, 21.51794], [101.7475, 21.5873], [101.80001, 21.57461], [101.83257, 21.61562], [101.74555, 21.72852], [101.7791, 21.83019], [101.62566, 21.96574], [101.57525, 22.13026], [101.60675, 22.13513], [101.53638, 22.24794], [101.56789, 22.28876], [101.61306, 22.27515], [101.68973, 22.46843], [101.7685, 22.50337], [101.86828, 22.38397], [101.90714, 22.38688], [101.91344, 22.44417], [101.98487, 22.42766], [102.03633, 22.46164], [102.1245, 22.43372], [102.14099, 22.40092], [102.16621, 22.43336], [102.26428, 22.41321], [102.25339, 22.4607], [102.41061, 22.64184], [102.38415, 22.67919], [102.42618, 22.69212], [102.46665, 22.77108], [102.51802, 22.77969], [102.57095, 22.7036], [102.60675, 22.73376], [102.8636, 22.60735], [102.9321, 22.48659], [103.0722, 22.44775], [103.07843, 22.50097], [103.17961, 22.55705], [103.15782, 22.59873], [103.18895, 22.64471], [103.28079, 22.68063], [103.32282, 22.8127], [103.43179, 22.75816], [103.43646, 22.70648], [103.52675, 22.59155], [103.57812, 22.65764], [103.56255, 22.69499], [103.64506, 22.79979], [103.87904, 22.56683], [103.93286, 22.52703], [103.94513, 22.52553], [103.95191, 22.5134], [103.96352, 22.50584], [103.96783, 22.51173], [103.97384, 22.50634], [103.99247, 22.51958], [104.01088, 22.51823], [104.03734, 22.72945], [104.11384, 22.80363], [104.27084, 22.8457], [104.25683, 22.76534], [104.35593, 22.69353], [104.47225, 22.75813], [104.58122, 22.85571], [104.60457, 22.81841], [104.65283, 22.83419], [104.72755, 22.81984], [104.77114, 22.90017], [104.84942, 22.93631], [104.86765, 22.95178], [104.8334, 23.01484], [104.79478, 23.12934], [104.87382, 23.12854], [104.87992, 23.17141], [104.91435, 23.18666], [104.9486, 23.17235], [104.96532, 23.20463], [104.98712, 23.19176], [105.07002, 23.26248], [105.11672, 23.25247], [105.17276, 23.28679], [105.22569, 23.27249], [105.32376, 23.39684], [105.40782, 23.28107], [105.42805, 23.30824], [105.49966, 23.20669], [105.56037, 23.16806], [105.57594, 23.075], [105.72382, 23.06641], [105.8726, 22.92756], [105.90119, 22.94168], [105.99568, 22.94178], [106.00179, 22.99049], [106.19705, 22.98475], [106.27022, 22.87722], [106.34961, 22.86718], [106.49749, 22.91164], [106.51306, 22.94891], [106.55976, 22.92311], [106.60179, 22.92884], [106.6516, 22.86862], [106.6734, 22.89587], [106.71387, 22.88296], [106.71128, 22.85982], [106.78422, 22.81532], [106.81271, 22.8226], [106.83685, 22.8098], [106.82404, 22.7881], [106.76293, 22.73491], [106.72321, 22.63606], [106.71698, 22.58432], [106.65316, 22.5757], [106.61269, 22.60301], [106.58395, 22.474], [106.55665, 22.46498], [106.57221, 22.37], [106.55976, 22.34841], [106.6516, 22.33977], [106.69986, 22.22309], [106.67495, 22.1885], [106.6983, 22.15102], [106.70142, 22.02409], [106.68274, 21.99811], [106.69276, 21.96013], [106.72551, 21.97923], [106.74345, 22.00965], [106.81038, 21.97934], [106.9178, 21.97357], [106.92714, 21.93459], [106.97228, 21.92592], [106.99252, 21.95191], [107.05634, 21.92303], [107.06101, 21.88982], [107.00964, 21.85948], [107.02615, 21.81981], [107.10771, 21.79879], [107.20734, 21.71493], [107.24625, 21.7077], [107.29296, 21.74674], [107.35834, 21.6672], [107.35989, 21.60063], [107.38636, 21.59774], [107.41593, 21.64839], [107.47197, 21.6672], [107.49532, 21.62958], [107.49065, 21.59774], [107.54047, 21.5934], [107.56537, 21.61945], [107.66967, 21.60787], [107.80355, 21.66141], [107.86114, 21.65128], [107.90006, 21.5905], [107.92652, 21.58906], [107.95232, 21.5388], [107.96774, 21.53601], [107.97074, 21.54072], [107.97383, 21.53961], [107.97932, 21.54503], [108.02926, 21.54997], [108.0569, 21.53604], [108.10003, 21.47338], [108.00365, 17.98159], [111.60491, 13.57105], [118.41371, 24.06775], [118.11703, 24.39734], [118.28244, 24.51231], [118.35291, 24.51645], [118.42453, 24.54644], [118.56434, 24.49266], [120.49232, 25.22863], [121.03532, 26.8787], [123.5458, 31.01942], [122.29378, 31.76513], [122.80525, 33.30571], [123.85601, 37.49093], [123.90497, 38.79949], [124.17532, 39.8232], [124.23201, 39.9248], [124.35029, 39.95639], [124.37089, 40.03004], [124.3322, 40.05573], [124.38556, 40.11047], [124.40719, 40.13655], [124.86913, 40.45387], [125.71172, 40.85223], [125.76869, 40.87908], [126.00335, 40.92835], [126.242, 41.15454], [126.53189, 41.35206], [126.60631, 41.65565], [126.90729, 41.79955], [127.17841, 41.59714], [127.29712, 41.49473], [127.92943, 41.44291], [128.02633, 41.42103], [128.03311, 41.39232], [128.12967, 41.37931], [128.18546, 41.41279], [128.20061, 41.40895], [128.30716, 41.60322], [128.15119, 41.74568], [128.04487, 42.01769], [128.94007, 42.03537], [128.96068, 42.06657], [129.15178, 42.17224], [129.22285, 42.26491], [129.22423, 42.3553], [129.28541, 42.41574], [129.42882, 42.44702], [129.54701, 42.37254], [129.60482, 42.44461], [129.72541, 42.43739], [129.75294, 42.59409], [129.77183, 42.69435], [129.7835, 42.76521], [129.80719, 42.79218], [129.83277, 42.86746], [129.85261, 42.96494], [129.8865, 43.00395], [129.95082, 43.01051], [129.96409, 42.97306], [130.12957, 42.98361], [130.09764, 42.91425], [130.26095, 42.9027], [130.23068, 42.80125], [130.2385, 42.71127], [130.41826, 42.6011], [130.44361, 42.54849], [130.50123, 42.61636], [130.55143, 42.52158], [130.62107, 42.58413], [130.56576, 42.68925], [130.40213, 42.70788], [130.44361, 42.76205], [130.66524, 42.84753], [131.02438, 42.86518], [131.02668, 42.91246], [131.135, 42.94114], [131.10274, 43.04734], [131.20414, 43.13654], [131.19031, 43.21385], [131.30324, 43.39498], [131.29402, 43.46695], [131.19492, 43.53047], [131.21105, 43.82383], [131.26176, 43.94011], [131.23583, 43.96085], [131.25484, 44.03131], [131.30365, 44.04262], [131.1108, 44.70266], [130.95639, 44.85154], [131.48415, 44.99513], [131.68466, 45.12374], [131.66852, 45.2196], [131.76532, 45.22609], [131.86903, 45.33636], [131.99417, 45.2567], [132.83978, 45.05916], [132.96373, 45.0212], [133.12293, 45.1332], [133.09279, 45.25693], [133.19419, 45.51913], [133.41083, 45.57723], [133.48457, 45.86203], [133.60442, 45.90053], [133.67569, 45.9759], [133.72695, 46.05576], [133.68047, 46.14697], [133.88097, 46.25066], [133.91496, 46.4274], [133.84104, 46.46681], [134.03538, 46.75668], [134.20016, 47.33458], [134.50898, 47.4812], [134.7671, 47.72051], [134.55508, 47.98651], [134.67098, 48.1564], [134.75328, 48.36763], [134.49516, 48.42884], [132.66989, 47.96491], [132.57309, 47.71741], [131.90448, 47.68011], [131.2635, 47.73325], [131.09871, 47.6852], [130.95985, 47.6957], [130.90915, 47.90623], [130.65103, 48.10052], [130.84462, 48.30942], [130.52147, 48.61745], [130.66946, 48.88251], [130.43232, 48.90844], [130.2355, 48.86741], [129.85416, 49.11067], [129.67598, 49.29596], [129.50685, 49.42398], [129.40398, 49.44194], [129.35317, 49.3481], [129.23232, 49.40353], [129.11153, 49.36813], [128.72896, 49.58676], [127.83476, 49.5748], [127.53516, 49.84306], [127.49299, 50.01251], [127.60515, 50.23503], [127.37384, 50.28393], [127.36009, 50.43787], [127.28765, 50.46585], [127.36335, 50.58306], [127.28165, 50.72075], [127.14586, 50.91152], [126.93135, 51.0841], [126.90369, 51.3238], [126.68349, 51.70607], [126.44606, 51.98254], [126.558, 52.13738], [125.6131, 53.07229]], [[113.56865, 22.20973], [113.57123, 22.20416], [113.60504, 22.20464], [113.63011, 22.10782], [113.57191, 22.07696], [113.54839, 22.10909], [113.54942, 22.14519], [113.54093, 22.15497], [113.52659, 22.18271], [113.53552, 22.20607], [113.53301, 22.21235], [113.53591, 22.21369], [113.54093, 22.21314], [113.54333, 22.21688], [113.5508, 22.21672], [113.56865, 22.20973]], [[114.50148, 22.15017], [113.92195, 22.13873], [113.83338, 22.1826], [113.81621, 22.2163], [113.86771, 22.42972], [114.03113, 22.5065], [114.05438, 22.5026], [114.05729, 22.51104], [114.06272, 22.51617], [114.07267, 22.51855], [114.07817, 22.52997], [114.08606, 22.53276], [114.09048, 22.53716], [114.09692, 22.53435], [114.1034, 22.5352], [114.11181, 22.52878], [114.11656, 22.53415], [114.12665, 22.54003], [114.13823, 22.54319], [114.1482, 22.54091], [114.15123, 22.55163], [114.1597, 22.56041], [114.17247, 22.55944], [114.18338, 22.55444], [114.20655, 22.55706], [114.22185, 22.55343], [114.22888, 22.5436], [114.25154, 22.55977], [114.44998, 22.55977], [114.50148, 22.15017]]]]
21636 wikidata: "Q22890",
21638 level: "sharedLandform"
21644 wikidata: "Q23666",
21645 nameEn: "Great Britain",
21647 level: "sharedLandform"
21653 wikidata: "Q23681",
21654 nameEn: "Northern Cyprus",
21655 groups: ["Q644636", "145", "142"],
21657 callingCodes: ["90 392"]
21660 type: "MultiPolygon",
21661 coordinates: [[[[33.67678, 35.03866], [33.67742, 35.05963], [33.68474, 35.06602], [33.69095, 35.06237], [33.70861, 35.07644], [33.7161, 35.07279], [33.70209, 35.04882], [33.71482, 35.03722], [33.73824, 35.05321], [33.76106, 35.04253], [33.78581, 35.05104], [33.82067, 35.07826], [33.84168, 35.06823], [33.8541, 35.07201], [33.87479, 35.08881], [33.87097, 35.09389], [33.87622, 35.10457], [33.87224, 35.12293], [33.88561, 35.12449], [33.88943, 35.12007], [33.88737, 35.11408], [33.89853, 35.11377], [33.91789, 35.08688], [33.91299, 35.07579], [33.90247, 35.07686], [33.89485, 35.06873], [33.88367, 35.07877], [33.85261, 35.0574], [33.8355, 35.05777], [33.82051, 35.0667], [33.8012, 35.04786], [33.81524, 35.04192], [33.83055, 35.02865], [33.82875, 35.01685], [33.84045, 35.00616], [33.85216, 35.00579], [33.85891, 35.001], [33.85621, 34.98956], [33.83505, 34.98108], [33.84811, 34.97075], [33.86432, 34.97592], [33.90075, 34.96623], [33.98684, 34.76642], [35.48515, 34.70851], [35.51152, 36.10954], [32.82353, 35.70297], [32.46489, 35.48584], [32.60361, 35.16647], [32.64864, 35.19967], [32.70947, 35.18328], [32.70779, 35.14127], [32.85733, 35.07742], [32.86406, 35.1043], [32.94471, 35.09422], [33.01192, 35.15639], [33.08249, 35.17319], [33.11105, 35.15639], [33.15138, 35.19504], [33.27068, 35.16815], [33.3072, 35.16816], [33.31955, 35.18096], [33.35056, 35.18328], [33.34964, 35.17803], [33.35596, 35.17942], [33.35612, 35.17402], [33.36569, 35.17479], [33.3717, 35.1788], [33.37248, 35.18698], [33.38575, 35.2018], [33.4076, 35.20062], [33.41675, 35.16325], [33.46813, 35.10564], [33.48136, 35.0636], [33.47825, 35.04103], [33.45178, 35.02078], [33.45256, 35.00288], [33.47666, 35.00701], [33.48915, 35.06594], [33.53975, 35.08151], [33.57478, 35.06049], [33.567, 35.04803], [33.59658, 35.03635], [33.61215, 35.0527], [33.63765, 35.03869], [33.67678, 35.03866]]]]
21666 wikidata: "Q25231",
21667 nameEn: "Svalbard",
21668 aliases: ["NO-21"],
21670 groups: ["SJ", "154", "150", "UN"],
21671 level: "subterritory",
21672 callingCodes: ["47 79"]
21675 type: "MultiPolygon",
21676 coordinates: [[[[-7.49892, 77.24208], [32.07813, 72.01005], [36.85549, 84.09565], [-7.49892, 77.24208]]]]
21681 wikidata: "Q25263",
21683 aliases: ["PT-20"],
21685 groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21686 callingCodes: ["351"]
21689 type: "MultiPolygon",
21690 coordinates: [[[[-23.12984, 40.26428], [-36.43765, 41.39418], [-22.54767, 33.34416], [-23.12984, 40.26428]]]]
21695 wikidata: "Q25359",
21696 nameEn: "Navassa Island",
21697 aliases: ["UM-76"],
21699 groups: ["UM", "Q1352230", "029", "003", "419", "019", "UN"],
21700 level: "subterritory",
21701 roadSpeedUnit: "mph",
21702 roadHeightUnit: "ft"
21705 type: "MultiPolygon",
21706 coordinates: [[[[-74.7289, 18.71009], [-75.71816, 18.46438], [-74.76465, 18.06252], [-74.7289, 18.71009]]]]
21711 wikidata: "Q25396",
21713 aliases: ["BQ-BO", "NL-BQ1"],
21715 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21716 level: "subterritory",
21717 callingCodes: ["599 7"]
21720 type: "MultiPolygon",
21721 coordinates: [[[[-67.89186, 12.4116], [-68.90012, 12.62309], [-68.33524, 11.78151], [-68.01417, 11.77722], [-67.89186, 12.4116]]]]
21726 wikidata: "Q25528",
21728 aliases: ["BQ-SA", "NL-BQ2"],
21730 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21731 level: "subterritory",
21732 callingCodes: ["599 4"]
21735 type: "MultiPolygon",
21736 coordinates: [[[[-63.07669, 17.79659], [-63.81314, 17.95045], [-63.22932, 17.32592], [-63.07669, 17.79659]]]]
21741 wikidata: "Q26180",
21742 nameEn: "Sint Eustatius",
21743 aliases: ["BQ-SE", "NL-BQ3"],
21745 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21746 level: "subterritory",
21747 callingCodes: ["599 3"]
21750 type: "MultiPolygon",
21751 coordinates: [[[[-63.07669, 17.79659], [-63.34999, 16.94218], [-62.76692, 17.64353], [-63.07669, 17.79659]]]]
21756 wikidata: "Q26253",
21758 aliases: ["PT-30"],
21760 groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21761 callingCodes: ["351"]
21764 type: "MultiPolygon",
21765 coordinates: [[[[-19.30302, 33.65304], [-16.04789, 29.65076], [-11.68307, 33.12333], [-19.30302, 33.65304]]]]
21770 wikidata: "Q26927",
21771 nameEn: "Lakshadweep",
21772 aliases: ["IN-LD"],
21774 groups: ["034", "142", "UN"],
21776 callingCodes: ["91"]
21779 type: "MultiPolygon",
21780 coordinates: [[[[67.64074, 11.57295], [76.59015, 5.591], [72.67494, 13.58102], [67.64074, 11.57295]]]]
21785 wikidata: "Q27329",
21786 nameEn: "Asian Russia",
21788 groups: ["142", "UN"],
21789 callingCodes: ["7"]
21792 type: "MultiPolygon",
21793 coordinates: [[[[-179.99933, 64.74703], [-172.76104, 63.77445], [-169.03888, 65.48473], [-168.95635, 65.98512], [-168.25765, 71.99091], [-179.9843, 71.90735], [-179.99933, 64.74703]]], [[[59.99809, 51.98263], [60.19925, 51.99173], [60.48915, 52.15175], [60.72581, 52.15538], [60.78201, 52.22067], [61.05417, 52.35096], [60.98021, 52.50068], [60.84709, 52.52228], [60.84118, 52.63912], [60.71693, 52.66245], [60.71989, 52.75923], [61.05842, 52.92217], [61.23462, 53.03227], [62.0422, 52.96105], [62.12799, 52.99133], [62.14574, 53.09626], [61.19024, 53.30536], [61.14291, 53.41481], [61.29082, 53.50992], [61.37957, 53.45887], [61.57185, 53.50112], [61.55706, 53.57144], [60.90626, 53.62937], [61.22574, 53.80268], [61.14283, 53.90063], [60.99796, 53.93699], [61.26863, 53.92797], [61.3706, 54.08464], [61.47603, 54.08048], [61.56941, 53.95703], [61.65318, 54.02445], [62.03913, 53.94768], [62.00966, 54.04134], [62.38535, 54.03961], [62.45931, 53.90737], [62.56876, 53.94047], [62.58651, 54.05871], [63.80604, 54.27079], [63.91224, 54.20013], [64.02715, 54.22679], [63.97686, 54.29763], [64.97216, 54.4212], [65.11033, 54.33028], [65.24663, 54.35721], [65.20174, 54.55216], [68.21308, 54.98645], [68.26661, 55.09226], [68.19206, 55.18823], [68.90865, 55.38148], [69.34224, 55.36344], [69.74917, 55.35545], [70.19179, 55.1476], [70.76493, 55.3027], [70.96009, 55.10558], [71.08288, 54.71253], [71.24185, 54.64965], [71.08706, 54.33376], [71.10379, 54.13326], [71.96141, 54.17736], [72.17477, 54.36303], [72.43415, 53.92685], [72.71026, 54.1161], [73.37963, 53.96132], [73.74778, 54.07194], [73.68921, 53.86522], [73.25412, 53.61532], [73.39218, 53.44623], [75.07405, 53.80831], [75.43398, 53.98652], [75.3668, 54.07439], [76.91052, 54.4677], [76.82266, 54.1798], [76.44076, 54.16017], [76.54243, 53.99329], [77.90383, 53.29807], [79.11255, 52.01171], [80.08138, 50.77658], [80.4127, 50.95581], [80.44819, 51.20855], [80.80318, 51.28262], [81.16999, 51.15662], [81.06091, 50.94833], [81.41248, 50.97524], [81.46581, 50.77658], [81.94999, 50.79307], [82.55443, 50.75412], [83.14607, 51.00796], [83.8442, 50.87375], [84.29385, 50.27257], [84.99198, 50.06793], [85.24047, 49.60239], [86.18709, 49.50259], [86.63674, 49.80136], [86.79056, 49.74787], [86.61307, 49.60239], [86.82606, 49.51796], [87.03071, 49.25142], [87.31465, 49.23603], [87.28386, 49.11626], [87.478, 49.07403], [87.48983, 49.13794], [87.81333, 49.17354], [87.98977, 49.18147], [88.15543, 49.30314], [88.17223, 49.46934], [88.42449, 49.48821], [88.82499, 49.44808], [89.70687, 49.72535], [89.59711, 49.90851], [91.86048, 50.73734], [92.07173, 50.69585], [92.44714, 50.78762], [93.01109, 50.79001], [92.99595, 50.63183], [94.30823, 50.57498], [94.39258, 50.22193], [94.49477, 50.17832], [94.6121, 50.04239], [94.97166, 50.04725], [95.02465, 49.96941], [95.74757, 49.97915], [95.80056, 50.04239], [96.97388, 49.88413], [97.24639, 49.74737], [97.56811, 49.84265], [97.56432, 49.92801], [97.76871, 49.99861], [97.85197, 49.91339], [98.29481, 50.33561], [98.31373, 50.4996], [98.06393, 50.61262], [97.9693, 50.78044], [98.01472, 50.86652], [97.83305, 51.00248], [98.05257, 51.46696], [98.22053, 51.46579], [98.33222, 51.71832], [98.74142, 51.8637], [98.87768, 52.14563], [99.27888, 51.96876], [99.75578, 51.90108], [99.89203, 51.74903], [100.61116, 51.73028], [101.39085, 51.45753], [101.5044, 51.50467], [102.14032, 51.35566], [102.32194, 50.67982], [102.71178, 50.38873], [103.70343, 50.13952], [105.32528, 50.4648], [106.05562, 50.40582], [106.07865, 50.33474], [106.47156, 50.31909], [106.49628, 50.32436], [106.51122, 50.34408], [106.58373, 50.34044], [106.80326, 50.30177], [107.00007, 50.1977], [107.1174, 50.04239], [107.36407, 49.97612], [107.96116, 49.93191], [107.95387, 49.66659], [108.27937, 49.53167], [108.53969, 49.32325], [109.18017, 49.34709], [109.51325, 49.22859], [110.24373, 49.16676], [110.39891, 49.25083], [110.64493, 49.1816], [113.02647, 49.60772], [113.20216, 49.83356], [114.325, 50.28098], [114.9703, 50.19254], [115.26068, 49.97367], [115.73602, 49.87688], [116.22402, 50.04477], [116.62502, 49.92919], [116.71193, 49.83813], [117.27597, 49.62544], [117.48208, 49.62324], [117.82343, 49.52696], [118.61623, 49.93809], [119.11003, 50.00276], [119.27996, 50.13348], [119.38598, 50.35162], [119.13553, 50.37412], [120.10963, 51.671], [120.65907, 51.93544], [120.77337, 52.20805], [120.61346, 52.32447], [120.71673, 52.54099], [120.46454, 52.63811], [120.04049, 52.58773], [120.0451, 52.7359], [120.85633, 53.28499], [121.39213, 53.31888], [122.35063, 53.49565], [122.85966, 53.47395], [123.26989, 53.54843], [123.86158, 53.49391], [124.46078, 53.21881], [125.17522, 53.20225], [125.6131, 53.07229], [126.558, 52.13738], [126.44606, 51.98254], [126.68349, 51.70607], [126.90369, 51.3238], [126.93135, 51.0841], [127.14586, 50.91152], [127.28165, 50.72075], [127.36335, 50.58306], [127.28765, 50.46585], [127.36009, 50.43787], [127.37384, 50.28393], [127.60515, 50.23503], [127.49299, 50.01251], [127.53516, 49.84306], [127.83476, 49.5748], [128.72896, 49.58676], [129.11153, 49.36813], [129.23232, 49.40353], [129.35317, 49.3481], [129.40398, 49.44194], [129.50685, 49.42398], [129.67598, 49.29596], [129.85416, 49.11067], [130.2355, 48.86741], [130.43232, 48.90844], [130.66946, 48.88251], [130.52147, 48.61745], [130.84462, 48.30942], [130.65103, 48.10052], [130.90915, 47.90623], [130.95985, 47.6957], [131.09871, 47.6852], [131.2635, 47.73325], [131.90448, 47.68011], [132.57309, 47.71741], [132.66989, 47.96491], [134.49516, 48.42884], [134.75328, 48.36763], [134.67098, 48.1564], [134.55508, 47.98651], [134.7671, 47.72051], [134.50898, 47.4812], [134.20016, 47.33458], [134.03538, 46.75668], [133.84104, 46.46681], [133.91496, 46.4274], [133.88097, 46.25066], [133.68047, 46.14697], [133.72695, 46.05576], [133.67569, 45.9759], [133.60442, 45.90053], [133.48457, 45.86203], [133.41083, 45.57723], [133.19419, 45.51913], [133.09279, 45.25693], [133.12293, 45.1332], [132.96373, 45.0212], [132.83978, 45.05916], [131.99417, 45.2567], [131.86903, 45.33636], [131.76532, 45.22609], [131.66852, 45.2196], [131.68466, 45.12374], [131.48415, 44.99513], [130.95639, 44.85154], [131.1108, 44.70266], [131.30365, 44.04262], [131.25484, 44.03131], [131.23583, 43.96085], [131.26176, 43.94011], [131.21105, 43.82383], [131.19492, 43.53047], [131.29402, 43.46695], [131.30324, 43.39498], [131.19031, 43.21385], [131.20414, 43.13654], [131.10274, 43.04734], [131.135, 42.94114], [131.02668, 42.91246], [131.02438, 42.86518], [130.66524, 42.84753], [130.44361, 42.76205], [130.40213, 42.70788], [130.56576, 42.68925], [130.62107, 42.58413], [130.55143, 42.52158], [130.56835, 42.43281], [130.60805, 42.4317], [130.64181, 42.41422], [130.66367, 42.38024], [130.65022, 42.32281], [131.95041, 41.5445], [140.9182, 45.92937], [145.82343, 44.571], [145.23667, 43.76813], [153.94307, 38.42848], [180, 62.52334], [180, 71.53642], [155.31937, 81.93282], [76.13964, 83.37843], [64.18965, 69.94255], [66.1708, 67.61252], [61.98014, 65.72191], [60.74386, 64.95767], [59.63945, 64.78384], [59.80579, 64.13948], [59.24834, 63.01859], [59.61398, 62.44915], [59.36223, 61.3882], [59.50685, 60.91162], [58.3853, 59.487], [59.15636, 59.14682], [59.40376, 58.45822], [58.71104, 58.07475], [58.81412, 57.71602], [58.13789, 57.68097], [58.07604, 57.08308], [57.28024, 56.87898], [57.51527, 56.08729], [59.28419, 56.15739], [59.49035, 55.60486], [58.81825, 55.03378], [57.25137, 55.26262], [57.14829, 54.84204], [57.95234, 54.39672], [59.95217, 54.85853], [59.70487, 54.14846], [58.94336, 53.953], [58.79644, 52.43392], [59.22409, 52.28437], [59.25033, 52.46803], [60.17516, 52.39457], [60.17253, 52.25814], [59.91279, 52.06924], [59.99809, 51.98263]]]]
21798 wikidata: "Q34366",
21799 nameEn: "Tasmania",
21800 aliases: ["AU-TAS"],
21802 groups: ["053", "009", "UN"],
21804 callingCodes: ["61"]
21807 type: "MultiPolygon",
21808 coordinates: [[[[123.64533, -39.13605], [159.69067, -56.28945], [159.74028, -39.1978], [123.64533, -39.13605]]]]
21813 wikidata: "Q34497",
21814 nameEn: "Saint Helena",
21815 aliases: ["SH-HL"],
21817 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
21818 level: "subterritory",
21820 roadSpeedUnit: "mph",
21821 roadHeightUnit: "ft",
21822 callingCodes: ["290"]
21825 type: "MultiPolygon",
21826 coordinates: [[[[-8.3824, -13.9131], [-6.17428, -19.07236], [-3.29308, -15.22647], [-8.3824, -13.9131]]]]
21831 wikidata: "Q35657",
21832 nameEn: "US States",
21834 level: "subcountryGroup"
21840 wikidata: "Q36117",
21842 level: "sharedLandform"
21848 wikidata: "Q36678",
21849 nameEn: "West Bank",
21851 groups: ["145", "142"],
21852 callingCodes: ["970"]
21855 type: "MultiPolygon",
21856 coordinates: [[[[35.47672, 31.49578], [35.55941, 31.76535], [35.52758, 31.9131], [35.54375, 31.96587], [35.52012, 32.04076], [35.57111, 32.21877], [35.55807, 32.38674], [35.42078, 32.41562], [35.41048, 32.43706], [35.41598, 32.45593], [35.42034, 32.46009], [35.40224, 32.50136], [35.35212, 32.52047], [35.30685, 32.51024], [35.29306, 32.50947], [35.25049, 32.52453], [35.2244, 32.55289], [35.15937, 32.50466], [35.10882, 32.4757], [35.10024, 32.47856], [35.09236, 32.47614], [35.08564, 32.46948], [35.07059, 32.4585], [35.05423, 32.41754], [35.05311, 32.4024], [35.0421, 32.38242], [35.05142, 32.3667], [35.04243, 32.35008], [35.01772, 32.33863], [35.01119, 32.28684], [35.02939, 32.2671], [35.01841, 32.23981], [34.98885, 32.20758], [34.95703, 32.19522], [34.96009, 32.17503], [34.99039, 32.14626], [34.98507, 32.12606], [34.99437, 32.10962], [34.9863, 32.09551], [35.00261, 32.027], [34.98682, 31.96935], [35.00124, 31.93264], [35.03489, 31.92448], [35.03978, 31.89276], [35.03489, 31.85919], [34.99712, 31.85569], [34.9724, 31.83352], [35.01978, 31.82944], [35.05617, 31.85685], [35.07677, 31.85627], [35.14174, 31.81325], [35.18603, 31.80901], [35.18169, 31.82542], [35.19461, 31.82687], [35.21469, 31.81835], [35.216, 31.83894], [35.21128, 31.863], [35.20381, 31.86716], [35.20673, 31.88151], [35.20791, 31.8821], [35.20945, 31.8815], [35.21016, 31.88237], [35.21276, 31.88153], [35.2136, 31.88241], [35.22014, 31.88264], [35.22294, 31.87889], [35.22567, 31.86745], [35.22817, 31.8638], [35.2249, 31.85433], [35.2304, 31.84222], [35.24816, 31.8458], [35.25753, 31.8387], [35.251, 31.83085], [35.26404, 31.82567], [35.25573, 31.81362], [35.26058, 31.79064], [35.25225, 31.7678], [35.26319, 31.74846], [35.25182, 31.73945], [35.24981, 31.72543], [35.2438, 31.7201], [35.24315, 31.71244], [35.23972, 31.70896], [35.22392, 31.71899], [35.21937, 31.71578], [35.20538, 31.72388], [35.18023, 31.72067], [35.16478, 31.73242], [35.15474, 31.73352], [35.15119, 31.73634], [35.13931, 31.73012], [35.12933, 31.7325], [35.11895, 31.71454], [35.10782, 31.71594], [35.08226, 31.69107], [35.00879, 31.65426], [34.95249, 31.59813], [34.9415, 31.55601], [34.94356, 31.50743], [34.93258, 31.47816], [34.89756, 31.43891], [34.87833, 31.39321], [34.88932, 31.37093], [34.92571, 31.34337], [35.02459, 31.35979], [35.13033, 31.3551], [35.22921, 31.37445], [35.39675, 31.49572], [35.47672, 31.49578]]]]
21861 wikidata: "Q37362",
21862 nameEn: "Akrotiri and Dhekelia",
21870 wikidata: "Q38095",
21871 nameEn: "Gal\xE1pagos Islands",
21874 groups: ["005", "419", "019", "UN"],
21875 callingCodes: ["593"]
21878 type: "MultiPolygon",
21879 coordinates: [[[[-93.12365, 2.64343], [-92.46744, -2.52874], [-87.07749, -0.8849], [-93.12365, 2.64343]]]]
21884 wikidata: "Q39760",
21885 nameEn: "Gaza Strip",
21887 groups: ["145", "142"],
21888 callingCodes: ["970"]
21891 type: "MultiPolygon",
21892 coordinates: [[[[34.052, 31.46619], [34.21853, 31.32363], [34.23572, 31.2966], [34.24012, 31.29591], [34.26742, 31.21998], [34.29417, 31.24194], [34.36523, 31.28963], [34.37381, 31.30598], [34.36505, 31.36404], [34.40077, 31.40926], [34.48892, 31.48365], [34.56797, 31.54197], [34.48681, 31.59711], [34.29262, 31.70393], [34.052, 31.46619]]]]
21897 wikidata: "Q40888",
21898 nameEn: "Andaman and Nicobar Islands",
21899 aliases: ["IN-AN"],
21901 groups: ["034", "142", "UN"],
21903 callingCodes: ["91"]
21906 type: "MultiPolygon",
21907 coordinates: [[[[94.42132, 5.96581], [94.6371, 13.81803], [86.7822, 13.41052], [94.42132, 5.96581]]]]
21912 wikidata: "Q41684",
21913 nameEn: "Stewart Island",
21915 groups: ["053", "009", "UN"],
21917 callingCodes: ["64"]
21920 type: "MultiPolygon",
21921 coordinates: [[[[166.59185, -47.61313], [169.70504, -47.56021], [167.52103, -46.41337], [166.59185, -47.61313]]]]
21926 wikidata: "Q43296",
21927 nameEn: "Wake Island",
21928 aliases: ["WK", "WAK", "WKUM", "872", "UM-79"],
21930 groups: ["UM", "Q1352230", "057", "009", "UN"],
21931 level: "subterritory",
21932 roadSpeedUnit: "mph",
21933 roadHeightUnit: "ft",
21934 callingCodes: ["1"]
21937 type: "MultiPolygon",
21938 coordinates: [[[[167.34779, 18.97692], [166.67967, 20.14834], [165.82549, 18.97692], [167.34779, 18.97692]]]]
21943 wikidata: "Q46275",
21944 nameEn: "New Zealand Subantarctic Islands",
21946 groups: ["Q851132", "053", "009", "UN"],
21950 type: "MultiPolygon",
21951 coordinates: [[[[164.30551, -47.88072], [161.96603, -56.07661], [179.49541, -50.04657], [179.49541, -47.2902], [169.91032, -47.66283], [164.30551, -47.88072]]]]
21956 wikidata: "Q46395",
21957 nameEn: "British Overseas Territories",
21958 aliases: ["BOTS", "UKOTS"],
21960 level: "subcountryGroup"
21966 wikidata: "Q46772",
21967 nameEn: "Kerguelen Islands",
21969 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
21970 level: "subterritory"
21973 type: "MultiPolygon",
21974 coordinates: [[[[61.9216, -49.39746], [70.67507, -51.14192], [74.25129, -45.45074], [61.9216, -49.39746]]]]
21979 wikidata: "Q46879",
21980 nameEn: "Baker Island",
21981 aliases: ["UM-81"],
21983 groups: ["UM", "Q1352230", "061", "009", "UN"],
21984 level: "subterritory",
21985 roadSpeedUnit: "mph",
21986 roadHeightUnit: "ft"
21989 type: "MultiPolygon",
21990 coordinates: [[[[-175.33482, -1.40631], [-175.31323, 0.5442], [-177.91421, 0.39582], [-175.33482, -1.40631]]]]
21995 wikidata: "Q47863",
21996 nameEn: "Midway Atoll",
21997 aliases: ["MI", "MID", "MIUM", "488", "UM-71"],
21999 groups: ["UM", "Q1352230", "061", "009", "UN"],
22000 level: "subterritory",
22001 roadSpeedUnit: "mph",
22002 roadHeightUnit: "ft",
22003 callingCodes: ["1"]
22006 type: "MultiPolygon",
22007 coordinates: [[[[-176.29741, 29.09786], [-177.77531, 29.29793], [-177.5224, 27.7635], [-176.29741, 29.09786]]]]
22012 wikidata: "Q62218",
22013 nameEn: "Jarvis Island",
22014 aliases: ["UM-86"],
22016 groups: ["UM", "Q1352230", "061", "009", "UN"],
22017 level: "subterritory",
22018 roadSpeedUnit: "mph",
22019 roadHeightUnit: "ft"
22022 type: "MultiPolygon",
22023 coordinates: [[[[-160.42921, -1.4364], [-159.12443, 0.19975], [-160.38779, 0.30331], [-160.42921, -1.4364]]]]
22028 wikidata: "Q105472",
22029 nameEn: "Macaronesia",
22030 level: "sharedLandform"
22036 wikidata: "Q114935",
22037 nameEn: "Kermadec Islands",
22039 groups: ["Q851132", "053", "009", "UN"],
22041 callingCodes: ["64"]
22044 type: "MultiPolygon",
22045 coordinates: [[[[-174.40891, -29.09438], [-180, -24.21376], [-179.96512, -35.00791], [-174.40891, -29.09438]]]]
22050 wikidata: "Q115459",
22051 nameEn: "Chatham Islands",
22052 aliases: ["NZ-CIT"],
22054 groups: ["Q851132", "053", "009", "UN"],
22056 callingCodes: ["64"]
22059 type: "MultiPolygon",
22060 coordinates: [[[[-179.93224, -45.18423], [-172.47015, -45.17912], [-176.30998, -41.38382], [-179.93224, -45.18423]]]]
22065 wikidata: "Q118863",
22066 nameEn: "North Island",
22068 groups: ["053", "009", "UN"],
22070 callingCodes: ["64"]
22073 type: "MultiPolygon",
22074 coordinates: [[[[179.49541, -47.2902], [179.49541, -36.79303], [174.17679, -32.62487], [170.27492, -36.38133], [174.58663, -40.80446], [174.46634, -41.55028], [179.49541, -47.2902]]]]
22079 wikidata: "Q120755",
22080 nameEn: "South Island",
22082 groups: ["053", "009", "UN"],
22084 callingCodes: ["64"]
22087 type: "MultiPolygon",
22088 coordinates: [[[[169.70504, -47.56021], [179.49541, -47.2902], [174.46634, -41.55028], [174.58663, -40.80446], [170.27492, -36.38133], [166.56976, -39.94841], [164.8365, -46.0205], [167.52103, -46.41337], [169.70504, -47.56021]]]]
22093 wikidata: "Q123076",
22094 nameEn: "Palmyra Atoll",
22095 aliases: ["UM-95"],
22097 groups: ["UM", "Q1352230", "061", "009", "UN"],
22098 level: "subterritory",
22099 roadSpeedUnit: "mph",
22100 roadHeightUnit: "ft",
22101 callingCodes: ["1"]
22104 type: "MultiPolygon",
22105 coordinates: [[[[-161.06795, 5.2462], [-161.0731, 7.1291], [-163.24478, 5.24198], [-161.06795, 5.2462]]]]
22110 wikidata: "Q130574",
22111 nameEn: "Chafarinas Islands",
22113 groups: ["EU", "Q191011", "015", "002", "UN"],
22114 level: "subterritory"
22117 type: "MultiPolygon",
22118 coordinates: [[[[-2.40316, 35.16893], [-2.43262, 35.20652], [-2.45965, 35.16527], [-2.40316, 35.16893]]]]
22123 wikidata: "Q130895",
22124 nameEn: "Kingman Reef",
22125 aliases: ["UM-89"],
22127 groups: ["UM", "Q1352230", "061", "009", "UN"],
22128 level: "subterritory",
22129 roadSpeedUnit: "mph",
22130 roadHeightUnit: "ft"
22133 type: "MultiPolygon",
22134 coordinates: [[[[-161.0731, 7.1291], [-163.16627, 7.15036], [-163.24478, 5.24198], [-161.0731, 7.1291]]]]
22139 wikidata: "Q131008",
22140 nameEn: "Johnston Atoll",
22141 aliases: ["JT", "JTN", "JTUM", "396", "UM-67"],
22143 groups: ["UM", "Q1352230", "061", "009", "UN"],
22144 level: "subterritory",
22145 roadSpeedUnit: "mph",
22146 roadHeightUnit: "ft",
22147 callingCodes: ["1"]
22150 type: "MultiPolygon",
22151 coordinates: [[[[-170.65691, 16.57199], [-168.87689, 16.01159], [-169.2329, 17.4933], [-170.65691, 16.57199]]]]
22156 wikidata: "Q131305",
22157 nameEn: "Howland Island",
22158 aliases: ["UM-84"],
22160 groups: ["UM", "Q1352230", "061", "009", "UN"],
22161 level: "subterritory",
22162 roadSpeedUnit: "mph",
22163 roadHeightUnit: "ft"
22166 type: "MultiPolygon",
22167 coordinates: [[[[-177.91421, 0.39582], [-175.31323, 0.5442], [-176.74464, 2.28109], [-177.91421, 0.39582]]]]
22172 wikidata: "Q133888",
22173 nameEn: "Ashmore and Cartier Islands",
22175 groups: ["053", "009", "UN"],
22177 callingCodes: ["61"]
22180 type: "MultiPolygon",
22181 coordinates: [[[[123.7463, -11.1783], [120.6877, -13.59408], [125.29076, -12.33139], [123.7463, -11.1783]]]]
22186 wikidata: "Q153732",
22187 nameEn: "Mariana Islands",
22188 level: "sharedLandform"
22194 wikidata: "Q172216",
22195 nameEn: "Coral Sea Islands",
22197 groups: ["053", "009", "UN"],
22199 callingCodes: ["61"]
22202 type: "MultiPolygon",
22203 coordinates: [[[[159.77159, -28.41151], [156.73836, -14.50464], [145.2855, -9.62524], [147.69992, -17.5933], [152.93188, -20.92631], [154.02855, -24.43238], [159.77159, -28.41151]]]]
22208 wikidata: "Q179313",
22209 nameEn: "Alderney",
22211 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22212 level: "subterritory",
22214 roadSpeedUnit: "mph",
22215 roadHeightUnit: "ft",
22216 callingCodes: ["44 01481"]
22219 type: "MultiPolygon",
22220 coordinates: [[[[-2.36485, 49.48223], [-2.09454, 49.46288], [-2.02963, 49.91866], [-2.49556, 49.79012], [-2.36485, 49.48223]]]]
22225 wikidata: "Q185086",
22226 nameEn: "Crown Dependencies",
22228 level: "subcountryGroup"
22234 wikidata: "Q190571",
22235 nameEn: "Scattered Islands",
22237 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22238 level: "subterritory"
22241 type: "MultiPolygon",
22242 coordinates: [[[[53.53458, -16.36909], [54.96649, -16.28353], [54.61476, -15.02273], [53.53458, -16.36909]]], [[[38.55969, -20.75596], [40.68027, -23.38889], [43.52893, -15.62903], [38.55969, -20.75596]]], [[[47.03092, -11.05648], [47.11593, -12.08552], [47.96702, -11.46447], [47.03092, -11.05648]]]]
22247 wikidata: "Q191011",
22248 nameEn: "Plazas de soberan\xEDa",
22255 wikidata: "Q191146",
22256 nameEn: "Pe\xF1\xF3n de V\xE9lez de la Gomera",
22258 groups: ["EU", "Q191011", "015", "002", "UN"],
22259 level: "subterritory"
22262 type: "MultiPolygon",
22263 coordinates: [[[[-4.30191, 35.17419], [-4.30112, 35.17058], [-4.29436, 35.17149], [-4.30191, 35.17419]]]]
22268 wikidata: "Q201698",
22269 nameEn: "Crozet Islands",
22271 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22272 level: "subterritory"
22275 type: "MultiPolygon",
22276 coordinates: [[[[55.03425, -43.65017], [46.31615, -46.28749], [54.5587, -47.93013], [55.03425, -43.65017]]]]
22281 wikidata: "Q578170",
22282 nameEn: "Contiguous United States",
22283 aliases: ["CONUS"],
22285 groups: ["Q35657", "021", "003", "019", "UN"],
22286 roadSpeedUnit: "mph",
22287 roadHeightUnit: "ft",
22288 callingCodes: ["1"]
22291 type: "MultiPolygon",
22292 coordinates: [[[[-97.13927, 25.96583], [-96.92418, 25.97377], [-80.57035, 24.0565], [-78.91214, 27.76553], [-61.98255, 37.34815], [-67.16117, 44.20069], [-66.93432, 44.82597], [-66.96824, 44.83078], [-66.98249, 44.87071], [-66.96824, 44.90965], [-67.0216, 44.95333], [-67.11316, 45.11176], [-67.15965, 45.16179], [-67.19603, 45.16771], [-67.20349, 45.1722], [-67.22751, 45.16344], [-67.27039, 45.1934], [-67.29748, 45.18173], [-67.29754, 45.14865], [-67.34927, 45.122], [-67.48201, 45.27351], [-67.42394, 45.37969], [-67.50578, 45.48971], [-67.42144, 45.50584], [-67.43815, 45.59162], [-67.6049, 45.60725], [-67.80705, 45.69528], [-67.80653, 45.80022], [-67.75654, 45.82324], [-67.80961, 45.87531], [-67.75196, 45.91814], [-67.78111, 45.9392], [-67.78578, 47.06473], [-67.87993, 47.10377], [-67.94843, 47.1925], [-68.23244, 47.35712], [-68.37458, 47.35851], [-68.38332, 47.28723], [-68.57914, 47.28431], [-68.60575, 47.24659], [-68.70125, 47.24399], [-68.89222, 47.1807], [-69.05039, 47.2456], [-69.05073, 47.30076], [-69.05148, 47.42012], [-69.22119, 47.46461], [-69.99966, 46.69543], [-70.05812, 46.41768], [-70.18547, 46.35357], [-70.29078, 46.18832], [-70.23855, 46.1453], [-70.31025, 45.96424], [-70.24694, 45.95138], [-70.25976, 45.89675], [-70.41523, 45.79497], [-70.38934, 45.73215], [-70.54019, 45.67291], [-70.68516, 45.56964], [-70.72651, 45.49771], [-70.62518, 45.42286], [-70.65383, 45.37592], [-70.78372, 45.43269], [-70.82638, 45.39828], [-70.80236, 45.37444], [-70.84816, 45.22698], [-70.89864, 45.2398], [-70.91169, 45.29849], [-70.95193, 45.33895], [-71.0107, 45.34819], [-71.01866, 45.31573], [-71.08364, 45.30623], [-71.14568, 45.24128], [-71.19723, 45.25438], [-71.22338, 45.25184], [-71.29371, 45.29996], [-71.37133, 45.24624], [-71.44252, 45.2361], [-71.40364, 45.21382], [-71.42778, 45.12624], [-71.48735, 45.07784], [-71.50067, 45.01357], [-73.35025, 45.00942], [-74.32699, 44.99029], [-74.66689, 45.00646], [-74.8447, 45.00606], [-74.99101, 44.98051], [-75.01363, 44.95608], [-75.2193, 44.87821], [-75.41441, 44.76614], [-75.76813, 44.51537], [-75.8217, 44.43176], [-75.95947, 44.34463], [-76.00018, 44.34896], [-76.16285, 44.28262], [-76.1664, 44.23051], [-76.244, 44.19643], [-76.31222, 44.19894], [-76.35324, 44.13493], [-76.43859, 44.09393], [-76.79706, 43.63099], [-79.25796, 43.54052], [-79.06921, 43.26183], [-79.05512, 43.25375], [-79.05544, 43.21224], [-79.05002, 43.20133], [-79.05384, 43.17418], [-79.04652, 43.16396], [-79.0427, 43.13934], [-79.06881, 43.12029], [-79.05671, 43.10937], [-79.07486, 43.07845], [-79.01055, 43.06659], [-78.99941, 43.05612], [-79.02424, 43.01983], [-79.02074, 42.98444], [-78.98126, 42.97], [-78.96312, 42.95509], [-78.93224, 42.95229], [-78.90905, 42.93022], [-78.90712, 42.89733], [-78.93684, 42.82887], [-82.67862, 41.67615], [-83.11184, 41.95671], [-83.14962, 42.04089], [-83.12724, 42.2376], [-83.09837, 42.28877], [-83.07837, 42.30978], [-83.02253, 42.33045], [-82.82964, 42.37355], [-82.64242, 42.55594], [-82.58873, 42.54984], [-82.57583, 42.5718], [-82.51858, 42.611], [-82.51063, 42.66025], [-82.46613, 42.76615], [-82.4826, 42.8068], [-82.45331, 42.93139], [-82.4253, 42.95423], [-82.4146, 42.97626], [-82.42469, 42.992], [-82.48419, 45.30225], [-83.59589, 45.82131], [-83.43746, 45.99749], [-83.57017, 46.105], [-83.83329, 46.12169], [-83.90453, 46.05922], [-83.95399, 46.05634], [-84.1096, 46.23987], [-84.09756, 46.25512], [-84.11615, 46.2681], [-84.11254, 46.32329], [-84.13451, 46.39218], [-84.11196, 46.50248], [-84.12885, 46.53068], [-84.17723, 46.52753], [-84.1945, 46.54061], [-84.2264, 46.53337], [-84.26351, 46.49508], [-84.29893, 46.49127], [-84.34174, 46.50683], [-84.42101, 46.49853], [-84.4481, 46.48972], [-84.47607, 46.45225], [-84.55635, 46.45974], [-84.85871, 46.88881], [-88.37033, 48.30586], [-89.48837, 48.01412], [-89.57972, 48.00023], [-89.77248, 48.02607], [-89.89974, 47.98109], [-90.07418, 48.11043], [-90.56312, 48.09488], [-90.56444, 48.12184], [-90.75045, 48.09143], [-90.87588, 48.2484], [-91.08016, 48.18096], [-91.25025, 48.08522], [-91.43248, 48.04912], [-91.45829, 48.07454], [-91.58025, 48.04339], [-91.55649, 48.10611], [-91.70451, 48.11805], [-91.71231, 48.19875], [-91.86125, 48.21278], [-91.98929, 48.25409], [-92.05339, 48.35958], [-92.14732, 48.36578], [-92.202, 48.35252], [-92.26662, 48.35651], [-92.30939, 48.31251], [-92.27167, 48.25046], [-92.37185, 48.22259], [-92.48147, 48.36609], [-92.45588, 48.40624], [-92.50712, 48.44921], [-92.65606, 48.43471], [-92.71323, 48.46081], [-92.69927, 48.49573], [-92.62747, 48.50278], [-92.6342, 48.54133], [-92.7287, 48.54005], [-92.94973, 48.60866], [-93.25391, 48.64266], [-93.33946, 48.62787], [-93.3712, 48.60599], [-93.39758, 48.60364], [-93.40693, 48.60948], [-93.44472, 48.59147], [-93.47022, 48.54357], [-93.66382, 48.51845], [-93.79267, 48.51631], [-93.80939, 48.52439], [-93.80676, 48.58232], [-93.83288, 48.62745], [-93.85769, 48.63284], [-94.23215, 48.65202], [-94.25104, 48.65729], [-94.25172, 48.68404], [-94.27153, 48.70232], [-94.4174, 48.71049], [-94.44258, 48.69223], [-94.53826, 48.70216], [-94.54885, 48.71543], [-94.58903, 48.71803], [-94.69335, 48.77883], [-94.69669, 48.80918], [-94.70486, 48.82365], [-94.70087, 48.8339], [-94.687, 48.84077], [-94.75017, 49.09931], [-94.77355, 49.11998], [-94.82487, 49.29483], [-94.8159, 49.32299], [-94.85381, 49.32492], [-94.95681, 49.37035], [-94.99532, 49.36579], [-95.01419, 49.35647], [-95.05825, 49.35311], [-95.12903, 49.37056], [-95.15357, 49.384], [-95.15355, 48.9996], [-123.32163, 49.00419], [-123.0093, 48.83186], [-123.0093, 48.76586], [-123.26565, 48.6959], [-123.15614, 48.35395], [-123.50039, 48.21223], [-125.03842, 48.53282], [-133.98258, 38.06389], [-118.48109, 32.5991], [-117.1243, 32.53427], [-115.88053, 32.63624], [-114.71871, 32.71894], [-114.76736, 32.64094], [-114.80584, 32.62028], [-114.81141, 32.55543], [-114.79524, 32.55731], [-114.82011, 32.49609], [-111.07523, 31.33232], [-108.20979, 31.33316], [-108.20899, 31.78534], [-106.529, 31.784], [-106.52266, 31.77509], [-106.51251, 31.76922], [-106.50962, 31.76155], [-106.50111, 31.75714], [-106.48815, 31.74769], [-106.47298, 31.75054], [-106.46726, 31.75998], [-106.45244, 31.76523], [-106.43419, 31.75478], [-106.41773, 31.75196], [-106.38003, 31.73151], [-106.3718, 31.71165], [-106.34864, 31.69663], [-106.33419, 31.66303], [-106.30305, 31.62154], [-106.28084, 31.56173], [-106.24612, 31.54193], [-106.23711, 31.51262], [-106.20346, 31.46305], [-106.09025, 31.40569], [-106.00363, 31.39181], [-104.77674, 30.4236], [-104.5171, 29.64671], [-104.3969, 29.57105], [-104.39363, 29.55396], [-104.37752, 29.54255], [-103.15787, 28.93865], [-102.60596, 29.8192], [-101.47277, 29.7744], [-101.05686, 29.44738], [-101.01128, 29.36947], [-100.96725, 29.3477], [-100.94579, 29.34523], [-100.94056, 29.33371], [-100.87982, 29.296], [-100.79696, 29.24688], [-100.67294, 29.09744], [-100.63689, 28.90812], [-100.59809, 28.88197], [-100.52313, 28.75598], [-100.5075, 28.74066], [-100.51222, 28.70679], [-100.50029, 28.66117], [-99.55409, 27.61314], [-99.51478, 27.55836], [-99.52955, 27.49747], [-99.50208, 27.50021], [-99.48045, 27.49016], [-99.482, 27.47128], [-99.49744, 27.43746], [-99.53573, 27.30926], [-99.08477, 26.39849], [-99.03053, 26.41249], [-99.00546, 26.3925], [-98.35126, 26.15129], [-98.30491, 26.10475], [-98.27075, 26.09457], [-98.24603, 26.07191], [-97.97017, 26.05232], [-97.95155, 26.0625], [-97.66511, 26.01708], [-97.52025, 25.88518], [-97.49828, 25.89877], [-97.45669, 25.86874], [-97.42511, 25.83969], [-97.37332, 25.83854], [-97.35946, 25.92189], [-97.13927, 25.96583]]]]
22297 wikidata: "Q620634",
22298 nameEn: "Bir Tawil",
22299 groups: ["015", "002"],
22303 type: "MultiPolygon",
22304 coordinates: [[[[33.17563, 22.00405], [33.57251, 21.72406], [33.99686, 21.76784], [34.0765, 22.00501], [33.17563, 22.00405]]]]
22309 wikidata: "Q639185",
22310 nameEn: "Peros Banhos",
22312 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22313 level: "subterritory"
22316 type: "MultiPolygon",
22317 coordinates: [[[[72.12587, -4.02588], [70.1848, -6.37445], [72.09518, -5.61768], [72.12587, -4.02588]]]]
22322 wikidata: "Q644636",
22324 level: "sharedLandform"
22330 wikidata: "Q851132",
22331 nameEn: "New Zealand Outlying Islands",
22333 level: "subcountryGroup"
22339 wikidata: "Q875134",
22340 nameEn: "European Russia",
22342 groups: ["151", "150", "UN"],
22343 callingCodes: ["7"]
22346 type: "MultiPolygon",
22347 coordinates: [[[[18.57853, 55.25302], [19.64312, 54.45423], [19.8038, 54.44203], [20.63871, 54.3706], [21.41123, 54.32395], [22.79705, 54.36264], [22.7253, 54.41732], [22.70208, 54.45312], [22.67788, 54.532], [22.71293, 54.56454], [22.68021, 54.58486], [22.7522, 54.63525], [22.74225, 54.64339], [22.75467, 54.6483], [22.73397, 54.66604], [22.73631, 54.72952], [22.87317, 54.79492], [22.85083, 54.88711], [22.76422, 54.92521], [22.68723, 54.9811], [22.65451, 54.97037], [22.60075, 55.01863], [22.58907, 55.07085], [22.47688, 55.04408], [22.31562, 55.0655], [22.14267, 55.05345], [22.11697, 55.02131], [22.06087, 55.02935], [22.02582, 55.05078], [22.03984, 55.07888], [21.99543, 55.08691], [21.96505, 55.07353], [21.85521, 55.09493], [21.64954, 55.1791], [21.55605, 55.20311], [21.51095, 55.18507], [21.46766, 55.21115], [21.38446, 55.29348], [21.35465, 55.28427], [21.26425, 55.24456], [20.95181, 55.27994], [20.60454, 55.40986], [18.57853, 55.25302]]], [[[26.32936, 60.00121], [26.90044, 59.63819], [27.85643, 59.58538], [28.04187, 59.47017], [28.19061, 59.39962], [28.21137, 59.38058], [28.20537, 59.36491], [28.19284, 59.35791], [28.14215, 59.28934], [28.00689, 59.28351], [27.90911, 59.24353], [27.87978, 59.18097], [27.80482, 59.1116], [27.74429, 58.98351], [27.36366, 58.78381], [27.55489, 58.39525], [27.48541, 58.22615], [27.62393, 58.09462], [27.67282, 57.92627], [27.81841, 57.89244], [27.78526, 57.83963], [27.56689, 57.83356], [27.50171, 57.78842], [27.52615, 57.72843], [27.3746, 57.66834], [27.40393, 57.62125], [27.31919, 57.57672], [27.34698, 57.52242], [27.56832, 57.53728], [27.52453, 57.42826], [27.86101, 57.29402], [27.66511, 56.83921], [27.86101, 56.88204], [28.04768, 56.59004], [28.13526, 56.57989], [28.10069, 56.524], [28.19057, 56.44637], [28.16599, 56.37806], [28.23716, 56.27588], [28.15217, 56.16964], [28.30571, 56.06035], [28.36888, 56.05805], [28.37987, 56.11399], [28.43068, 56.09407], [28.5529, 56.11705], [28.68337, 56.10173], [28.63668, 56.07262], [28.73418, 55.97131], [29.08299, 56.03427], [29.21717, 55.98971], [29.44692, 55.95978], [29.3604, 55.75862], [29.51283, 55.70294], [29.61446, 55.77716], [29.80672, 55.79569], [29.97975, 55.87281], [30.12136, 55.8358], [30.27776, 55.86819], [30.30987, 55.83592], [30.48257, 55.81066], [30.51346, 55.78982], [30.51037, 55.76568], [30.63344, 55.73079], [30.67464, 55.64176], [30.72957, 55.66268], [30.7845, 55.58514], [30.86003, 55.63169], [30.93419, 55.6185], [30.95204, 55.50667], [30.90123, 55.46621], [30.93144, 55.3914], [30.8257, 55.3313], [30.81946, 55.27931], [30.87944, 55.28223], [30.97369, 55.17134], [31.02071, 55.06167], [31.00972, 55.02783], [30.94243, 55.03964], [30.9081, 55.02232], [30.95754, 54.98609], [30.93144, 54.9585], [30.81759, 54.94064], [30.8264, 54.90062], [30.75165, 54.80699], [30.95479, 54.74346], [30.97127, 54.71967], [31.0262, 54.70698], [30.98226, 54.68872], [30.99187, 54.67046], [31.19339, 54.66947], [31.21399, 54.63113], [31.08543, 54.50361], [31.22945, 54.46585], [31.3177, 54.34067], [31.30791, 54.25315], [31.57002, 54.14535], [31.89599, 54.0837], [31.88744, 54.03653], [31.85019, 53.91801], [31.77028, 53.80015], [31.89137, 53.78099], [32.12621, 53.81586], [32.36663, 53.7166], [32.45717, 53.74039], [32.50112, 53.68594], [32.40499, 53.6656], [32.47777, 53.5548], [32.74968, 53.45597], [32.73257, 53.33494], [32.51725, 53.28431], [32.40773, 53.18856], [32.15368, 53.07594], [31.82373, 53.10042], [31.787, 53.18033], [31.62496, 53.22886], [31.56316, 53.19432], [31.40523, 53.21406], [31.36403, 53.13504], [31.3915, 53.09712], [31.33519, 53.08805], [31.32283, 53.04101], [31.24147, 53.031], [31.35667, 52.97854], [31.592, 52.79011], [31.57277, 52.71613], [31.50406, 52.69707], [31.63869, 52.55361], [31.56316, 52.51518], [31.61397, 52.48843], [31.62084, 52.33849], [31.57971, 52.32146], [31.70735, 52.26711], [31.6895, 52.1973], [31.77877, 52.18636], [31.7822, 52.11406], [31.81722, 52.09955], [31.85018, 52.11305], [31.96141, 52.08015], [31.92159, 52.05144], [32.08813, 52.03319], [32.23331, 52.08085], [32.2777, 52.10266], [32.34044, 52.1434], [32.33083, 52.23685], [32.38988, 52.24946], [32.3528, 52.32842], [32.54781, 52.32423], [32.69475, 52.25535], [32.85405, 52.27888], [32.89937, 52.2461], [33.18913, 52.3754], [33.51323, 52.35779], [33.48027, 52.31499], [33.55718, 52.30324], [33.78789, 52.37204], [34.05239, 52.20132], [34.11199, 52.14087], [34.09413, 52.00835], [34.41136, 51.82793], [34.42922, 51.72852], [34.07765, 51.67065], [34.17599, 51.63253], [34.30562, 51.5205], [34.22048, 51.4187], [34.33446, 51.363], [34.23009, 51.26429], [34.31661, 51.23936], [34.38802, 51.2746], [34.6613, 51.25053], [34.6874, 51.18], [34.82472, 51.17483], [34.97304, 51.2342], [35.14058, 51.23162], [35.12685, 51.16191], [35.20375, 51.04723], [35.31774, 51.08434], [35.40837, 51.04119], [35.32598, 50.94524], [35.39307, 50.92145], [35.41367, 50.80227], [35.47704, 50.77274], [35.48116, 50.66405], [35.39464, 50.64751], [35.47463, 50.49247], [35.58003, 50.45117], [35.61711, 50.35707], [35.73659, 50.35489], [35.80388, 50.41356], [35.8926, 50.43829], [36.06893, 50.45205], [36.20763, 50.3943], [36.30101, 50.29088], [36.47817, 50.31457], [36.58371, 50.28563], [36.56655, 50.2413], [36.64571, 50.218], [36.69377, 50.26982], [36.91762, 50.34963], [37.08468, 50.34935], [37.48204, 50.46079], [37.47243, 50.36277], [37.62486, 50.29966], [37.62879, 50.24481], [37.61113, 50.21976], [37.75807, 50.07896], [37.79515, 50.08425], [37.90776, 50.04194], [38.02999, 49.94482], [38.02999, 49.90592], [38.21675, 49.98104], [38.18517, 50.08161], [38.32524, 50.08866], [38.35408, 50.00664], [38.65688, 49.97176], [38.68677, 50.00904], [38.73311, 49.90238], [38.90477, 49.86787], [38.9391, 49.79524], [39.1808, 49.88911], [39.27968, 49.75976], [39.44496, 49.76067], [39.59142, 49.73758], [39.65047, 49.61761], [39.84548, 49.56064], [40.13249, 49.61672], [40.16683, 49.56865], [40.03636, 49.52321], [40.03087, 49.45452], [40.1141, 49.38798], [40.14912, 49.37681], [40.18331, 49.34996], [40.22176, 49.25683], [40.01988, 49.1761], [39.93437, 49.05709], [39.6836, 49.05121], [39.6683, 48.99454], [39.71353, 48.98959], [39.72649, 48.9754], [39.74874, 48.98675], [39.78368, 48.91596], [39.98967, 48.86901], [40.03636, 48.91957], [40.08168, 48.87443], [39.97182, 48.79398], [39.79466, 48.83739], [39.73104, 48.7325], [39.71765, 48.68673], [39.67226, 48.59368], [39.79764, 48.58668], [39.84548, 48.57821], [39.86196, 48.46633], [39.88794, 48.44226], [39.94847, 48.35055], [39.84136, 48.33321], [39.84273, 48.30947], [39.90041, 48.3049], [39.91465, 48.26743], [39.95248, 48.29972], [39.9693, 48.29904], [39.97325, 48.31399], [39.99241, 48.31768], [40.00752, 48.22445], [39.94847, 48.22811], [39.83724, 48.06501], [39.88256, 48.04482], [39.77544, 48.04206], [39.82213, 47.96396], [39.73935, 47.82876], [38.87979, 47.87719], [38.79628, 47.81109], [38.76379, 47.69346], [38.35062, 47.61631], [38.28679, 47.53552], [38.28954, 47.39255], [38.22225, 47.30788], [38.33074, 47.30508], [38.32112, 47.2585], [38.23049, 47.2324], [38.22955, 47.12069], [38.3384, 46.98085], [38.12112, 46.86078], [37.62608, 46.82615], [35.23066, 45.79231], [35.04991, 45.76827], [36.6645, 45.4514], [36.6545, 45.3417], [36.5049, 45.3136], [36.475, 45.2411], [36.4883, 45.0488], [33.5943, 44.03313], [39.81147, 43.06294], [40.0078, 43.38551], [40.00853, 43.40578], [40.01552, 43.42025], [40.01007, 43.42411], [40.03312, 43.44262], [40.04445, 43.47776], [40.10657, 43.57344], [40.65957, 43.56212], [41.64935, 43.22331], [42.40563, 43.23226], [42.66667, 43.13917], [42.75889, 43.19651], [43.03322, 43.08883], [43.0419, 43.02413], [43.81453, 42.74297], [43.73119, 42.62043], [43.95517, 42.55396], [44.54202, 42.75699], [44.70002, 42.74679], [44.80941, 42.61277], [44.88754, 42.74934], [45.15318, 42.70598], [45.36501, 42.55268], [45.78692, 42.48358], [45.61676, 42.20768], [46.42738, 41.91323], [46.5332, 41.87389], [46.58924, 41.80547], [46.75269, 41.8623], [46.8134, 41.76252], [47.00955, 41.63583], [46.99554, 41.59743], [47.03757, 41.55434], [47.10762, 41.59044], [47.34579, 41.27884], [47.49004, 41.26366], [47.54504, 41.20275], [47.62288, 41.22969], [47.75831, 41.19455], [47.87973, 41.21798], [48.07587, 41.49957], [48.22064, 41.51472], [48.2878, 41.56221], [48.40277, 41.60441], [48.42301, 41.65444], [48.55078, 41.77917], [48.5867, 41.84306], [48.80971, 41.95365], [49.2134, 44.84989], [49.88945, 46.04554], [49.32259, 46.26944], [49.16518, 46.38542], [48.54988, 46.56267], [48.51142, 46.69268], [49.01136, 46.72716], [48.52326, 47.4102], [48.45173, 47.40818], [48.15348, 47.74545], [47.64973, 47.76559], [47.41689, 47.83687], [47.38731, 47.68176], [47.12107, 47.83687], [47.11516, 48.27188], [46.49011, 48.43019], [46.78392, 48.95352], [47.00857, 49.04921], [47.04658, 49.19834], [46.78398, 49.34026], [46.9078, 49.86707], [47.18319, 49.93721], [47.34589, 50.09308], [47.30448, 50.30894], [47.58551, 50.47867], [48.10044, 50.09242], [48.24519, 49.86099], [48.42564, 49.82283], [48.68352, 49.89546], [48.90782, 50.02281], [48.57946, 50.63278], [48.86936, 50.61589], [49.12673, 50.78639], [49.41959, 50.85927], [49.39001, 51.09396], [49.76866, 51.11067], [49.97277, 51.2405], [50.26859, 51.28677], [50.59695, 51.61859], [51.26254, 51.68466], [51.301, 51.48799], [51.77431, 51.49536], [51.8246, 51.67916], [52.36119, 51.74161], [52.54329, 51.48444], [53.46165, 51.49445], [53.69299, 51.23466], [54.12248, 51.11542], [54.46331, 50.85554], [54.41894, 50.61214], [54.55797, 50.52006], [54.71476, 50.61214], [54.56685, 51.01958], [54.72067, 51.03261], [55.67774, 50.54508], [56.11398, 50.7471], [56.17906, 50.93204], [57.17302, 51.11253], [57.44221, 50.88354], [57.74986, 50.93017], [57.75578, 51.13852], [58.3208, 51.15151], [58.87974, 50.70852], [59.48928, 50.64216], [59.51886, 50.49937], [59.81172, 50.54451], [60.01288, 50.8163], [60.17262, 50.83312], [60.31914, 50.67705], [60.81833, 50.6629], [61.4431, 50.80679], [61.56889, 51.23679], [61.6813, 51.25716], [61.55114, 51.32746], [61.50677, 51.40687], [60.95655, 51.48615], [60.92401, 51.61124], [60.5424, 51.61675], [60.36787, 51.66815], [60.50986, 51.7964], [60.09867, 51.87135], [59.99809, 51.98263], [59.91279, 52.06924], [60.17253, 52.25814], [60.17516, 52.39457], [59.25033, 52.46803], [59.22409, 52.28437], [58.79644, 52.43392], [58.94336, 53.953], [59.70487, 54.14846], [59.95217, 54.85853], [57.95234, 54.39672], [57.14829, 54.84204], [57.25137, 55.26262], [58.81825, 55.03378], [59.49035, 55.60486], [59.28419, 56.15739], [57.51527, 56.08729], [57.28024, 56.87898], [58.07604, 57.08308], [58.13789, 57.68097], [58.81412, 57.71602], [58.71104, 58.07475], [59.40376, 58.45822], [59.15636, 59.14682], [58.3853, 59.487], [59.50685, 60.91162], [59.36223, 61.3882], [59.61398, 62.44915], [59.24834, 63.01859], [59.80579, 64.13948], [59.63945, 64.78384], [60.74386, 64.95767], [61.98014, 65.72191], [66.1708, 67.61252], [64.18965, 69.94255], [76.13964, 83.37843], [36.85549, 84.09565], [32.07813, 72.01005], [31.59909, 70.16571], [30.84095, 69.80584], [30.95011, 69.54699], [30.52662, 69.54699], [30.16363, 69.65244], [29.97205, 69.41623], [29.27631, 69.2811], [29.26623, 69.13794], [29.0444, 69.0119], [28.91738, 69.04774], [28.45957, 68.91417], [28.78224, 68.86696], [28.43941, 68.53366], [28.62982, 68.19816], [29.34179, 68.06655], [29.66955, 67.79872], [30.02041, 67.67523], [29.91155, 67.51507], [28.9839, 66.94139], [29.91155, 66.13863], [30.16363, 65.66935], [29.97205, 65.70256], [29.74013, 65.64025], [29.84096, 65.56945], [29.68972, 65.31803], [29.61914, 65.23791], [29.8813, 65.22101], [29.84096, 65.1109], [29.61914, 65.05993], [29.68972, 64.80789], [30.05271, 64.79072], [30.12329, 64.64862], [30.01238, 64.57513], [30.06279, 64.35782], [30.4762, 64.25728], [30.55687, 64.09036], [30.25437, 63.83364], [29.98213, 63.75795], [30.49637, 63.46666], [31.23244, 63.22239], [31.29294, 63.09035], [31.58535, 62.91642], [31.38369, 62.66284], [31.10136, 62.43042], [29.01829, 61.17448], [28.82816, 61.1233], [28.47974, 60.93365], [27.77352, 60.52722], [27.71177, 60.3893], [27.44953, 60.22766], [26.32936, 60.00121]]]]
22352 wikidata: "Q1083368",
22353 nameEn: "Mainland Finland",
22355 groups: ["EU", "154", "150", "UN"],
22356 callingCodes: ["358"]
22359 type: "MultiPolygon",
22360 coordinates: [[[[29.12697, 69.69193], [28.36883, 69.81658], [28.32849, 69.88605], [27.97558, 69.99671], [27.95542, 70.0965], [27.57226, 70.06215], [27.05802, 69.92069], [26.64461, 69.96565], [26.40261, 69.91377], [25.96904, 69.68397], [25.69679, 69.27039], [25.75729, 68.99383], [25.61613, 68.89602], [25.42455, 68.90328], [25.12206, 68.78684], [25.10189, 68.63307], [24.93048, 68.61102], [24.90023, 68.55579], [24.74898, 68.65143], [24.18432, 68.73936], [24.02299, 68.81601], [23.781, 68.84514], [23.68017, 68.70276], [23.13064, 68.64684], [22.53321, 68.74393], [22.38367, 68.71561], [22.27276, 68.89514], [21.63833, 69.27485], [21.27827, 69.31281], [21.00732, 69.22755], [20.98641, 69.18809], [21.11099, 69.10291], [21.05775, 69.0356], [20.72171, 69.11874], [20.55258, 69.06069], [20.78802, 69.03087], [20.91658, 68.96764], [20.85104, 68.93142], [20.90649, 68.89696], [21.03001, 68.88969], [22.00429, 68.50692], [22.73028, 68.40881], [23.10336, 68.26551], [23.15377, 68.14759], [23.26469, 68.15134], [23.40081, 68.05545], [23.65793, 67.9497], [23.45627, 67.85297], [23.54701, 67.59306], [23.39577, 67.46974], [23.75372, 67.43688], [23.75372, 67.29914], [23.54701, 67.25435], [23.58735, 67.20752], [23.56214, 67.17038], [23.98563, 66.84149], [23.98059, 66.79585], [23.89488, 66.772], [23.85959, 66.56434], [23.63776, 66.43568], [23.67591, 66.3862], [23.64982, 66.30603], [23.71339, 66.21299], [23.90497, 66.15802], [24.15791, 65.85385], [24.14798, 65.83466], [24.15107, 65.81427], [24.14112, 65.39731], [20.15877, 63.06556], [19.23413, 60.61414], [20.96741, 60.71528], [21.15143, 60.54555], [21.08159, 60.20167], [21.02509, 60.12142], [21.35468, 59.67511], [20.5104, 59.15546], [26.32936, 60.00121], [27.44953, 60.22766], [27.71177, 60.3893], [27.77352, 60.52722], [28.47974, 60.93365], [28.82816, 61.1233], [29.01829, 61.17448], [31.10136, 62.43042], [31.38369, 62.66284], [31.58535, 62.91642], [31.29294, 63.09035], [31.23244, 63.22239], [30.49637, 63.46666], [29.98213, 63.75795], [30.25437, 63.83364], [30.55687, 64.09036], [30.4762, 64.25728], [30.06279, 64.35782], [30.01238, 64.57513], [30.12329, 64.64862], [30.05271, 64.79072], [29.68972, 64.80789], [29.61914, 65.05993], [29.84096, 65.1109], [29.8813, 65.22101], [29.61914, 65.23791], [29.68972, 65.31803], [29.84096, 65.56945], [29.74013, 65.64025], [29.97205, 65.70256], [30.16363, 65.66935], [29.91155, 66.13863], [28.9839, 66.94139], [29.91155, 67.51507], [30.02041, 67.67523], [29.66955, 67.79872], [29.34179, 68.06655], [28.62982, 68.19816], [28.43941, 68.53366], [28.78224, 68.86696], [28.45957, 68.91417], [28.91738, 69.04774], [28.81248, 69.11997], [28.8629, 69.22395], [29.31664, 69.47994], [29.12697, 69.69193]]]]
22365 wikidata: "Q1184963",
22366 nameEn: "Alhucemas Islands",
22368 groups: ["EU", "Q191011", "015", "002", "UN"],
22369 level: "subterritory"
22372 type: "MultiPolygon",
22373 coordinates: [[[[-3.90602, 35.21494], [-3.88372, 35.20767], [-3.89343, 35.22728], [-3.90602, 35.21494]]]]
22378 wikidata: "Q1298289",
22379 nameEn: "Egmont Islands",
22381 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22382 level: "subterritory"
22385 type: "MultiPolygon",
22386 coordinates: [[[[70.1848, -6.37445], [70.67958, -8.2663], [72.17991, -6.68509], [70.1848, -6.37445]]]]
22391 wikidata: "Q1352230",
22392 nameEn: "US Territories",
22394 level: "subcountryGroup"
22400 wikidata: "Q1451600",
22401 nameEn: "Overseas Countries and Territories of the EU",
22409 wikidata: "Q1544253",
22410 nameEn: "Great Chagos Bank",
22412 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22413 level: "subterritory"
22416 type: "MultiPolygon",
22417 coordinates: [[[[70.1848, -6.37445], [72.17991, -6.68509], [73.20573, -5.20727], [70.1848, -6.37445]]]]
22422 wikidata: "Q1585511",
22423 nameEn: "Salomon Atoll",
22425 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22426 level: "subterritory"
22429 type: "MultiPolygon",
22430 coordinates: [[[[72.09518, -5.61768], [73.20573, -5.20727], [72.12587, -4.02588], [72.09518, -5.61768]]]]
22435 wikidata: "Q1681727",
22436 nameEn: "Saint-Paul and Amsterdam",
22438 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22439 level: "subterritory"
22442 type: "MultiPolygon",
22443 coordinates: [[[[76.31747, -42.16264], [80.15867, -36.04977], [71.22311, -38.75287], [76.31747, -42.16264]]]]
22448 wikidata: "Q1901211",
22449 nameEn: "East Malaysia",
22451 groups: ["Q36117", "035", "142", "UN"],
22453 callingCodes: ["60"]
22456 type: "MultiPolygon",
22457 coordinates: [[[[110.90339, 7.52694], [109.82788, 2.86812], [109.62558, 1.99182], [109.53794, 1.91771], [109.57923, 1.80624], [109.66397, 1.79972], [109.66397, 1.60425], [110.35354, 0.98869], [110.49182, 0.88088], [110.62374, 0.873], [111.22979, 1.08326], [111.55434, 0.97864], [111.82846, 0.99349], [111.94553, 1.12016], [112.15679, 1.17004], [112.2127, 1.44135], [112.48648, 1.56516], [113.021, 1.57819], [113.01448, 1.42832], [113.64677, 1.23933], [114.03788, 1.44787], [114.57892, 1.5], [114.80706, 1.92351], [114.80706, 2.21665], [115.1721, 2.49671], [115.11343, 2.82879], [115.53713, 3.14776], [115.58276, 3.93499], [115.90217, 4.37708], [117.25801, 4.35108], [117.47313, 4.18857], [117.67641, 4.16535], [118.06469, 4.16638], [118.93936, 4.09009], [119.52945, 5.35672], [117.98544, 6.27477], [117.93857, 6.89845], [117.17735, 7.52841], [116.79524, 7.43869], [115.02521, 5.35005], [115.16236, 5.01011], [115.15092, 4.87604], [115.20737, 4.8256], [115.27819, 4.63661], [115.2851, 4.42295], [115.36346, 4.33563], [115.31275, 4.30806], [115.09978, 4.39123], [115.07737, 4.53418], [115.04064, 4.63706], [115.02278, 4.74137], [115.02955, 4.82087], [115.05038, 4.90275], [114.99417, 4.88201], [114.96982, 4.81146], [114.88841, 4.81905], [114.8266, 4.75062], [114.77303, 4.72871], [114.83189, 4.42387], [114.88039, 4.4257], [114.78539, 4.12205], [114.64211, 4.00694], [114.49922, 4.13108], [114.4416, 4.27588], [114.32176, 4.2552], [114.32176, 4.34942], [114.26876, 4.49878], [114.15813, 4.57], [114.07448, 4.58441], [114.10166, 4.76112], [110.90339, 7.52694]]]]
22462 wikidata: "Q1973345",
22463 nameEn: "Peninsular Malaysia",
22465 groups: ["035", "142", "UN"],
22467 callingCodes: ["60"]
22470 type: "MultiPolygon",
22471 coordinates: [[[[102.46318, 7.22462], [102.09086, 6.23546], [102.08127, 6.22679], [102.07732, 6.193], [102.09182, 6.14161], [102.01835, 6.05407], [101.99209, 6.04075], [101.97114, 6.01992], [101.9714, 6.00575], [101.94712, 5.98421], [101.92819, 5.85511], [101.91776, 5.84269], [101.89188, 5.8386], [101.80144, 5.74505], [101.75074, 5.79091], [101.69773, 5.75881], [101.58019, 5.93534], [101.25524, 5.78633], [101.25755, 5.71065], [101.14062, 5.61613], [100.98815, 5.79464], [101.02708, 5.91013], [101.087, 5.9193], [101.12388, 6.11411], [101.06165, 6.14161], [101.12618, 6.19431], [101.10313, 6.25617], [100.85884, 6.24929], [100.81045, 6.45086], [100.74822, 6.46231], [100.74361, 6.50811], [100.66986, 6.45086], [100.43027, 6.52389], [100.42351, 6.51762], [100.41791, 6.5189], [100.41152, 6.52299], [100.35413, 6.54932], [100.31929, 6.65413], [100.32607, 6.65933], [100.32671, 6.66526], [100.31884, 6.66423], [100.31618, 6.66781], [100.30828, 6.66462], [100.29651, 6.68439], [100.19511, 6.72559], [100.12, 6.42105], [100.0756, 6.4045], [99.91873, 6.50233], [99.50117, 6.44501], [99.31854, 5.99868], [99.75778, 3.86466], [103.03657, 1.30383], [103.56591, 1.19719], [103.62738, 1.35255], [103.67468, 1.43166], [103.7219, 1.46108], [103.74161, 1.4502], [103.76395, 1.45183], [103.81181, 1.47953], [103.86383, 1.46288], [103.89565, 1.42841], [103.93384, 1.42926], [104.00131, 1.42405], [104.02277, 1.4438], [104.04622, 1.44691], [104.07348, 1.43322], [104.08871, 1.42015], [104.09162, 1.39694], [104.08072, 1.35998], [104.12282, 1.27714], [104.34728, 1.33529], [104.56723, 1.44271], [105.01437, 3.24936], [102.46318, 7.22462]]]]
22476 wikidata: "Q2093907",
22477 nameEn: "Three Kings Islands",
22479 groups: ["Q851132", "053", "009", "UN"],
22483 type: "MultiPolygon",
22484 coordinates: [[[[174.17679, -32.62487], [170.93268, -32.97889], [171.97383, -34.64644], [174.17679, -32.62487]]]]
22489 wikidata: "Q2298216",
22490 nameEn: "Solander Islands",
22492 groups: ["Q851132", "053", "009", "UN"],
22496 type: "MultiPolygon",
22497 coordinates: [[[[167.39068, -46.49187], [166.5534, -46.39484], [166.84561, -46.84889], [167.39068, -46.49187]]]]
22502 wikidata: "Q2872203",
22503 nameEn: "Mainland Australia",
22505 groups: ["053", "009", "UN"],
22506 level: "subcountryGroup",
22508 callingCodes: ["61"]
22511 type: "MultiPolygon",
22512 coordinates: [[[[88.16419, -23.49578], [123.64533, -39.13605], [159.74028, -39.1978], [159.76765, -29.76946], [154.02855, -24.43238], [152.93188, -20.92631], [147.69992, -17.5933], [145.2855, -9.62524], [143.87386, -9.02382], [143.29772, -9.33993], [142.48658, -9.36754], [142.19246, -9.15378], [141.88934, -9.36111], [141.01842, -9.35091], [135.49042, -9.2276], [127.55165, -9.05052], [125.29076, -12.33139], [88.16419, -23.49578]]]]
22517 wikidata: "Q2914565",
22518 nameEn: "Autonomous Regions of Portugal",
22520 level: "subcountryGroup"
22526 wikidata: "Q2915956",
22527 nameEn: "Mainland Portugal",
22529 groups: ["Q12837", "EU", "039", "150", "UN"],
22530 level: "subcountryGroup",
22531 callingCodes: ["351"]
22534 type: "MultiPolygon",
22535 coordinates: [[[[-10.39881, 36.12218], [-7.37282, 36.96896], [-7.39769, 37.16868], [-7.41133, 37.20314], [-7.41854, 37.23813], [-7.43227, 37.25152], [-7.43974, 37.38913], [-7.46878, 37.47127], [-7.51759, 37.56119], [-7.41981, 37.75729], [-7.33441, 37.81193], [-7.27314, 37.90145], [-7.24544, 37.98884], [-7.12648, 38.00296], [-7.10366, 38.04404], [-7.05966, 38.01966], [-7.00375, 38.01914], [-6.93418, 38.21454], [-7.09389, 38.17227], [-7.15581, 38.27597], [-7.32529, 38.44336], [-7.265, 38.61674], [-7.26174, 38.72107], [-7.03848, 38.87221], [-7.051, 38.907], [-6.95211, 39.0243], [-6.97004, 39.07619], [-7.04011, 39.11919], [-7.10692, 39.10275], [-7.14929, 39.11287], [-7.12811, 39.17101], [-7.23566, 39.20132], [-7.23403, 39.27579], [-7.3149, 39.34857], [-7.2927, 39.45847], [-7.49477, 39.58794], [-7.54121, 39.66717], [-7.33507, 39.64569], [-7.24707, 39.66576], [-7.01613, 39.66877], [-6.97492, 39.81488], [-6.91463, 39.86618], [-6.86737, 40.01986], [-6.94233, 40.10716], [-7.00589, 40.12087], [-7.02544, 40.18564], [-7.00426, 40.23169], [-6.86085, 40.26776], [-6.86085, 40.2976], [-6.80218, 40.33239], [-6.78426, 40.36468], [-6.84618, 40.42177], [-6.84944, 40.46394], [-6.7973, 40.51723], [-6.80218, 40.55067], [-6.84292, 40.56801], [-6.79567, 40.65955], [-6.82826, 40.74603], [-6.82337, 40.84472], [-6.79892, 40.84842], [-6.80707, 40.88047], [-6.84292, 40.89771], [-6.8527, 40.93958], [-6.9357, 41.02888], [-6.913, 41.03922], [-6.88843, 41.03027], [-6.84781, 41.02692], [-6.80942, 41.03629], [-6.79241, 41.05397], [-6.75655, 41.10187], [-6.77319, 41.13049], [-6.69711, 41.1858], [-6.68286, 41.21641], [-6.65046, 41.24725], [-6.55937, 41.24417], [-6.38551, 41.35274], [-6.38553, 41.38655], [-6.3306, 41.37677], [-6.26777, 41.48796], [-6.19128, 41.57638], [-6.29863, 41.66432], [-6.44204, 41.68258], [-6.49907, 41.65823], [-6.54633, 41.68623], [-6.56426, 41.74219], [-6.51374, 41.8758], [-6.56752, 41.88429], [-6.5447, 41.94371], [-6.58544, 41.96674], [-6.61967, 41.94008], [-6.75004, 41.94129], [-6.76959, 41.98734], [-6.81196, 41.99097], [-6.82174, 41.94493], [-6.94396, 41.94403], [-6.95537, 41.96553], [-6.98144, 41.9728], [-7.01078, 41.94977], [-7.07596, 41.94977], [-7.08574, 41.97401], [-7.14115, 41.98855], [-7.18549, 41.97515], [-7.18677, 41.88793], [-7.32366, 41.8406], [-7.37092, 41.85031], [-7.42864, 41.80589], [-7.42854, 41.83262], [-7.44759, 41.84451], [-7.45566, 41.86488], [-7.49803, 41.87095], [-7.52737, 41.83939], [-7.62188, 41.83089], [-7.58603, 41.87944], [-7.65774, 41.88308], [-7.69848, 41.90977], [-7.84188, 41.88065], [-7.88055, 41.84571], [-7.88751, 41.92553], [-7.90707, 41.92432], [-7.92336, 41.8758], [-7.9804, 41.87337], [-8.01136, 41.83453], [-8.0961, 41.81024], [-8.16455, 41.81753], [-8.16944, 41.87944], [-8.19551, 41.87459], [-8.2185, 41.91237], [-8.16232, 41.9828], [-8.08796, 42.01398], [-8.08847, 42.05767], [-8.11729, 42.08537], [-8.18178, 42.06436], [-8.19406, 42.12141], [-8.18947, 42.13853], [-8.1986, 42.15402], [-8.22406, 42.1328], [-8.24681, 42.13993], [-8.2732, 42.12396], [-8.29809, 42.106], [-8.32161, 42.10218], [-8.33912, 42.08358], [-8.36353, 42.09065], [-8.38323, 42.07683], [-8.40143, 42.08052], [-8.42512, 42.07199], [-8.44123, 42.08218], [-8.48185, 42.0811], [-8.52837, 42.07658], [-8.5252, 42.06264], [-8.54563, 42.0537], [-8.58086, 42.05147], [-8.59493, 42.05708], [-8.63791, 42.04691], [-8.64626, 42.03668], [-8.65832, 42.02972], [-8.6681, 41.99703], [-8.69071, 41.98862], [-8.7478, 41.96282], [-8.74606, 41.9469], [-8.75712, 41.92833], [-8.81794, 41.90375], [-8.87157, 41.86488], [-11.19304, 41.83075], [-10.39881, 36.12218]]]]
22540 wikidata: "Q3311985",
22541 nameEn: "Guernsey",
22543 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22544 level: "subterritory",
22546 roadSpeedUnit: "mph",
22547 roadHeightUnit: "ft",
22548 callingCodes: ["44 01481"]
22551 type: "MultiPolygon",
22552 coordinates: [[[[-2.49556, 49.79012], [-3.28154, 49.57329], [-2.65349, 49.15373], [-2.36485, 49.48223], [-2.49556, 49.79012]]]]
22557 wikidata: "Q3320166",
22558 nameEn: "Outermost Regions of the EU",
22566 wikidata: "Q3336843",
22567 nameEn: "Countries of the United Kingdom",
22569 level: "subcountryGroup"
22575 wikidata: "Q6736667",
22576 nameEn: "Mainland India",
22578 groups: ["034", "142", "UN"],
22580 callingCodes: ["91"]
22583 type: "MultiPolygon",
22584 coordinates: [[[[89.08044, 21.41871], [89.07114, 22.15335], [88.9367, 22.58527], [88.94614, 22.66941], [88.9151, 22.75228], [88.96713, 22.83346], [88.87063, 22.95235], [88.88327, 23.03885], [88.86377, 23.08759], [88.99148, 23.21134], [88.71133, 23.2492], [88.79254, 23.46028], [88.79351, 23.50535], [88.74841, 23.47361], [88.56507, 23.64044], [88.58087, 23.87105], [88.66189, 23.87607], [88.73743, 23.91751], [88.6976, 24.14703], [88.74841, 24.1959], [88.68801, 24.31464], [88.50934, 24.32474], [88.12296, 24.51301], [88.08786, 24.63232], [88.00683, 24.66477], [88.15515, 24.85806], [88.14004, 24.93529], [88.21832, 24.96642], [88.27325, 24.88796], [88.33917, 24.86803], [88.46277, 25.07468], [88.44766, 25.20149], [88.94067, 25.18534], [89.00463, 25.26583], [89.01105, 25.30303], [88.85278, 25.34679], [88.81296, 25.51546], [88.677, 25.46959], [88.4559, 25.59227], [88.45103, 25.66245], [88.242, 25.80811], [88.13138, 25.78773], [88.08804, 25.91334], [88.16581, 26.0238], [88.1844, 26.14417], [88.34757, 26.22216], [88.35153, 26.29123], [88.51649, 26.35923], [88.48749, 26.45855], [88.36938, 26.48683], [88.35153, 26.45241], [88.33093, 26.48929], [88.41196, 26.63837], [88.4298, 26.54489], [88.62144, 26.46783], [88.69485, 26.38353], [88.67837, 26.26291], [88.78961, 26.31093], [88.85004, 26.23211], [89.05328, 26.2469], [88.91321, 26.37984], [88.92357, 26.40711], [88.95612, 26.4564], [89.08899, 26.38845], [89.15869, 26.13708], [89.35953, 26.0077], [89.53515, 26.00382], [89.57101, 25.9682], [89.63968, 26.22595], [89.70201, 26.15138], [89.73581, 26.15818], [89.77865, 26.08387], [89.77728, 26.04254], [89.86592, 25.93115], [89.80585, 25.82489], [89.84388, 25.70042], [89.86129, 25.61714], [89.81208, 25.37244], [89.84086, 25.31854], [89.83371, 25.29548], [89.87629, 25.28337], [89.90478, 25.31038], [90.1155, 25.22686], [90.40034, 25.1534], [90.65042, 25.17788], [90.87427, 25.15799], [91.25517, 25.20677], [91.63648, 25.12846], [92.0316, 25.1834], [92.33957, 25.07593], [92.39147, 25.01471], [92.49887, 24.88796], [92.38626, 24.86055], [92.25854, 24.9191], [92.15796, 24.54435], [92.11662, 24.38997], [91.96603, 24.3799], [91.89258, 24.14674], [91.82596, 24.22345], [91.76004, 24.23848], [91.73257, 24.14703], [91.65292, 24.22095], [91.63782, 24.1132], [91.55542, 24.08687], [91.37414, 24.10693], [91.35741, 23.99072], [91.29587, 24.0041], [91.22308, 23.89616], [91.25192, 23.83463], [91.15579, 23.6599], [91.28293, 23.37538], [91.36453, 23.06612], [91.40848, 23.07117], [91.4035, 23.27522], [91.46615, 23.2328], [91.54993, 23.01051], [91.61571, 22.93929], [91.7324, 23.00043], [91.81634, 23.08001], [91.76417, 23.26619], [91.84789, 23.42235], [91.95642, 23.47361], [91.95093, 23.73284], [92.04706, 23.64229], [92.15417, 23.73409], [92.26541, 23.70392], [92.38214, 23.28705], [92.37665, 22.9435], [92.5181, 22.71441], [92.60029, 22.1522], [92.56616, 22.13554], [92.60949, 21.97638], [92.67532, 22.03547], [92.70416, 22.16017], [92.86208, 22.05456], [92.89504, 21.95143], [92.93899, 22.02656], [92.99804, 21.98964], [92.99255, 22.05965], [93.04885, 22.20595], [93.15734, 22.18687], [93.14224, 22.24535], [93.19991, 22.25425], [93.18206, 22.43716], [93.13537, 22.45873], [93.11477, 22.54374], [93.134, 22.59573], [93.09417, 22.69459], [93.134, 22.92498], [93.12988, 23.05772], [93.2878, 23.00464], [93.38478, 23.13698], [93.36862, 23.35426], [93.38781, 23.36139], [93.39981, 23.38828], [93.38805, 23.4728], [93.43475, 23.68299], [93.3908, 23.7622], [93.3908, 23.92925], [93.36059, 23.93176], [93.32351, 24.04468], [93.34735, 24.10151], [93.41415, 24.07854], [93.46633, 23.97067], [93.50616, 23.94432], [93.62871, 24.00922], [93.75952, 24.0003], [93.80279, 23.92549], [93.92089, 23.95812], [94.14081, 23.83333], [94.30215, 24.23752], [94.32362, 24.27692], [94.45279, 24.56656], [94.50729, 24.59281], [94.5526, 24.70764], [94.60204, 24.70889], [94.73937, 25.00545], [94.74212, 25.13606], [94.57458, 25.20318], [94.68032, 25.47003], [94.80117, 25.49359], [95.18556, 26.07338], [95.11428, 26.1019], [95.12801, 26.38397], [95.05798, 26.45408], [95.23513, 26.68499], [95.30339, 26.65372], [95.437, 26.7083], [95.81603, 27.01335], [95.93002, 27.04149], [96.04949, 27.19428], [96.15591, 27.24572], [96.40779, 27.29818], [96.55761, 27.29928], [96.73888, 27.36638], [96.88445, 27.25046], [96.85287, 27.2065], [96.89132, 27.17474], [97.14675, 27.09041], [97.17422, 27.14052], [96.91431, 27.45752], [96.90112, 27.62149], [97.29919, 27.92233], [97.35824, 27.87256], [97.38845, 28.01329], [97.35412, 28.06663], [97.31292, 28.06784], [97.34547, 28.21385], [97.1289, 28.3619], [96.98882, 28.32564], [96.88445, 28.39452], [96.85561, 28.4875], [96.6455, 28.61657], [96.48895, 28.42955], [96.40929, 28.51526], [96.61391, 28.72742], [96.3626, 29.10607], [96.20467, 29.02325], [96.18682, 29.11087], [96.31316, 29.18643], [96.05361, 29.38167], [95.84899, 29.31464], [95.75149, 29.32063], [95.72086, 29.20797], [95.50842, 29.13487], [95.41091, 29.13007], [95.3038, 29.13847], [95.26122, 29.07727], [95.2214, 29.10727], [95.11291, 29.09527], [95.0978, 29.14446], [94.81353, 29.17804], [94.69318, 29.31739], [94.2752, 29.11687], [94.35897, 29.01965], [93.72797, 28.68821], [93.44621, 28.67189], [93.18069, 28.50319], [93.14635, 28.37035], [92.93075, 28.25671], [92.67486, 28.15018], [92.65472, 28.07632], [92.73025, 28.05814], [92.7275, 27.98662], [92.42538, 27.80092], [92.32101, 27.79363], [92.27432, 27.89077], [91.87057, 27.7195], [91.84722, 27.76325], [91.6469, 27.76358], [91.55819, 27.6144], [91.65007, 27.48287], [92.01132, 27.47352], [92.12019, 27.27829], [92.04702, 27.26861], [92.03457, 27.07334], [92.11863, 26.893], [92.05523, 26.8692], [91.83181, 26.87318], [91.50067, 26.79223], [90.67715, 26.77215], [90.48504, 26.8594], [90.39271, 26.90704], [90.30402, 26.85098], [90.04535, 26.72422], [89.86124, 26.73307], [89.63369, 26.74402], [89.42349, 26.83727], [89.3901, 26.84225], [89.38319, 26.85963], [89.37913, 26.86224], [89.1926, 26.81329], [89.12825, 26.81661], [89.09554, 26.89089], [88.95807, 26.92668], [88.92301, 26.99286], [88.8714, 26.97488], [88.86984, 27.10937], [88.74219, 27.144], [88.91901, 27.32483], [88.82981, 27.38814], [88.77517, 27.45415], [88.88091, 27.85192], [88.83559, 28.01936], [88.63235, 28.12356], [88.54858, 28.06057], [88.25332, 27.9478], [88.1278, 27.95417], [88.13378, 27.88015], [88.1973, 27.85067], [88.19107, 27.79285], [88.04008, 27.49223], [88.07277, 27.43007], [88.01646, 27.21612], [88.01587, 27.21388], [87.9887, 27.11045], [88.11719, 26.98758], [88.13422, 26.98705], [88.12302, 26.95324], [88.19107, 26.75516], [88.1659, 26.68177], [88.16452, 26.64111], [88.09963, 26.54195], [88.09414, 26.43732], [88.00895, 26.36029], [87.90115, 26.44923], [87.89085, 26.48565], [87.84193, 26.43663], [87.7918, 26.46737], [87.76004, 26.40711], [87.67893, 26.43501], [87.66803, 26.40294], [87.59175, 26.38342], [87.55274, 26.40596], [87.51571, 26.43106], [87.46566, 26.44058], [87.37314, 26.40815], [87.34568, 26.34787], [87.26568, 26.37294], [87.26587, 26.40592], [87.24682, 26.4143], [87.18863, 26.40558], [87.14751, 26.40542], [87.09147, 26.45039], [87.0707, 26.58571], [87.04691, 26.58685], [87.01559, 26.53228], [86.95912, 26.52076], [86.94543, 26.52076], [86.82898, 26.43919], [86.76797, 26.45892], [86.74025, 26.42386], [86.69124, 26.45169], [86.62686, 26.46891], [86.61313, 26.48658], [86.57073, 26.49825], [86.54258, 26.53819], [86.49726, 26.54218], [86.31564, 26.61925], [86.26235, 26.61886], [86.22513, 26.58863], [86.13596, 26.60651], [86.02729, 26.66756], [85.8492, 26.56667], [85.85126, 26.60866], [85.83126, 26.61134], [85.76907, 26.63076], [85.72315, 26.67471], [85.73483, 26.79613], [85.66239, 26.84822], [85.61621, 26.86721], [85.59461, 26.85161], [85.5757, 26.85955], [85.56471, 26.84133], [85.47752, 26.79292], [85.34302, 26.74954], [85.21159, 26.75933], [85.18046, 26.80519], [85.19291, 26.86909], [85.15883, 26.86966], [85.02635, 26.85381], [85.05592, 26.88991], [85.00536, 26.89523], [84.97186, 26.9149], [84.96687, 26.95599], [84.85754, 26.98984], [84.82913, 27.01989], [84.793, 26.9968], [84.64496, 27.04669], [84.69166, 27.21294], [84.62161, 27.33885], [84.29315, 27.39], [84.25735, 27.44941], [84.21376, 27.45218], [84.10791, 27.52399], [84.02229, 27.43836], [83.93306, 27.44939], [83.86182, 27.4241], [83.85595, 27.35797], [83.61288, 27.47013], [83.39495, 27.4798], [83.38872, 27.39276], [83.35136, 27.33885], [83.29999, 27.32778], [83.2673, 27.36235], [83.27197, 27.38309], [83.19413, 27.45632], [82.94938, 27.46036], [82.93261, 27.50328], [82.74119, 27.49838], [82.70378, 27.72122], [82.46405, 27.6716], [82.06554, 27.92222], [81.97214, 27.93322], [81.91223, 27.84995], [81.47867, 28.08303], [81.48179, 28.12148], [81.38683, 28.17638], [81.32923, 28.13521], [81.19847, 28.36284], [81.03471, 28.40054], [80.55142, 28.69182], [80.50575, 28.6706], [80.52443, 28.54897], [80.44504, 28.63098], [80.37188, 28.63371], [80.12125, 28.82346], [80.06957, 28.82763], [80.05743, 28.91479], [80.18085, 29.13649], [80.23178, 29.11626], [80.26602, 29.13938], [80.24112, 29.21414], [80.28626, 29.20327], [80.31428, 29.30784], [80.24322, 29.44299], [80.37939, 29.57098], [80.41858, 29.63581], [80.38428, 29.68513], [80.36803, 29.73865], [80.41554, 29.79451], [80.43458, 29.80466], [80.48997, 29.79566], [80.56247, 29.86661], [80.57179, 29.91422], [80.60226, 29.95732], [80.67076, 29.95732], [80.8778, 30.13384], [80.86673, 30.17321], [80.91143, 30.22173], [80.92547, 30.17193], [81.03953, 30.20059], [80.83343, 30.32023], [80.54504, 30.44936], [80.20721, 30.58541], [79.93255, 30.88288], [79.59884, 30.93943], [79.30694, 31.17357], [79.14016, 31.43403], [79.01931, 31.42817], [78.89344, 31.30481], [78.77898, 31.31209], [78.71032, 31.50197], [78.84516, 31.60631], [78.69933, 31.78723], [78.78036, 31.99478], [78.74404, 32.00384], [78.68754, 32.10256], [78.49609, 32.2762], [78.4645, 32.45367], [78.38897, 32.53938], [78.73916, 32.69438], [78.7831, 32.46873], [78.96713, 32.33655], [78.99322, 32.37948], [79.0979, 32.38051], [79.13174, 32.47766], [79.26768, 32.53277], [79.46562, 32.69668], [79.14016, 33.02545], [79.15252, 33.17156], [78.73636, 33.56521], [78.67599, 33.66445], [78.77349, 33.73871], [78.73367, 34.01121], [78.65657, 34.03195], [78.66225, 34.08858], [78.91769, 34.15452], [78.99802, 34.3027], [79.05364, 34.32482], [78.74465, 34.45174], [78.56475, 34.50835], [78.54964, 34.57283], [78.27781, 34.61484], [78.18435, 34.7998], [78.22692, 34.88771], [78.00033, 35.23954], [78.03466, 35.3785], [78.11664, 35.48022], [77.80532, 35.52058], [77.70232, 35.46244], [77.44277, 35.46132], [76.96624, 35.5932], [76.84539, 35.67356], [76.77323, 35.66062], [76.75475, 35.52617], [76.85088, 35.39754], [76.93465, 35.39866], [77.11796, 35.05419], [76.99251, 34.93349], [76.87193, 34.96906], [76.74514, 34.92488], [76.74377, 34.84039], [76.67648, 34.76371], [76.47186, 34.78965], [76.15463, 34.6429], [76.04614, 34.67566], [75.75438, 34.51827], [75.38009, 34.55021], [75.01479, 34.64629], [74.6663, 34.703], [74.58083, 34.77386], [74.31239, 34.79626], [74.12897, 34.70073], [73.96423, 34.68244], [73.93401, 34.63386], [73.93951, 34.57169], [73.89419, 34.54568], [73.88732, 34.48911], [73.74999, 34.3781], [73.74862, 34.34183], [73.8475, 34.32935], [73.90517, 34.35317], [73.98208, 34.2522], [73.90677, 34.10504], [73.88732, 34.05105], [73.91341, 34.01235], [74.21554, 34.03853], [74.25262, 34.01577], [74.26086, 33.92237], [74.14001, 33.83002], [74.05898, 33.82089], [74.00891, 33.75437], [73.96423, 33.73071], [73.98968, 33.66155], [73.97367, 33.64061], [74.03576, 33.56718], [74.10115, 33.56392], [74.18121, 33.4745], [74.17983, 33.3679], [74.08782, 33.26232], [74.01366, 33.25199], [74.02144, 33.18908], [74.15374, 33.13477], [74.17571, 33.07495], [74.31854, 33.02891], [74.34875, 32.97823], [74.31227, 32.92795], [74.41467, 32.90563], [74.45312, 32.77755], [74.6289, 32.75561], [74.64675, 32.82604], [74.7113, 32.84219], [74.65345, 32.71225], [74.69542, 32.66792], [74.64424, 32.60985], [74.65251, 32.56416], [74.67431, 32.56676], [74.68362, 32.49298], [74.84725, 32.49075], [74.97634, 32.45367], [75.03265, 32.49538], [75.28259, 32.36556], [75.38046, 32.26836], [75.25649, 32.10187], [75.00793, 32.03786], [74.9269, 32.0658], [74.86236, 32.04485], [74.79919, 31.95983], [74.58907, 31.87824], [74.47771, 31.72227], [74.57498, 31.60382], [74.61517, 31.55698], [74.59319, 31.50197], [74.64713, 31.45605], [74.59773, 31.4136], [74.53223, 31.30321], [74.51629, 31.13829], [74.56023, 31.08303], [74.60281, 31.10419], [74.60006, 31.13711], [74.6852, 31.12771], [74.67971, 31.05479], [74.5616, 31.04153], [73.88993, 30.36305], [73.95736, 30.28466], [73.97225, 30.19829], [73.80299, 30.06969], [73.58665, 30.01848], [73.3962, 29.94707], [73.28094, 29.56646], [73.05886, 29.1878], [73.01337, 29.16422], [72.94272, 29.02487], [72.40402, 28.78283], [72.29495, 28.66367], [72.20329, 28.3869], [71.9244, 28.11555], [71.89921, 27.96035], [70.79054, 27.68423], [70.60927, 28.02178], [70.37307, 28.01208], [70.12502, 27.8057], [70.03136, 27.56627], [69.58519, 27.18109], [69.50904, 26.74892], [69.88555, 26.56836], [70.05584, 26.60398], [70.17532, 26.55362], [70.17532, 26.24118], [70.08193, 26.08094], [70.0985, 25.93238], [70.2687, 25.71156], [70.37444, 25.67443], [70.53649, 25.68928], [70.60378, 25.71898], [70.67382, 25.68186], [70.66695, 25.39314], [70.89148, 25.15064], [70.94002, 24.92843], [71.09405, 24.69017], [70.97594, 24.60904], [71.00341, 24.46038], [71.12838, 24.42662], [71.04461, 24.34657], [70.94985, 24.3791], [70.85784, 24.30903], [70.88393, 24.27398], [70.71502, 24.23517], [70.57906, 24.27774], [70.5667, 24.43787], [70.11712, 24.30915], [70.03428, 24.172], [69.73335, 24.17007], [69.59579, 24.29777], [69.29778, 24.28712], [69.19341, 24.25646], [69.07806, 24.29777], [68.97781, 24.26021], [68.90914, 24.33156], [68.7416, 24.31904], [68.74643, 23.97027], [68.39339, 23.96838], [68.20763, 23.85849], [68.11329, 23.53945], [76.59015, 5.591], [79.50447, 8.91876], [79.42124, 9.80115], [80.48418, 10.20786], [89.08044, 21.41871]]]]
22589 wikidata: "Q9143535",
22590 nameEn: "Akrotiri",
22592 groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22593 level: "subterritory",
22595 callingCodes: ["357"]
22598 type: "MultiPolygon",
22599 coordinates: [[[[32.86014, 34.70585], [32.82717, 34.70622], [32.79433, 34.67883], [32.76136, 34.68318], [32.75515, 34.64985], [32.74412, 34.43926], [33.26744, 34.49942], [33.0138, 34.64424], [32.96968, 34.64046], [32.96718, 34.63446], [32.95891, 34.62919], [32.95323, 34.64075], [32.95471, 34.64528], [32.94976, 34.65204], [32.94796, 34.6587], [32.95325, 34.66462], [32.97079, 34.66112], [32.97736, 34.65277], [32.99014, 34.65518], [32.98668, 34.67268], [32.99135, 34.68061], [32.95539, 34.68471], [32.94683, 34.67907], [32.94379, 34.67111], [32.93693, 34.67027], [32.93449, 34.66241], [32.92807, 34.66736], [32.93043, 34.67091], [32.91398, 34.67343], [32.9068, 34.66102], [32.86167, 34.68734], [32.86014, 34.70585]]]]
22604 wikidata: "Q9206745",
22605 nameEn: "Dhekelia",
22607 groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22608 level: "subterritory",
22610 callingCodes: ["357"]
22613 type: "MultiPolygon",
22614 coordinates: [[[[33.70575, 34.97947], [33.83531, 34.73974], [33.98684, 34.76642], [33.90075, 34.96623], [33.86432, 34.97592], [33.84811, 34.97075], [33.83505, 34.98108], [33.85621, 34.98956], [33.85891, 35.001], [33.85216, 35.00579], [33.84045, 35.00616], [33.82875, 35.01685], [33.83055, 35.02865], [33.81524, 35.04192], [33.8012, 35.04786], [33.82051, 35.0667], [33.8355, 35.05777], [33.85261, 35.0574], [33.88367, 35.07877], [33.89485, 35.06873], [33.90247, 35.07686], [33.91299, 35.07579], [33.91789, 35.08688], [33.89853, 35.11377], [33.88737, 35.11408], [33.88943, 35.12007], [33.88561, 35.12449], [33.87224, 35.12293], [33.87622, 35.10457], [33.87097, 35.09389], [33.87479, 35.08881], [33.8541, 35.07201], [33.84168, 35.06823], [33.82067, 35.07826], [33.78581, 35.05104], [33.76106, 35.04253], [33.73824, 35.05321], [33.71482, 35.03722], [33.70209, 35.04882], [33.7161, 35.07279], [33.70861, 35.07644], [33.69095, 35.06237], [33.68474, 35.06602], [33.67742, 35.05963], [33.67678, 35.03866], [33.69938, 35.03123], [33.69731, 35.01754], [33.71514, 35.00294], [33.70639, 34.99303], [33.70575, 34.97947]], [[33.77312, 34.9976], [33.77553, 34.99518], [33.78516, 34.99582], [33.79191, 34.98914], [33.78917, 34.98854], [33.78571, 34.98951], [33.78318, 34.98699], [33.78149, 34.98854], [33.77843, 34.988], [33.7778, 34.98981], [33.76738, 34.99188], [33.76605, 34.99543], [33.75682, 34.99916], [33.75994, 35.00113], [33.77312, 34.9976]], [[33.74144, 35.01053], [33.7343, 35.01178], [33.73781, 35.02181], [33.74265, 35.02329], [33.74983, 35.02274], [33.7492, 35.01319], [33.74144, 35.01053]]]]
22619 wikidata: "Q16390686",
22620 nameEn: "Peninsular Spain",
22622 groups: ["Q12837", "EU", "039", "150", "UN"],
22623 callingCodes: ["34"]
22626 type: "MultiPolygon",
22627 coordinates: [[[[3.75438, 42.33445], [3.17156, 42.43545], [3.11379, 42.43646], [3.10027, 42.42621], [3.08167, 42.42748], [3.03734, 42.47363], [2.96518, 42.46692], [2.94283, 42.48174], [2.92107, 42.4573], [2.88413, 42.45938], [2.86983, 42.46843], [2.85675, 42.45444], [2.84335, 42.45724], [2.77464, 42.41046], [2.75497, 42.42578], [2.72056, 42.42298], [2.65311, 42.38771], [2.6747, 42.33974], [2.57934, 42.35808], [2.55516, 42.35351], [2.54382, 42.33406], [2.48457, 42.33933], [2.43508, 42.37568], [2.43299, 42.39423], [2.38504, 42.39977], [2.25551, 42.43757], [2.20578, 42.41633], [2.16599, 42.42314], [2.12789, 42.41291], [2.11621, 42.38393], [2.06241, 42.35906], [2.00488, 42.35399], [1.96482, 42.37787], [1.9574, 42.42401], [1.94084, 42.43039], [1.94061, 42.43333], [1.94292, 42.44316], [1.93663, 42.45439], [1.88853, 42.4501], [1.83037, 42.48395], [1.76335, 42.48863], [1.72515, 42.50338], [1.70571, 42.48867], [1.66826, 42.50779], [1.65674, 42.47125], [1.58933, 42.46275], [1.57953, 42.44957], [1.55937, 42.45808], [1.55073, 42.43299], [1.5127, 42.42959], [1.44529, 42.43724], [1.43838, 42.47848], [1.41648, 42.48315], [1.46661, 42.50949], [1.44759, 42.54431], [1.41245, 42.53539], [1.4234, 42.55959], [1.44529, 42.56722], [1.42512, 42.58292], [1.44197, 42.60217], [1.35562, 42.71944], [1.15928, 42.71407], [1.0804, 42.78569], [0.98292, 42.78754], [0.96166, 42.80629], [0.93089, 42.79154], [0.711, 42.86372], [0.66121, 42.84021], [0.65421, 42.75872], [0.67873, 42.69458], [0.40214, 42.69779], [0.36251, 42.72282], [0.29407, 42.67431], [0.25336, 42.7174], [0.17569, 42.73424], [-0.02468, 42.68513], [-0.10519, 42.72761], [-0.16141, 42.79535], [-0.17939, 42.78974], [-0.3122, 42.84788], [-0.38833, 42.80132], [-0.41319, 42.80776], [-0.44334, 42.79939], [-0.50863, 42.82713], [-0.55497, 42.77846], [-0.67637, 42.88303], [-0.69837, 42.87945], [-0.72608, 42.89318], [-0.73422, 42.91228], [-0.72037, 42.92541], [-0.75478, 42.96916], [-0.81652, 42.95166], [-0.97133, 42.96239], [-1.00963, 42.99279], [-1.10333, 43.0059], [-1.22881, 43.05534], [-1.25244, 43.04164], [-1.30531, 43.06859], [-1.30052, 43.09581], [-1.27118, 43.11961], [-1.32209, 43.1127], [-1.34419, 43.09665], [-1.35272, 43.02658], [-1.44067, 43.047], [-1.47555, 43.08372], [-1.41562, 43.12815], [-1.3758, 43.24511], [-1.40942, 43.27272], [-1.45289, 43.27049], [-1.50992, 43.29481], [-1.55963, 43.28828], [-1.57674, 43.25269], [-1.61341, 43.25269], [-1.63052, 43.28591], [-1.62481, 43.30726], [-1.69407, 43.31378], [-1.73074, 43.29481], [-1.7397, 43.32979], [-1.75079, 43.3317], [-1.75334, 43.34107], [-1.77068, 43.34396], [-1.78714, 43.35476], [-1.78332, 43.36399], [-1.79319, 43.37497], [-1.77289, 43.38957], [-1.81005, 43.59738], [-10.14298, 44.17365], [-11.19304, 41.83075], [-8.87157, 41.86488], [-8.81794, 41.90375], [-8.75712, 41.92833], [-8.74606, 41.9469], [-8.7478, 41.96282], [-8.69071, 41.98862], [-8.6681, 41.99703], [-8.65832, 42.02972], [-8.64626, 42.03668], [-8.63791, 42.04691], [-8.59493, 42.05708], [-8.58086, 42.05147], [-8.54563, 42.0537], [-8.5252, 42.06264], [-8.52837, 42.07658], [-8.48185, 42.0811], [-8.44123, 42.08218], [-8.42512, 42.07199], [-8.40143, 42.08052], [-8.38323, 42.07683], [-8.36353, 42.09065], [-8.33912, 42.08358], [-8.32161, 42.10218], [-8.29809, 42.106], [-8.2732, 42.12396], [-8.24681, 42.13993], [-8.22406, 42.1328], [-8.1986, 42.15402], [-8.18947, 42.13853], [-8.19406, 42.12141], [-8.18178, 42.06436], [-8.11729, 42.08537], [-8.08847, 42.05767], [-8.08796, 42.01398], [-8.16232, 41.9828], [-8.2185, 41.91237], [-8.19551, 41.87459], [-8.16944, 41.87944], [-8.16455, 41.81753], [-8.0961, 41.81024], [-8.01136, 41.83453], [-7.9804, 41.87337], [-7.92336, 41.8758], [-7.90707, 41.92432], [-7.88751, 41.92553], [-7.88055, 41.84571], [-7.84188, 41.88065], [-7.69848, 41.90977], [-7.65774, 41.88308], [-7.58603, 41.87944], [-7.62188, 41.83089], [-7.52737, 41.83939], [-7.49803, 41.87095], [-7.45566, 41.86488], [-7.44759, 41.84451], [-7.42854, 41.83262], [-7.42864, 41.80589], [-7.37092, 41.85031], [-7.32366, 41.8406], [-7.18677, 41.88793], [-7.18549, 41.97515], [-7.14115, 41.98855], [-7.08574, 41.97401], [-7.07596, 41.94977], [-7.01078, 41.94977], [-6.98144, 41.9728], [-6.95537, 41.96553], [-6.94396, 41.94403], [-6.82174, 41.94493], [-6.81196, 41.99097], [-6.76959, 41.98734], [-6.75004, 41.94129], [-6.61967, 41.94008], [-6.58544, 41.96674], [-6.5447, 41.94371], [-6.56752, 41.88429], [-6.51374, 41.8758], [-6.56426, 41.74219], [-6.54633, 41.68623], [-6.49907, 41.65823], [-6.44204, 41.68258], [-6.29863, 41.66432], [-6.19128, 41.57638], [-6.26777, 41.48796], [-6.3306, 41.37677], [-6.38553, 41.38655], [-6.38551, 41.35274], [-6.55937, 41.24417], [-6.65046, 41.24725], [-6.68286, 41.21641], [-6.69711, 41.1858], [-6.77319, 41.13049], [-6.75655, 41.10187], [-6.79241, 41.05397], [-6.80942, 41.03629], [-6.84781, 41.02692], [-6.88843, 41.03027], [-6.913, 41.03922], [-6.9357, 41.02888], [-6.8527, 40.93958], [-6.84292, 40.89771], [-6.80707, 40.88047], [-6.79892, 40.84842], [-6.82337, 40.84472], [-6.82826, 40.74603], [-6.79567, 40.65955], [-6.84292, 40.56801], [-6.80218, 40.55067], [-6.7973, 40.51723], [-6.84944, 40.46394], [-6.84618, 40.42177], [-6.78426, 40.36468], [-6.80218, 40.33239], [-6.86085, 40.2976], [-6.86085, 40.26776], [-7.00426, 40.23169], [-7.02544, 40.18564], [-7.00589, 40.12087], [-6.94233, 40.10716], [-6.86737, 40.01986], [-6.91463, 39.86618], [-6.97492, 39.81488], [-7.01613, 39.66877], [-7.24707, 39.66576], [-7.33507, 39.64569], [-7.54121, 39.66717], [-7.49477, 39.58794], [-7.2927, 39.45847], [-7.3149, 39.34857], [-7.23403, 39.27579], [-7.23566, 39.20132], [-7.12811, 39.17101], [-7.14929, 39.11287], [-7.10692, 39.10275], [-7.04011, 39.11919], [-6.97004, 39.07619], [-6.95211, 39.0243], [-7.051, 38.907], [-7.03848, 38.87221], [-7.26174, 38.72107], [-7.265, 38.61674], [-7.32529, 38.44336], [-7.15581, 38.27597], [-7.09389, 38.17227], [-6.93418, 38.21454], [-7.00375, 38.01914], [-7.05966, 38.01966], [-7.10366, 38.04404], [-7.12648, 38.00296], [-7.24544, 37.98884], [-7.27314, 37.90145], [-7.33441, 37.81193], [-7.41981, 37.75729], [-7.51759, 37.56119], [-7.46878, 37.47127], [-7.43974, 37.38913], [-7.43227, 37.25152], [-7.41854, 37.23813], [-7.41133, 37.20314], [-7.39769, 37.16868], [-7.37282, 36.96896], [-7.2725, 35.73269], [-5.10878, 36.05227], [-2.27707, 35.35051], [3.75438, 42.33445]], [[-5.27801, 36.14942], [-5.34064, 36.03744], [-5.40526, 36.15488], [-5.34536, 36.15501], [-5.33822, 36.15272], [-5.27801, 36.14942]]], [[[1.99838, 42.44682], [2.01564, 42.45171], [1.99216, 42.46208], [1.98579, 42.47486], [1.99766, 42.4858], [1.98916, 42.49351], [1.98022, 42.49569], [1.97697, 42.48568], [1.97227, 42.48487], [1.97003, 42.48081], [1.96215, 42.47854], [1.95606, 42.45785], [1.96125, 42.45364], [1.98378, 42.44697], [1.99838, 42.44682]]]]
22632 wikidata: "Q98059339",
22633 nameEn: "Mainland Norway",
22635 groups: ["154", "150", "UN"],
22636 callingCodes: ["47"]
22639 type: "MultiPolygon",
22640 coordinates: [[[[10.40861, 58.38489], [10.64958, 58.89391], [11.08911, 58.98745], [11.15367, 59.07862], [11.34459, 59.11672], [11.4601, 58.99022], [11.45199, 58.89604], [11.65732, 58.90177], [11.8213, 59.24985], [11.69297, 59.59442], [11.92112, 59.69531], [11.87121, 59.86039], [12.15641, 59.8926], [12.36317, 59.99259], [12.52003, 60.13846], [12.59133, 60.50559], [12.2277, 61.02442], [12.69115, 61.06584], [12.86939, 61.35427], [12.57707, 61.56547], [12.40595, 61.57226], [12.14746, 61.7147], [12.29187, 62.25699], [12.07085, 62.6297], [12.19919, 63.00104], [11.98529, 63.27487], [12.19919, 63.47935], [12.14928, 63.59373], [12.74105, 64.02171], [13.23411, 64.09087], [13.98222, 64.00953], [14.16051, 64.18725], [14.11117, 64.46674], [13.64276, 64.58402], [14.50926, 65.31786], [14.53778, 66.12399], [15.05113, 66.15572], [15.49318, 66.28509], [15.37197, 66.48217], [16.35589, 67.06419], [16.39154, 67.21653], [16.09922, 67.4364], [16.12774, 67.52106], [16.38441, 67.52923], [16.7409, 67.91037], [17.30416, 68.11591], [17.90787, 67.96537], [18.13836, 68.20874], [18.1241, 68.53721], [18.39503, 68.58672], [18.63032, 68.50849], [18.97255, 68.52416], [19.93508, 68.35911], [20.22027, 68.48759], [19.95647, 68.55546], [20.22027, 68.67246], [20.33435, 68.80174], [20.28444, 68.93283], [20.0695, 69.04469], [20.55258, 69.06069], [20.72171, 69.11874], [21.05775, 69.0356], [21.11099, 69.10291], [20.98641, 69.18809], [21.00732, 69.22755], [21.27827, 69.31281], [21.63833, 69.27485], [22.27276, 68.89514], [22.38367, 68.71561], [22.53321, 68.74393], [23.13064, 68.64684], [23.68017, 68.70276], [23.781, 68.84514], [24.02299, 68.81601], [24.18432, 68.73936], [24.74898, 68.65143], [24.90023, 68.55579], [24.93048, 68.61102], [25.10189, 68.63307], [25.12206, 68.78684], [25.42455, 68.90328], [25.61613, 68.89602], [25.75729, 68.99383], [25.69679, 69.27039], [25.96904, 69.68397], [26.40261, 69.91377], [26.64461, 69.96565], [27.05802, 69.92069], [27.57226, 70.06215], [27.95542, 70.0965], [27.97558, 69.99671], [28.32849, 69.88605], [28.36883, 69.81658], [29.12697, 69.69193], [29.31664, 69.47994], [28.8629, 69.22395], [28.81248, 69.11997], [28.91738, 69.04774], [29.0444, 69.0119], [29.26623, 69.13794], [29.27631, 69.2811], [29.97205, 69.41623], [30.16363, 69.65244], [30.52662, 69.54699], [30.95011, 69.54699], [30.84095, 69.80584], [31.59909, 70.16571], [32.07813, 72.01005], [-11.60274, 67.73467], [7.28637, 57.35913], [10.40861, 58.38489]]]]
22645 wikidata: "Q98543636",
22646 nameEn: "Mainland Ecuador",
22648 groups: ["005", "419", "019", "UN"],
22649 callingCodes: ["593"]
22652 type: "MultiPolygon",
22653 coordinates: [[[[-84.52388, -3.36941], [-80.30602, -3.39149], [-80.20647, -3.431], [-80.24123, -3.46124], [-80.24586, -3.48677], [-80.23651, -3.48652], [-80.22629, -3.501], [-80.20535, -3.51667], [-80.21642, -3.5888], [-80.19848, -3.59249], [-80.18741, -3.63994], [-80.19926, -3.68894], [-80.13232, -3.90317], [-80.46386, -4.01342], [-80.4822, -4.05477], [-80.45023, -4.20938], [-80.32114, -4.21323], [-80.46386, -4.41516], [-80.39256, -4.48269], [-80.13945, -4.29786], [-79.79722, -4.47558], [-79.59402, -4.46848], [-79.26248, -4.95167], [-79.1162, -4.97774], [-79.01659, -5.01481], [-78.85149, -4.66795], [-78.68394, -4.60754], [-78.34362, -3.38633], [-78.24589, -3.39907], [-78.22642, -3.51113], [-78.14324, -3.47653], [-78.19369, -3.36431], [-77.94147, -3.05454], [-76.6324, -2.58397], [-76.05203, -2.12179], [-75.57429, -1.55961], [-75.3872, -0.9374], [-75.22862, -0.95588], [-75.22862, -0.60048], [-75.53615, -0.19213], [-75.60169, -0.18708], [-75.61997, -0.10012], [-75.40192, -0.17196], [-75.25764, -0.11943], [-75.82927, 0.09578], [-76.23441, 0.42294], [-76.41215, 0.38228], [-76.4094, 0.24015], [-76.89177, 0.24736], [-77.52001, 0.40782], [-77.49984, 0.64476], [-77.67815, 0.73863], [-77.66416, 0.81604], [-77.68613, 0.83029], [-77.7148, 0.85003], [-77.85677, 0.80197], [-78.42749, 1.15389], [-78.87137, 1.47457], [-82.12561, 4.00341], [-84.52388, -3.36941]]]]
22661 aliases: ["Earth", "Planet"],
22679 nameEn: "North America",
22688 nameEn: "South America",
22689 level: "intermediateRegion"
22706 nameEn: "Western Africa",
22707 level: "intermediateRegion"
22714 wikidata: "Q27611",
22715 nameEn: "Central America",
22716 level: "intermediateRegion"
22723 wikidata: "Q27407",
22724 nameEn: "Eastern Africa",
22725 level: "intermediateRegion"
22732 wikidata: "Q27381",
22733 nameEn: "Northern Africa",
22741 wikidata: "Q27433",
22742 nameEn: "Middle Africa",
22743 level: "intermediateRegion"
22750 wikidata: "Q27394",
22751 nameEn: "Southern Africa",
22752 level: "intermediateRegion"
22760 nameEn: "Americas",
22768 wikidata: "Q2017699",
22769 nameEn: "Northern America",
22777 wikidata: "Q664609",
22778 nameEn: "Caribbean",
22779 level: "intermediateRegion"
22786 wikidata: "Q27231",
22787 nameEn: "Eastern Asia",
22795 wikidata: "Q771405",
22796 nameEn: "Southern Asia",
22804 wikidata: "Q11708",
22805 nameEn: "South-eastern Asia",
22813 wikidata: "Q27449",
22814 nameEn: "Southern Europe",
22822 wikidata: "Q45256",
22823 nameEn: "Australia and New Zealand",
22824 aliases: ["Australasia"],
22832 wikidata: "Q37394",
22833 nameEn: "Melanesia",
22841 wikidata: "Q3359409",
22842 nameEn: "Micronesia",
22850 wikidata: "Q35942",
22851 nameEn: "Polynesia",
22868 wikidata: "Q27275",
22869 nameEn: "Central Asia",
22877 wikidata: "Q27293",
22878 nameEn: "Western Asia",
22895 wikidata: "Q27468",
22896 nameEn: "Eastern Europe",
22904 wikidata: "Q27479",
22905 nameEn: "Northern Europe",
22913 wikidata: "Q27496",
22914 nameEn: "Western Europe",
22922 wikidata: "Q132959",
22923 nameEn: "Sub-Saharan Africa",
22931 wikidata: "Q72829598",
22932 nameEn: "Latin America and the Caribbean",
22940 wikidata: "Q3405693",
22943 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22944 level: "subterritory",
22946 roadSpeedUnit: "mph",
22947 roadHeightUnit: "ft",
22948 callingCodes: ["44 01481"]
22951 type: "MultiPolygon",
22952 coordinates: [[[[-2.36485, 49.48223], [-2.65349, 49.15373], [-2.09454, 49.46288], [-2.36485, 49.48223]]]]
22958 wikidata: "Q42314",
22959 nameEn: "Channel Islands",
22960 level: "intermediateRegion"
22968 wikidata: "Q46197",
22969 nameEn: "Ascension Island",
22970 aliases: ["SH-AC"],
22972 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
22973 isoStatus: "excRes",
22975 roadSpeedUnit: "mph",
22976 roadHeightUnit: "ft",
22977 callingCodes: ["247"]
22980 type: "MultiPolygon",
22981 coordinates: [[[[-14.82771, -8.70814], [-13.33271, -8.07391], [-14.91926, -6.63386], [-14.82771, -8.70814]]]]
22991 groups: ["Q12837", "039", "150", "UN"],
22992 callingCodes: ["376"]
22995 type: "MultiPolygon",
22996 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]]]]
23005 nameEn: "United Arab Emirates",
23006 groups: ["145", "142", "UN"],
23007 callingCodes: ["971"]
23010 type: "MultiPolygon",
23011 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]]]]
23020 nameEn: "Afghanistan",
23021 groups: ["034", "142", "UN"],
23022 callingCodes: ["93"]
23025 type: "MultiPolygon",
23026 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]]]]
23035 nameEn: "Antigua and Barbuda",
23036 groups: ["029", "003", "419", "019", "UN"],
23038 roadSpeedUnit: "mph",
23039 callingCodes: ["1 268"]
23042 type: "MultiPolygon",
23043 coordinates: [[[[-61.66959, 18.6782], [-62.58307, 16.68909], [-62.1023, 16.97277], [-61.23098, 16.62484], [-61.66959, 18.6782]]]]
23051 wikidata: "Q25228",
23052 nameEn: "Anguilla",
23054 groups: ["BOTS", "029", "003", "419", "019", "UN"],
23056 roadSpeedUnit: "mph",
23057 callingCodes: ["1 264"]
23060 type: "MultiPolygon",
23061 coordinates: [[[[-63.79029, 19.11219], [-63.35989, 18.06012], [-62.62718, 18.26185], [-63.79029, 19.11219]]]]
23071 groups: ["039", "150", "UN"],
23072 callingCodes: ["355"]
23075 type: "MultiPolygon",
23076 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]]]]
23086 groups: ["145", "142", "UN"],
23087 callingCodes: ["374"]
23090 type: "MultiPolygon",
23091 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]]]]
23101 groups: ["017", "202", "002", "UN"],
23102 callingCodes: ["244"]
23105 type: "MultiPolygon",
23106 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]]]]
23115 nameEn: "Antarctica",
23117 callingCodes: ["672"]
23120 type: "MultiPolygon",
23121 coordinates: [[[[180, -60], [-180, -60], [-180, -90], [180, -90], [180, -60]]]]
23130 nameEn: "Argentina",
23132 groups: ["005", "419", "019", "UN"],
23133 callingCodes: ["54"]
23136 type: "MultiPolygon",
23137 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]]]]
23145 wikidata: "Q16641",
23146 nameEn: "American Samoa",
23147 aliases: ["US-AS"],
23149 groups: ["Q1352230", "061", "009", "UN"],
23150 roadSpeedUnit: "mph",
23151 roadHeightUnit: "ft",
23152 callingCodes: ["1 684"]
23155 type: "MultiPolygon",
23156 coordinates: [[[[-171.39864, -10.21587], [-170.99605, -15.1275], [-166.32598, -15.26169], [-171.39864, -10.21587]]]]
23166 groups: ["EU", "155", "150", "UN"],
23167 callingCodes: ["43"]
23170 type: "MultiPolygon",
23171 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]]]]
23180 nameEn: "Australia"
23189 wikidata: "Q21203",
23191 aliases: ["NL-AW"],
23193 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23194 callingCodes: ["297"]
23197 type: "MultiPolygon",
23198 coordinates: [[[[-70.00823, 12.98375], [-70.35625, 12.58277], [-69.60231, 12.17], [-70.00823, 12.98375]]]]
23207 nameEn: "\xC5land Islands",
23209 groups: ["EU", "154", "150", "UN"],
23210 callingCodes: ["358 18", "358 457"]
23213 type: "MultiPolygon",
23214 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]]]]
23223 nameEn: "Azerbaijan",
23224 groups: ["145", "142", "UN"],
23225 callingCodes: ["994"]
23228 type: "MultiPolygon",
23229 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]]]]
23238 nameEn: "Bosnia and Herzegovina",
23239 groups: ["039", "150", "UN"],
23240 callingCodes: ["387"]
23243 type: "MultiPolygon",
23244 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]]]]
23253 nameEn: "Barbados",
23254 groups: ["029", "003", "419", "019", "UN"],
23256 callingCodes: ["1 246"]
23259 type: "MultiPolygon",
23260 coordinates: [[[[-58.56442, 13.24471], [-59.80731, 13.87556], [-59.82929, 12.70644], [-58.56442, 13.24471]]]]
23269 nameEn: "Bangladesh",
23270 groups: ["034", "142", "UN"],
23272 callingCodes: ["880"]
23275 type: "MultiPolygon",
23276 coordinates: [[[[89.15869, 26.13708], [89.08899, 26.38845], [88.95612, 26.4564], [88.92357, 26.40711], [88.91321, 26.37984], [89.05328, 26.2469], [88.85004, 26.23211], [88.78961, 26.31093], [88.67837, 26.26291], [88.69485, 26.38353], [88.62144, 26.46783], [88.4298, 26.54489], [88.41196, 26.63837], [88.33093, 26.48929], [88.35153, 26.45241], [88.36938, 26.48683], [88.48749, 26.45855], [88.51649, 26.35923], [88.35153, 26.29123], [88.34757, 26.22216], [88.1844, 26.14417], [88.16581, 26.0238], [88.08804, 25.91334], [88.13138, 25.78773], [88.242, 25.80811], [88.45103, 25.66245], [88.4559, 25.59227], [88.677, 25.46959], [88.81296, 25.51546], [88.85278, 25.34679], [89.01105, 25.30303], [89.00463, 25.26583], [88.94067, 25.18534], [88.44766, 25.20149], [88.46277, 25.07468], [88.33917, 24.86803], [88.27325, 24.88796], [88.21832, 24.96642], [88.14004, 24.93529], [88.15515, 24.85806], [88.00683, 24.66477], [88.08786, 24.63232], [88.12296, 24.51301], [88.50934, 24.32474], [88.68801, 24.31464], [88.74841, 24.1959], [88.6976, 24.14703], [88.73743, 23.91751], [88.66189, 23.87607], [88.58087, 23.87105], [88.56507, 23.64044], [88.74841, 23.47361], [88.79351, 23.50535], [88.79254, 23.46028], [88.71133, 23.2492], [88.99148, 23.21134], [88.86377, 23.08759], [88.88327, 23.03885], [88.87063, 22.95235], [88.96713, 22.83346], [88.9151, 22.75228], [88.94614, 22.66941], [88.9367, 22.58527], [89.07114, 22.15335], [89.08044, 21.41871], [92.47409, 20.38654], [92.26071, 21.05697], [92.17752, 21.17445], [92.20087, 21.337], [92.37939, 21.47764], [92.43158, 21.37025], [92.55105, 21.3856], [92.60187, 21.24615], [92.68152, 21.28454], [92.59775, 21.6092], [92.62187, 21.87037], [92.60949, 21.97638], [92.56616, 22.13554], [92.60029, 22.1522], [92.5181, 22.71441], [92.37665, 22.9435], [92.38214, 23.28705], [92.26541, 23.70392], [92.15417, 23.73409], [92.04706, 23.64229], [91.95093, 23.73284], [91.95642, 23.47361], [91.84789, 23.42235], [91.76417, 23.26619], [91.81634, 23.08001], [91.7324, 23.00043], [91.61571, 22.93929], [91.54993, 23.01051], [91.46615, 23.2328], [91.4035, 23.27522], [91.40848, 23.07117], [91.36453, 23.06612], [91.28293, 23.37538], [91.15579, 23.6599], [91.25192, 23.83463], [91.22308, 23.89616], [91.29587, 24.0041], [91.35741, 23.99072], [91.37414, 24.10693], [91.55542, 24.08687], [91.63782, 24.1132], [91.65292, 24.22095], [91.73257, 24.14703], [91.76004, 24.23848], [91.82596, 24.22345], [91.89258, 24.14674], [91.96603, 24.3799], [92.11662, 24.38997], [92.15796, 24.54435], [92.25854, 24.9191], [92.38626, 24.86055], [92.49887, 24.88796], [92.39147, 25.01471], [92.33957, 25.07593], [92.0316, 25.1834], [91.63648, 25.12846], [91.25517, 25.20677], [90.87427, 25.15799], [90.65042, 25.17788], [90.40034, 25.1534], [90.1155, 25.22686], [89.90478, 25.31038], [89.87629, 25.28337], [89.83371, 25.29548], [89.84086, 25.31854], [89.81208, 25.37244], [89.86129, 25.61714], [89.84388, 25.70042], [89.80585, 25.82489], [89.86592, 25.93115], [89.77728, 26.04254], [89.77865, 26.08387], [89.73581, 26.15818], [89.70201, 26.15138], [89.63968, 26.22595], [89.57101, 25.9682], [89.53515, 26.00382], [89.35953, 26.0077], [89.15869, 26.13708]]]]
23286 groups: ["EU", "155", "150", "UN"],
23287 callingCodes: ["32"]
23290 type: "MultiPolygon",
23291 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]]]]
23300 nameEn: "Burkina Faso",
23301 groups: ["011", "202", "002", "UN"],
23302 callingCodes: ["226"]
23305 type: "MultiPolygon",
23306 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]]]]
23315 nameEn: "Bulgaria",
23316 groups: ["EU", "151", "150", "UN"],
23317 callingCodes: ["359"]
23320 type: "MultiPolygon",
23321 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]]]]
23331 groups: ["145", "142", "UN"],
23332 callingCodes: ["973"]
23335 type: "MultiPolygon",
23336 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]]]]
23346 groups: ["014", "202", "002", "UN"],
23347 callingCodes: ["257"]
23350 type: "MultiPolygon",
23351 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]]]]
23362 groups: ["011", "202", "002", "UN"],
23363 callingCodes: ["229"]
23366 type: "MultiPolygon",
23367 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]]]]
23375 wikidata: "Q25362",
23376 nameEn: "Saint-Barth\xE9lemy",
23378 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23379 callingCodes: ["590"]
23382 type: "MultiPolygon",
23383 coordinates: [[[[-62.62718, 18.26185], [-63.1055, 17.86651], [-62.34423, 17.49165], [-62.62718, 18.26185]]]]
23391 wikidata: "Q23635",
23394 groups: ["BOTS", "021", "003", "019", "UN"],
23396 callingCodes: ["1 441"]
23399 type: "MultiPolygon",
23400 coordinates: [[[[-63.20987, 32.6953], [-65.31453, 32.68437], [-65.63955, 31.43417], [-63.20987, 32.6953]]]]
23410 groups: ["Q36117", "035", "142", "UN"],
23412 callingCodes: ["673"]
23415 type: "MultiPolygon",
23416 coordinates: [[[[115.16236, 5.01011], [115.02521, 5.35005], [114.10166, 4.76112], [114.07448, 4.58441], [114.15813, 4.57], [114.26876, 4.49878], [114.32176, 4.34942], [114.32176, 4.2552], [114.4416, 4.27588], [114.49922, 4.13108], [114.64211, 4.00694], [114.78539, 4.12205], [114.88039, 4.4257], [114.83189, 4.42387], [114.77303, 4.72871], [114.8266, 4.75062], [114.88841, 4.81905], [114.96982, 4.81146], [114.99417, 4.88201], [115.05038, 4.90275], [115.02955, 4.82087], [115.02278, 4.74137], [115.04064, 4.63706], [115.07737, 4.53418], [115.09978, 4.39123], [115.31275, 4.30806], [115.36346, 4.33563], [115.2851, 4.42295], [115.27819, 4.63661], [115.20737, 4.8256], [115.15092, 4.87604], [115.16236, 5.01011]]]]
23426 groups: ["005", "419", "019", "UN"],
23427 callingCodes: ["591"]
23430 type: "MultiPolygon",
23431 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]]]]
23439 wikidata: "Q27561",
23440 nameEn: "Caribbean Netherlands",
23452 groups: ["005", "419", "019", "UN"],
23453 callingCodes: ["55"]
23456 type: "MultiPolygon",
23457 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]]]]
23466 nameEn: "The Bahamas",
23467 groups: ["029", "003", "419", "019", "UN"],
23469 roadSpeedUnit: "mph",
23470 callingCodes: ["1 242"]
23473 type: "MultiPolygon",
23474 coordinates: [[[[-72.98446, 20.4801], [-71.70065, 25.7637], [-78.91214, 27.76553], [-80.65727, 23.71953], [-72.98446, 20.4801]]]]
23484 groups: ["034", "142", "UN"],
23486 callingCodes: ["975"]
23489 type: "MultiPolygon",
23490 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]]]]
23498 wikidata: "Q23408",
23499 nameEn: "Bouvet Island",
23501 groups: ["005", "419", "019", "UN"]
23504 type: "MultiPolygon",
23505 coordinates: [[[[4.54042, -54.0949], [2.28941, -54.13089], [3.35353, -55.17558], [4.54042, -54.0949]]]]
23514 nameEn: "Botswana",
23515 groups: ["018", "202", "002", "UN"],
23517 callingCodes: ["267"]
23520 type: "MultiPolygon",
23521 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]]]]
23531 groups: ["151", "150", "UN"],
23532 callingCodes: ["375"]
23535 type: "MultiPolygon",
23536 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]]]]
23546 groups: ["013", "003", "419", "019", "UN"],
23547 roadSpeedUnit: "mph",
23548 callingCodes: ["501"]
23551 type: "MultiPolygon",
23552 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]]]]
23562 groups: ["021", "003", "019", "UN"],
23563 callingCodes: ["1"]
23566 type: "MultiPolygon",
23567 coordinates: [[[[-67.20349, 45.1722], [-67.19603, 45.16771], [-67.15965, 45.16179], [-67.11316, 45.11176], [-67.0216, 44.95333], [-66.96824, 44.90965], [-66.98249, 44.87071], [-66.96824, 44.83078], [-66.93432, 44.82597], [-67.16117, 44.20069], [-61.98255, 37.34815], [-56.27503, 47.39728], [-53.12387, 41.40385], [-46.37635, 57.3249], [-77.52957, 77.23408], [-68.21821, 80.48551], [-49.33696, 84.57952], [-140.97446, 84.39275], [-141.00116, 60.30648], [-140.5227, 60.22077], [-140.45648, 60.30919], [-139.98024, 60.18027], [-139.68991, 60.33693], [-139.05831, 60.35205], [-139.20603, 60.08896], [-139.05365, 59.99655], [-138.71149, 59.90728], [-138.62145, 59.76431], [-137.60623, 59.24465], [-137.4925, 58.89415], [-136.82619, 59.16198], [-136.52365, 59.16752], [-136.47323, 59.46617], [-136.33727, 59.44466], [-136.22381, 59.55526], [-136.31566, 59.59083], [-135.48007, 59.79937], [-135.03069, 59.56208], [-135.00267, 59.28745], [-134.7047, 59.2458], [-134.55699, 59.1297], [-134.48059, 59.13231], [-134.27175, 58.8634], [-133.84645, 58.73543], [-133.38523, 58.42773], [-131.8271, 56.62247], [-130.77769, 56.36185], [-130.33965, 56.10849], [-130.10173, 56.12178], [-130.00093, 56.00325], [-130.00857, 55.91344], [-130.15373, 55.74895], [-129.97513, 55.28029], [-130.08035, 55.21556], [-130.18765, 55.07744], [-130.27203, 54.97174], [-130.44184, 54.85377], [-130.64499, 54.76912], [-130.61931, 54.70835], [-133.92876, 54.62289], [-133.36909, 48.51151], [-125.03842, 48.53282], [-123.50039, 48.21223], [-123.15614, 48.35395], [-123.26565, 48.6959], [-123.0093, 48.76586], [-123.0093, 48.83186], [-123.32163, 49.00419], [-95.15355, 48.9996], [-95.15357, 49.384], [-95.12903, 49.37056], [-95.05825, 49.35311], [-95.01419, 49.35647], [-94.99532, 49.36579], [-94.95681, 49.37035], [-94.85381, 49.32492], [-94.8159, 49.32299], [-94.82487, 49.29483], [-94.77355, 49.11998], [-94.75017, 49.09931], [-94.687, 48.84077], [-94.70087, 48.8339], [-94.70486, 48.82365], [-94.69669, 48.80918], [-94.69335, 48.77883], [-94.58903, 48.71803], [-94.54885, 48.71543], [-94.53826, 48.70216], [-94.44258, 48.69223], [-94.4174, 48.71049], [-94.27153, 48.70232], [-94.25172, 48.68404], [-94.25104, 48.65729], [-94.23215, 48.65202], [-93.85769, 48.63284], [-93.83288, 48.62745], [-93.80676, 48.58232], [-93.80939, 48.52439], [-93.79267, 48.51631], [-93.66382, 48.51845], [-93.47022, 48.54357], [-93.44472, 48.59147], [-93.40693, 48.60948], [-93.39758, 48.60364], [-93.3712, 48.60599], [-93.33946, 48.62787], [-93.25391, 48.64266], [-92.94973, 48.60866], [-92.7287, 48.54005], [-92.6342, 48.54133], [-92.62747, 48.50278], [-92.69927, 48.49573], [-92.71323, 48.46081], [-92.65606, 48.43471], [-92.50712, 48.44921], [-92.45588, 48.40624], [-92.48147, 48.36609], [-92.37185, 48.22259], [-92.27167, 48.25046], [-92.30939, 48.31251], [-92.26662, 48.35651], [-92.202, 48.35252], [-92.14732, 48.36578], [-92.05339, 48.35958], [-91.98929, 48.25409], [-91.86125, 48.21278], [-91.71231, 48.19875], [-91.70451, 48.11805], [-91.55649, 48.10611], [-91.58025, 48.04339], [-91.45829, 48.07454], [-91.43248, 48.04912], [-91.25025, 48.08522], [-91.08016, 48.18096], [-90.87588, 48.2484], [-90.75045, 48.09143], [-90.56444, 48.12184], [-90.56312, 48.09488], [-90.07418, 48.11043], [-89.89974, 47.98109], [-89.77248, 48.02607], [-89.57972, 48.00023], [-89.48837, 48.01412], [-88.37033, 48.30586], [-84.85871, 46.88881], [-84.55635, 46.45974], [-84.47607, 46.45225], [-84.4481, 46.48972], [-84.42101, 46.49853], [-84.34174, 46.50683], [-84.29893, 46.49127], [-84.26351, 46.49508], [-84.2264, 46.53337], [-84.1945, 46.54061], [-84.17723, 46.52753], [-84.12885, 46.53068], [-84.11196, 46.50248], [-84.13451, 46.39218], [-84.11254, 46.32329], [-84.11615, 46.2681], [-84.09756, 46.25512], [-84.1096, 46.23987], [-83.95399, 46.05634], [-83.90453, 46.05922], [-83.83329, 46.12169], [-83.57017, 46.105], [-83.43746, 45.99749], [-83.59589, 45.82131], [-82.48419, 45.30225], [-82.42469, 42.992], [-82.4146, 42.97626], [-82.4253, 42.95423], [-82.45331, 42.93139], [-82.4826, 42.8068], [-82.46613, 42.76615], [-82.51063, 42.66025], [-82.51858, 42.611], [-82.57583, 42.5718], [-82.58873, 42.54984], [-82.64242, 42.55594], [-82.82964, 42.37355], [-83.02253, 42.33045], [-83.07837, 42.30978], [-83.09837, 42.28877], [-83.12724, 42.2376], [-83.14962, 42.04089], [-83.11184, 41.95671], [-82.67862, 41.67615], [-78.93684, 42.82887], [-78.90712, 42.89733], [-78.90905, 42.93022], [-78.93224, 42.95229], [-78.96312, 42.95509], [-78.98126, 42.97], [-79.02074, 42.98444], [-79.02424, 43.01983], [-78.99941, 43.05612], [-79.01055, 43.06659], [-79.07486, 43.07845], [-79.05671, 43.10937], [-79.06881, 43.12029], [-79.0427, 43.13934], [-79.04652, 43.16396], [-79.05384, 43.17418], [-79.05002, 43.20133], [-79.05544, 43.21224], [-79.05512, 43.25375], [-79.06921, 43.26183], [-79.25796, 43.54052], [-76.79706, 43.63099], [-76.43859, 44.09393], [-76.35324, 44.13493], [-76.31222, 44.19894], [-76.244, 44.19643], [-76.1664, 44.23051], [-76.16285, 44.28262], [-76.00018, 44.34896], [-75.95947, 44.34463], [-75.8217, 44.43176], [-75.76813, 44.51537], [-75.41441, 44.76614], [-75.2193, 44.87821], [-75.01363, 44.95608], [-74.99101, 44.98051], [-74.8447, 45.00606], [-74.66689, 45.00646], [-74.32699, 44.99029], [-73.35025, 45.00942], [-71.50067, 45.01357], [-71.48735, 45.07784], [-71.42778, 45.12624], [-71.40364, 45.21382], [-71.44252, 45.2361], [-71.37133, 45.24624], [-71.29371, 45.29996], [-71.22338, 45.25184], [-71.19723, 45.25438], [-71.14568, 45.24128], [-71.08364, 45.30623], [-71.01866, 45.31573], [-71.0107, 45.34819], [-70.95193, 45.33895], [-70.91169, 45.29849], [-70.89864, 45.2398], [-70.84816, 45.22698], [-70.80236, 45.37444], [-70.82638, 45.39828], [-70.78372, 45.43269], [-70.65383, 45.37592], [-70.62518, 45.42286], [-70.72651, 45.49771], [-70.68516, 45.56964], [-70.54019, 45.67291], [-70.38934, 45.73215], [-70.41523, 45.79497], [-70.25976, 45.89675], [-70.24694, 45.95138], [-70.31025, 45.96424], [-70.23855, 46.1453], [-70.29078, 46.18832], [-70.18547, 46.35357], [-70.05812, 46.41768], [-69.99966, 46.69543], [-69.22119, 47.46461], [-69.05148, 47.42012], [-69.05073, 47.30076], [-69.05039, 47.2456], [-68.89222, 47.1807], [-68.70125, 47.24399], [-68.60575, 47.24659], [-68.57914, 47.28431], [-68.38332, 47.28723], [-68.37458, 47.35851], [-68.23244, 47.35712], [-67.94843, 47.1925], [-67.87993, 47.10377], [-67.78578, 47.06473], [-67.78111, 45.9392], [-67.75196, 45.91814], [-67.80961, 45.87531], [-67.75654, 45.82324], [-67.80653, 45.80022], [-67.80705, 45.69528], [-67.6049, 45.60725], [-67.43815, 45.59162], [-67.42144, 45.50584], [-67.50578, 45.48971], [-67.42394, 45.37969], [-67.48201, 45.27351], [-67.34927, 45.122], [-67.29754, 45.14865], [-67.29748, 45.18173], [-67.27039, 45.1934], [-67.22751, 45.16344], [-67.20349, 45.1722]]]]
23575 wikidata: "Q36004",
23576 nameEn: "Cocos (Keeling) Islands",
23578 groups: ["053", "009", "UN"],
23580 callingCodes: ["61"]
23583 type: "MultiPolygon",
23584 coordinates: [[[[96.61846, -10.82438], [96.02343, -12.68334], [97.93979, -12.33309], [96.61846, -10.82438]]]]
23593 nameEn: "Democratic Republic of the Congo",
23595 groups: ["017", "202", "002", "UN"],
23596 callingCodes: ["243"]
23599 type: "MultiPolygon",
23600 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]]]]
23609 nameEn: "Central African Republic",
23610 groups: ["017", "202", "002", "UN"],
23611 callingCodes: ["236"]
23614 type: "MultiPolygon",
23615 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]]]]
23624 nameEn: "Republic of the Congo",
23625 groups: ["017", "202", "002", "UN"],
23626 callingCodes: ["242"]
23629 type: "MultiPolygon",
23630 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]]]]
23639 nameEn: "Switzerland",
23640 groups: ["155", "150", "UN"],
23641 callingCodes: ["41"]
23644 type: "MultiPolygon",
23645 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]]]]
23654 nameEn: "C\xF4te d'Ivoire",
23655 groups: ["011", "202", "002", "UN"],
23656 callingCodes: ["225"]
23659 type: "MultiPolygon",
23660 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]]]]
23668 wikidata: "Q26988",
23669 nameEn: "Cook Islands",
23671 groups: ["061", "009", "UN"],
23673 callingCodes: ["682"]
23676 type: "MultiPolygon",
23677 coordinates: [[[[-168.15106, -10.26955], [-156.45576, -31.75456], [-156.48634, -15.52824], [-156.50903, -7.4975], [-168.15106, -10.26955]]]]
23687 groups: ["005", "419", "019", "UN"],
23688 callingCodes: ["56"]
23691 type: "MultiPolygon",
23692 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]]]]
23701 nameEn: "Cameroon",
23702 groups: ["017", "202", "002", "UN"],
23703 callingCodes: ["237"]
23706 type: "MultiPolygon",
23707 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]]]]
23716 nameEn: "People's Republic of China"
23726 nameEn: "Colombia",
23727 groups: ["005", "419", "019", "UN"],
23728 callingCodes: ["57"]
23731 type: "MultiPolygon",
23732 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]]]]
23739 wikidata: "Q161258",
23740 nameEn: "Clipperton Island",
23742 groups: ["013", "003", "019", "UN"],
23743 isoStatus: "excRes"
23746 type: "MultiPolygon",
23747 coordinates: [[[[-110.36279, 9.79626], [-108.755, 9.84085], [-109.04145, 11.13245], [-110.36279, 9.79626]]]]
23756 nameEn: "Costa Rica",
23757 groups: ["013", "003", "419", "019", "UN"],
23758 callingCodes: ["506"]
23761 type: "MultiPolygon",
23762 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]]]]
23772 groups: ["029", "003", "419", "019", "UN"],
23773 callingCodes: ["53"]
23776 type: "MultiPolygon",
23777 coordinates: [[[[-73.62304, 20.6935], [-82.02215, 24.23074], [-85.77883, 21.92705], [-74.81171, 18.82201], [-73.62304, 20.6935]]]]
23786 nameEn: "Cape Verde",
23787 groups: ["Q105472", "011", "202", "002", "UN"],
23788 callingCodes: ["238"]
23791 type: "MultiPolygon",
23792 coordinates: [[[[-28.81604, 14.57305], [-20.39702, 14.12816], [-23.37101, 19.134], [-28.81604, 14.57305]]]]
23800 wikidata: "Q25279",
23801 nameEn: "Cura\xE7ao",
23802 aliases: ["NL-CW"],
23804 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23805 callingCodes: ["599"]
23808 type: "MultiPolygon",
23809 coordinates: [[[[-68.90012, 12.62309], [-69.59009, 12.46019], [-68.99639, 11.79035], [-68.33524, 11.78151], [-68.90012, 12.62309]]]]
23817 wikidata: "Q31063",
23818 nameEn: "Christmas Island",
23820 groups: ["053", "009", "UN"],
23822 callingCodes: ["61"]
23825 type: "MultiPolygon",
23826 coordinates: [[[[105.66835, -9.31927], [104.67494, -11.2566], [106.66176, -11.14349], [105.66835, -9.31927]]]]
23835 nameEn: "Republic of Cyprus",
23836 groups: ["Q644636", "EU", "145", "142", "UN"],
23838 callingCodes: ["357"]
23841 type: "MultiPolygon",
23842 coordinates: [[[[32.46489, 35.48584], [30.15137, 34.08517], [32.74412, 34.43926], [32.75515, 34.64985], [32.76136, 34.68318], [32.79433, 34.67883], [32.82717, 34.70622], [32.86014, 34.70585], [32.86167, 34.68734], [32.9068, 34.66102], [32.91398, 34.67343], [32.93043, 34.67091], [32.92807, 34.66736], [32.93449, 34.66241], [32.93693, 34.67027], [32.94379, 34.67111], [32.94683, 34.67907], [32.95539, 34.68471], [32.99135, 34.68061], [32.98668, 34.67268], [32.99014, 34.65518], [32.97736, 34.65277], [32.97079, 34.66112], [32.95325, 34.66462], [32.94796, 34.6587], [32.94976, 34.65204], [32.95471, 34.64528], [32.95323, 34.64075], [32.95891, 34.62919], [32.96718, 34.63446], [32.96968, 34.64046], [33.0138, 34.64424], [33.26744, 34.49942], [33.83531, 34.73974], [33.70575, 34.97947], [33.70639, 34.99303], [33.71514, 35.00294], [33.69731, 35.01754], [33.69938, 35.03123], [33.67678, 35.03866], [33.63765, 35.03869], [33.61215, 35.0527], [33.59658, 35.03635], [33.567, 35.04803], [33.57478, 35.06049], [33.53975, 35.08151], [33.48915, 35.06594], [33.47666, 35.00701], [33.45256, 35.00288], [33.45178, 35.02078], [33.47825, 35.04103], [33.48136, 35.0636], [33.46813, 35.10564], [33.41675, 35.16325], [33.4076, 35.20062], [33.38575, 35.2018], [33.37248, 35.18698], [33.3717, 35.1788], [33.36569, 35.17479], [33.35612, 35.17402], [33.35596, 35.17942], [33.34964, 35.17803], [33.35056, 35.18328], [33.31955, 35.18096], [33.3072, 35.16816], [33.27068, 35.16815], [33.15138, 35.19504], [33.11105, 35.15639], [33.08249, 35.17319], [33.01192, 35.15639], [32.94471, 35.09422], [32.86406, 35.1043], [32.85733, 35.07742], [32.70779, 35.14127], [32.70947, 35.18328], [32.64864, 35.19967], [32.60361, 35.16647], [32.46489, 35.48584]]], [[[33.74144, 35.01053], [33.7492, 35.01319], [33.74983, 35.02274], [33.74265, 35.02329], [33.73781, 35.02181], [33.7343, 35.01178], [33.74144, 35.01053]]], [[[33.77312, 34.9976], [33.75994, 35.00113], [33.75682, 34.99916], [33.76605, 34.99543], [33.76738, 34.99188], [33.7778, 34.98981], [33.77843, 34.988], [33.78149, 34.98854], [33.78318, 34.98699], [33.78571, 34.98951], [33.78917, 34.98854], [33.79191, 34.98914], [33.78516, 34.99582], [33.77553, 34.99518], [33.77312, 34.9976]]]]
23852 groups: ["EU", "151", "150", "UN"],
23853 callingCodes: ["420"]
23856 type: "MultiPolygon",
23857 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]]]]
23867 groups: ["EU", "155", "150", "UN"],
23868 callingCodes: ["49"]
23871 type: "MultiPolygon",
23872 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]]]]
23879 wikidata: "Q184851",
23880 nameEn: "Diego Garcia",
23882 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
23883 isoStatus: "excRes",
23884 callingCodes: ["246"]
23887 type: "MultiPolygon",
23888 coordinates: [[[[73.14823, -7.76302], [73.09982, -6.07324], [71.43792, -7.73904], [73.14823, -7.76302]]]]
23897 nameEn: "Djibouti",
23898 groups: ["014", "202", "002", "UN"],
23899 callingCodes: ["253"]
23902 type: "MultiPolygon",
23903 coordinates: [[[[43.90659, 12.3823], [43.90659, 12.3823], [43.32909, 12.59711], [43.29075, 12.79154], [42.86195, 12.58747], [42.7996, 12.42629], [42.6957, 12.36201], [42.46941, 12.52661], [42.4037, 12.46478], [41.95461, 11.81157], [41.82878, 11.72361], [41.77727, 11.49902], [41.8096, 11.33606], [41.80056, 10.97127], [42.06302, 10.92599], [42.13691, 10.97586], [42.42669, 10.98493], [42.62989, 11.09711], [42.75111, 11.06992], [42.79037, 10.98493], [42.95776, 10.98533], [43.90659, 12.3823]]]]
23911 wikidata: "Q756617",
23912 nameEn: "Kingdom of Denmark"
23922 nameEn: "Dominica",
23923 groups: ["029", "003", "419", "019", "UN"],
23925 roadSpeedUnit: "mph",
23926 callingCodes: ["1 767"]
23929 type: "MultiPolygon",
23930 coordinates: [[[[-61.32485, 14.91445], [-60.86656, 15.82603], [-61.95646, 15.5094], [-61.32485, 14.91445]]]]
23939 nameEn: "Dominican Republic",
23940 groups: ["029", "003", "419", "019", "UN"],
23941 callingCodes: ["1 809", "1 829", "1 849"]
23944 type: "MultiPolygon",
23945 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]]]]
23955 groups: ["015", "002", "UN"],
23956 callingCodes: ["213"]
23959 type: "MultiPolygon",
23960 coordinates: [[[[8.59123, 37.14286], [5.10072, 39.89531], [-2.27707, 35.35051], [-2.21248, 35.08532], [-2.21445, 35.04378], [-2.04734, 34.93218], [-1.97833, 34.93218], [-1.97469, 34.886], [-1.73707, 34.74226], [-1.84569, 34.61907], [-1.69788, 34.48056], [-1.78042, 34.39018], [-1.64666, 34.10405], [-1.73494, 33.71721], [-1.59508, 33.59929], [-1.67067, 33.27084], [-1.46249, 33.0499], [-1.54244, 32.95499], [-1.37794, 32.73628], [-0.9912, 32.52467], [-1.24998, 32.32993], [-1.24453, 32.1917], [-1.15735, 32.12096], [-1.22829, 32.07832], [-2.46166, 32.16603], [-2.93873, 32.06557], [-2.82784, 31.79459], [-3.66314, 31.6339], [-3.66386, 31.39202], [-3.77647, 31.31912], [-3.77103, 31.14984], [-3.54944, 31.0503], [-3.65418, 30.85566], [-3.64735, 30.67539], [-4.31774, 30.53229], [-4.6058, 30.28343], [-5.21671, 29.95253], [-5.58831, 29.48103], [-5.72121, 29.52322], [-5.75616, 29.61407], [-6.69965, 29.51623], [-6.78351, 29.44634], [-6.95824, 29.50924], [-7.61585, 29.36252], [-8.6715, 28.71194], [-8.66879, 27.6666], [-8.66674, 27.31569], [-4.83423, 24.99935], [1.15698, 21.12843], [1.20992, 20.73533], [3.24648, 19.81703], [3.12501, 19.1366], [3.36082, 18.9745], [4.26651, 19.14224], [5.8153, 19.45101], [7.38361, 20.79165], [7.48273, 20.87258], [11.96886, 23.51735], [11.62498, 24.26669], [11.41061, 24.21456], [10.85323, 24.5595], [10.33159, 24.5465], [10.02432, 24.98124], [10.03146, 25.35635], [9.38834, 26.19288], [9.51696, 26.39148], [9.89569, 26.57696], [9.78136, 29.40961], [9.3876, 30.16738], [9.55544, 30.23971], [9.07483, 32.07865], [8.35999, 32.50101], [8.31895, 32.83483], [8.1179, 33.05086], [8.11433, 33.10175], [7.83028, 33.18851], [7.73687, 33.42114], [7.54088, 33.7726], [7.52851, 34.06493], [7.66174, 34.20167], [7.74207, 34.16492], [7.81242, 34.21841], [7.86264, 34.3987], [8.20482, 34.57575], [8.29655, 34.72798], [8.25189, 34.92009], [8.30727, 34.95378], [8.3555, 35.10007], [8.47318, 35.23376], [8.30329, 35.29884], [8.36086, 35.47774], [8.35371, 35.66373], [8.26472, 35.73669], [8.2626, 35.91733], [8.40731, 36.42208], [8.18936, 36.44939], [8.16167, 36.48817], [8.47609, 36.66607], [8.46537, 36.7706], [8.57613, 36.78062], [8.67706, 36.8364], [8.62972, 36.86499], [8.64044, 36.9401], [8.59123, 37.14286]]]]
23966 wikidata: "Q28868874",
23967 nameEn: "Ceuta, Melilla",
23969 level: "territory",
23970 isoStatus: "excRes"
23992 groups: ["EU", "154", "150", "UN"],
23993 callingCodes: ["372"]
23996 type: "MultiPolygon",
23997 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]]]]
24007 groups: ["015", "002", "UN"],
24008 callingCodes: ["20"]
24011 type: "MultiPolygon",
24012 coordinates: [[[[33.62659, 31.82938], [26.92891, 33.39516], [24.8458, 31.39877], [25.01077, 30.73861], [24.71117, 30.17441], [24.99968, 29.24574], [24.99885, 21.99535], [33.17563, 22.00405], [34.0765, 22.00501], [37.8565, 22.00903], [34.4454, 27.91479], [34.8812, 29.36878], [34.92298, 29.45305], [34.26742, 31.21998], [34.24012, 31.29591], [34.23572, 31.2966], [34.21853, 31.32363], [34.052, 31.46619], [33.62659, 31.82938]]]]
24021 nameEn: "Western Sahara",
24022 groups: ["015", "002"],
24023 callingCodes: ["212"]
24026 type: "MultiPolygon",
24027 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]]]]
24037 groups: ["014", "202", "002", "UN"],
24038 callingCodes: ["291"]
24041 type: "MultiPolygon",
24042 coordinates: [[[[40.99158, 15.81743], [39.63762, 18.37348], [38.57727, 17.98125], [38.45916, 17.87167], [38.37133, 17.66269], [38.13362, 17.53906], [37.50967, 17.32199], [37.42694, 17.04041], [36.99777, 17.07172], [36.92193, 16.23451], [36.76371, 15.80831], [36.69761, 15.75323], [36.54276, 15.23478], [36.44337, 15.14963], [36.54376, 14.25597], [36.56536, 14.26177], [36.55659, 14.28237], [36.63364, 14.31172], [36.85787, 14.32201], [37.01622, 14.2561], [37.09486, 14.27155], [37.13206, 14.40746], [37.3106, 14.44657], [37.47319, 14.2149], [37.528, 14.18413], [37.91287, 14.89447], [38.0364, 14.72745], [38.25562, 14.67287], [38.3533, 14.51323], [38.45748, 14.41445], [38.78306, 14.4754], [38.98058, 14.54895], [39.02834, 14.63717], [39.16074, 14.65187], [39.14772, 14.61827], [39.19547, 14.56996], [39.23888, 14.56365], [39.26927, 14.48801], [39.2302, 14.44598], [39.2519, 14.40393], [39.37685, 14.54402], [39.52756, 14.49011], [39.50585, 14.55735], [39.58182, 14.60987], [39.76632, 14.54264], [39.9443, 14.41024], [40.07236, 14.54264], [40.14649, 14.53969], [40.21128, 14.39342], [40.25686, 14.41445], [40.9167, 14.11152], [41.25097, 13.60787], [41.62864, 13.38626], [42.05841, 12.80912], [42.21469, 12.75832], [42.2798, 12.6355], [42.4037, 12.46478], [42.46941, 12.52661], [42.6957, 12.36201], [42.7996, 12.42629], [42.86195, 12.58747], [43.29075, 12.79154], [40.99158, 15.81743]]]]
24061 nameEn: "Ethiopia",
24062 groups: ["014", "202", "002", "UN"],
24063 callingCodes: ["251"]
24066 type: "MultiPolygon",
24067 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]]]]
24075 nameEn: "European Union",
24077 isoStatus: "excRes"
24099 groups: ["054", "009", "UN"],
24101 callingCodes: ["679"]
24104 type: "MultiPolygon",
24105 coordinates: [[[[174.245, -23.1974], [179.99999, -22.5], [179.99999, -11.5], [174, -11.5], [174.245, -23.1974]]], [[[-176.76826, -14.95183], [-180, -14.96041], [-180, -22.90585], [-176.74538, -22.89767], [-176.76826, -14.95183]]]]
24114 nameEn: "Falkland Islands",
24116 groups: ["BOTS", "005", "419", "019", "UN"],
24118 roadSpeedUnit: "mph",
24119 roadHeightUnit: "ft",
24120 callingCodes: ["500"]
24123 type: "MultiPolygon",
24124 coordinates: [[[[-63.67376, -55.11859], [-54.56126, -51.26248], [-61.26735, -50.63919], [-63.67376, -55.11859]]]]
24133 nameEn: "Federated States of Micronesia",
24134 groups: ["057", "009", "UN"],
24135 roadSpeedUnit: "mph",
24136 roadHeightUnit: "ft",
24137 callingCodes: ["691"]
24140 type: "MultiPolygon",
24141 coordinates: [[[[138.20583, 13.3783], [136.27107, 6.73747], [156.88247, -1.39237], [165.19726, 6.22546], [138.20583, 13.3783]]]]
24150 nameEn: "Faroe Islands",
24152 groups: ["154", "150", "UN"],
24153 callingCodes: ["298"]
24156 type: "MultiPolygon",
24157 coordinates: [[[[-8.51774, 62.35338], [-6.51083, 60.95272], [-5.70102, 62.77194], [-8.51774, 62.35338]]]]
24175 wikidata: "Q212429",
24176 nameEn: "Metropolitan France",
24178 groups: ["EU", "155", "150", "UN"],
24179 isoStatus: "excRes",
24180 callingCodes: ["33"]
24183 type: "MultiPolygon",
24184 coordinates: [[[[2.55904, 51.07014], [2.18458, 51.52087], [1.17405, 50.74239], [-2.02963, 49.91866], [-2.09454, 49.46288], [-1.83944, 49.23037], [-2.00491, 48.86706], [-2.65349, 49.15373], [-6.28985, 48.93406], [-1.81005, 43.59738], [-1.77289, 43.38957], [-1.79319, 43.37497], [-1.78332, 43.36399], [-1.78714, 43.35476], [-1.77068, 43.34396], [-1.75334, 43.34107], [-1.75079, 43.3317], [-1.7397, 43.32979], [-1.73074, 43.29481], [-1.69407, 43.31378], [-1.62481, 43.30726], [-1.63052, 43.28591], [-1.61341, 43.25269], [-1.57674, 43.25269], [-1.55963, 43.28828], [-1.50992, 43.29481], [-1.45289, 43.27049], [-1.40942, 43.27272], [-1.3758, 43.24511], [-1.41562, 43.12815], [-1.47555, 43.08372], [-1.44067, 43.047], [-1.35272, 43.02658], [-1.34419, 43.09665], [-1.32209, 43.1127], [-1.27118, 43.11961], [-1.30052, 43.09581], [-1.30531, 43.06859], [-1.25244, 43.04164], [-1.22881, 43.05534], [-1.10333, 43.0059], [-1.00963, 42.99279], [-0.97133, 42.96239], [-0.81652, 42.95166], [-0.75478, 42.96916], [-0.72037, 42.92541], [-0.73422, 42.91228], [-0.72608, 42.89318], [-0.69837, 42.87945], [-0.67637, 42.88303], [-0.55497, 42.77846], [-0.50863, 42.82713], [-0.44334, 42.79939], [-0.41319, 42.80776], [-0.38833, 42.80132], [-0.3122, 42.84788], [-0.17939, 42.78974], [-0.16141, 42.79535], [-0.10519, 42.72761], [-0.02468, 42.68513], [0.17569, 42.73424], [0.25336, 42.7174], [0.29407, 42.67431], [0.36251, 42.72282], [0.40214, 42.69779], [0.67873, 42.69458], [0.65421, 42.75872], [0.66121, 42.84021], [0.711, 42.86372], [0.93089, 42.79154], [0.96166, 42.80629], [0.98292, 42.78754], [1.0804, 42.78569], [1.15928, 42.71407], [1.35562, 42.71944], [1.44197, 42.60217], [1.47986, 42.61346], [1.46718, 42.63296], [1.48043, 42.65203], [1.50867, 42.64483], [1.55418, 42.65669], [1.60085, 42.62703], [1.63485, 42.62957], [1.6625, 42.61982], [1.68267, 42.62533], [1.73452, 42.61515], [1.72588, 42.59098], [1.7858, 42.57698], [1.73683, 42.55492], [1.72515, 42.50338], [1.76335, 42.48863], [1.83037, 42.48395], [1.88853, 42.4501], [1.93663, 42.45439], [1.94292, 42.44316], [1.94061, 42.43333], [1.94084, 42.43039], [1.9574, 42.42401], [1.96482, 42.37787], [2.00488, 42.35399], [2.06241, 42.35906], [2.11621, 42.38393], [2.12789, 42.41291], [2.16599, 42.42314], [2.20578, 42.41633], [2.25551, 42.43757], [2.38504, 42.39977], [2.43299, 42.39423], [2.43508, 42.37568], [2.48457, 42.33933], [2.54382, 42.33406], [2.55516, 42.35351], [2.57934, 42.35808], [2.6747, 42.33974], [2.65311, 42.38771], [2.72056, 42.42298], [2.75497, 42.42578], [2.77464, 42.41046], [2.84335, 42.45724], [2.85675, 42.45444], [2.86983, 42.46843], [2.88413, 42.45938], [2.92107, 42.4573], [2.94283, 42.48174], [2.96518, 42.46692], [3.03734, 42.47363], [3.08167, 42.42748], [3.10027, 42.42621], [3.11379, 42.43646], [3.17156, 42.43545], [3.75438, 42.33445], [7.60802, 41.05927], [10.09675, 41.44089], [9.56115, 43.20816], [7.50102, 43.51859], [7.42422, 43.72209], [7.40903, 43.7296], [7.41113, 43.73156], [7.41291, 43.73168], [7.41298, 43.73311], [7.41233, 43.73439], [7.42062, 43.73977], [7.42299, 43.74176], [7.42443, 43.74087], [7.42809, 43.74396], [7.43013, 43.74895], [7.43624, 43.75014], [7.43708, 43.75197], [7.4389, 43.75151], [7.4379, 43.74963], [7.47823, 43.73341], [7.53006, 43.78405], [7.50423, 43.84345], [7.49355, 43.86551], [7.51162, 43.88301], [7.56075, 43.89932], [7.56858, 43.94506], [7.60771, 43.95772], [7.65266, 43.9763], [7.66848, 43.99943], [7.6597, 44.03009], [7.72508, 44.07578], [7.66878, 44.12795], [7.68694, 44.17487], [7.63245, 44.17877], [7.62155, 44.14881], [7.36364, 44.11882], [7.34547, 44.14359], [7.27827, 44.1462], [7.16929, 44.20352], [7.00764, 44.23736], [6.98221, 44.28289], [6.89171, 44.36637], [6.88784, 44.42043], [6.94504, 44.43112], [6.86233, 44.49834], [6.85507, 44.53072], [6.96042, 44.62129], [6.95133, 44.66264], [7.00582, 44.69364], [7.07484, 44.68073], [7.00401, 44.78782], [7.02217, 44.82519], [6.93499, 44.8664], [6.90774, 44.84322], [6.75518, 44.89915], [6.74519, 44.93661], [6.74791, 45.01939], [6.66981, 45.02324], [6.62803, 45.11175], [6.7697, 45.16044], [6.85144, 45.13226], [6.96706, 45.20841], [7.07074, 45.21228], [7.13115, 45.25386], [7.10572, 45.32924], [7.18019, 45.40071], [7.00037, 45.509], [6.98948, 45.63869], [6.80785, 45.71864], [6.80785, 45.83265], [6.95315, 45.85163], [7.04151, 45.92435], [7.00946, 45.9944], [6.93862, 46.06502], [6.87868, 46.03855], [6.89321, 46.12548], [6.78968, 46.14058], [6.86052, 46.28512], [6.77152, 46.34784], [6.8024, 46.39171], [6.82312, 46.42661], [6.53358, 46.45431], [6.25432, 46.3632], [6.21981, 46.31304], [6.24826, 46.30175], [6.25137, 46.29014], [6.23775, 46.27822], [6.24952, 46.26255], [6.26749, 46.24745], [6.29474, 46.26221], [6.31041, 46.24417], [6.29663, 46.22688], [6.27694, 46.21566], [6.26007, 46.21165], [6.24821, 46.20531], [6.23913, 46.20511], [6.23544, 46.20714], [6.22175, 46.20045], [6.22222, 46.19888], [6.21844, 46.19837], [6.21603, 46.19507], [6.21273, 46.19409], [6.21114, 46.1927], [6.20539, 46.19163], [6.19807, 46.18369], [6.19552, 46.18401], [6.18707, 46.17999], [6.18871, 46.16644], [6.18116, 46.16187], [6.15305, 46.15194], [6.13397, 46.1406], [6.09926, 46.14373], [6.09199, 46.15191], [6.07491, 46.14879], [6.05203, 46.15191], [6.04564, 46.14031], [6.03614, 46.13712], [6.01791, 46.14228], [5.9871, 46.14499], [5.97893, 46.13303], [5.95781, 46.12925], [5.9641, 46.14412], [5.97508, 46.15863], [5.98188, 46.17392], [5.98846, 46.17046], [5.99573, 46.18587], [5.96515, 46.19638], [5.97542, 46.21525], [6.02461, 46.23313], [6.03342, 46.2383], [6.04602, 46.23127], [6.05029, 46.23518], [6.0633, 46.24583], [6.07072, 46.24085], [6.08563, 46.24651], [6.10071, 46.23772], [6.12446, 46.25059], [6.11926, 46.2634], [6.1013, 46.28512], [6.11697, 46.29547], [6.1198, 46.31157], [6.13876, 46.33844], [6.15738, 46.3491], [6.16987, 46.36759], [6.15985, 46.37721], [6.15016, 46.3778], [6.09926, 46.40768], [6.06407, 46.41676], [6.08427, 46.44305], [6.07269, 46.46244], [6.1567, 46.54402], [6.11084, 46.57649], [6.27135, 46.68251], [6.38351, 46.73171], [6.45209, 46.77502], [6.43216, 46.80336], [6.46456, 46.88865], [6.43341, 46.92703], [6.71531, 47.0494], [6.68823, 47.06616], [6.76788, 47.1208], [6.8489, 47.15933], [6.9508, 47.24338], [6.95108, 47.26428], [6.94316, 47.28747], [7.05305, 47.33304], [7.0564, 47.35134], [7.03125, 47.36996], [6.87959, 47.35335], [6.88542, 47.37262], [6.93744, 47.40714], [6.93953, 47.43388], [7.0024, 47.45264], [6.98425, 47.49432], [7.0231, 47.50522], [7.07425, 47.48863], [7.12781, 47.50371], [7.16249, 47.49025], [7.19583, 47.49455], [7.17026, 47.44312], [7.24669, 47.4205], [7.33526, 47.44186], [7.35603, 47.43432], [7.40308, 47.43638], [7.43088, 47.45846], [7.4462, 47.46264], [7.4583, 47.47216], [7.42923, 47.48628], [7.43356, 47.49712], [7.47534, 47.47932], [7.51076, 47.49651], [7.49804, 47.51798], [7.5229, 47.51644], [7.53199, 47.5284], [7.51904, 47.53515], [7.50588, 47.52856], [7.49691, 47.53821], [7.50873, 47.54546], [7.51723, 47.54578], [7.52831, 47.55347], [7.53634, 47.55553], [7.55652, 47.56779], [7.55689, 47.57232], [7.56548, 47.57617], [7.56684, 47.57785], [7.58386, 47.57536], [7.58945, 47.59017], [7.59301, 47.60058], [7.58851, 47.60794], [7.57423, 47.61628], [7.5591, 47.63849], [7.53384, 47.65115], [7.52067, 47.66437], [7.51915, 47.68335], [7.51266, 47.70197], [7.53722, 47.71635], [7.54761, 47.72912], [7.52921, 47.77747], [7.55673, 47.87371], [7.62302, 47.97898], [7.56966, 48.03265], [7.57137, 48.12292], [7.6648, 48.22219], [7.69022, 48.30018], [7.74562, 48.32736], [7.73109, 48.39192], [7.76833, 48.48945], [7.80647, 48.51239], [7.80167, 48.54758], [7.80057, 48.5857], [7.84098, 48.64217], [7.89002, 48.66317], [7.96812, 48.72491], [7.96994, 48.75606], [8.01534, 48.76085], [8.0326, 48.79017], [8.06802, 48.78957], [8.10253, 48.81829], [8.12813, 48.87985], [8.19989, 48.95825], [8.20031, 48.95856], [8.22604, 48.97352], [8.14189, 48.97833], [7.97783, 49.03161], [7.93641, 49.05544], [7.86386, 49.03499], [7.79557, 49.06583], [7.75948, 49.04562], [7.63618, 49.05428], [7.62575, 49.07654], [7.56416, 49.08136], [7.53012, 49.09818], [7.49172, 49.13915], [7.49473, 49.17], [7.44455, 49.16765], [7.44052, 49.18354], [7.3662, 49.17308], [7.35995, 49.14399], [7.3195, 49.14231], [7.29514, 49.11426], [7.23473, 49.12971], [7.1593, 49.1204], [7.1358, 49.1282], [7.12504, 49.14253], [7.10384, 49.13787], [7.10715, 49.15631], [7.07859, 49.15031], [7.09007, 49.13094], [7.07162, 49.1255], [7.06642, 49.11415], [7.05548, 49.11185], [7.04843, 49.11422], [7.04409, 49.12123], [7.04662, 49.13724], [7.03178, 49.15734], [7.0274, 49.17042], [7.03459, 49.19096], [7.01318, 49.19018], [6.97273, 49.2099], [6.95963, 49.203], [6.94028, 49.21641], [6.93831, 49.2223], [6.91875, 49.22261], [6.89298, 49.20863], [6.85939, 49.22376], [6.83555, 49.21249], [6.85119, 49.20038], [6.85016, 49.19354], [6.86225, 49.18185], [6.84703, 49.15734], [6.83385, 49.15162], [6.78265, 49.16793], [6.73765, 49.16375], [6.71137, 49.18808], [6.73256, 49.20486], [6.71843, 49.2208], [6.69274, 49.21661], [6.66583, 49.28065], [6.60186, 49.31055], [6.572, 49.35027], [6.58807, 49.35358], [6.60091, 49.36864], [6.533, 49.40748], [6.55404, 49.42464], [6.42432, 49.47683], [6.40274, 49.46546], [6.39168, 49.4667], [6.38352, 49.46463], [6.36778, 49.46937], [6.3687, 49.4593], [6.28818, 49.48465], [6.27875, 49.503], [6.25029, 49.50609], [6.2409, 49.51408], [6.19543, 49.50536], [6.17386, 49.50934], [6.15366, 49.50226], [6.16115, 49.49297], [6.14321, 49.48796], [6.12814, 49.49365], [6.12346, 49.4735], [6.10325, 49.4707], [6.09845, 49.46351], [6.10072, 49.45268], [6.08373, 49.45594], [6.07887, 49.46399], [6.05553, 49.46663], [6.04176, 49.44801], [6.02743, 49.44845], [6.02648, 49.45451], [5.97693, 49.45513], [5.96876, 49.49053], [5.94224, 49.49608], [5.94128, 49.50034], [5.86571, 49.50015], [5.83389, 49.52152], [5.83467, 49.52717], [5.84466, 49.53027], [5.83648, 49.5425], [5.81664, 49.53775], [5.80871, 49.5425], [5.81838, 49.54777], [5.79195, 49.55228], [5.77435, 49.56298], [5.7577, 49.55915], [5.75649, 49.54321], [5.64505, 49.55146], [5.60909, 49.51228], [5.55001, 49.52729], [5.46541, 49.49825], [5.46734, 49.52648], [5.43713, 49.5707], [5.3974, 49.61596], [5.34837, 49.62889], [5.33851, 49.61599], [5.3137, 49.61225], [5.30214, 49.63055], [5.33039, 49.6555], [5.31465, 49.66846], [5.26232, 49.69456], [5.14545, 49.70287], [5.09249, 49.76193], [4.96714, 49.79872], [4.85464, 49.78995], [4.86965, 49.82271], [4.85134, 49.86457], [4.88529, 49.9236], [4.78827, 49.95609], [4.8382, 50.06738], [4.88602, 50.15182], [4.83279, 50.15331], [4.82438, 50.16878], [4.75237, 50.11314], [4.70064, 50.09384], [4.68695, 49.99685], [4.5414, 49.96911], [4.51098, 49.94659], [4.43488, 49.94122], [4.35051, 49.95315], [4.31963, 49.97043], [4.20532, 49.95803], [4.14239, 49.98034], [4.13508, 50.01976], [4.16294, 50.04719], [4.23101, 50.06945], [4.20147, 50.13535], [4.13561, 50.13078], [4.16014, 50.19239], [4.15524, 50.21103], [4.21945, 50.25539], [4.20651, 50.27333], [4.17861, 50.27443], [4.17347, 50.28838], [4.15524, 50.2833], [4.16808, 50.25786], [4.13665, 50.25609], [4.11954, 50.30425], [4.10957, 50.30234], [4.10237, 50.31247], [4.0689, 50.3254], [4.0268, 50.35793], [3.96771, 50.34989], [3.90781, 50.32814], [3.84314, 50.35219], [3.73911, 50.34809], [3.70987, 50.3191], [3.71009, 50.30305], [3.66976, 50.34563], [3.65709, 50.36873], [3.67262, 50.38663], [3.67494, 50.40239], [3.66153, 50.45165], [3.64426, 50.46275], [3.61014, 50.49568], [3.58361, 50.49049], [3.5683, 50.50192], [3.49509, 50.48885], [3.51564, 50.5256], [3.47385, 50.53397], [3.44629, 50.51009], [3.37693, 50.49538], [3.28575, 50.52724], [3.2729, 50.60718], [3.23951, 50.6585], [3.264, 50.67668], [3.2536, 50.68977], [3.26141, 50.69151], [3.26063, 50.70086], [3.24593, 50.71389], [3.22042, 50.71019], [3.20845, 50.71662], [3.19017, 50.72569], [3.20064, 50.73547], [3.18811, 50.74025], [3.18339, 50.74981], [3.16476, 50.76843], [3.15017, 50.79031], [3.1257, 50.78603], [3.11987, 50.79188], [3.11206, 50.79416], [3.10614, 50.78303], [3.09163, 50.77717], [3.04314, 50.77674], [3.00537, 50.76588], [2.96778, 50.75242], [2.95019, 50.75138], [2.90873, 50.702], [2.91036, 50.6939], [2.90069, 50.69263], [2.88504, 50.70656], [2.87937, 50.70298], [2.86985, 50.7033], [2.8483, 50.72276], [2.81056, 50.71773], [2.71165, 50.81295], [2.63331, 50.81457], [2.59093, 50.91751], [2.63074, 50.94746], [2.57551, 51.00326], [2.55904, 51.07014]], [[1.99838, 42.44682], [1.98378, 42.44697], [1.96125, 42.45364], [1.95606, 42.45785], [1.96215, 42.47854], [1.97003, 42.48081], [1.97227, 42.48487], [1.97697, 42.48568], [1.98022, 42.49569], [1.98916, 42.49351], [1.99766, 42.4858], [1.98579, 42.47486], [1.99216, 42.46208], [2.01564, 42.45171], [1.99838, 42.44682]]]]
24194 groups: ["017", "202", "002", "UN"],
24195 callingCodes: ["241"]
24198 type: "MultiPolygon",
24199 coordinates: [[[[13.29457, 2.16106], [13.28534, 2.25716], [11.37116, 2.29975], [11.3561, 2.17217], [11.35307, 1.00251], [9.79648, 1.0019], [9.75065, 1.06753], [9.66433, 1.06723], [7.24416, -0.64092], [10.75913, -4.39519], [11.12647, -3.94169], [11.22301, -3.69888], [11.48764, -3.51089], [11.57949, -3.52798], [11.68608, -3.68942], [11.87083, -3.71571], [11.92719, -3.62768], [11.8318, -3.5812], [11.96554, -3.30267], [11.70227, -3.17465], [11.70558, -3.0773], [11.80365, -3.00424], [11.64798, -2.81146], [11.5359, -2.85654], [11.64487, -2.61865], [11.57637, -2.33379], [11.74605, -2.39936], [11.96866, -2.33559], [12.04895, -2.41704], [12.47925, -2.32626], [12.44656, -1.92025], [12.61312, -1.8129], [12.82172, -1.91091], [13.02759, -2.33098], [13.47977, -2.43224], [13.75884, -2.09293], [13.92073, -2.35581], [13.85846, -2.46935], [14.10442, -2.49268], [14.23829, -2.33715], [14.16202, -2.23916], [14.23518, -2.15671], [14.25932, -1.97624], [14.41838, -1.89412], [14.52569, -0.57818], [14.41887, -0.44799], [14.2165, -0.38261], [14.06862, -0.20826], [13.90632, -0.2287], [13.88648, 0.26652], [14.10909, 0.58563], [14.26066, 0.57255], [14.48179, 0.9152], [14.25186, 1.39842], [13.89582, 1.4261], [13.15519, 1.23368], [13.25447, 1.32339], [13.13461, 1.57238], [13.29457, 2.16106]]]]
24209 nameEn: "United Kingdom",
24222 groups: ["029", "003", "419", "019", "UN"],
24224 roadSpeedUnit: "mph",
24225 callingCodes: ["1 473"]
24228 type: "MultiPolygon",
24229 coordinates: [[[[-62.64026, 12.69984], [-61.77886, 11.36496], [-59.94058, 12.34011], [-62.64026, 12.69984]]]]
24239 groups: ["145", "142", "UN"],
24240 callingCodes: ["995"]
24243 type: "MultiPolygon",
24244 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]]]]
24253 nameEn: "French Guiana",
24255 groups: ["Q3320166", "EU", "005", "419", "019", "UN"],
24256 callingCodes: ["594"]
24259 type: "MultiPolygon",
24260 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]]]]
24268 wikidata: "Q25230",
24269 nameEn: "Bailiwick of Guernsey",
24281 groups: ["011", "202", "002", "UN"],
24282 callingCodes: ["233"]
24285 type: "MultiPolygon",
24286 coordinates: [[[[-0.13493, 11.14075], [-0.27374, 11.17157], [-0.28566, 11.12713], [-0.35955, 11.07801], [-0.38219, 11.12596], [-0.42391, 11.11661], [-0.44298, 11.04292], [-0.61937, 10.91305], [-0.67143, 10.99811], [-2.83373, 11.0067], [-2.94232, 10.64281], [-2.83108, 10.40252], [-2.74174, 9.83172], [-2.76534, 9.56589], [-2.68802, 9.49343], [-2.69814, 9.22717], [-2.77799, 9.04949], [-2.66357, 9.01771], [-2.58243, 8.7789], [-2.49037, 8.20872], [-2.62901, 8.11495], [-2.61232, 8.02645], [-2.67787, 8.02055], [-2.74819, 7.92613], [-2.78395, 7.94974], [-2.79467, 7.86002], [-2.92339, 7.60847], [-2.97822, 7.27165], [-2.95438, 7.23737], [-3.23327, 6.81744], [-3.21954, 6.74407], [-3.25999, 6.62521], [-3.01896, 5.71697], [-2.95323, 5.71865], [-2.96671, 5.6415], [-2.93132, 5.62137], [-2.85378, 5.65156], [-2.76614, 5.60963], [-2.72737, 5.34789], [-2.77625, 5.34621], [-2.73074, 5.1364], [-2.75502, 5.10657], [-2.95261, 5.12477], [-2.96554, 5.10397], [-3.063, 5.13665], [-3.11073, 5.12675], [-3.10675, 5.08515], [-3.34019, 4.17519], [1.07031, 5.15655], [1.27574, 5.93551], [1.19771, 6.11522], [1.19966, 6.17069], [1.09187, 6.17074], [1.05969, 6.22998], [1.03108, 6.24064], [0.99652, 6.33779], [0.89283, 6.33779], [0.71048, 6.53083], [0.74862, 6.56517], [0.63659, 6.63857], [0.6497, 6.73682], [0.58176, 6.76049], [0.57406, 6.80348], [0.52853, 6.82921], [0.56508, 6.92971], [0.52098, 6.94391], [0.52217, 6.9723], [0.59606, 7.01252], [0.65327, 7.31643], [0.62943, 7.41099], [0.57223, 7.39326], [0.52455, 7.45354], [0.51979, 7.58706], [0.58295, 7.62368], [0.62943, 7.85751], [0.58891, 8.12779], [0.6056, 8.13959], [0.61156, 8.18324], [0.5913, 8.19622], [0.63897, 8.25873], [0.73432, 8.29529], [0.64731, 8.48866], [0.47211, 8.59945], [0.37319, 8.75262], [0.52455, 8.87746], [0.45424, 9.04581], [0.56388, 9.40697], [0.49118, 9.48339], [0.36485, 9.49749], [0.33148, 9.44812], [0.25758, 9.42696], [0.2254, 9.47869], [0.31241, 9.50337], [0.30406, 9.521], [0.2409, 9.52335], [0.23851, 9.57389], [0.38153, 9.58682], [0.36008, 9.6256], [0.29334, 9.59387], [0.26712, 9.66437], [0.28261, 9.69022], [0.32313, 9.6491], [0.34816, 9.66907], [0.34816, 9.71607], [0.32075, 9.72781], [0.36366, 10.03309], [0.41252, 10.02018], [0.41371, 10.06361], [0.35293, 10.09412], [0.39584, 10.31112], [0.33028, 10.30408], [0.29453, 10.41546], [0.18846, 10.4096], [0.12886, 10.53149], [-0.05945, 10.63458], [-0.09141, 10.7147], [-0.07327, 10.71845], [-0.07183, 10.76794], [-0.0228, 10.81916], [-0.02685, 10.8783], [-908e-5, 10.91644], [-63e-4, 10.96417], [0.03355, 10.9807], [0.02395, 11.06229], [342e-5, 11.08317], [-514e-5, 11.10763], [-0.0275, 11.11202], [-0.05733, 11.08628], [-0.14462, 11.10811], [-0.13493, 11.14075]]]]
24295 nameEn: "Gibraltar",
24297 groups: ["Q12837", "BOTS", "039", "150", "UN"],
24298 callingCodes: ["350"]
24301 type: "MultiPolygon",
24302 coordinates: [[[[-5.34064, 36.03744], [-5.27801, 36.14942], [-5.33822, 36.15272], [-5.34536, 36.15501], [-5.40526, 36.15488], [-5.34064, 36.03744]]]]
24311 nameEn: "Greenland",
24313 groups: ["Q1451600", "021", "003", "019", "UN"],
24314 callingCodes: ["299"]
24317 type: "MultiPolygon",
24318 coordinates: [[[[-49.33696, 84.57952], [-68.21821, 80.48551], [-77.52957, 77.23408], [-46.37635, 57.3249], [-9.68082, 72.73731], [-5.7106, 84.28058], [-49.33696, 84.57952]]]]
24327 nameEn: "The Gambia",
24328 groups: ["011", "202", "002", "UN"],
24329 callingCodes: ["220"]
24332 type: "MultiPolygon",
24333 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]]]]
24343 groups: ["011", "202", "002", "UN"],
24344 callingCodes: ["224"]
24347 type: "MultiPolygon",
24348 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]]]]
24356 wikidata: "Q17012",
24357 nameEn: "Guadeloupe",
24359 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
24360 callingCodes: ["590"]
24363 type: "MultiPolygon",
24364 coordinates: [[[[-60.03183, 16.1129], [-61.60296, 16.73066], [-63.00549, 15.26166], [-60.03183, 16.1129]]]]
24373 nameEn: "Equatorial Guinea",
24374 groups: ["017", "202", "002", "UN"],
24375 callingCodes: ["240"]
24378 type: "MultiPolygon",
24379 coordinates: [[[[9.22018, 3.72052], [8.34397, 4.30689], [7.71762, 0.6674], [3.35016, -3.29031], [9.66433, 1.06723], [9.75065, 1.06753], [9.79648, 1.0019], [11.35307, 1.00251], [11.3561, 2.17217], [9.991, 2.16561], [9.90749, 2.20049], [9.89012, 2.20457], [9.84716, 2.24676], [9.83238, 2.29079], [9.83754, 2.32428], [9.82123, 2.35097], [9.81162, 2.33797], [9.22018, 3.72052]]]]
24390 groups: ["EU", "039", "150", "UN"],
24391 callingCodes: ["30"]
24394 type: "MultiPolygon",
24395 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]]]]
24403 wikidata: "Q35086",
24404 nameEn: "South Georgia and South Sandwich Islands",
24406 groups: ["BOTS", "005", "419", "019", "UN"],
24408 roadSpeedUnit: "mph",
24409 roadHeightUnit: "ft",
24410 callingCodes: ["500"]
24413 type: "MultiPolygon",
24414 coordinates: [[[[-35.26394, -43.68272], [-53.39656, -59.87088], [-22.31757, -59.85974], [-35.26394, -43.68272]]]]
24423 nameEn: "Guatemala",
24424 groups: ["013", "003", "419", "019", "UN"],
24425 callingCodes: ["502"]
24428 type: "MultiPolygon",
24429 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]]]]
24437 wikidata: "Q16635",
24439 aliases: ["US-GU"],
24441 groups: ["Q1352230", "Q153732", "057", "009", "UN"],
24442 roadSpeedUnit: "mph",
24443 roadHeightUnit: "ft",
24444 callingCodes: ["1 671"]
24447 type: "MultiPolygon",
24448 coordinates: [[[[146.25931, 13.85876], [143.82485, 13.92273], [144.61642, 12.82462], [146.25931, 13.85876]]]]
24457 nameEn: "Guinea-Bissau",
24458 groups: ["011", "202", "002", "UN"],
24459 callingCodes: ["245"]
24462 type: "MultiPolygon",
24463 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]]]]
24473 groups: ["005", "419", "019", "UN"],
24475 callingCodes: ["592"]
24478 type: "MultiPolygon",
24479 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]]]]
24488 nameEn: "Hong Kong",
24490 groups: ["030", "142", "UN"],
24492 callingCodes: ["852"]
24495 type: "MultiPolygon",
24496 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]]]]
24504 wikidata: "Q131198",
24505 nameEn: "Heard Island and McDonald Islands",
24507 groups: ["053", "009", "UN"],
24511 type: "MultiPolygon",
24512 coordinates: [[[[71.08716, -53.87687], [75.44182, -53.99822], [72.87012, -51.48322], [71.08716, -53.87687]]]]
24521 nameEn: "Honduras",
24522 groups: ["013", "003", "419", "019", "UN"],
24523 callingCodes: ["504"]
24526 type: "MultiPolygon",
24527 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]]]]
24537 groups: ["EU", "039", "150", "UN"],
24538 callingCodes: ["385"]
24541 type: "MultiPolygon",
24542 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]]]]
24553 groups: ["029", "003", "419", "019", "UN"],
24554 callingCodes: ["509"]
24557 type: "MultiPolygon",
24558 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]]]]
24568 groups: ["EU", "151", "150", "UN"],
24569 callingCodes: ["36"]
24572 type: "MultiPolygon",
24573 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]]]]
24580 nameEn: "Canary Islands",
24582 groups: ["Q3320166", "Q105472", "EU", "039", "150", "UN"],
24583 isoStatus: "excRes",
24584 callingCodes: ["34"]
24587 type: "MultiPolygon",
24588 coordinates: [[[[-12.00985, 30.24121], [-25.3475, 27.87574], [-14.43883, 27.02969], [-12.00985, 30.24121]]]]
24597 nameEn: "Indonesia",
24608 nameEn: "Republic of Ireland",
24609 groups: ["EU", "Q22890", "154", "150", "UN"],
24611 callingCodes: ["353"]
24614 type: "MultiPolygon",
24615 coordinates: [[[[-6.26218, 54.09785], [-6.29003, 54.11278], [-6.32694, 54.09337], [-6.36279, 54.11248], [-6.36605, 54.07234], [-6.47849, 54.06947], [-6.62842, 54.03503], [-6.66264, 54.0666], [-6.6382, 54.17071], [-6.70175, 54.20218], [-6.74575, 54.18788], [-6.81583, 54.22791], [-6.85179, 54.29176], [-6.87775, 54.34682], [-7.02034, 54.4212], [-7.19145, 54.31296], [-7.14908, 54.22732], [-7.25012, 54.20063], [-7.26316, 54.13863], [-7.29493, 54.12013], [-7.29687, 54.1354], [-7.28017, 54.16714], [-7.29157, 54.17191], [-7.34005, 54.14698], [-7.30553, 54.11869], [-7.32834, 54.11475], [-7.44567, 54.1539], [-7.4799, 54.12239], [-7.55812, 54.12239], [-7.69501, 54.20731], [-7.81397, 54.20159], [-7.8596, 54.21779], [-7.87101, 54.29299], [-8.04555, 54.36292], [-8.179, 54.46763], [-8.04538, 54.48941], [-7.99812, 54.54427], [-7.8596, 54.53671], [-7.70315, 54.62077], [-7.93293, 54.66603], [-7.83352, 54.73854], [-7.75041, 54.7103], [-7.64449, 54.75265], [-7.54671, 54.74606], [-7.54508, 54.79401], [-7.47626, 54.83084], [-7.4473, 54.87003], [-7.44404, 54.9403], [-7.40004, 54.94498], [-7.4033, 55.00391], [-7.34464, 55.04688], [-7.2471, 55.06933], [-6.34755, 55.49206], [-7.75229, 55.93854], [-22.01468, 48.19557], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]]
24625 groups: ["145", "142", "UN"],
24626 callingCodes: ["972"]
24629 type: "MultiPolygon",
24630 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]]]]
24639 nameEn: "Isle of Man",
24641 groups: ["Q185086", "154", "150", "UN"],
24643 roadSpeedUnit: "mph",
24644 roadHeightUnit: "ft",
24645 callingCodes: ["44 01624", "44 07624", "44 07524", "44 07924"]
24648 type: "MultiPolygon",
24649 coordinates: [[[[-3.98763, 54.07351], [-4.1819, 54.57861], [-5.6384, 53.81157], [-3.98763, 54.07351]]]]
24667 wikidata: "Q43448",
24668 nameEn: "British Indian Ocean Territory",
24680 groups: ["145", "142", "UN"],
24681 callingCodes: ["964"]
24684 type: "MultiPolygon",
24685 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]]]]
24695 groups: ["034", "142", "UN"],
24696 callingCodes: ["98"]
24699 type: "MultiPolygon",
24700 coordinates: [[[[44.96746, 39.42998], [44.88916, 39.59653], [44.81043, 39.62677], [44.71806, 39.71124], [44.65422, 39.72163], [44.6137, 39.78393], [44.47298, 39.68788], [44.48111, 39.61579], [44.41849, 39.56659], [44.42832, 39.4131], [44.37921, 39.4131], [44.29818, 39.378], [44.22452, 39.4169], [44.03667, 39.39223], [44.1043, 39.19842], [44.20946, 39.13975], [44.18863, 38.93881], [44.30322, 38.81581], [44.26155, 38.71427], [44.28065, 38.6465], [44.32058, 38.62752], [44.3207, 38.49799], [44.3119, 38.37887], [44.38309, 38.36117], [44.44386, 38.38295], [44.50115, 38.33939], [44.42476, 38.25763], [44.22509, 37.88859], [44.3883, 37.85433], [44.45948, 37.77065], [44.55498, 37.783], [44.62096, 37.71985], [44.56887, 37.6429], [44.61401, 37.60165], [44.58449, 37.45018], [44.81021, 37.2915], [44.75986, 37.21549], [44.7868, 37.16644], [44.78319, 37.1431], [44.75229, 37.11958], [44.81611, 37.04383], [44.89862, 37.01897], [44.91199, 36.91468], [44.90173, 36.86096], [44.83479, 36.81362], [44.84725, 36.77622], [45.01537, 36.75128], [45.06985, 36.6814], [45.06985, 36.62645], [45.00759, 36.5402], [45.11811, 36.40751], [45.23953, 36.43257], [45.27394, 36.35846], [45.26261, 36.3001], [45.30038, 36.27769], [45.32235, 36.17383], [45.37312, 36.09917], [45.37652, 36.06222], [45.33916, 35.99424], [45.38275, 35.97156], [45.46594, 36.00042], [45.55245, 35.99943], [45.60018, 35.96069], [45.6645, 35.92872], [45.76145, 35.79898], [45.81442, 35.82107], [45.89784, 35.83708], [45.94711, 35.82218], [46.08325, 35.8581], [46.17198, 35.8013], [46.32921, 35.82655], [46.34166, 35.78363], [46.23736, 35.71414], [46.01631, 35.69139], [46.0117, 35.65059], [45.99452, 35.63574], [46.0165, 35.61501], [46.01307, 35.59756], [46.03028, 35.57416], [45.97584, 35.58132], [46.01518, 35.52012], [45.98453, 35.49848], [46.05358, 35.38568], [46.13152, 35.32548], [46.15474, 35.2883], [46.11367, 35.23729], [46.18457, 35.22561], [46.19738, 35.18536], [46.16229, 35.16984], [46.15642, 35.1268], [46.19116, 35.11097], [46.11763, 35.07551], [46.07747, 35.0838], [46.06508, 35.03699], [45.94756, 35.09188], [45.93108, 35.08148], [45.92203, 35.09538], [45.92173, 35.0465], [45.87864, 35.03441], [45.89477, 34.95805], [45.86532, 34.89858], [45.78904, 34.91135], [45.79682, 34.85133], [45.73641, 34.83975], [45.70031, 34.82322], [45.68284, 34.76624], [45.65672, 34.7222], [45.70031, 34.69277], [45.73923, 34.54416], [45.60224, 34.55057], [45.59074, 34.55558], [45.53219, 34.60441], [45.51883, 34.47692], [45.43879, 34.45949], [45.46697, 34.38221], [45.49171, 34.3439], [45.53552, 34.35148], [45.58667, 34.30147], [45.56176, 34.15088], [45.47264, 34.03099], [45.41077, 33.97421], [45.42789, 33.9458], [45.50261, 33.94968], [45.77814, 33.60938], [45.89801, 33.63661], [45.96183, 33.55751], [45.86687, 33.49263], [45.99919, 33.5082], [46.20623, 33.20395], [46.11905, 33.11924], [46.05367, 33.13097], [46.03966, 33.09577], [46.15175, 33.07229], [46.09103, 32.98354], [46.17198, 32.95612], [46.32298, 32.9731], [46.46788, 32.91992], [47.17218, 32.45393], [47.37529, 32.47808], [47.57144, 32.20583], [47.52474, 32.15972], [47.64771, 32.07666], [47.86337, 31.78422], [47.6804, 31.39086], [47.68219, 31.00004], [48.03221, 30.9967], [48.02443, 30.4789], [48.14585, 30.44133], [48.18321, 30.39703], [48.19425, 30.32796], [48.21279, 30.31644], [48.24385, 30.33846], [48.26393, 30.3408], [48.41117, 30.19846], [48.41671, 30.17254], [48.38714, 30.13485], [48.38869, 30.11062], [48.43384, 30.08233], [48.4494, 30.04456], [48.44785, 30.00148], [48.51011, 29.96238], [48.61441, 29.93675], [48.83867, 29.78572], [49.98877, 27.87827], [50.37726, 27.89227], [54.39838, 25.68383], [55.14145, 25.62624], [55.81777, 26.18798], [56.2644, 26.58649], [56.68954, 26.76645], [56.79239, 26.41236], [56.82555, 25.7713], [56.86325, 25.03856], [61.46682, 24.57869], [61.6433, 25.27541], [61.683, 25.66638], [61.83968, 25.7538], [61.83831, 26.07249], [61.89391, 26.26251], [62.05117, 26.31647], [62.21304, 26.26601], [62.31484, 26.528], [62.77352, 26.64099], [63.1889, 26.65072], [63.18688, 26.83844], [63.25005, 26.84212], [63.25005, 27.08692], [63.32283, 27.14437], [63.19649, 27.25674], [62.80604, 27.22412], [62.79684, 27.34381], [62.84905, 27.47627], [62.7638, 28.02992], [62.79412, 28.28108], [62.59499, 28.24842], [62.40259, 28.42703], [61.93581, 28.55284], [61.65978, 28.77937], [61.53765, 29.00507], [61.31508, 29.38903], [60.87231, 29.86514], [61.80829, 30.84224], [61.78268, 30.92724], [61.8335, 30.97669], [61.83257, 31.0452], [61.80957, 31.12576], [61.80569, 31.16167], [61.70929, 31.37391], [60.84541, 31.49561], [60.86191, 32.22565], [60.56485, 33.12944], [60.88908, 33.50219], [60.91133, 33.55596], [60.69573, 33.56054], [60.57762, 33.59772], [60.5485, 33.73422], [60.5838, 33.80793], [60.50209, 34.13992], [60.66502, 34.31539], [60.91321, 34.30411], [60.72316, 34.52857], [60.99922, 34.63064], [61.00197, 34.70631], [61.06926, 34.82139], [61.12831, 35.09938], [61.0991, 35.27845], [61.18187, 35.30249], [61.27371, 35.61482], [61.22719, 35.67038], [61.26152, 35.80749], [61.22444, 35.92879], [61.12007, 35.95992], [61.22719, 36.12759], [61.1393, 36.38782], [61.18187, 36.55348], [61.14516, 36.64644], [60.34767, 36.63214], [60.00768, 37.04102], [59.74678, 37.12499], [59.55178, 37.13594], [59.39385, 37.34257], [59.39797, 37.47892], [59.33507, 37.53146], [59.22905, 37.51161], [58.9338, 37.67374], [58.6921, 37.64548], [58.5479, 37.70526], [58.47786, 37.6433], [58.39959, 37.63134], [58.22999, 37.6856], [58.21399, 37.77281], [57.79534, 37.89299], [57.35042, 37.98546], [57.37236, 38.09321], [57.21169, 38.28965], [57.03453, 38.18717], [56.73928, 38.27887], [56.62255, 38.24005], [56.43303, 38.26054], [56.32454, 38.18502], [56.33278, 38.08132], [55.97847, 38.08024], [55.76561, 38.12238], [55.44152, 38.08564], [55.13412, 37.94705], [54.851, 37.75739], [54.77684, 37.62264], [54.81804, 37.61285], [54.77822, 37.51597], [54.67247, 37.43532], [54.58664, 37.45809], [54.36211, 37.34912], [54.24565, 37.32047], [53.89734, 37.3464], [48.88288, 38.43975], [48.84969, 38.45015], [48.81072, 38.44853], [48.78979, 38.45026], [48.70001, 38.40564], [48.62217, 38.40198], [48.58793, 38.45076], [48.45084, 38.61013], [48.3146, 38.59958], [48.24773, 38.71883], [48.02581, 38.82705], [48.01409, 38.90333], [48.07734, 38.91616], [48.08627, 38.94434], [48.28437, 38.97186], [48.33884, 39.03022], [48.31239, 39.09278], [48.15361, 39.19419], [48.12404, 39.25208], [48.15984, 39.30028], [48.37385, 39.37584], [48.34264, 39.42935], [47.98977, 39.70999], [47.84774, 39.66285], [47.50099, 39.49615], [47.38978, 39.45999], [47.31301, 39.37492], [47.05927, 39.24846], [47.05771, 39.20143], [46.95341, 39.13505], [46.92539, 39.16644], [46.83822, 39.13143], [46.75752, 39.03231], [46.53497, 38.86548], [46.34059, 38.92076], [46.20601, 38.85262], [46.14785, 38.84206], [46.06766, 38.87861], [46.00228, 38.87376], [45.94624, 38.89072], [45.90266, 38.87739], [45.83883, 38.90768], [45.65172, 38.95199], [45.6155, 38.94304], [45.6131, 38.964], [45.44966, 38.99243], [45.44811, 39.04927], [45.40452, 39.07224], [45.40148, 39.09007], [45.30489, 39.18333], [45.16168, 39.21952], [45.08751, 39.35052], [45.05932, 39.36435], [44.96746, 39.42998]]]]
24710 groups: ["154", "150", "UN"],
24711 callingCodes: ["354"]
24714 type: "MultiPolygon",
24715 coordinates: [[[[-33.15676, 62.62995], [-8.25539, 63.0423], [-15.70914, 69.67442], [-33.15676, 62.62995]]]]
24725 groups: ["EU", "039", "150", "UN"],
24726 callingCodes: ["39"]
24729 type: "MultiPolygon",
24730 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]]]]
24739 nameEn: "Bailiwick of Jersey",
24741 groups: ["830", "Q185086", "154", "150", "UN"],
24743 roadSpeedUnit: "mph",
24744 roadHeightUnit: "ft",
24745 callingCodes: ["44 01534"]
24748 type: "MultiPolygon",
24749 coordinates: [[[[-2.00491, 48.86706], [-1.83944, 49.23037], [-2.09454, 49.46288], [-2.65349, 49.15373], [-2.00491, 48.86706]]]]
24760 groups: ["029", "003", "419", "019", "UN"],
24762 callingCodes: ["1 876", "1 658"]
24765 type: "MultiPolygon",
24766 coordinates: [[[[-74.09729, 17.36817], [-78.9741, 19.59515], [-78.34606, 16.57862], [-74.09729, 17.36817]]]]
24776 groups: ["145", "142", "UN"],
24777 callingCodes: ["962"]
24780 type: "MultiPolygon",
24781 coordinates: [[[[39.04251, 32.30203], [38.98762, 32.47694], [39.08202, 32.50304], [38.79171, 33.37328], [36.83946, 32.31293], [36.40959, 32.37908], [36.23948, 32.50108], [36.20875, 32.49529], [36.20379, 32.52751], [36.08074, 32.51463], [36.02239, 32.65911], [35.96633, 32.66237], [35.93307, 32.71966], [35.88405, 32.71321], [35.75983, 32.74803], [35.68467, 32.70715], [35.66527, 32.681], [35.61669, 32.67999], [35.59813, 32.65159], [35.56614, 32.64393], [35.57485, 32.48669], [35.55494, 32.42687], [35.55807, 32.38674], [35.57111, 32.21877], [35.52012, 32.04076], [35.54375, 31.96587], [35.52758, 31.9131], [35.55941, 31.76535], [35.47672, 31.49578], [35.40316, 31.25535], [35.43658, 31.12444], [35.41371, 30.95565], [35.33984, 30.8802], [35.33456, 30.81224], [35.29311, 30.71365], [35.21379, 30.60401], [35.19595, 30.50297], [35.16218, 30.43535], [35.19183, 30.34636], [35.14108, 30.07374], [35.02147, 29.66343], [34.98207, 29.58147], [34.97718, 29.54294], [34.92298, 29.45305], [34.8812, 29.36878], [36.07081, 29.18469], [36.50005, 29.49696], [36.75083, 29.86903], [37.4971, 29.99949], [37.66395, 30.33245], [37.99354, 30.49998], [36.99791, 31.50081], [38.99233, 31.99721], [39.29903, 32.23259], [39.26157, 32.35555], [39.04251, 32.30203]]]]
24791 groups: ["030", "142", "UN"],
24793 callingCodes: ["81"]
24796 type: "MultiPolygon",
24797 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]]]]
24807 groups: ["014", "202", "002", "UN"],
24809 callingCodes: ["254"]
24812 type: "MultiPolygon",
24813 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]]]]
24822 nameEn: "Kyrgyzstan",
24823 groups: ["143", "142", "UN"],
24824 callingCodes: ["996"]
24827 type: "MultiPolygon",
24828 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]]]]
24837 nameEn: "Cambodia",
24838 groups: ["035", "142", "UN"],
24839 callingCodes: ["855"]
24842 type: "MultiPolygon",
24843 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]]]]
24852 nameEn: "Kiribati",
24853 groups: ["057", "009", "UN"],
24855 callingCodes: ["686"]
24858 type: "MultiPolygon",
24859 coordinates: [[[[169, 3.9], [169, -3.5], [178, -3.5], [178, 3.9], [169, 3.9]]], [[[-161.06795, 5.2462], [-158.12991, -1.86122], [-175.33482, -1.40631], [-175.31804, -7.54825], [-156.50903, -7.4975], [-156.48634, -15.52824], [-135.59706, -4.70473], [-161.06795, 5.2462]]]]
24869 groups: ["014", "202", "002", "UN"],
24870 callingCodes: ["269"]
24873 type: "MultiPolygon",
24874 coordinates: [[[[42.63904, -10.02522], [43.28731, -13.97126], [45.4971, -11.75965], [42.63904, -10.02522]]]]
24883 nameEn: "St. Kitts and Nevis",
24884 groups: ["029", "003", "419", "019", "UN"],
24886 roadSpeedUnit: "mph",
24887 callingCodes: ["1 869"]
24890 type: "MultiPolygon",
24891 coordinates: [[[[-62.29333, 17.43155], [-62.76692, 17.64353], [-63.09677, 17.21372], [-62.63813, 16.65446], [-62.29333, 17.43155]]]]
24900 nameEn: "North Korea",
24901 groups: ["030", "142", "UN"],
24902 callingCodes: ["850"]
24905 type: "MultiPolygon",
24906 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]]]]
24915 nameEn: "South Korea",
24916 groups: ["030", "142", "UN"],
24917 callingCodes: ["82"]
24920 type: "MultiPolygon",
24921 coordinates: [[[[133.11729, 37.53115], [128.65655, 38.61914], [128.37487, 38.62345], [128.31105, 38.58462], [128.27652, 38.41657], [128.02917, 38.31861], [127.55013, 38.32257], [127.49672, 38.30647], [127.38727, 38.33227], [127.15749, 38.30722], [127.04479, 38.25518], [126.95338, 38.17735], [126.95887, 38.1347], [126.88106, 38.10246], [126.84961, 38.0344], [126.67023, 37.95852], [126.68793, 37.9175], [126.68793, 37.83728], [126.66067, 37.7897], [126.59918, 37.76364], [126.56709, 37.76857], [126.46818, 37.80873], [126.43239, 37.84095], [126.24402, 37.83113], [126.19097, 37.81462], [126.18776, 37.74728], [126.13074, 37.70512], [125.81159, 37.72949], [125.37112, 37.62643], [125.06408, 37.66334], [124.87921, 37.80827], [124.84224, 37.977], [124.67666, 38.05679], [123.85601, 37.49093], [122.80525, 33.30571], [125.99728, 32.63328], [129.2669, 34.87122], [133.11729, 37.53115]]]]
24931 groups: ["145", "142", "UN"],
24932 callingCodes: ["965"]
24935 type: "MultiPolygon",
24936 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]]]]
24945 nameEn: "Cayman Islands",
24947 groups: ["BOTS", "029", "003", "419", "019", "UN"],
24949 roadSpeedUnit: "mph",
24950 roadHeightUnit: "ft",
24951 callingCodes: ["1 345"]
24954 type: "MultiPolygon",
24955 coordinates: [[[[-82.11509, 19.60401], [-80.36068, 18.11751], [-79.32727, 20.06742], [-82.11509, 19.60401]]]]
24964 nameEn: "Kazakhstan",
24965 groups: ["143", "142", "UN"],
24966 callingCodes: ["7"]
24969 type: "MultiPolygon",
24970 coordinates: [[[[68.90865, 55.38148], [68.19206, 55.18823], [68.26661, 55.09226], [68.21308, 54.98645], [65.20174, 54.55216], [65.24663, 54.35721], [65.11033, 54.33028], [64.97216, 54.4212], [63.97686, 54.29763], [64.02715, 54.22679], [63.91224, 54.20013], [63.80604, 54.27079], [62.58651, 54.05871], [62.56876, 53.94047], [62.45931, 53.90737], [62.38535, 54.03961], [62.00966, 54.04134], [62.03913, 53.94768], [61.65318, 54.02445], [61.56941, 53.95703], [61.47603, 54.08048], [61.3706, 54.08464], [61.26863, 53.92797], [60.99796, 53.93699], [61.14283, 53.90063], [61.22574, 53.80268], [60.90626, 53.62937], [61.55706, 53.57144], [61.57185, 53.50112], [61.37957, 53.45887], [61.29082, 53.50992], [61.14291, 53.41481], [61.19024, 53.30536], [62.14574, 53.09626], [62.12799, 52.99133], [62.0422, 52.96105], [61.23462, 53.03227], [61.05842, 52.92217], [60.71989, 52.75923], [60.71693, 52.66245], [60.84118, 52.63912], [60.84709, 52.52228], [60.98021, 52.50068], [61.05417, 52.35096], [60.78201, 52.22067], [60.72581, 52.15538], [60.48915, 52.15175], [60.19925, 51.99173], [59.99809, 51.98263], [60.09867, 51.87135], [60.50986, 51.7964], [60.36787, 51.66815], [60.5424, 51.61675], [60.92401, 51.61124], [60.95655, 51.48615], [61.50677, 51.40687], [61.55114, 51.32746], [61.6813, 51.25716], [61.56889, 51.23679], [61.4431, 50.80679], [60.81833, 50.6629], [60.31914, 50.67705], [60.17262, 50.83312], [60.01288, 50.8163], [59.81172, 50.54451], [59.51886, 50.49937], [59.48928, 50.64216], [58.87974, 50.70852], [58.3208, 51.15151], [57.75578, 51.13852], [57.74986, 50.93017], [57.44221, 50.88354], [57.17302, 51.11253], [56.17906, 50.93204], [56.11398, 50.7471], [55.67774, 50.54508], [54.72067, 51.03261], [54.56685, 51.01958], [54.71476, 50.61214], [54.55797, 50.52006], [54.41894, 50.61214], [54.46331, 50.85554], [54.12248, 51.11542], [53.69299, 51.23466], [53.46165, 51.49445], [52.54329, 51.48444], [52.36119, 51.74161], [51.8246, 51.67916], [51.77431, 51.49536], [51.301, 51.48799], [51.26254, 51.68466], [50.59695, 51.61859], [50.26859, 51.28677], [49.97277, 51.2405], [49.76866, 51.11067], [49.39001, 51.09396], [49.41959, 50.85927], [49.12673, 50.78639], [48.86936, 50.61589], [48.57946, 50.63278], [48.90782, 50.02281], [48.68352, 49.89546], [48.42564, 49.82283], [48.24519, 49.86099], [48.10044, 50.09242], [47.58551, 50.47867], [47.30448, 50.30894], [47.34589, 50.09308], [47.18319, 49.93721], [46.9078, 49.86707], [46.78398, 49.34026], [47.04658, 49.19834], [47.00857, 49.04921], [46.78392, 48.95352], [46.49011, 48.43019], [47.11516, 48.27188], [47.12107, 47.83687], [47.38731, 47.68176], [47.41689, 47.83687], [47.64973, 47.76559], [48.15348, 47.74545], [48.45173, 47.40818], [48.52326, 47.4102], [49.01136, 46.72716], [48.51142, 46.69268], [48.54988, 46.56267], [49.16518, 46.38542], [49.32259, 46.26944], [49.88945, 46.04554], [49.2134, 44.84989], [52.26048, 41.69249], [52.47884, 41.78034], [52.97575, 42.1308], [54.20635, 42.38477], [54.95182, 41.92424], [55.45471, 41.25609], [56.00314, 41.32584], [55.97584, 44.99322], [55.97584, 44.99328], [55.97584, 44.99338], [55.97584, 44.99343], [55.97584, 44.99348], [55.97584, 44.99353], [55.97584, 44.99359], [55.97584, 44.99369], [55.97584, 44.99374], [55.97584, 44.99384], [55.97584, 44.9939], [55.97584, 44.994], [55.97584, 44.99405], [55.97584, 44.99415], [55.97584, 44.99421], [55.97584, 44.99426], [55.97584, 44.99431], [55.97584, 44.99436], [55.97584, 44.99441], [55.97594, 44.99446], [55.97605, 44.99452], [55.97605, 44.99457], [55.97605, 44.99462], [55.97605, 44.99467], [55.97605, 44.99477], [55.97615, 44.99477], [55.97615, 44.99483], [55.97615, 44.99493], [55.97615, 44.99498], [55.97615, 44.99503], [55.97615, 44.99508], [55.97625, 44.99514], [55.97636, 44.99519], [55.97636, 44.99524], [55.97646, 44.99529], [55.97646, 44.99534], [55.97656, 44.99539], [55.97667, 44.99545], [55.97677, 44.9955], [55.97677, 44.99555], [55.97677, 44.9956], [55.97687, 44.9956], [55.97698, 44.99565], [55.97698, 44.9957], [55.97708, 44.99576], [55.97718, 44.99581], [55.97729, 44.99586], [55.97739, 44.99586], [55.97739, 44.99591], [55.97749, 44.99591], [55.9776, 44.99591], [55.9777, 44.99596], [55.9777, 44.99601], [55.9778, 44.99607], [55.97791, 44.99607], [55.97801, 44.99607], [55.97801, 44.99612], [55.97811, 44.99617], [55.97822, 44.99617], [55.97832, 44.99622], [55.97842, 44.99622], [58.59711, 45.58671], [61.01475, 44.41383], [62.01711, 43.51008], [63.34656, 43.64003], [64.53885, 43.56941], [64.96464, 43.74748], [65.18666, 43.48835], [65.53277, 43.31856], [65.85194, 42.85481], [66.09482, 42.93426], [66.00546, 41.94455], [66.53302, 41.87388], [66.69129, 41.1311], [67.9644, 41.14611], [67.98511, 41.02794], [68.08273, 41.08148], [68.1271, 41.0324], [67.96736, 40.83798], [68.49983, 40.56437], [68.63, 40.59358], [68.58444, 40.91447], [68.49983, 40.99669], [68.62221, 41.03019], [68.65662, 40.93861], [68.73945, 40.96989], [68.7217, 41.05025], [69.01308, 41.22804], [69.05006, 41.36183], [69.15137, 41.43078], [69.17701, 41.43769], [69.18528, 41.45175], [69.20439, 41.45391], [69.22671, 41.46298], [69.23332, 41.45847], [69.25059, 41.46693], [69.29778, 41.43673], [69.35554, 41.47211], [69.37468, 41.46555], [69.45081, 41.46246], [69.39485, 41.51518], [69.45751, 41.56863], [69.49545, 41.545], [70.94483, 42.26238], [70.85973, 42.30188], [70.97717, 42.50147], [71.15232, 42.60486], [71.17807, 42.67381], [71.22785, 42.69248], [71.2724, 42.77853], [71.53272, 42.8014], [71.62405, 42.76613], [71.88792, 42.83578], [73.44393, 42.43098], [73.50992, 42.82356], [73.55634, 43.03071], [74.22489, 43.24657], [74.57491, 43.13702], [74.64615, 43.05881], [74.70331, 43.02519], [74.75, 42.99029], [74.88756, 42.98612], [75.22619, 42.85528], [75.29966, 42.86183], [75.72174, 42.79672], [75.82823, 42.94848], [78.48469, 42.89649], [78.91502, 42.76839], [79.19763, 42.804], [79.52921, 42.44778], [79.97364, 42.42816], [80.17807, 42.21166], [80.26841, 42.23797], [80.16892, 42.61137], [80.26886, 42.8366], [80.38169, 42.83142], [80.58999, 42.9011], [80.3735, 43.01557], [80.62913, 43.141], [80.78817, 43.14235], [80.77771, 43.30065], [80.69718, 43.32589], [80.75156, 43.44948], [80.40031, 44.10986], [80.40229, 44.23319], [80.38384, 44.63073], [79.8987, 44.89957], [80.11169, 45.03352], [81.73278, 45.3504], [82.51374, 45.1755], [82.58474, 45.40027], [82.21792, 45.56619], [83.04622, 47.19053], [83.92184, 46.98912], [84.73077, 47.01394], [84.93995, 46.87399], [85.22443, 47.04816], [85.54294, 47.06171], [85.69696, 47.2898], [85.61067, 47.49753], [85.5169, 48.05493], [85.73581, 48.3939], [86.38069, 48.46064], [86.75343, 48.70331], [86.73568, 48.99918], [86.87238, 49.12432], [87.28386, 49.11626], [87.31465, 49.23603], [87.03071, 49.25142], [86.82606, 49.51796], [86.61307, 49.60239], [86.79056, 49.74787], [86.63674, 49.80136], [86.18709, 49.50259], [85.24047, 49.60239], [84.99198, 50.06793], [84.29385, 50.27257], [83.8442, 50.87375], [83.14607, 51.00796], [82.55443, 50.75412], [81.94999, 50.79307], [81.46581, 50.77658], [81.41248, 50.97524], [81.06091, 50.94833], [81.16999, 51.15662], [80.80318, 51.28262], [80.44819, 51.20855], [80.4127, 50.95581], [80.08138, 50.77658], [79.11255, 52.01171], [77.90383, 53.29807], [76.54243, 53.99329], [76.44076, 54.16017], [76.82266, 54.1798], [76.91052, 54.4677], [75.3668, 54.07439], [75.43398, 53.98652], [75.07405, 53.80831], [73.39218, 53.44623], [73.25412, 53.61532], [73.68921, 53.86522], [73.74778, 54.07194], [73.37963, 53.96132], [72.71026, 54.1161], [72.43415, 53.92685], [72.17477, 54.36303], [71.96141, 54.17736], [71.10379, 54.13326], [71.08706, 54.33376], [71.24185, 54.64965], [71.08288, 54.71253], [70.96009, 55.10558], [70.76493, 55.3027], [70.19179, 55.1476], [69.74917, 55.35545], [69.34224, 55.36344], [68.90865, 55.38148]]]]
24980 groups: ["035", "142", "UN"],
24981 callingCodes: ["856"]
24984 type: "MultiPolygon",
24985 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]]]]
24996 groups: ["145", "142", "UN"],
24997 callingCodes: ["961"]
25000 type: "MultiPolygon",
25001 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]]]]
25010 nameEn: "St. Lucia",
25012 groups: ["029", "003", "419", "019", "UN"],
25014 roadSpeedUnit: "mph",
25015 callingCodes: ["1 758"]
25018 type: "MultiPolygon",
25019 coordinates: [[[[-59.95997, 14.20285], [-61.69315, 14.26451], [-59.94058, 12.34011], [-59.95997, 14.20285]]]]
25028 nameEn: "Liechtenstein",
25030 groups: ["155", "150", "UN"],
25031 callingCodes: ["423"]
25034 type: "MultiPolygon",
25035 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]]]]
25044 nameEn: "Sri Lanka",
25045 groups: ["034", "142", "UN"],
25047 callingCodes: ["94"]
25050 type: "MultiPolygon",
25051 coordinates: [[[[76.59015, 5.591], [85.15017, 5.21497], [80.48418, 10.20786], [79.42124, 9.80115], [79.50447, 8.91876], [76.59015, 5.591]]]]
25061 groups: ["011", "202", "002", "UN"],
25062 callingCodes: ["231"]
25065 type: "MultiPolygon",
25066 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]]]]
25076 groups: ["018", "202", "002", "UN"],
25078 callingCodes: ["266"]
25081 type: "MultiPolygon",
25082 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]]]]
25091 nameEn: "Lithuania",
25092 groups: ["EU", "154", "150", "UN"],
25093 callingCodes: ["370"]
25096 type: "MultiPolygon",
25097 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]]]]
25106 nameEn: "Luxembourg",
25107 groups: ["EU", "155", "150", "UN"],
25108 callingCodes: ["352"]
25111 type: "MultiPolygon",
25112 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]]]]
25122 groups: ["EU", "154", "150", "UN"],
25123 callingCodes: ["371"]
25126 type: "MultiPolygon",
25127 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]]]]
25137 groups: ["015", "002", "UN"],
25138 callingCodes: ["218"]
25141 type: "MultiPolygon",
25142 coordinates: [[[[26.92891, 33.39516], [11.58941, 33.36891], [11.55852, 33.1409], [11.51549, 33.09826], [11.46037, 32.6307], [11.57828, 32.48013], [11.53898, 32.4138], [11.04234, 32.2145], [10.7315, 31.97235], [10.62788, 31.96629], [10.48497, 31.72956], [10.31364, 31.72648], [10.12239, 31.42098], [10.29516, 30.90337], [9.88152, 30.34074], [9.76848, 30.34366], [9.55544, 30.23971], [9.3876, 30.16738], [9.78136, 29.40961], [9.89569, 26.57696], [9.51696, 26.39148], [9.38834, 26.19288], [10.03146, 25.35635], [10.02432, 24.98124], [10.33159, 24.5465], [10.85323, 24.5595], [11.41061, 24.21456], [11.62498, 24.26669], [11.96886, 23.51735], [13.5631, 23.16574], [14.22918, 22.61719], [14.99751, 23.00539], [15.99566, 23.49639], [23.99539, 19.49944], [23.99715, 20.00038], [24.99794, 19.99661], [24.99885, 21.99535], [24.99968, 29.24574], [24.71117, 30.17441], [25.01077, 30.73861], [24.8458, 31.39877], [26.92891, 33.39516]]]]
25152 groups: ["015", "002", "UN"],
25153 callingCodes: ["212"]
25156 type: "MultiPolygon",
25157 coordinates: [[[[-2.27707, 35.35051], [-5.10878, 36.05227], [-7.2725, 35.73269], [-14.43883, 27.02969], [-17.27295, 21.93519], [-17.21511, 21.34226], [-17.02707, 21.34022], [-16.9978, 21.36239], [-16.44269, 21.39745], [-14.78487, 21.36587], [-14.47329, 21.63839], [-14.48112, 22.00886], [-14.1291, 22.41636], [-14.10361, 22.75501], [-13.75627, 23.77231], [-13.00628, 24.01923], [-12.92147, 24.39502], [-12.12281, 25.13682], [-12.06001, 26.04442], [-11.62052, 26.05229], [-11.38635, 26.611], [-11.23622, 26.72023], [-11.35695, 26.8505], [-10.68417, 26.90984], [-9.81998, 26.71379], [-9.56957, 26.90042], [-9.08698, 26.98639], [-8.71787, 26.9898], [-8.77527, 27.66663], [-8.66879, 27.6666], [-8.6715, 28.71194], [-7.61585, 29.36252], [-6.95824, 29.50924], [-6.78351, 29.44634], [-6.69965, 29.51623], [-5.75616, 29.61407], [-5.72121, 29.52322], [-5.58831, 29.48103], [-5.21671, 29.95253], [-4.6058, 30.28343], [-4.31774, 30.53229], [-3.64735, 30.67539], [-3.65418, 30.85566], [-3.54944, 31.0503], [-3.77103, 31.14984], [-3.77647, 31.31912], [-3.66386, 31.39202], [-3.66314, 31.6339], [-2.82784, 31.79459], [-2.93873, 32.06557], [-2.46166, 32.16603], [-1.22829, 32.07832], [-1.15735, 32.12096], [-1.24453, 32.1917], [-1.24998, 32.32993], [-0.9912, 32.52467], [-1.37794, 32.73628], [-1.54244, 32.95499], [-1.46249, 33.0499], [-1.67067, 33.27084], [-1.59508, 33.59929], [-1.73494, 33.71721], [-1.64666, 34.10405], [-1.78042, 34.39018], [-1.69788, 34.48056], [-1.84569, 34.61907], [-1.73707, 34.74226], [-1.97469, 34.886], [-1.97833, 34.93218], [-2.04734, 34.93218], [-2.21445, 35.04378], [-2.21248, 35.08532], [-2.27707, 35.35051]], [[-2.91909, 35.33927], [-2.92272, 35.27509], [-2.93893, 35.26737], [-2.95065, 35.26576], [-2.95431, 35.2728], [-2.96516, 35.27967], [-2.96826, 35.28296], [-2.96507, 35.28801], [-2.97035, 35.28852], [-2.96978, 35.29459], [-2.96648, 35.30475], [-2.96038, 35.31609], [-2.91909, 35.33927]], [[-3.90602, 35.21494], [-3.89343, 35.22728], [-3.88372, 35.20767], [-3.90602, 35.21494]], [[-4.30191, 35.17419], [-4.29436, 35.17149], [-4.30112, 35.17058], [-4.30191, 35.17419]], [[-2.40316, 35.16893], [-2.45965, 35.16527], [-2.43262, 35.20652], [-2.40316, 35.16893]], [[-5.38491, 35.92591], [-5.21179, 35.90091], [-5.34379, 35.8711], [-5.35844, 35.87375], [-5.37338, 35.88417], [-5.38491, 35.92591]]]]
25167 groups: ["155", "150", "UN"],
25168 callingCodes: ["377"]
25171 type: "MultiPolygon",
25172 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]]]]
25182 groups: ["151", "150", "UN"],
25183 callingCodes: ["373"]
25186 type: "MultiPolygon",
25187 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]]]]
25196 nameEn: "Montenegro",
25197 groups: ["039", "150", "UN"],
25198 callingCodes: ["382"]
25201 type: "MultiPolygon",
25202 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]]]]
25210 wikidata: "Q126125",
25211 nameEn: "Saint-Martin",
25213 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25214 callingCodes: ["590"]
25217 type: "MultiPolygon",
25218 coordinates: [[[[-62.93924, 18.02904], [-62.62718, 18.26185], [-63.35989, 18.06012], [-63.33064, 17.9615], [-63.13502, 18.05445], [-63.11042, 18.05339], [-63.09686, 18.04608], [-63.07759, 18.04943], [-63.0579, 18.06614], [-63.04039, 18.05619], [-63.02323, 18.05757], [-62.93924, 18.02904]]]]
25227 nameEn: "Madagascar",
25229 groups: ["014", "202", "002", "UN"],
25230 callingCodes: ["261"]
25233 type: "MultiPolygon",
25234 coordinates: [[[[51.93891, -10.85085], [45.84651, -12.77177], [42.14681, -19.63341], [45.80092, -33.00974], [51.93891, -10.85085]]]]
25243 nameEn: "Marshall Islands",
25244 groups: ["057", "009", "UN"],
25245 roadSpeedUnit: "mph",
25246 callingCodes: ["692"]
25249 type: "MultiPolygon",
25250 coordinates: [[[[169, 3.9], [173.53711, 5.70687], [169.29099, 15.77133], [159.04653, 10.59067], [169, 3.9]]]]
25259 nameEn: "North Macedonia",
25260 groups: ["039", "150", "UN"],
25261 callingCodes: ["389"]
25264 type: "MultiPolygon",
25265 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]]]]
25275 groups: ["011", "202", "002", "UN"],
25276 callingCodes: ["223"]
25279 type: "MultiPolygon",
25280 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]]]]
25290 aliases: ["Burma", "BU"],
25291 groups: ["035", "142", "UN"],
25292 callingCodes: ["95"]
25295 type: "MultiPolygon",
25296 coordinates: [[[[92.62187, 21.87037], [92.59775, 21.6092], [92.68152, 21.28454], [92.60187, 21.24615], [92.55105, 21.3856], [92.43158, 21.37025], [92.37939, 21.47764], [92.20087, 21.337], [92.17752, 21.17445], [92.26071, 21.05697], [92.47409, 20.38654], [92.61042, 13.76986], [94.6371, 13.81803], [97.63455, 9.60854], [98.12555, 9.44056], [98.33094, 9.91973], [98.47298, 9.95782], [98.52291, 9.92389], [98.55174, 9.92804], [98.7391, 10.31488], [98.81944, 10.52761], [98.77275, 10.62548], [98.78511, 10.68351], [98.86819, 10.78336], [99.0069, 10.85485], [98.99701, 10.92962], [99.02337, 10.97217], [99.06938, 10.94857], [99.32756, 11.28545], [99.31573, 11.32081], [99.39485, 11.3925], [99.47598, 11.62434], [99.5672, 11.62732], [99.64108, 11.78948], [99.64891, 11.82699], [99.53424, 12.02317], [99.56445, 12.14805], [99.47519, 12.1353], [99.409, 12.60603], [99.29254, 12.68921], [99.18905, 12.84799], [99.18748, 12.9898], [99.10646, 13.05804], [99.12225, 13.19847], [99.20617, 13.20575], [99.16695, 13.72621], [98.97356, 14.04868], [98.56762, 14.37701], [98.24874, 14.83013], [98.18821, 15.13125], [98.22, 15.21327], [98.30446, 15.30667], [98.40522, 15.25268], [98.41906, 15.27103], [98.39351, 15.34177], [98.4866, 15.39154], [98.56027, 15.33471], [98.58598, 15.46821], [98.541, 15.65406], [98.59853, 15.87197], [98.57019, 16.04578], [98.69585, 16.13353], [98.8376, 16.11706], [98.92656, 16.36425], [98.84485, 16.42354], [98.68074, 16.27068], [98.63817, 16.47424], [98.57912, 16.55983], [98.5695, 16.62826], [98.51113, 16.64503], [98.51833, 16.676], [98.51472, 16.68521], [98.51579, 16.69433], [98.51043, 16.70107], [98.49713, 16.69022], [98.50253, 16.7139], [98.46994, 16.73613], [98.53833, 16.81934], [98.49603, 16.8446], [98.52624, 16.89979], [98.39441, 17.06266], [98.34566, 17.04822], [98.10439, 17.33847], [98.11185, 17.36829], [97.91829, 17.54504], [97.76407, 17.71595], [97.66794, 17.88005], [97.73723, 17.97912], [97.60841, 18.23846], [97.64116, 18.29778], [97.56219, 18.33885], [97.50383, 18.26844], [97.34522, 18.54596], [97.36444, 18.57138], [97.5258, 18.4939], [97.76752, 18.58097], [97.73836, 18.88478], [97.66487, 18.9371], [97.73654, 18.9812], [97.73797, 19.04261], [97.83479, 19.09972], [97.84024, 19.22217], [97.78606, 19.26769], [97.84186, 19.29526], [97.78769, 19.39429], [97.88423, 19.5041], [97.84715, 19.55782], [98.04364, 19.65755], [98.03314, 19.80941], [98.13829, 19.78541], [98.24884, 19.67876], [98.51182, 19.71303], [98.56065, 19.67807], [98.83661, 19.80931], [98.98679, 19.7419], [99.0735, 20.10298], [99.20328, 20.12877], [99.416, 20.08614], [99.52943, 20.14811], [99.5569, 20.20676], [99.46077, 20.36198], [99.46008, 20.39673], [99.68255, 20.32077], [99.81096, 20.33687], [99.86383, 20.44371], [99.88211, 20.44488], [99.88451, 20.44596], [99.89168, 20.44548], [99.89301, 20.44311], [99.89692, 20.44789], [99.90499, 20.4487], [99.91616, 20.44986], [99.95721, 20.46301], [100.08404, 20.36626], [100.1957, 20.68247], [100.36375, 20.82783], [100.51079, 20.82194], [100.60112, 20.8347], [100.64628, 20.88279], [100.50974, 20.88574], [100.55281, 21.02796], [100.63578, 21.05639], [100.72716, 21.31786], [100.80173, 21.2934], [101.00234, 21.39612], [101.16198, 21.52808], [101.15156, 21.56129], [101.11744, 21.77659], [100.87265, 21.67396], [100.72143, 21.51898], [100.57861, 21.45637], [100.4811, 21.46148], [100.42892, 21.54325], [100.35201, 21.53176], [100.25863, 21.47043], [100.18447, 21.51898], [100.1625, 21.48704], [100.12542, 21.50365], [100.10757, 21.59945], [100.17486, 21.65306], [100.12679, 21.70539], [100.04956, 21.66843], [99.98654, 21.71064], [99.94003, 21.82782], [99.99084, 21.97053], [99.96612, 22.05965], [99.85351, 22.04183], [99.47585, 22.13345], [99.33166, 22.09656], [99.1552, 22.15874], [99.19176, 22.16983], [99.17318, 22.18025], [99.28771, 22.4105], [99.37972, 22.50188], [99.38247, 22.57544], [99.31243, 22.73893], [99.45654, 22.85726], [99.43537, 22.94086], [99.54218, 22.90014], [99.52214, 23.08218], [99.34127, 23.13099], [99.25741, 23.09025], [99.04601, 23.12215], [99.05975, 23.16382], [98.88597, 23.18656], [98.92515, 23.29535], [98.93958, 23.31414], [98.87573, 23.33038], [98.92104, 23.36946], [98.87683, 23.48995], [98.82877, 23.47908], [98.80294, 23.5345], [98.88396, 23.59555], [98.81775, 23.694], [98.82933, 23.72921], [98.79607, 23.77947], [98.68209, 23.80492], [98.67797, 23.9644], [98.89632, 24.10612], [98.87998, 24.15624], [98.85319, 24.13042], [98.59256, 24.08371], [98.54476, 24.13119], [98.20666, 24.11406], [98.07806, 24.07988], [98.06703, 24.08028], [98.0607, 24.07812], [98.05671, 24.07961], [98.05302, 24.07408], [98.04709, 24.07616], [97.99583, 24.04932], [97.98691, 24.03897], [97.93951, 24.01953], [97.90998, 24.02094], [97.88616, 24.00463], [97.88414, 23.99405], [97.88814, 23.98605], [97.89683, 23.98389], [97.89676, 23.97931], [97.8955, 23.97758], [97.88811, 23.97446], [97.86545, 23.97723], [97.84328, 23.97603], [97.79416, 23.95663], [97.79456, 23.94836], [97.72302, 23.89288], [97.64667, 23.84574], [97.5247, 23.94032], [97.62363, 24.00506], [97.72903, 24.12606], [97.75305, 24.16902], [97.72799, 24.18883], [97.72998, 24.2302], [97.76799, 24.26365], [97.71941, 24.29652], [97.66723, 24.30027], [97.65624, 24.33781], [97.7098, 24.35658], [97.66998, 24.45288], [97.60029, 24.4401], [97.52757, 24.43748], [97.56286, 24.54535], [97.56525, 24.72838], [97.54675, 24.74202], [97.5542, 24.74943], [97.56383, 24.75535], [97.56648, 24.76475], [97.64354, 24.79171], [97.70181, 24.84557], [97.73127, 24.83015], [97.76481, 24.8289], [97.79949, 24.85655], [97.72903, 24.91332], [97.72216, 25.08508], [97.77023, 25.11492], [97.83614, 25.2715], [97.92541, 25.20815], [98.14925, 25.41547], [98.12591, 25.50722], [98.18084, 25.56298], [98.16848, 25.62739], [98.25774, 25.6051], [98.31268, 25.55307], [98.40606, 25.61129], [98.54064, 25.85129], [98.63128, 25.79937], [98.70818, 25.86241], [98.60763, 26.01512], [98.57085, 26.11547], [98.63128, 26.15492], [98.66884, 26.09165], [98.7329, 26.17218], [98.67797, 26.24487], [98.72741, 26.36183], [98.77547, 26.61994], [98.7333, 26.85615], [98.69582, 27.56499], [98.43353, 27.67086], [98.42529, 27.55404], [98.32641, 27.51385], [98.13964, 27.9478], [98.15337, 28.12114], [97.90069, 28.3776], [97.79632, 28.33168], [97.70705, 28.5056], [97.56835, 28.55628], [97.50518, 28.49716], [97.47085, 28.2688], [97.41729, 28.29783], [97.34547, 28.21385], [97.31292, 28.06784], [97.35412, 28.06663], [97.38845, 28.01329], [97.35824, 27.87256], [97.29919, 27.92233], [96.90112, 27.62149], [96.91431, 27.45752], [97.17422, 27.14052], [97.14675, 27.09041], [96.89132, 27.17474], [96.85287, 27.2065], [96.88445, 27.25046], [96.73888, 27.36638], [96.55761, 27.29928], [96.40779, 27.29818], [96.15591, 27.24572], [96.04949, 27.19428], [95.93002, 27.04149], [95.81603, 27.01335], [95.437, 26.7083], [95.30339, 26.65372], [95.23513, 26.68499], [95.05798, 26.45408], [95.12801, 26.38397], [95.11428, 26.1019], [95.18556, 26.07338], [94.80117, 25.49359], [94.68032, 25.47003], [94.57458, 25.20318], [94.74212, 25.13606], [94.73937, 25.00545], [94.60204, 24.70889], [94.5526, 24.70764], [94.50729, 24.59281], [94.45279, 24.56656], [94.32362, 24.27692], [94.30215, 24.23752], [94.14081, 23.83333], [93.92089, 23.95812], [93.80279, 23.92549], [93.75952, 24.0003], [93.62871, 24.00922], [93.50616, 23.94432], [93.46633, 23.97067], [93.41415, 24.07854], [93.34735, 24.10151], [93.32351, 24.04468], [93.36059, 23.93176], [93.3908, 23.92925], [93.3908, 23.7622], [93.43475, 23.68299], [93.38805, 23.4728], [93.39981, 23.38828], [93.38781, 23.36139], [93.36862, 23.35426], [93.38478, 23.13698], [93.2878, 23.00464], [93.12988, 23.05772], [93.134, 22.92498], [93.09417, 22.69459], [93.134, 22.59573], [93.11477, 22.54374], [93.13537, 22.45873], [93.18206, 22.43716], [93.19991, 22.25425], [93.14224, 22.24535], [93.15734, 22.18687], [93.04885, 22.20595], [92.99255, 22.05965], [92.99804, 21.98964], [92.93899, 22.02656], [92.89504, 21.95143], [92.86208, 22.05456], [92.70416, 22.16017], [92.67532, 22.03547], [92.60949, 21.97638], [92.62187, 21.87037]]]]
25305 nameEn: "Mongolia",
25306 groups: ["030", "142", "UN"],
25307 callingCodes: ["976"]
25310 type: "MultiPolygon",
25311 coordinates: [[[[102.14032, 51.35566], [101.5044, 51.50467], [101.39085, 51.45753], [100.61116, 51.73028], [99.89203, 51.74903], [99.75578, 51.90108], [99.27888, 51.96876], [98.87768, 52.14563], [98.74142, 51.8637], [98.33222, 51.71832], [98.22053, 51.46579], [98.05257, 51.46696], [97.83305, 51.00248], [98.01472, 50.86652], [97.9693, 50.78044], [98.06393, 50.61262], [98.31373, 50.4996], [98.29481, 50.33561], [97.85197, 49.91339], [97.76871, 49.99861], [97.56432, 49.92801], [97.56811, 49.84265], [97.24639, 49.74737], [96.97388, 49.88413], [95.80056, 50.04239], [95.74757, 49.97915], [95.02465, 49.96941], [94.97166, 50.04725], [94.6121, 50.04239], [94.49477, 50.17832], [94.39258, 50.22193], [94.30823, 50.57498], [92.99595, 50.63183], [93.01109, 50.79001], [92.44714, 50.78762], [92.07173, 50.69585], [91.86048, 50.73734], [89.59711, 49.90851], [89.70687, 49.72535], [88.82499, 49.44808], [88.42449, 49.48821], [88.17223, 49.46934], [88.15543, 49.30314], [87.98977, 49.18147], [87.81333, 49.17354], [87.88171, 48.95853], [87.73822, 48.89582], [88.0788, 48.71436], [87.96361, 48.58478], [88.58939, 48.34531], [88.58316, 48.21893], [88.8011, 48.11302], [88.93186, 48.10263], [89.0711, 47.98528], [89.55453, 48.0423], [89.76624, 47.82745], [90.06512, 47.88177], [90.10871, 47.7375], [90.33598, 47.68303], [90.48854, 47.41826], [90.48542, 47.30438], [90.76108, 46.99399], [90.84035, 46.99525], [91.03649, 46.72916], [91.0147, 46.58171], [91.07696, 46.57315], [90.89639, 46.30711], [90.99672, 46.14207], [91.03026, 46.04194], [90.70907, 45.73437], [90.65114, 45.49314], [90.89169, 45.19667], [91.64048, 45.07408], [93.51161, 44.95964], [94.10003, 44.71016], [94.71959, 44.35284], [95.01191, 44.25274], [95.39772, 44.2805], [95.32891, 44.02407], [95.52594, 43.99353], [95.89543, 43.2528], [96.35658, 42.90363], [96.37926, 42.72055], [97.1777, 42.7964], [99.50671, 42.56535], [100.33297, 42.68231], [100.84979, 42.67087], [101.80515, 42.50074], [102.07645, 42.22519], [102.72403, 42.14675], [103.92804, 41.78246], [104.52258, 41.8706], [104.51667, 41.66113], [105.0123, 41.63188], [106.76517, 42.28741], [107.24774, 42.36107], [107.29755, 42.41395], [107.49681, 42.46221], [107.57258, 42.40898], [108.84489, 42.40246], [109.00679, 42.45302], [109.452, 42.44842], [109.89402, 42.63111], [110.08401, 42.6411], [110.4327, 42.78293], [111.0149, 43.3289], [111.59087, 43.51207], [111.79758, 43.6637], [111.93776, 43.68709], [111.96289, 43.81596], [111.40498, 44.3461], [111.76275, 44.98032], [111.98695, 45.09074], [112.4164, 45.06858], [112.74662, 44.86297], [113.70918, 44.72891], [114.5166, 45.27189], [114.54801, 45.38337], [114.74612, 45.43585], [114.94546, 45.37377], [115.60329, 45.44717], [116.16989, 45.68603], [116.27366, 45.78637], [116.24012, 45.8778], [116.26678, 45.96479], [116.58612, 46.30211], [116.75551, 46.33083], [116.83166, 46.38637], [117.36609, 46.36335], [117.41782, 46.57862], [117.60748, 46.59771], [117.69554, 46.50991], [118.30534, 46.73519], [118.78747, 46.68689], [118.8337, 46.77742], [118.89974, 46.77139], [118.92616, 46.72765], [119.00541, 46.74273], [119.10448, 46.65516], [119.24978, 46.64761], [119.32827, 46.61433], [119.42827, 46.63783], [119.65265, 46.62342], [119.68127, 46.59015], [119.77373, 46.62947], [119.80455, 46.67631], [119.89261, 46.66423], [119.91242, 46.90091], [119.85518, 46.92196], [119.71209, 47.19192], [119.62403, 47.24575], [119.56019, 47.24874], [119.54918, 47.29505], [119.31964, 47.42617], [119.35892, 47.48104], [119.13995, 47.53997], [119.12343, 47.66458], [118.7564, 47.76947], [118.55766, 47.99277], [118.29654, 48.00246], [118.22677, 48.03853], [118.11009, 48.04], [118.03676, 48.00982], [117.80196, 48.01661], [117.50181, 47.77216], [117.37875, 47.63627], [116.9723, 47.87285], [116.67405, 47.89039], [116.4465, 47.83662], [116.21879, 47.88505], [115.94296, 47.67741], [115.57128, 47.91988], [115.52082, 48.15367], [115.811, 48.25699], [115.78876, 48.51781], [116.06565, 48.81716], [116.03781, 48.87014], [116.71193, 49.83813], [116.62502, 49.92919], [116.22402, 50.04477], [115.73602, 49.87688], [115.26068, 49.97367], [114.9703, 50.19254], [114.325, 50.28098], [113.20216, 49.83356], [113.02647, 49.60772], [110.64493, 49.1816], [110.39891, 49.25083], [110.24373, 49.16676], [109.51325, 49.22859], [109.18017, 49.34709], [108.53969, 49.32325], [108.27937, 49.53167], [107.95387, 49.66659], [107.96116, 49.93191], [107.36407, 49.97612], [107.1174, 50.04239], [107.00007, 50.1977], [106.80326, 50.30177], [106.58373, 50.34044], [106.51122, 50.34408], [106.49628, 50.32436], [106.47156, 50.31909], [106.07865, 50.33474], [106.05562, 50.40582], [105.32528, 50.4648], [103.70343, 50.13952], [102.71178, 50.38873], [102.32194, 50.67982], [102.14032, 51.35566]]]]
25319 wikidata: "Q14773",
25321 aliases: ["Macao"],
25323 groups: ["030", "142", "UN"],
25325 callingCodes: ["853"]
25328 type: "MultiPolygon",
25329 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]]]]
25337 wikidata: "Q16644",
25338 nameEn: "Northern Mariana Islands",
25339 aliases: ["US-MP"],
25341 groups: ["Q1352230", "Q153732", "057", "009", "UN"],
25342 roadSpeedUnit: "mph",
25343 callingCodes: ["1 670"]
25346 type: "MultiPolygon",
25347 coordinates: [[[[135.52896, 14.32623], [152.19114, 13.63487], [145.05972, 21.28731], [135.52896, 14.32623]]]]
25355 wikidata: "Q17054",
25356 nameEn: "Martinique",
25358 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25359 callingCodes: ["596"]
25362 type: "MultiPolygon",
25363 coordinates: [[[[-59.95997, 14.20285], [-61.07821, 15.25109], [-61.69315, 14.26451], [-59.95997, 14.20285]]]]
25372 nameEn: "Mauritania",
25373 groups: ["011", "202", "002", "UN"],
25374 callingCodes: ["222"]
25377 type: "MultiPolygon",
25378 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]]]]
25386 wikidata: "Q13353",
25387 nameEn: "Montserrat",
25389 groups: ["BOTS", "029", "003", "419", "019", "UN"],
25391 roadSpeedUnit: "mph",
25392 roadHeightUnit: "ft",
25393 callingCodes: ["1 664"]
25396 type: "MultiPolygon",
25397 coordinates: [[[[-61.91508, 16.51165], [-62.1023, 16.97277], [-62.58307, 16.68909], [-61.91508, 16.51165]]]]
25407 groups: ["EU", "039", "150", "UN"],
25409 callingCodes: ["356"]
25412 type: "MultiPolygon",
25413 coordinates: [[[[15.70991, 35.79901], [14.07544, 36.41525], [13.27636, 35.20764], [15.70991, 35.79901]]]]
25422 nameEn: "Mauritius",
25423 groups: ["014", "202", "002", "UN"],
25425 callingCodes: ["230"]
25428 type: "MultiPolygon",
25429 coordinates: [[[[56.09755, -9.55401], [57.50644, -31.92637], [68.4673, -19.15185], [56.09755, -9.55401]]]]
25438 nameEn: "Maldives",
25439 groups: ["034", "142", "UN"],
25441 callingCodes: ["960"]
25444 type: "MultiPolygon",
25445 coordinates: [[[[71.9161, 8.55531], [72.57428, -3.7623], [76.59015, 5.591], [71.9161, 8.55531]]]]
25455 groups: ["014", "202", "002", "UN"],
25457 callingCodes: ["265"]
25460 type: "MultiPolygon",
25461 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]]]]
25471 groups: ["013", "003", "419", "019", "UN"],
25472 callingCodes: ["52"]
25475 type: "MultiPolygon",
25476 coordinates: [[[[-117.1243, 32.53427], [-118.48109, 32.5991], [-120.12904, 18.41089], [-92.37213, 14.39277], [-92.2261, 14.53423], [-92.1454, 14.6804], [-92.18161, 14.84147], [-92.1423, 14.88647], [-92.1454, 14.98143], [-92.0621, 15.07406], [-92.20983, 15.26077], [-91.73182, 16.07371], [-90.44567, 16.07573], [-90.40499, 16.40524], [-90.61212, 16.49832], [-90.69064, 16.70697], [-91.04436, 16.92175], [-91.43809, 17.25373], [-90.99199, 17.25192], [-90.98678, 17.81655], [-89.14985, 17.81563], [-89.15105, 17.95104], [-89.03839, 18.0067], [-88.8716, 17.89535], [-88.71505, 18.0707], [-88.48242, 18.49164], [-88.3268, 18.49048], [-88.29909, 18.47591], [-88.26593, 18.47617], [-88.03238, 18.41778], [-88.03165, 18.16657], [-87.90671, 18.15213], [-87.87604, 18.18313], [-87.86657, 18.19971], [-87.85693, 18.18266], [-87.84815, 18.18511], [-86.92368, 17.61462], [-85.9092, 21.8218], [-96.92418, 25.97377], [-97.13927, 25.96583], [-97.35946, 25.92189], [-97.37332, 25.83854], [-97.42511, 25.83969], [-97.45669, 25.86874], [-97.49828, 25.89877], [-97.52025, 25.88518], [-97.66511, 26.01708], [-97.95155, 26.0625], [-97.97017, 26.05232], [-98.24603, 26.07191], [-98.27075, 26.09457], [-98.30491, 26.10475], [-98.35126, 26.15129], [-99.00546, 26.3925], [-99.03053, 26.41249], [-99.08477, 26.39849], [-99.53573, 27.30926], [-99.49744, 27.43746], [-99.482, 27.47128], [-99.48045, 27.49016], [-99.50208, 27.50021], [-99.52955, 27.49747], [-99.51478, 27.55836], [-99.55409, 27.61314], [-100.50029, 28.66117], [-100.51222, 28.70679], [-100.5075, 28.74066], [-100.52313, 28.75598], [-100.59809, 28.88197], [-100.63689, 28.90812], [-100.67294, 29.09744], [-100.79696, 29.24688], [-100.87982, 29.296], [-100.94056, 29.33371], [-100.94579, 29.34523], [-100.96725, 29.3477], [-101.01128, 29.36947], [-101.05686, 29.44738], [-101.47277, 29.7744], [-102.60596, 29.8192], [-103.15787, 28.93865], [-104.37752, 29.54255], [-104.39363, 29.55396], [-104.3969, 29.57105], [-104.5171, 29.64671], [-104.77674, 30.4236], [-106.00363, 31.39181], [-106.09025, 31.40569], [-106.20346, 31.46305], [-106.23711, 31.51262], [-106.24612, 31.54193], [-106.28084, 31.56173], [-106.30305, 31.62154], [-106.33419, 31.66303], [-106.34864, 31.69663], [-106.3718, 31.71165], [-106.38003, 31.73151], [-106.41773, 31.75196], [-106.43419, 31.75478], [-106.45244, 31.76523], [-106.46726, 31.75998], [-106.47298, 31.75054], [-106.48815, 31.74769], [-106.50111, 31.75714], [-106.50962, 31.76155], [-106.51251, 31.76922], [-106.52266, 31.77509], [-106.529, 31.784], [-108.20899, 31.78534], [-108.20979, 31.33316], [-111.07523, 31.33232], [-114.82011, 32.49609], [-114.79524, 32.55731], [-114.81141, 32.55543], [-114.80584, 32.62028], [-114.76736, 32.64094], [-114.71871, 32.71894], [-115.88053, 32.63624], [-117.1243, 32.53427]]]]
25495 nameEn: "Mozambique",
25496 groups: ["014", "202", "002", "UN"],
25498 callingCodes: ["258"]
25501 type: "MultiPolygon",
25502 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]]]]
25512 groups: ["018", "202", "002", "UN"],
25514 callingCodes: ["264"]
25517 type: "MultiPolygon",
25518 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]]]]
25526 wikidata: "Q33788",
25527 nameEn: "New Caledonia",
25529 groups: ["Q1451600", "054", "009", "UN"],
25530 callingCodes: ["687"]
25533 type: "MultiPolygon",
25534 coordinates: [[[[159.77159, -28.41151], [174.245, -23.1974], [156.73836, -14.50464], [159.77159, -28.41151]]]]
25545 groups: ["011", "202", "002", "UN"],
25546 callingCodes: ["227"]
25549 type: "MultiPolygon",
25550 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]]]]
25558 wikidata: "Q31057",
25559 nameEn: "Norfolk Island",
25561 groups: ["053", "009", "UN"],
25563 callingCodes: ["672 3"]
25566 type: "MultiPolygon",
25567 coordinates: [[[[169.82316, -28.16667], [166.29505, -28.29175], [167.94076, -30.60745], [169.82316, -28.16667]]]]
25577 groups: ["011", "202", "002", "UN"],
25578 callingCodes: ["234"]
25581 type: "MultiPolygon",
25582 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]]]]
25591 nameEn: "Nicaragua",
25592 groups: ["013", "003", "419", "019", "UN"],
25593 callingCodes: ["505"]
25596 type: "MultiPolygon",
25597 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]]]]
25605 wikidata: "Q29999",
25606 nameEn: "Kingdom of the Netherlands"
25627 groups: ["034", "142", "UN"],
25629 callingCodes: ["977"]
25632 type: "MultiPolygon",
25633 coordinates: [[[[88.13378, 27.88015], [87.82681, 27.95248], [87.72718, 27.80938], [87.56996, 27.84517], [87.11696, 27.84104], [87.03757, 27.94835], [86.75582, 28.04182], [86.74181, 28.10638], [86.56265, 28.09569], [86.51609, 27.96623], [86.42736, 27.91122], [86.22966, 27.9786], [86.18607, 28.17364], [86.088, 28.09264], [86.08333, 28.02121], [86.12069, 27.93047], [86.06309, 27.90021], [85.94946, 27.9401], [85.97813, 27.99023], [85.90743, 28.05144], [85.84672, 28.18187], [85.74864, 28.23126], [85.71907, 28.38064], [85.69105, 28.38475], [85.60854, 28.25045], [85.59765, 28.30529], [85.4233, 28.32996], [85.38127, 28.28336], [85.10729, 28.34092], [85.18668, 28.54076], [85.19135, 28.62825], [85.06059, 28.68562], [84.85511, 28.58041], [84.62317, 28.73887], [84.47528, 28.74023], [84.2231, 28.89571], [84.24801, 29.02783], [84.18107, 29.23451], [83.97559, 29.33091], [83.82303, 29.30513], [83.63156, 29.16249], [83.44787, 29.30513], [83.28131, 29.56813], [83.07116, 29.61957], [82.73024, 29.81695], [82.5341, 29.9735], [82.38622, 30.02608], [82.16984, 30.0692], [82.19475, 30.16884], [82.10757, 30.23745], [82.10135, 30.35439], [81.99082, 30.33423], [81.62033, 30.44703], [81.5459, 30.37688], [81.41018, 30.42153], [81.39928, 30.21862], [81.33355, 30.15303], [81.2623, 30.14596], [81.29032, 30.08806], [81.24362, 30.0126], [81.12842, 30.01395], [81.03953, 30.20059], [80.92547, 30.17193], [80.91143, 30.22173], [80.86673, 30.17321], [80.8778, 30.13384], [80.67076, 29.95732], [80.60226, 29.95732], [80.57179, 29.91422], [80.56247, 29.86661], [80.48997, 29.79566], [80.43458, 29.80466], [80.41554, 29.79451], [80.36803, 29.73865], [80.38428, 29.68513], [80.41858, 29.63581], [80.37939, 29.57098], [80.24322, 29.44299], [80.31428, 29.30784], [80.28626, 29.20327], [80.24112, 29.21414], [80.26602, 29.13938], [80.23178, 29.11626], [80.18085, 29.13649], [80.05743, 28.91479], [80.06957, 28.82763], [80.12125, 28.82346], [80.37188, 28.63371], [80.44504, 28.63098], [80.52443, 28.54897], [80.50575, 28.6706], [80.55142, 28.69182], [81.03471, 28.40054], [81.19847, 28.36284], [81.32923, 28.13521], [81.38683, 28.17638], [81.48179, 28.12148], [81.47867, 28.08303], [81.91223, 27.84995], [81.97214, 27.93322], [82.06554, 27.92222], [82.46405, 27.6716], [82.70378, 27.72122], [82.74119, 27.49838], [82.93261, 27.50328], [82.94938, 27.46036], [83.19413, 27.45632], [83.27197, 27.38309], [83.2673, 27.36235], [83.29999, 27.32778], [83.35136, 27.33885], [83.38872, 27.39276], [83.39495, 27.4798], [83.61288, 27.47013], [83.85595, 27.35797], [83.86182, 27.4241], [83.93306, 27.44939], [84.02229, 27.43836], [84.10791, 27.52399], [84.21376, 27.45218], [84.25735, 27.44941], [84.29315, 27.39], [84.62161, 27.33885], [84.69166, 27.21294], [84.64496, 27.04669], [84.793, 26.9968], [84.82913, 27.01989], [84.85754, 26.98984], [84.96687, 26.95599], [84.97186, 26.9149], [85.00536, 26.89523], [85.05592, 26.88991], [85.02635, 26.85381], [85.15883, 26.86966], [85.19291, 26.86909], [85.18046, 26.80519], [85.21159, 26.75933], [85.34302, 26.74954], [85.47752, 26.79292], [85.56471, 26.84133], [85.5757, 26.85955], [85.59461, 26.85161], [85.61621, 26.86721], [85.66239, 26.84822], [85.73483, 26.79613], [85.72315, 26.67471], [85.76907, 26.63076], [85.83126, 26.61134], [85.85126, 26.60866], [85.8492, 26.56667], [86.02729, 26.66756], [86.13596, 26.60651], [86.22513, 26.58863], [86.26235, 26.61886], [86.31564, 26.61925], [86.49726, 26.54218], [86.54258, 26.53819], [86.57073, 26.49825], [86.61313, 26.48658], [86.62686, 26.46891], [86.69124, 26.45169], [86.74025, 26.42386], [86.76797, 26.45892], [86.82898, 26.43919], [86.94543, 26.52076], [86.95912, 26.52076], [87.01559, 26.53228], [87.04691, 26.58685], [87.0707, 26.58571], [87.09147, 26.45039], [87.14751, 26.40542], [87.18863, 26.40558], [87.24682, 26.4143], [87.26587, 26.40592], [87.26568, 26.37294], [87.34568, 26.34787], [87.37314, 26.40815], [87.46566, 26.44058], [87.51571, 26.43106], [87.55274, 26.40596], [87.59175, 26.38342], [87.66803, 26.40294], [87.67893, 26.43501], [87.76004, 26.40711], [87.7918, 26.46737], [87.84193, 26.43663], [87.89085, 26.48565], [87.90115, 26.44923], [88.00895, 26.36029], [88.09414, 26.43732], [88.09963, 26.54195], [88.16452, 26.64111], [88.1659, 26.68177], [88.19107, 26.75516], [88.12302, 26.95324], [88.13422, 26.98705], [88.11719, 26.98758], [87.9887, 27.11045], [88.01587, 27.21388], [88.01646, 27.21612], [88.07277, 27.43007], [88.04008, 27.49223], [88.19107, 27.79285], [88.1973, 27.85067], [88.13378, 27.88015]]]]
25643 groups: ["057", "009", "UN"],
25645 callingCodes: ["674"]
25648 type: "MultiPolygon",
25649 coordinates: [[[[166.95155, 0.14829], [166.21778, -0.7977], [167.60042, -0.88259], [166.95155, 0.14829]]]]
25657 wikidata: "Q34020",
25660 groups: ["061", "009", "UN"],
25662 callingCodes: ["683"]
25665 type: "MultiPolygon",
25666 coordinates: [[[[-170.83899, -18.53439], [-170.82274, -20.44429], [-168.63096, -18.60489], [-170.83899, -18.53439]]]]
25675 nameEn: "New Zealand"
25686 groups: ["145", "142", "UN"],
25687 callingCodes: ["968"]
25690 type: "MultiPolygon",
25691 coordinates: [[[[56.82555, 25.7713], [56.79239, 26.41236], [56.68954, 26.76645], [56.2644, 26.58649], [55.81777, 26.18798], [56.08666, 26.05038], [56.15498, 26.06828], [56.19334, 25.9795], [56.13963, 25.82765], [56.17416, 25.77239], [56.13579, 25.73524], [56.14826, 25.66351], [56.18363, 25.65508], [56.20473, 25.61119], [56.25365, 25.60211], [56.26636, 25.60643], [56.25341, 25.61443], [56.26534, 25.62825], [56.82555, 25.7713]]], [[[56.26062, 25.33108], [56.23362, 25.31253], [56.25008, 25.28843], [56.24465, 25.27505], [56.20838, 25.25668], [56.20872, 25.24104], [56.24341, 25.22867], [56.27628, 25.23404], [56.34438, 25.26653], [56.35172, 25.30681], [56.3111, 25.30107], [56.3005, 25.31815], [56.26062, 25.33108]], [[56.28423, 25.26344], [56.27086, 25.26128], [56.2716, 25.27916], [56.28102, 25.28486], [56.29379, 25.2754], [56.28423, 25.26344]]], [[[61.45114, 22.55394], [56.86325, 25.03856], [56.3227, 24.97284], [56.34873, 24.93205], [56.30269, 24.88334], [56.20568, 24.85063], [56.20062, 24.78565], [56.13684, 24.73699], [56.06128, 24.74457], [56.03535, 24.81161], [55.97836, 24.87673], [55.97467, 24.89639], [56.05106, 24.87461], [56.05715, 24.95727], [55.96316, 25.00857], [55.90849, 24.96771], [55.85094, 24.96858], [55.81116, 24.9116], [55.81348, 24.80102], [55.83408, 24.77858], [55.83271, 24.68567], [55.76461, 24.5287], [55.83271, 24.41521], [55.83395, 24.32776], [55.80747, 24.31069], [55.79145, 24.27914], [55.76781, 24.26209], [55.75939, 24.26114], [55.75382, 24.2466], [55.75257, 24.23466], [55.76558, 24.23227], [55.77658, 24.23476], [55.83367, 24.20193], [55.95472, 24.2172], [56.01799, 24.07426], [55.8308, 24.01633], [55.73301, 24.05994], [55.48677, 23.94946], [55.57358, 23.669], [55.22634, 23.10378], [55.2137, 22.71065], [55.66469, 21.99658], [54.99756, 20.00083], [52.00311, 19.00083], [52.78009, 17.35124], [52.74267, 17.29519], [52.81185, 17.28568], [57.49095, 8.14549], [61.45114, 22.55394]]]]
25701 groups: ["013", "003", "419", "019", "UN"],
25702 callingCodes: ["507"]
25705 type: "MultiPolygon",
25706 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]]]]
25716 groups: ["005", "419", "019", "UN"],
25717 callingCodes: ["51"]
25720 type: "MultiPolygon",
25721 coordinates: [[[[-74.26675, -0.97229], [-74.42701, -0.50218], [-75.18513, -0.0308], [-75.25764, -0.11943], [-75.40192, -0.17196], [-75.61997, -0.10012], [-75.60169, -0.18708], [-75.53615, -0.19213], [-75.22862, -0.60048], [-75.22862, -0.95588], [-75.3872, -0.9374], [-75.57429, -1.55961], [-76.05203, -2.12179], [-76.6324, -2.58397], [-77.94147, -3.05454], [-78.19369, -3.36431], [-78.14324, -3.47653], [-78.22642, -3.51113], [-78.24589, -3.39907], [-78.34362, -3.38633], [-78.68394, -4.60754], [-78.85149, -4.66795], [-79.01659, -5.01481], [-79.1162, -4.97774], [-79.26248, -4.95167], [-79.59402, -4.46848], [-79.79722, -4.47558], [-80.13945, -4.29786], [-80.39256, -4.48269], [-80.46386, -4.41516], [-80.32114, -4.21323], [-80.45023, -4.20938], [-80.4822, -4.05477], [-80.46386, -4.01342], [-80.13232, -3.90317], [-80.19926, -3.68894], [-80.18741, -3.63994], [-80.19848, -3.59249], [-80.21642, -3.5888], [-80.20535, -3.51667], [-80.22629, -3.501], [-80.23651, -3.48652], [-80.24586, -3.48677], [-80.24123, -3.46124], [-80.20647, -3.431], [-80.30602, -3.39149], [-84.52388, -3.36941], [-85.71054, -21.15413], [-70.59118, -18.35072], [-70.378, -18.3495], [-70.31267, -18.31258], [-70.16394, -18.31737], [-69.96732, -18.25992], [-69.81607, -18.12582], [-69.75305, -17.94605], [-69.82868, -17.72048], [-69.79087, -17.65563], [-69.66483, -17.65083], [-69.46897, -17.4988], [-69.46863, -17.37466], [-69.62883, -17.28142], [-69.16896, -16.72233], [-69.00853, -16.66769], [-69.04027, -16.57214], [-68.98358, -16.42165], [-68.79464, -16.33272], [-68.96238, -16.194], [-69.09986, -16.22693], [-69.20291, -16.16668], [-69.40336, -15.61358], [-69.14856, -15.23478], [-69.36254, -14.94634], [-68.88135, -14.18639], [-69.05265, -13.68546], [-68.8864, -13.40792], [-68.85615, -12.87769], [-68.65044, -12.50689], [-68.98115, -11.8979], [-69.57156, -10.94555], [-69.57835, -10.94051], [-69.90896, -10.92744], [-70.38791, -11.07096], [-70.51395, -10.92249], [-70.64134, -11.0108], [-70.62487, -9.80666], [-70.55429, -9.76692], [-70.58453, -9.58303], [-70.53373, -9.42628], [-71.23394, -9.9668], [-72.14742, -9.98049], [-72.31883, -9.5184], [-72.72216, -9.41397], [-73.21498, -9.40904], [-72.92886, -9.04074], [-73.76576, -7.89884], [-73.65485, -7.77897], [-73.96938, -7.58465], [-73.77011, -7.28944], [-73.73986, -6.87919], [-73.12983, -6.43852], [-73.24579, -6.05764], [-72.83973, -5.14765], [-72.64391, -5.0391], [-71.87003, -4.51661], [-70.96814, -4.36915], [-70.77601, -4.15717], [-70.33236, -4.15214], [-70.19582, -4.3607], [-70.11305, -4.27281], [-70.00888, -4.37833], [-69.94708, -4.2431], [-70.3374, -3.79505], [-70.52393, -3.87553], [-70.71396, -3.7921], [-70.04609, -2.73906], [-70.94377, -2.23142], [-71.75223, -2.15058], [-72.92587, -2.44514], [-73.65312, -1.26222], [-74.26675, -0.97229]]]]
25729 wikidata: "Q30971",
25730 nameEn: "French Polynesia",
25732 groups: ["Q1451600", "061", "009", "UN"],
25733 callingCodes: ["689"]
25736 type: "MultiPolygon",
25737 coordinates: [[[[-135.59706, -4.70473], [-156.48634, -15.52824], [-156.45576, -31.75456], [-133.59543, -28.4709], [-135.59706, -4.70473]]]]
25746 nameEn: "Papua New Guinea",
25747 groups: ["054", "009", "UN"],
25749 callingCodes: ["675"]
25752 type: "MultiPolygon",
25753 coordinates: [[[[141.03157, 2.12829], [140.99813, -6.3233], [140.85295, -6.72996], [140.90448, -6.85033], [141.01763, -6.90181], [141.01842, -9.35091], [141.88934, -9.36111], [142.19246, -9.15378], [142.48658, -9.36754], [143.29772, -9.33993], [143.87386, -9.02382], [145.2855, -9.62524], [156.73836, -14.50464], [154.74815, -7.33315], [155.60735, -6.92266], [155.69784, -6.92661], [155.92557, -6.84664], [156.03993, -6.65703], [156.03296, -6.55528], [160.43769, -4.17974], [141.03157, 2.12829]]]]
25762 nameEn: "Philippines",
25763 aliases: ["PI", "RP"],
25764 groups: ["035", "142", "UN"],
25765 callingCodes: ["63"]
25768 type: "MultiPolygon",
25769 coordinates: [[[[129.19694, 7.84182], [121.8109, 21.77688], [120.69238, 21.52331], [118.82252, 14.67191], [115.39742, 10.92666], [116.79524, 7.43869], [117.17735, 7.52841], [117.93857, 6.89845], [117.98544, 6.27477], [119.52945, 5.35672], [118.93936, 4.09009], [118.06469, 4.16638], [121.14448, 2.12444], [129.19694, 7.84182]]]]
25778 nameEn: "Pakistan",
25779 groups: ["034", "142", "UN"],
25781 callingCodes: ["92"]
25784 type: "MultiPolygon",
25785 coordinates: [[[[75.72737, 36.7529], [75.45562, 36.71971], [75.40481, 36.95382], [75.13839, 37.02622], [74.56453, 37.03023], [74.53739, 36.96224], [74.43389, 37.00977], [74.04856, 36.82648], [73.82685, 36.91421], [72.6323, 36.84601], [72.18135, 36.71838], [71.80267, 36.49924], [71.60491, 36.39429], [71.19505, 36.04134], [71.37969, 35.95865], [71.55273, 35.71483], [71.49917, 35.6267], [71.65435, 35.4479], [71.54294, 35.31037], [71.5541, 35.28776], [71.67495, 35.21262], [71.52938, 35.09023], [71.55273, 35.02615], [71.49917, 35.00478], [71.50329, 34.97328], [71.29472, 34.87728], [71.28356, 34.80882], [71.08718, 34.69034], [71.11602, 34.63047], [71.0089, 34.54568], [71.02401, 34.44835], [71.17662, 34.36769], [71.12815, 34.26619], [71.13078, 34.16503], [71.09453, 34.13524], [71.09307, 34.11961], [71.06933, 34.10564], [71.07345, 34.06242], [70.88119, 33.97933], [70.54336, 33.9463], [69.90203, 34.04194], [69.87307, 33.9689], [69.85671, 33.93719], [70.00503, 33.73528], [70.14236, 33.71701], [70.14785, 33.6553], [70.20141, 33.64387], [70.17062, 33.53535], [70.32775, 33.34496], [70.13686, 33.21064], [70.07369, 33.22557], [70.02563, 33.14282], [69.85259, 33.09451], [69.79766, 33.13247], [69.71526, 33.09911], [69.57656, 33.09911], [69.49004, 33.01509], [69.49854, 32.88843], [69.5436, 32.8768], [69.47082, 32.85834], [69.38018, 32.76601], [69.43649, 32.7302], [69.44747, 32.6678], [69.38155, 32.56601], [69.2868, 32.53938], [69.23599, 32.45946], [69.27932, 32.29119], [69.27032, 32.14141], [69.3225, 31.93186], [69.20577, 31.85957], [69.11514, 31.70782], [69.00939, 31.62249], [68.95995, 31.64822], [68.91078, 31.59687], [68.79997, 31.61665], [68.6956, 31.75687], [68.57475, 31.83158], [68.44222, 31.76446], [68.27605, 31.75863], [68.25614, 31.80357], [68.1655, 31.82691], [68.00071, 31.6564], [67.86887, 31.63536], [67.72056, 31.52304], [67.58323, 31.52772], [67.62374, 31.40473], [67.7748, 31.4188], [67.78854, 31.33203], [67.29964, 31.19586], [67.03323, 31.24519], [67.04147, 31.31561], [66.83273, 31.26867], [66.72561, 31.20526], [66.68166, 31.07597], [66.58175, 30.97532], [66.42645, 30.95309], [66.39194, 30.9408], [66.28413, 30.57001], [66.34869, 30.404], [66.23609, 30.06321], [66.36042, 29.9583], [66.24175, 29.85181], [65.04005, 29.53957], [64.62116, 29.58903], [64.19796, 29.50407], [64.12966, 29.39157], [63.5876, 29.50456], [62.47751, 29.40782], [60.87231, 29.86514], [61.31508, 29.38903], [61.53765, 29.00507], [61.65978, 28.77937], [61.93581, 28.55284], [62.40259, 28.42703], [62.59499, 28.24842], [62.79412, 28.28108], [62.7638, 28.02992], [62.84905, 27.47627], [62.79684, 27.34381], [62.80604, 27.22412], [63.19649, 27.25674], [63.32283, 27.14437], [63.25005, 27.08692], [63.25005, 26.84212], [63.18688, 26.83844], [63.1889, 26.65072], [62.77352, 26.64099], [62.31484, 26.528], [62.21304, 26.26601], [62.05117, 26.31647], [61.89391, 26.26251], [61.83831, 26.07249], [61.83968, 25.7538], [61.683, 25.66638], [61.6433, 25.27541], [61.46682, 24.57869], [68.11329, 23.53945], [68.20763, 23.85849], [68.39339, 23.96838], [68.74643, 23.97027], [68.7416, 24.31904], [68.90914, 24.33156], [68.97781, 24.26021], [69.07806, 24.29777], [69.19341, 24.25646], [69.29778, 24.28712], [69.59579, 24.29777], [69.73335, 24.17007], [70.03428, 24.172], [70.11712, 24.30915], [70.5667, 24.43787], [70.57906, 24.27774], [70.71502, 24.23517], [70.88393, 24.27398], [70.85784, 24.30903], [70.94985, 24.3791], [71.04461, 24.34657], [71.12838, 24.42662], [71.00341, 24.46038], [70.97594, 24.60904], [71.09405, 24.69017], [70.94002, 24.92843], [70.89148, 25.15064], [70.66695, 25.39314], [70.67382, 25.68186], [70.60378, 25.71898], [70.53649, 25.68928], [70.37444, 25.67443], [70.2687, 25.71156], [70.0985, 25.93238], [70.08193, 26.08094], [70.17532, 26.24118], [70.17532, 26.55362], [70.05584, 26.60398], [69.88555, 26.56836], [69.50904, 26.74892], [69.58519, 27.18109], [70.03136, 27.56627], [70.12502, 27.8057], [70.37307, 28.01208], [70.60927, 28.02178], [70.79054, 27.68423], [71.89921, 27.96035], [71.9244, 28.11555], [72.20329, 28.3869], [72.29495, 28.66367], [72.40402, 28.78283], [72.94272, 29.02487], [73.01337, 29.16422], [73.05886, 29.1878], [73.28094, 29.56646], [73.3962, 29.94707], [73.58665, 30.01848], [73.80299, 30.06969], [73.97225, 30.19829], [73.95736, 30.28466], [73.88993, 30.36305], [74.5616, 31.04153], [74.67971, 31.05479], [74.6852, 31.12771], [74.60006, 31.13711], [74.60281, 31.10419], [74.56023, 31.08303], [74.51629, 31.13829], [74.53223, 31.30321], [74.59773, 31.4136], [74.64713, 31.45605], [74.59319, 31.50197], [74.61517, 31.55698], [74.57498, 31.60382], [74.47771, 31.72227], [74.58907, 31.87824], [74.79919, 31.95983], [74.86236, 32.04485], [74.9269, 32.0658], [75.00793, 32.03786], [75.25649, 32.10187], [75.38046, 32.26836], [75.28259, 32.36556], [75.03265, 32.49538], [74.97634, 32.45367], [74.84725, 32.49075], [74.68362, 32.49298], [74.67431, 32.56676], [74.65251, 32.56416], [74.64424, 32.60985], [74.69542, 32.66792], [74.65345, 32.71225], [74.7113, 32.84219], [74.64675, 32.82604], [74.6289, 32.75561], [74.45312, 32.77755], [74.41467, 32.90563], [74.31227, 32.92795], [74.34875, 32.97823], [74.31854, 33.02891], [74.17571, 33.07495], [74.15374, 33.13477], [74.02144, 33.18908], [74.01366, 33.25199], [74.08782, 33.26232], [74.17983, 33.3679], [74.18121, 33.4745], [74.10115, 33.56392], [74.03576, 33.56718], [73.97367, 33.64061], [73.98968, 33.66155], [73.96423, 33.73071], [74.00891, 33.75437], [74.05898, 33.82089], [74.14001, 33.83002], [74.26086, 33.92237], [74.25262, 34.01577], [74.21554, 34.03853], [73.91341, 34.01235], [73.88732, 34.05105], [73.90677, 34.10504], [73.98208, 34.2522], [73.90517, 34.35317], [73.8475, 34.32935], [73.74862, 34.34183], [73.74999, 34.3781], [73.88732, 34.48911], [73.89419, 34.54568], [73.93951, 34.57169], [73.93401, 34.63386], [73.96423, 34.68244], [74.12897, 34.70073], [74.31239, 34.79626], [74.58083, 34.77386], [74.6663, 34.703], [75.01479, 34.64629], [75.38009, 34.55021], [75.75438, 34.51827], [76.04614, 34.67566], [76.15463, 34.6429], [76.47186, 34.78965], [76.67648, 34.76371], [76.74377, 34.84039], [76.74514, 34.92488], [76.87193, 34.96906], [76.99251, 34.93349], [77.11796, 35.05419], [76.93465, 35.39866], [76.85088, 35.39754], [76.75475, 35.52617], [76.77323, 35.66062], [76.50961, 35.8908], [76.33453, 35.84296], [76.14913, 35.82848], [76.15325, 35.9264], [75.93028, 36.13136], [76.00906, 36.17511], [76.0324, 36.41198], [75.92391, 36.56986], [75.72737, 36.7529]]]]
25795 groups: ["EU", "151", "150", "UN"],
25796 callingCodes: ["48"]
25799 type: "MultiPolygon",
25800 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]]]]
25808 wikidata: "Q34617",
25809 nameEn: "Saint Pierre and Miquelon",
25811 groups: ["Q1451600", "021", "003", "019", "UN"],
25812 callingCodes: ["508"]
25815 type: "MultiPolygon",
25816 coordinates: [[[[-56.72993, 46.65575], [-55.90758, 46.6223], [-56.27503, 47.39728], [-56.72993, 46.65575]]]]
25824 wikidata: "Q35672",
25825 nameEn: "Pitcairn Islands",
25827 groups: ["BOTS", "061", "009", "UN"],
25829 callingCodes: ["64"]
25832 type: "MultiPolygon",
25833 coordinates: [[[[-133.59543, -28.4709], [-122.0366, -24.55017], [-133.61511, -21.93325], [-133.59543, -28.4709]]]]
25842 nameEn: "Puerto Rico",
25843 aliases: ["US-PR"],
25845 groups: ["Q1352230", "029", "003", "419", "019", "UN"],
25846 roadSpeedUnit: "mph",
25847 callingCodes: ["1 787", "1 939"]
25850 type: "MultiPolygon",
25851 coordinates: [[[[-65.27974, 17.56928], [-65.02435, 18.73231], [-67.99519, 18.97186], [-68.23894, 17.84663], [-65.27974, 17.56928]]]]
25859 wikidata: "Q219060",
25860 nameEn: "Palestine"
25881 groups: ["057", "009", "UN"],
25882 roadSpeedUnit: "mph",
25883 callingCodes: ["680"]
25886 type: "MultiPolygon",
25887 coordinates: [[[[128.97621, 3.08804], [136.39296, 1.54187], [136.04605, 12.45908], [128.97621, 3.08804]]]]
25896 nameEn: "Paraguay",
25897 groups: ["005", "419", "019", "UN"],
25898 callingCodes: ["595"]
25901 type: "MultiPolygon",
25902 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]]]]
25912 groups: ["145", "142", "UN"],
25913 callingCodes: ["974"]
25916 type: "MultiPolygon",
25917 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]]]]
25925 wikidata: "Q17070",
25926 nameEn: "R\xE9union",
25928 groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
25929 callingCodes: ["262"]
25932 type: "MultiPolygon",
25933 coordinates: [[[[53.37984, -21.23941], [56.73473, -21.9174], [56.62373, -20.2711], [53.37984, -21.23941]]]]
25943 groups: ["EU", "151", "150", "UN"],
25944 callingCodes: ["40"]
25947 type: "MultiPolygon",
25948 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]]]]
25958 groups: ["039", "150", "UN"],
25959 callingCodes: ["381"]
25962 type: "MultiPolygon",
25963 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]]]]
25983 groups: ["014", "202", "002", "UN"],
25984 callingCodes: ["250"]
25987 type: "MultiPolygon",
25988 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]]]]
25997 nameEn: "Saudi Arabia",
25998 groups: ["145", "142", "UN"],
25999 callingCodes: ["966"]
26002 type: "MultiPolygon",
26003 coordinates: [[[[40.01521, 32.05667], [39.29903, 32.23259], [38.99233, 31.99721], [36.99791, 31.50081], [37.99354, 30.49998], [37.66395, 30.33245], [37.4971, 29.99949], [36.75083, 29.86903], [36.50005, 29.49696], [36.07081, 29.18469], [34.8812, 29.36878], [34.4454, 27.91479], [37.8565, 22.00903], [39.63762, 18.37348], [40.99158, 15.81743], [42.15205, 16.40211], [42.76801, 16.40371], [42.94625, 16.39721], [42.94351, 16.49467], [42.97215, 16.51093], [43.11601, 16.53166], [43.15274, 16.67248], [43.22066, 16.65179], [43.21325, 16.74416], [43.25857, 16.75304], [43.26303, 16.79479], [43.24801, 16.80613], [43.22956, 16.80613], [43.22012, 16.83932], [43.18338, 16.84852], [43.1398, 16.90696], [43.19328, 16.94703], [43.1813, 16.98438], [43.18233, 17.02673], [43.23967, 17.03428], [43.17787, 17.14717], [43.20156, 17.25901], [43.32653, 17.31179], [43.22533, 17.38343], [43.29185, 17.53224], [43.43005, 17.56148], [43.70631, 17.35762], [44.50126, 17.47475], [46.31018, 17.20464], [46.76494, 17.29151], [47.00571, 16.94765], [47.48245, 17.10808], [47.58351, 17.50366], [48.19996, 18.20584], [49.04884, 18.59899], [52.00311, 19.00083], [54.99756, 20.00083], [55.66469, 21.99658], [55.2137, 22.71065], [55.13599, 22.63334], [52.56622, 22.94341], [51.59617, 24.12041], [51.58871, 24.27256], [51.41644, 24.39615], [51.58834, 24.66608], [51.39468, 24.62785], [51.29972, 24.50747], [51.09638, 24.46907], [50.92992, 24.54396], [50.8133, 24.74049], [50.57069, 25.57887], [50.302, 25.87592], [50.26923, 26.08243], [50.38162, 26.53976], [50.71771, 26.73086], [50.37726, 27.89227], [49.98877, 27.87827], [49.00421, 28.81495], [48.42991, 28.53628], [47.70561, 28.5221], [47.59863, 28.66798], [47.58376, 28.83382], [47.46202, 29.0014], [46.5527, 29.10283], [46.42415, 29.05947], [44.72255, 29.19736], [42.97796, 30.48295], [42.97601, 30.72204], [40.01521, 32.05667]]]]
26012 nameEn: "Solomon Islands",
26013 groups: ["054", "009", "UN"],
26015 callingCodes: ["677"]
26018 type: "MultiPolygon",
26019 coordinates: [[[[172.71443, -12.01327], [160.43769, -4.17974], [156.03296, -6.55528], [156.03993, -6.65703], [155.92557, -6.84664], [155.69784, -6.92661], [155.60735, -6.92266], [154.74815, -7.33315], [156.73836, -14.50464], [172.71443, -12.01327]]]]
26028 nameEn: "Seychelles",
26029 groups: ["014", "202", "002", "UN"],
26031 callingCodes: ["248"]
26034 type: "MultiPolygon",
26035 coordinates: [[[[43.75112, -10.38913], [54.83239, -10.93575], [66.3222, 5.65313], [43.75112, -10.38913]]]]
26045 groups: ["015", "002", "UN"],
26046 callingCodes: ["249"]
26049 type: "MultiPolygon",
26050 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]]]]
26060 groups: ["EU", "154", "150", "UN"],
26061 callingCodes: ["46"]
26064 type: "MultiPolygon",
26065 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]]]]
26074 nameEn: "Singapore",
26075 groups: ["035", "142", "UN"],
26077 callingCodes: ["65"]
26080 type: "MultiPolygon",
26081 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]]]]
26089 wikidata: "Q192184",
26090 nameEn: "Saint Helena, Ascension and Tristan da Cunha",
26101 nameEn: "Slovenia",
26102 groups: ["EU", "039", "150", "UN"],
26103 callingCodes: ["386"]
26106 type: "MultiPolygon",
26107 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]]]]
26115 wikidata: "Q842829",
26116 nameEn: "Svalbard and Jan Mayen",
26127 nameEn: "Slovakia",
26128 groups: ["EU", "151", "150", "UN"],
26129 callingCodes: ["421"]
26132 type: "MultiPolygon",
26133 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]]]]
26142 nameEn: "Sierra Leone",
26143 groups: ["011", "202", "002", "UN"],
26144 callingCodes: ["232"]
26147 type: "MultiPolygon",
26148 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]]]]
26157 nameEn: "San Marino",
26158 groups: ["039", "150", "UN"],
26159 callingCodes: ["378"]
26162 type: "MultiPolygon",
26163 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]]]]
26173 groups: ["011", "202", "002", "UN"],
26174 callingCodes: ["221"]
26177 type: "MultiPolygon",
26178 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]]]]
26188 groups: ["014", "202", "002", "UN"],
26189 callingCodes: ["252"]
26192 type: "MultiPolygon",
26193 coordinates: [[[[51.12877, 12.56479], [43.90659, 12.3823], [42.95776, 10.98533], [42.69452, 10.62672], [42.87643, 10.18441], [43.0937, 9.90579], [43.23518, 9.84605], [43.32613, 9.59205], [44.19222, 8.93028], [46.99339, 7.9989], [47.92477, 8.00111], [47.97917, 8.00124], [44.98104, 4.91821], [44.02436, 4.9451], [43.40263, 4.79289], [43.04177, 4.57923], [42.97746, 4.44032], [42.84526, 4.28357], [42.55853, 4.20518], [42.07619, 4.17667], [41.89488, 3.97375], [41.31368, 3.14314], [40.98767, 2.82959], [41.00099, -0.83068], [41.56, -1.59812], [41.56362, -1.66375], [41.75542, -1.85308], [57.49095, 8.14549], [51.12877, 12.56479]]]]
26202 nameEn: "Suriname",
26203 groups: ["005", "419", "019", "UN"],
26205 callingCodes: ["597"]
26208 type: "MultiPolygon",
26209 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]]]]
26218 nameEn: "South Sudan",
26219 groups: ["014", "202", "002", "UN"],
26220 callingCodes: ["211"]
26223 type: "MultiPolygon",
26224 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]]]]
26233 nameEn: "S\xE3o Tom\xE9 and Principe",
26234 groups: ["017", "202", "002", "UN"],
26235 callingCodes: ["239"]
26238 type: "MultiPolygon",
26239 coordinates: [[[[4.34149, 1.91417], [6.6507, -0.28606], [7.9035, 1.92304], [4.34149, 1.91417]]]]
26248 nameEn: "El Salvador",
26249 groups: ["013", "003", "419", "019", "UN"],
26250 callingCodes: ["503"]
26253 type: "MultiPolygon",
26254 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]]]]
26262 wikidata: "Q26273",
26263 nameEn: "Sint Maarten",
26264 aliases: ["NL-SX"],
26266 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
26267 callingCodes: ["1 721"]
26270 type: "MultiPolygon",
26271 coordinates: [[[[-63.33064, 17.9615], [-63.1055, 17.86651], [-62.93924, 18.02904], [-63.02323, 18.05757], [-63.04039, 18.05619], [-63.0579, 18.06614], [-63.07759, 18.04943], [-63.09686, 18.04608], [-63.11042, 18.05339], [-63.13502, 18.05445], [-63.33064, 17.9615]]]]
26281 groups: ["145", "142", "UN"],
26282 callingCodes: ["963"]
26285 type: "MultiPolygon",
26286 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]]]]
26295 nameEn: "Eswatini",
26296 aliases: ["Swaziland"],
26297 groups: ["018", "202", "002", "UN"],
26299 callingCodes: ["268"]
26302 type: "MultiPolygon",
26303 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]]]]
26310 wikidata: "Q220982",
26311 nameEn: "Tristan da Cunha",
26312 aliases: ["SH-TA"],
26314 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
26315 isoStatus: "excRes",
26317 roadSpeedUnit: "mph",
26318 roadHeightUnit: "ft",
26319 callingCodes: ["290 8", "44 20"]
26322 type: "MultiPolygon",
26323 coordinates: [[[[-13.38232, -34.07258], [-16.67337, -41.9188], [-5.88482, -41.4829], [-13.38232, -34.07258]]]]
26331 wikidata: "Q18221",
26332 nameEn: "Turks and Caicos Islands",
26334 groups: ["BOTS", "029", "003", "419", "019", "UN"],
26336 roadSpeedUnit: "mph",
26337 roadHeightUnit: "ft",
26338 callingCodes: ["1 649"]
26341 type: "MultiPolygon",
26342 coordinates: [[[[-71.70065, 25.7637], [-72.98446, 20.4801], [-69.80718, 21.35956], [-71.70065, 25.7637]]]]
26352 groups: ["017", "202", "002", "UN"],
26353 callingCodes: ["235"]
26356 type: "MultiPolygon",
26357 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]]]]
26365 wikidata: "Q129003",
26366 nameEn: "French Southern Territories",
26378 groups: ["011", "202", "002", "UN"],
26379 callingCodes: ["228"]
26382 type: "MultiPolygon",
26383 coordinates: [[[[0.50388, 11.01011], [-0.13493, 11.14075], [-0.14462, 11.10811], [-0.05733, 11.08628], [-0.0275, 11.11202], [-514e-5, 11.10763], [342e-5, 11.08317], [0.02395, 11.06229], [0.03355, 10.9807], [-63e-4, 10.96417], [-908e-5, 10.91644], [-0.02685, 10.8783], [-0.0228, 10.81916], [-0.07183, 10.76794], [-0.07327, 10.71845], [-0.09141, 10.7147], [-0.05945, 10.63458], [0.12886, 10.53149], [0.18846, 10.4096], [0.29453, 10.41546], [0.33028, 10.30408], [0.39584, 10.31112], [0.35293, 10.09412], [0.41371, 10.06361], [0.41252, 10.02018], [0.36366, 10.03309], [0.32075, 9.72781], [0.34816, 9.71607], [0.34816, 9.66907], [0.32313, 9.6491], [0.28261, 9.69022], [0.26712, 9.66437], [0.29334, 9.59387], [0.36008, 9.6256], [0.38153, 9.58682], [0.23851, 9.57389], [0.2409, 9.52335], [0.30406, 9.521], [0.31241, 9.50337], [0.2254, 9.47869], [0.25758, 9.42696], [0.33148, 9.44812], [0.36485, 9.49749], [0.49118, 9.48339], [0.56388, 9.40697], [0.45424, 9.04581], [0.52455, 8.87746], [0.37319, 8.75262], [0.47211, 8.59945], [0.64731, 8.48866], [0.73432, 8.29529], [0.63897, 8.25873], [0.5913, 8.19622], [0.61156, 8.18324], [0.6056, 8.13959], [0.58891, 8.12779], [0.62943, 7.85751], [0.58295, 7.62368], [0.51979, 7.58706], [0.52455, 7.45354], [0.57223, 7.39326], [0.62943, 7.41099], [0.65327, 7.31643], [0.59606, 7.01252], [0.52217, 6.9723], [0.52098, 6.94391], [0.56508, 6.92971], [0.52853, 6.82921], [0.57406, 6.80348], [0.58176, 6.76049], [0.6497, 6.73682], [0.63659, 6.63857], [0.74862, 6.56517], [0.71048, 6.53083], [0.89283, 6.33779], [0.99652, 6.33779], [1.03108, 6.24064], [1.05969, 6.22998], [1.09187, 6.17074], [1.19966, 6.17069], [1.19771, 6.11522], [1.27574, 5.93551], [1.67336, 6.02702], [1.62913, 6.24075], [1.79826, 6.28221], [1.76906, 6.43189], [1.58105, 6.68619], [1.61812, 6.74843], [1.55877, 6.99737], [1.64249, 6.99562], [1.61838, 9.0527], [1.5649, 9.16941], [1.41746, 9.3226], [1.33675, 9.54765], [1.36624, 9.5951], [1.35507, 9.99525], [0.77666, 10.37665], [0.80358, 10.71459], [0.8804, 10.803], [0.91245, 10.99597], [0.66104, 10.99964], [0.4958, 10.93269], [0.50521, 10.98035], [0.48852, 10.98561], [0.50388, 11.01011]]]]
26392 nameEn: "Thailand",
26393 groups: ["035", "142", "UN"],
26395 callingCodes: ["66"]
26398 type: "MultiPolygon",
26399 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]]]]
26408 nameEn: "Tajikistan",
26409 groups: ["143", "142", "UN"],
26410 callingCodes: ["992"]
26413 type: "MultiPolygon",
26414 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]]]]
26422 wikidata: "Q36823",
26425 groups: ["061", "009", "UN"],
26427 callingCodes: ["690"]
26430 type: "MultiPolygon",
26431 coordinates: [[[[-168.251, -9.44289], [-174.18635, -7.80441], [-174.17993, -10.13616], [-168.251, -9.44289]]]]
26440 nameEn: "East Timor",
26441 aliases: ["Timor-Leste", "TP"],
26442 groups: ["035", "142", "UN"],
26444 callingCodes: ["670"]
26447 type: "MultiPolygon",
26448 coordinates: [[[[124.46701, -9.13002], [124.94011, -8.85617], [124.97742, -9.08128], [125.11764, -8.96359], [125.18632, -9.03142], [125.18907, -9.16434], [125.09434, -9.19669], [125.04044, -9.17093], [124.97892, -9.19281], [125.09025, -9.46406], [125.68138, -9.85176], [127.55165, -9.05052], [127.42116, -8.22471], [125.87691, -8.31789], [125.58506, -7.95311], [124.92337, -8.75859], [124.33472, -9.11416], [124.04628, -9.22671], [124.04286, -9.34243], [124.10539, -9.41206], [124.14517, -9.42324], [124.21247, -9.36904], [124.28115, -9.42189], [124.28115, -9.50453], [124.3535, -9.48493], [124.35258, -9.43002], [124.38554, -9.3582], [124.45971, -9.30263], [124.46701, -9.13002]]]]
26457 nameEn: "Turkmenistan",
26458 groups: ["143", "142", "UN"],
26459 callingCodes: ["993"]
26462 type: "MultiPolygon",
26463 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]]]]
26473 groups: ["015", "002", "UN"],
26474 callingCodes: ["216"]
26477 type: "MultiPolygon",
26478 coordinates: [[[[11.2718, 37.6713], [7.89009, 38.19924], [8.59123, 37.14286], [8.64044, 36.9401], [8.62972, 36.86499], [8.67706, 36.8364], [8.57613, 36.78062], [8.46537, 36.7706], [8.47609, 36.66607], [8.16167, 36.48817], [8.18936, 36.44939], [8.40731, 36.42208], [8.2626, 35.91733], [8.26472, 35.73669], [8.35371, 35.66373], [8.36086, 35.47774], [8.30329, 35.29884], [8.47318, 35.23376], [8.3555, 35.10007], [8.30727, 34.95378], [8.25189, 34.92009], [8.29655, 34.72798], [8.20482, 34.57575], [7.86264, 34.3987], [7.81242, 34.21841], [7.74207, 34.16492], [7.66174, 34.20167], [7.52851, 34.06493], [7.54088, 33.7726], [7.73687, 33.42114], [7.83028, 33.18851], [8.11433, 33.10175], [8.1179, 33.05086], [8.31895, 32.83483], [8.35999, 32.50101], [9.07483, 32.07865], [9.55544, 30.23971], [9.76848, 30.34366], [9.88152, 30.34074], [10.29516, 30.90337], [10.12239, 31.42098], [10.31364, 31.72648], [10.48497, 31.72956], [10.62788, 31.96629], [10.7315, 31.97235], [11.04234, 32.2145], [11.53898, 32.4138], [11.57828, 32.48013], [11.46037, 32.6307], [11.51549, 33.09826], [11.55852, 33.1409], [11.58941, 33.36891], [11.2718, 37.6713]]]]
26488 groups: ["061", "009", "UN"],
26490 callingCodes: ["676"]
26493 type: "MultiPolygon",
26494 coordinates: [[[[-176.74538, -22.89767], [-180, -22.90585], [-180, -24.21376], [-173.10761, -24.19665], [-173.13438, -14.94228], [-176.76826, -14.95183], [-176.74538, -22.89767]]]]
26504 groups: ["145", "142", "UN"],
26505 callingCodes: ["90"]
26508 type: "MultiPolygon",
26509 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]]]]
26518 nameEn: "Trinidad and Tobago",
26519 groups: ["029", "003", "419", "019", "UN"],
26521 callingCodes: ["1 868"]
26524 type: "MultiPolygon",
26525 coordinates: [[[[-61.62505, 11.18974], [-62.08693, 10.04435], [-60.89962, 9.81445], [-60.07172, 11.77667], [-61.62505, 11.18974]]]]
26535 groups: ["061", "009", "UN"],
26537 callingCodes: ["688"]
26540 type: "MultiPolygon",
26541 coordinates: [[[[174, -5], [174, -11.5], [179.99999, -11.5], [179.99999, -5], [174, -5]]]]
26552 groups: ["030", "142"],
26553 callingCodes: ["886"]
26556 type: "MultiPolygon",
26557 coordinates: [[[[121.8109, 21.77688], [122.26612, 25.98197], [120.49232, 25.22863], [118.56434, 24.49266], [118.42453, 24.54644], [118.35291, 24.51645], [118.28244, 24.51231], [118.11703, 24.39734], [120.69238, 21.52331], [121.8109, 21.77688]]]]
26566 nameEn: "Tanzania",
26567 groups: ["014", "202", "002", "UN"],
26569 callingCodes: ["255"]
26572 type: "MultiPolygon",
26573 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]]]]
26583 groups: ["151", "150", "UN"],
26584 callingCodes: ["380"]
26587 type: "MultiPolygon",
26588 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]]]]
26598 groups: ["014", "202", "002", "UN"],
26600 callingCodes: ["256"]
26603 type: "MultiPolygon",
26604 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]]]]
26612 wikidata: "Q16645",
26613 nameEn: "United States Minor Outlying Islands",
26622 nameEn: "United Nations",
26623 level: "unitedNations",
26624 isoStatus: "excRes"
26634 nameEn: "United States of America"
26645 groups: ["005", "419", "019", "UN"],
26646 callingCodes: ["598"]
26649 type: "MultiPolygon",
26650 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]]]]
26659 nameEn: "Uzbekistan",
26660 groups: ["143", "142", "UN"],
26661 callingCodes: ["998"]
26664 type: "MultiPolygon",
26665 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]]]]
26674 nameEn: "Vatican City",
26675 aliases: ["Holy See"],
26676 groups: ["039", "150"],
26677 callingCodes: ["379", "39 06"]
26680 type: "MultiPolygon",
26681 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]]]]
26690 nameEn: "St. Vincent and the Grenadines",
26692 groups: ["029", "003", "419", "019", "UN"],
26694 roadSpeedUnit: "mph",
26695 callingCodes: ["1 784"]
26698 type: "MultiPolygon",
26699 coordinates: [[[[-62.64026, 12.69984], [-59.94058, 12.34011], [-61.69315, 14.26451], [-62.64026, 12.69984]]]]
26708 nameEn: "Venezuela",
26710 groups: ["005", "419", "019", "UN"],
26711 callingCodes: ["58"]
26714 type: "MultiPolygon",
26715 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]]]]
26723 wikidata: "Q25305",
26724 nameEn: "British Virgin Islands",
26726 groups: ["BOTS", "029", "003", "419", "019", "UN"],
26728 roadSpeedUnit: "mph",
26729 roadHeightUnit: "ft",
26730 callingCodes: ["1 284"]
26733 type: "MultiPolygon",
26734 coordinates: [[[[-64.47127, 17.55688], [-63.88746, 19.15706], [-65.02435, 18.73231], [-64.86027, 18.39056], [-64.64673, 18.36549], [-64.47127, 17.55688]]]]
26742 wikidata: "Q11703",
26743 nameEn: "United States Virgin Islands",
26744 aliases: ["US-VI"],
26746 groups: ["Q1352230", "029", "003", "419", "019", "UN"],
26748 roadSpeedUnit: "mph",
26749 roadHeightUnit: "ft",
26750 callingCodes: ["1 340"]
26753 type: "MultiPolygon",
26754 coordinates: [[[[-65.02435, 18.73231], [-65.27974, 17.56928], [-64.47127, 17.55688], [-64.64673, 18.36549], [-64.86027, 18.39056], [-65.02435, 18.73231]]]]
26764 groups: ["035", "142", "UN"],
26765 callingCodes: ["84"]
26768 type: "MultiPolygon",
26769 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]]]]
26779 groups: ["054", "009", "UN"],
26780 callingCodes: ["678"]
26783 type: "MultiPolygon",
26784 coordinates: [[[[156.73836, -14.50464], [174.245, -23.1974], [172.71443, -12.01327], [156.73836, -14.50464]]]]
26792 wikidata: "Q35555",
26793 nameEn: "Wallis and Futuna",
26795 groups: ["Q1451600", "061", "009", "UN"],
26796 callingCodes: ["681"]
26799 type: "MultiPolygon",
26800 coordinates: [[[[-178.66551, -14.32452], [-176.76826, -14.95183], [-175.59809, -12.61507], [-178.66551, -14.32452]]]]
26810 groups: ["061", "009", "UN"],
26812 callingCodes: ["685"]
26815 type: "MultiPolygon",
26816 coordinates: [[[[-173.74402, -14.26669], [-170.99605, -15.1275], [-171.39864, -10.21587], [-173.74402, -14.26669]]]]
26826 groups: ["039", "150"],
26827 isoStatus: "usrAssn",
26828 callingCodes: ["383"]
26831 type: "MultiPolygon",
26832 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]]]]
26842 groups: ["145", "142", "UN"],
26843 callingCodes: ["967"]
26846 type: "MultiPolygon",
26847 coordinates: [[[[57.49095, 8.14549], [52.81185, 17.28568], [52.74267, 17.29519], [52.78009, 17.35124], [52.00311, 19.00083], [49.04884, 18.59899], [48.19996, 18.20584], [47.58351, 17.50366], [47.48245, 17.10808], [47.00571, 16.94765], [46.76494, 17.29151], [46.31018, 17.20464], [44.50126, 17.47475], [43.70631, 17.35762], [43.43005, 17.56148], [43.29185, 17.53224], [43.22533, 17.38343], [43.32653, 17.31179], [43.20156, 17.25901], [43.17787, 17.14717], [43.23967, 17.03428], [43.18233, 17.02673], [43.1813, 16.98438], [43.19328, 16.94703], [43.1398, 16.90696], [43.18338, 16.84852], [43.22012, 16.83932], [43.22956, 16.80613], [43.24801, 16.80613], [43.26303, 16.79479], [43.25857, 16.75304], [43.21325, 16.74416], [43.22066, 16.65179], [43.15274, 16.67248], [43.11601, 16.53166], [42.97215, 16.51093], [42.94351, 16.49467], [42.94625, 16.39721], [42.76801, 16.40371], [42.15205, 16.40211], [40.99158, 15.81743], [43.29075, 12.79154], [43.32909, 12.59711], [43.90659, 12.3823], [51.12877, 12.56479], [57.49095, 8.14549]]]]
26855 wikidata: "Q17063",
26858 groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
26859 callingCodes: ["262"]
26862 type: "MultiPolygon",
26863 coordinates: [[[[43.28731, -13.97126], [45.54824, -13.22353], [45.4971, -11.75965], [43.28731, -13.97126]]]]
26872 nameEn: "South Africa",
26873 groups: ["018", "202", "002", "UN"],
26875 callingCodes: ["27"]
26878 type: "MultiPolygon",
26879 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]]]]
26889 groups: ["014", "202", "002", "UN"],
26891 callingCodes: ["260"]
26894 type: "MultiPolygon",
26895 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]]]]
26904 nameEn: "Zimbabwe",
26905 groups: ["014", "202", "002", "UN"],
26907 callingCodes: ["263"]
26910 type: "MultiPolygon",
26911 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]]]]
26914 var borders_default = {
26917 }; // src/country-coder.ts
26919 var borders = borders_default;
26920 var whichPolygonGetter = {};
26921 var featuresByCode = {};
26922 var idFilterRegex = /(?=(?!^(and|the|of|el|la|de)$))(\b(and|the|of|el|la|de)\b)|[-_ .,'()&[\]/]/gi;
26924 function canonicalID(id) {
26927 if (s.charAt(0) === ".") {
26928 return s.toUpperCase();
26930 return s.replace(idFilterRegex, "").toUpperCase();
26934 var levels = ["subterritory", "territory", "subcountryGroup", "country", "sharedLandform", "intermediateRegion", "subregion", "region", "subunion", "union", "unitedNations", "world"];
26935 loadDerivedDataAndCaches(borders);
26937 function loadDerivedDataAndCaches(borders2) {
26938 var identifierProps = ["iso1A2", "iso1A3", "m49", "wikidata", "emojiFlag", "ccTLD", "nameEn"];
26939 var geometryFeatures = [];
26941 for (var i in borders2.features) {
26942 var feature2 = borders2.features[i];
26943 feature2.properties.id = feature2.properties.iso1A2 || feature2.properties.m49 || feature2.properties.wikidata;
26946 loadIsoStatus(feature2);
26947 loadLevel(feature2);
26948 loadGroups(feature2);
26949 loadFlag(feature2);
26950 cacheFeatureByIDs(feature2);
26951 if (feature2.geometry) geometryFeatures.push(feature2);
26954 for (var _i in borders2.features) {
26955 var _feature = borders2.features[_i];
26956 _feature.properties.groups = _feature.properties.groups.map(function (groupID) {
26957 return featuresByCode[groupID].properties.id;
26959 loadMembersForGroupsOf(_feature);
26962 for (var _i2 in borders2.features) {
26963 var _feature2 = borders2.features[_i2];
26964 loadRoadSpeedUnit(_feature2);
26965 loadRoadHeightUnit(_feature2);
26966 loadDriveSide(_feature2);
26967 loadCallingCodes(_feature2);
26968 loadGroupGroups(_feature2);
26971 for (var _i3 in borders2.features) {
26972 var _feature3 = borders2.features[_i3];
26974 _feature3.properties.groups.sort(function (groupID1, groupID2) {
26975 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
26978 if (_feature3.properties.members) _feature3.properties.members.sort(function (id1, id2) {
26979 var diff = levels.indexOf(featuresByCode[id1].properties.level) - levels.indexOf(featuresByCode[id2].properties.level);
26982 return borders2.features.indexOf(featuresByCode[id1]) - borders2.features.indexOf(featuresByCode[id2]);
26989 var geometryOnlyCollection = {
26990 type: "FeatureCollection",
26991 features: geometryFeatures
26993 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
26995 function loadGroups(feature2) {
26996 var props = feature2.properties;
26998 if (!props.groups) {
27002 if (feature2.geometry && props.country) {
27003 props.groups.push(props.country);
27006 if (props.m49 !== "001") {
27007 props.groups.push("001");
27011 function loadM49(feature2) {
27012 var props = feature2.properties;
27014 if (!props.m49 && props.iso1N3) {
27015 props.m49 = props.iso1N3;
27019 function loadTLD(feature2) {
27020 var props = feature2.properties;
27021 if (props.level === "unitedNations") return;
27023 if (!props.ccTLD && props.iso1A2) {
27024 props.ccTLD = "." + props.iso1A2.toLowerCase();
27028 function loadIsoStatus(feature2) {
27029 var props = feature2.properties;
27031 if (!props.isoStatus && props.iso1A2) {
27032 props.isoStatus = "official";
27036 function loadLevel(feature2) {
27037 var props = feature2.properties;
27038 if (props.level) return;
27040 if (!props.country) {
27041 props.level = "country";
27042 } else if (!props.iso1A2 || props.isoStatus === "official") {
27043 props.level = "territory";
27045 props.level = "subterritory";
27049 function loadGroupGroups(feature2) {
27050 var props = feature2.properties;
27051 if (feature2.geometry || !props.members) return;
27052 var featureLevelIndex = levels.indexOf(props.level);
27053 var sharedGroups = [];
27055 var _loop = function _loop(_i4) {
27056 var memberID = props.members[_i4];
27057 var member = featuresByCode[memberID];
27058 var memberGroups = member.properties.groups.filter(function (groupID) {
27059 return groupID !== feature2.properties.id && featureLevelIndex < levels.indexOf(featuresByCode[groupID].properties.level);
27063 sharedGroups = memberGroups;
27065 sharedGroups = sharedGroups.filter(function (groupID) {
27066 return memberGroups.indexOf(groupID) !== -1;
27071 for (var _i4 in props.members) {
27075 props.groups = props.groups.concat(sharedGroups.filter(function (groupID) {
27076 return props.groups.indexOf(groupID) === -1;
27079 for (var j in sharedGroups) {
27080 var groupFeature = featuresByCode[sharedGroups[j]];
27082 if (groupFeature.properties.members.indexOf(props.id) === -1) {
27083 groupFeature.properties.members.push(props.id);
27088 function loadRoadSpeedUnit(feature2) {
27089 var props = feature2.properties;
27091 if (feature2.geometry) {
27092 if (!props.roadSpeedUnit) props.roadSpeedUnit = "km/h";
27093 } else if (props.members) {
27094 var vals = Array.from(new Set(props.members.map(function (id) {
27095 var member = featuresByCode[id];
27096 if (member.geometry) return member.properties.roadSpeedUnit || "km/h";
27097 }).filter(Boolean)));
27098 if (vals.length === 1) props.roadSpeedUnit = vals[0];
27102 function loadRoadHeightUnit(feature2) {
27103 var props = feature2.properties;
27105 if (feature2.geometry) {
27106 if (!props.roadHeightUnit) props.roadHeightUnit = "m";
27107 } else if (props.members) {
27108 var vals = Array.from(new Set(props.members.map(function (id) {
27109 var member = featuresByCode[id];
27110 if (member.geometry) return member.properties.roadHeightUnit || "m";
27111 }).filter(Boolean)));
27112 if (vals.length === 1) props.roadHeightUnit = vals[0];
27116 function loadDriveSide(feature2) {
27117 var props = feature2.properties;
27119 if (feature2.geometry) {
27120 if (!props.driveSide) props.driveSide = "right";
27121 } else if (props.members) {
27122 var vals = Array.from(new Set(props.members.map(function (id) {
27123 var member = featuresByCode[id];
27124 if (member.geometry) return member.properties.driveSide || "right";
27125 }).filter(Boolean)));
27126 if (vals.length === 1) props.driveSide = vals[0];
27130 function loadCallingCodes(feature2) {
27131 var props = feature2.properties;
27133 if (!feature2.geometry && props.members) {
27134 props.callingCodes = Array.from(new Set(props.members.reduce(function (array, id) {
27135 var member = featuresByCode[id];
27136 if (member.geometry && member.properties.callingCodes) return array.concat(member.properties.callingCodes);
27142 function loadFlag(feature2) {
27143 if (!feature2.properties.iso1A2) return;
27144 var flag = feature2.properties.iso1A2.replace(/./g, function (_char) {
27145 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
27147 feature2.properties.emojiFlag = flag;
27150 function loadMembersForGroupsOf(feature2) {
27151 for (var j in feature2.properties.groups) {
27152 var groupID = feature2.properties.groups[j];
27153 var groupFeature = featuresByCode[groupID];
27154 if (!groupFeature.properties.members) groupFeature.properties.members = [];
27155 groupFeature.properties.members.push(feature2.properties.id);
27159 function cacheFeatureByIDs(feature2) {
27162 for (var k in identifierProps) {
27163 var prop = identifierProps[k];
27164 var id = feature2.properties[prop];
27165 if (id) ids.push(id);
27168 if (feature2.properties.aliases) {
27169 for (var j in feature2.properties.aliases) {
27170 ids.push(feature2.properties.aliases[j]);
27174 for (var _i5 in ids) {
27175 var _id = canonicalID(ids[_i5]);
27177 featuresByCode[_id] = feature2;
27182 function locArray(loc) {
27183 if (Array.isArray(loc)) {
27185 } else if (loc.coordinates) {
27186 return loc.coordinates;
27189 return loc.geometry.coordinates;
27192 function smallestFeature(loc) {
27193 var query = locArray(loc);
27194 var featureProperties = whichPolygonGetter(query);
27195 if (!featureProperties) return null;
27196 return featuresByCode[featureProperties.id];
27199 function countryFeature(loc) {
27200 var feature2 = smallestFeature(loc);
27201 if (!feature2) return null;
27202 var countryCode = feature2.properties.country || feature2.properties.iso1A2;
27203 return featuresByCode[countryCode] || null;
27206 var defaultOpts = {
27212 function featureForLoc(loc, opts) {
27213 var targetLevel = opts.level || "country";
27214 var maxLevel = opts.maxLevel || "world";
27215 var withProp = opts.withProp;
27216 var targetLevelIndex = levels.indexOf(targetLevel);
27217 if (targetLevelIndex === -1) return null;
27218 var maxLevelIndex = levels.indexOf(maxLevel);
27219 if (maxLevelIndex === -1) return null;
27220 if (maxLevelIndex < targetLevelIndex) return null;
27222 if (targetLevel === "country") {
27223 var fastFeature = countryFeature(loc);
27226 if (!withProp || fastFeature.properties[withProp]) {
27227 return fastFeature;
27232 var features2 = featuresContaining(loc);
27234 for (var i in features2) {
27235 var feature2 = features2[i];
27236 var levelIndex = levels.indexOf(feature2.properties.level);
27238 if (feature2.properties.level === targetLevel || levelIndex > targetLevelIndex && levelIndex <= maxLevelIndex) {
27239 if (!withProp || feature2.properties[withProp]) {
27248 function featureForID(id) {
27251 if (typeof id === "number") {
27252 stringID = id.toString();
27254 if (stringID.length === 1) {
27255 stringID = "00" + stringID;
27256 } else if (stringID.length === 2) {
27257 stringID = "0" + stringID;
27260 stringID = canonicalID(id);
27263 return featuresByCode[stringID] || null;
27266 function smallestFeaturesForBbox(bbox) {
27267 return whichPolygonGetter.bbox(bbox).map(function (props) {
27268 return featuresByCode[props.id];
27272 function smallestOrMatchingFeature(query) {
27273 if (_typeof(query) === "object") {
27274 return smallestFeature(query);
27277 return featureForID(query);
27280 function feature$1(query) {
27281 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27283 if (_typeof(query) === "object") {
27284 return featureForLoc(query, opts);
27287 return featureForID(query);
27290 function iso1A2Code(query) {
27291 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27292 opts.withProp = "iso1A2";
27293 var match = feature$1(query, opts);
27294 if (!match) return null;
27295 return match.properties.iso1A2 || null;
27298 function featuresContaining(query, strict) {
27299 var matchingFeatures;
27301 if (Array.isArray(query) && query.length === 4) {
27302 matchingFeatures = smallestFeaturesForBbox(query);
27304 var smallestOrMatching = smallestOrMatchingFeature(query);
27305 matchingFeatures = smallestOrMatching ? [smallestOrMatching] : [];
27308 if (!matchingFeatures.length) return [];
27309 var returnFeatures;
27311 if (!strict || _typeof(query) === "object") {
27312 returnFeatures = matchingFeatures.slice();
27314 returnFeatures = [];
27317 for (var j in matchingFeatures) {
27318 var properties = matchingFeatures[j].properties;
27320 for (var i in properties.groups) {
27321 var groupID = properties.groups[i];
27322 var groupFeature = featuresByCode[groupID];
27324 if (returnFeatures.indexOf(groupFeature) === -1) {
27325 returnFeatures.push(groupFeature);
27330 return returnFeatures;
27333 function featuresIn(id, strict) {
27334 var feature2 = featureForID(id);
27335 if (!feature2) return [];
27336 var features2 = [];
27339 features2.push(feature2);
27342 var properties = feature2.properties;
27344 if (properties.members) {
27345 for (var i in properties.members) {
27346 var memberID = properties.members[i];
27347 features2.push(featuresByCode[memberID]);
27354 function aggregateFeature(id) {
27355 var features2 = featuresIn(id, false);
27356 if (features2.length === 0) return null;
27357 var aggregateCoordinates = [];
27359 for (var i in features2) {
27360 var feature2 = features2[i];
27362 if (feature2.geometry && feature2.geometry.type === "MultiPolygon" && feature2.geometry.coordinates) {
27363 aggregateCoordinates = aggregateCoordinates.concat(feature2.geometry.coordinates);
27369 properties: features2[0].properties,
27371 type: "MultiPolygon",
27372 coordinates: aggregateCoordinates
27377 function roadSpeedUnit(query) {
27378 var feature2 = smallestOrMatchingFeature(query);
27379 return feature2 && feature2.properties.roadSpeedUnit || null;
27382 var RADIUS = 6378137;
27383 var FLATTENING = 1 / 298.257223563;
27384 var POLAR_RADIUS = 6356752.3142;
27387 FLATTENING: FLATTENING,
27388 POLAR_RADIUS: POLAR_RADIUS
27391 var geometry_1 = geometry;
27392 var ring = ringArea;
27394 function geometry(_) {
27400 return polygonArea(_.coordinates);
27402 case 'MultiPolygon':
27403 for (i = 0; i < _.coordinates.length; i++) {
27404 area += polygonArea(_.coordinates[i]);
27412 case 'MultiLineString':
27415 case 'GeometryCollection':
27416 for (i = 0; i < _.geometries.length; i++) {
27417 area += geometry(_.geometries[i]);
27424 function polygonArea(coords) {
27427 if (coords && coords.length > 0) {
27428 area += Math.abs(ringArea(coords[0]));
27430 for (var i = 1; i < coords.length; i++) {
27431 area -= Math.abs(ringArea(coords[i]));
27438 * Calculate the approximate area of the polygon were it projected onto
27439 * the earth. Note that this area will be positive if ring is oriented
27440 * clockwise, otherwise it will be negative.
27443 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
27444 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
27445 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
27448 * {float} The approximate signed geodesic area of the polygon in square
27453 function ringArea(coords) {
27462 coordsLength = coords.length;
27464 if (coordsLength > 2) {
27465 for (i = 0; i < coordsLength; i++) {
27466 if (i === coordsLength - 2) {
27468 lowerIndex = coordsLength - 2;
27469 middleIndex = coordsLength - 1;
27471 } else if (i === coordsLength - 1) {
27473 lowerIndex = coordsLength - 1;
27479 middleIndex = i + 1;
27480 upperIndex = i + 2;
27483 p1 = coords[lowerIndex];
27484 p2 = coords[middleIndex];
27485 p3 = coords[upperIndex];
27486 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
27489 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
27496 return _ * Math.PI / 180;
27499 var geojsonArea = {
27500 geometry: geometry_1,
27504 var $includes = arrayIncludes.includes;
27507 // `Array.prototype.includes` method
27508 // https://tc39.es/ecma262/#sec-array.prototype.includes
27509 _export({ target: 'Array', proto: true }, {
27510 includes: function includes(el /* , fromIndex = 0 */) {
27511 return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
27515 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
27516 addToUnscopables('includes');
27518 var validateCenter_1$1 = function validateCenter(center) {
27519 var validCenterLengths = [2, 3];
27521 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
27522 throw new Error("ERROR! Center has to be an array of length two or three");
27525 var _center = _slicedToArray(center, 2),
27529 if (typeof lng !== "number" || typeof lat !== "number") {
27530 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
27533 if (lng > 180 || lng < -180) {
27534 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
27537 if (lat > 90 || lat < -90) {
27538 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
27542 var validateCenter$1 = {
27543 validateCenter: validateCenter_1$1
27546 var validateRadius_1$1 = function validateRadius(radius) {
27547 if (typeof radius !== "number") {
27548 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
27552 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
27556 var validateRadius$1 = {
27557 validateRadius: validateRadius_1$1
27560 var validateNumberOfEdges_1$1 = function validateNumberOfEdges(numberOfEdges) {
27561 if (typeof numberOfEdges !== "number") {
27562 var ARGUMENT_TYPE = Array.isArray(numberOfEdges) ? "array" : _typeof(numberOfEdges);
27563 throw new Error("ERROR! Number of edges has to be a number but was: ".concat(ARGUMENT_TYPE));
27566 if (numberOfEdges < 3) {
27567 throw new Error("ERROR! Number of edges has to be at least 3 but was: ".concat(numberOfEdges));
27571 var validateNumberOfEdges$1 = {
27572 validateNumberOfEdges: validateNumberOfEdges_1$1
27575 var validateEarthRadius_1$1 = function validateEarthRadius(earthRadius) {
27576 if (typeof earthRadius !== "number") {
27577 var ARGUMENT_TYPE = Array.isArray(earthRadius) ? "array" : _typeof(earthRadius);
27578 throw new Error("ERROR! Earth radius has to be a number but was: ".concat(ARGUMENT_TYPE));
27581 if (earthRadius <= 0) {
27582 throw new Error("ERROR! Earth radius has to be a positive number but was: ".concat(earthRadius));
27586 var validateEarthRadius$1 = {
27587 validateEarthRadius: validateEarthRadius_1$1
27590 var validateBearing_1$1 = function validateBearing(bearing) {
27591 if (typeof bearing !== "number") {
27592 var ARGUMENT_TYPE = Array.isArray(bearing) ? "array" : _typeof(bearing);
27593 throw new Error("ERROR! Bearing has to be a number but was: ".concat(ARGUMENT_TYPE));
27597 var validateBearing$1 = {
27598 validateBearing: validateBearing_1$1
27601 var validateCenter = validateCenter$1.validateCenter;
27602 var validateRadius = validateRadius$1.validateRadius;
27603 var validateNumberOfEdges = validateNumberOfEdges$1.validateNumberOfEdges;
27604 var validateEarthRadius = validateEarthRadius$1.validateEarthRadius;
27605 var validateBearing = validateBearing$1.validateBearing;
27607 function validateInput$1(_ref) {
27608 var center = _ref.center,
27609 radius = _ref.radius,
27610 numberOfEdges = _ref.numberOfEdges,
27611 earthRadius = _ref.earthRadius,
27612 bearing = _ref.bearing;
27613 validateCenter(center);
27614 validateRadius(radius);
27615 validateNumberOfEdges(numberOfEdges);
27616 validateEarthRadius(earthRadius);
27617 validateBearing(bearing);
27620 var validateCenter_1 = validateCenter;
27621 var validateRadius_1 = validateRadius;
27622 var validateNumberOfEdges_1 = validateNumberOfEdges;
27623 var validateEarthRadius_1 = validateEarthRadius;
27624 var validateBearing_1 = validateBearing;
27625 var validateInput_1 = validateInput$1;
27626 var inputValidation = {
27627 validateCenter: validateCenter_1,
27628 validateRadius: validateRadius_1,
27629 validateNumberOfEdges: validateNumberOfEdges_1,
27630 validateEarthRadius: validateEarthRadius_1,
27631 validateBearing: validateBearing_1,
27632 validateInput: validateInput_1
27635 var validateInput = inputValidation.validateInput;
27636 var defaultEarthRadius = 6378137; // equatorial Earth radius
27638 function toRadians(angleInDegrees) {
27639 return angleInDegrees * Math.PI / 180;
27642 function toDegrees(angleInRadians) {
27643 return angleInRadians * 180 / Math.PI;
27646 function offset(c1, distance, earthRadius, bearing) {
27647 var lat1 = toRadians(c1[1]);
27648 var lon1 = toRadians(c1[0]);
27649 var dByR = distance / earthRadius;
27650 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
27651 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
27652 return [toDegrees(lon), toDegrees(lat)];
27655 var circleToPolygon = function circleToPolygon(center, radius, options) {
27656 var n = getNumberOfEdges(options);
27657 var earthRadius = getEarthRadius(options);
27658 var bearing = getBearing(options);
27659 var direction = getDirection(options); // validateInput() throws error on invalid input and do nothing on valid input
27665 earthRadius: earthRadius,
27668 var start = toRadians(bearing);
27669 var coordinates = [];
27671 for (var i = 0; i < n; ++i) {
27672 coordinates.push(offset(center, radius, earthRadius, start + direction * 2 * Math.PI * -i / n));
27675 coordinates.push(coordinates[0]);
27678 coordinates: [coordinates]
27682 function getNumberOfEdges(options) {
27683 if (isUndefinedOrNull(options)) {
27685 } else if (isObjectNotArray(options)) {
27686 var numberOfEdges = options.numberOfEdges;
27687 return numberOfEdges === undefined ? 32 : numberOfEdges;
27693 function getEarthRadius(options) {
27694 if (isUndefinedOrNull(options)) {
27695 return defaultEarthRadius;
27696 } else if (isObjectNotArray(options)) {
27697 var earthRadius = options.earthRadius;
27698 return earthRadius === undefined ? defaultEarthRadius : earthRadius;
27701 return defaultEarthRadius;
27704 function getDirection(options) {
27705 if (isObjectNotArray(options) && options.rightHandRule) {
27712 function getBearing(options) {
27713 if (isUndefinedOrNull(options)) {
27715 } else if (isObjectNotArray(options)) {
27716 var bearing = options.bearing;
27717 return bearing === undefined ? 0 : bearing;
27723 function isObjectNotArray(argument) {
27724 return argument !== null && _typeof(argument) === "object" && !Array.isArray(argument);
27727 function isUndefinedOrNull(argument) {
27728 return argument === null || argument === undefined;
27731 // `Number.EPSILON` constant
27732 // https://tc39.es/ecma262/#sec-number.epsilon
27733 _export({ target: 'Number', stat: true }, {
27734 EPSILON: Math.pow(2, -52)
27739 // `CreateHTML` abstract operation
27740 // https://tc39.es/ecma262/#sec-createhtml
27741 var createHtml = function (string, tag, attribute, value) {
27742 var S = String(requireObjectCoercible(string));
27743 var p1 = '<' + tag;
27744 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
27745 return p1 + '>' + S + '</' + tag + '>';
27748 // check the existence of a method, lowercase
27749 // of a tag and escaping quotes in arguments
27750 var stringHtmlForced = function (METHOD_NAME) {
27751 return fails(function () {
27752 var test = ''[METHOD_NAME]('"');
27753 return test !== test.toLowerCase() || test.split('"').length > 3;
27757 // `String.prototype.link` method
27758 // https://tc39.es/ecma262/#sec-string.prototype.link
27759 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
27760 link: function link(url) {
27761 return createHtml(this, 'a', 'href', url);
27767 * Fast Splay tree for Node and browser
27769 * @author Alexander Milevski <info@w8r.name>
27776 function Node(key, data) {
27786 /* follows "An implementation of top-down splaying"
27787 * by D. Sleator <sleator@cs.cmu.edu> March 1992
27791 function DEFAULT_COMPARE(a, b) {
27792 return a > b ? 1 : a < b ? -1 : 0;
27795 * Simple top down splay, not requiring i to be in the tree t.
27799 function splay(i, t, comparator) {
27800 var N = new Node$1(null, null);
27805 var cmp = comparator(i, t.key); //if (i < t.key) {
27808 if (t.left === null) break; //if (i < t.left.key) {
27810 if (comparator(i, t.left.key) < 0) {
27817 if (t.left === null) break;
27824 t = t.left; //} else if (i > t.key) {
27825 } else if (cmp > 0) {
27826 if (t.right === null) break; //if (i > t.right.key) {
27828 if (comparator(i, t.right.key) > 0) {
27835 if (t.right === null) break;
27855 function insert(i, data, t, comparator) {
27856 var node = new Node$1(i, data);
27859 node.left = node.right = null;
27863 t = splay(i, t, comparator);
27864 var cmp = comparator(i, t.key);
27867 node.left = t.left;
27870 } else if (cmp >= 0) {
27871 node.right = t.right;
27879 function split$1(key, v, comparator) {
27884 v = splay(key, v, comparator);
27885 var cmp = comparator(v.key, key);
27890 } else if (cmp < 0) {
27907 function merge$3(left, right, comparator) {
27908 if (right === null) return left;
27909 if (left === null) return right;
27910 right = splay(left.key, right, comparator);
27915 * Prints level of the tree
27919 function printRow(root, prefix, isTail, out, printNode) {
27921 out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n");
27922 var indent = prefix + (isTail ? ' ' : '│ ');
27923 if (root.left) printRow(root.left, indent, false, out, printNode);
27924 if (root.right) printRow(root.right, indent, true, out, printNode);
27931 function Tree(comparator) {
27932 if (comparator === void 0) {
27933 comparator = DEFAULT_COMPARE;
27938 this._comparator = comparator;
27941 * Inserts a key, allows duplicates
27945 Tree.prototype.insert = function (key, data) {
27947 return this._root = insert(key, data, this._root, this._comparator);
27950 * Adds a key, if it is not present in the tree
27954 Tree.prototype.add = function (key, data) {
27955 var node = new Node$1(key, data);
27957 if (this._root === null) {
27958 node.left = node.right = null;
27963 var comparator = this._comparator;
27964 var t = splay(key, this._root, comparator);
27965 var cmp = comparator(key, t.key);
27966 if (cmp === 0) this._root = t;else {
27968 node.left = t.left;
27971 } else if (cmp > 0) {
27972 node.right = t.right;
27984 * @return {Node|null}
27988 Tree.prototype.remove = function (key) {
27989 this._root = this._remove(key, this._root, this._comparator);
27992 * Deletes i from the tree if it's there
27996 Tree.prototype._remove = function (i, t, comparator) {
27998 if (t === null) return null;
27999 t = splay(i, t, comparator);
28000 var cmp = comparator(i, t.key);
28004 if (t.left === null) {
28007 x = splay(i, t.left, comparator);
28016 /* It wasn't there */
28019 * Removes and returns the node with smallest key
28023 Tree.prototype.pop = function () {
28024 var node = this._root;
28027 while (node.left) {
28031 this._root = splay(node.key, this._root, this._comparator);
28032 this._root = this._remove(node.key, this._root, this._comparator);
28042 * Find without splaying
28046 Tree.prototype.findStatic = function (key) {
28047 var current = this._root;
28048 var compare = this._comparator;
28051 var cmp = compare(key, current.key);
28052 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
28058 Tree.prototype.find = function (key) {
28060 this._root = splay(key, this._root, this._comparator);
28061 if (this._comparator(key, this._root.key) !== 0) return null;
28067 Tree.prototype.contains = function (key) {
28068 var current = this._root;
28069 var compare = this._comparator;
28072 var cmp = compare(key, current.key);
28073 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
28079 Tree.prototype.forEach = function (visitor, ctx) {
28080 var current = this._root;
28082 /* Initialize stack s */
28087 if (current !== null) {
28089 current = current.left;
28091 if (Q.length !== 0) {
28093 visitor.call(ctx, current);
28094 current = current.right;
28095 } else done = true;
28102 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
28106 Tree.prototype.range = function (low, high, fn, ctx) {
28108 var compare = this._comparator;
28109 var node = this._root;
28112 while (Q.length !== 0 || node) {
28118 cmp = compare(node.key, high);
28122 } else if (compare(node.key, low) >= 0) {
28123 if (fn.call(ctx, node)) return this; // stop if smth is returned
28133 * Returns array of keys
28137 Tree.prototype.keys = function () {
28139 this.forEach(function (_a) {
28141 return keys.push(key);
28146 * Returns array of all the data in the nodes
28150 Tree.prototype.values = function () {
28152 this.forEach(function (_a) {
28153 var data = _a.data;
28154 return values.push(data);
28159 Tree.prototype.min = function () {
28160 if (this._root) return this.minNode(this._root).key;
28164 Tree.prototype.max = function () {
28165 if (this._root) return this.maxNode(this._root).key;
28169 Tree.prototype.minNode = function (t) {
28170 if (t === void 0) {
28174 if (t) while (t.left) {
28180 Tree.prototype.maxNode = function (t) {
28181 if (t === void 0) {
28185 if (t) while (t.right) {
28191 * Returns node at given index
28195 Tree.prototype.at = function (index) {
28196 var current = this._root;
28204 current = current.left;
28206 if (Q.length > 0) {
28208 if (i === index) return current;
28210 current = current.right;
28211 } else done = true;
28218 Tree.prototype.next = function (d) {
28219 var root = this._root;
28220 var successor = null;
28223 successor = d.right;
28225 while (successor.left) {
28226 successor = successor.left;
28232 var comparator = this._comparator;
28235 var cmp = comparator(d.key, root.key);
28236 if (cmp === 0) break;else if (cmp < 0) {
28239 } else root = root.right;
28245 Tree.prototype.prev = function (d) {
28246 var root = this._root;
28247 var predecessor = null;
28249 if (d.left !== null) {
28250 predecessor = d.left;
28252 while (predecessor.right) {
28253 predecessor = predecessor.right;
28256 return predecessor;
28259 var comparator = this._comparator;
28262 var cmp = comparator(d.key, root.key);
28263 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
28264 predecessor = root;
28269 return predecessor;
28272 Tree.prototype.clear = function () {
28278 Tree.prototype.toList = function () {
28279 return toList(this._root);
28282 * Bulk-load items. Both array have to be same size
28286 Tree.prototype.load = function (keys, values, presort) {
28287 if (values === void 0) {
28291 if (presort === void 0) {
28295 var size = keys.length;
28296 var comparator = this._comparator; // sort if needed
28298 if (presort) sort(keys, values, 0, size - 1, comparator);
28300 if (this._root === null) {
28302 this._root = loadRecursive(keys, values, 0, size);
28305 // that re-builds the whole tree from two in-order traversals
28306 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
28307 size = this._size + size;
28308 this._root = sortedListToBST({
28316 Tree.prototype.isEmpty = function () {
28317 return this._root === null;
28320 Object.defineProperty(Tree.prototype, "size", {
28321 get: function get() {
28327 Object.defineProperty(Tree.prototype, "root", {
28328 get: function get() {
28335 Tree.prototype.toString = function (printNode) {
28336 if (printNode === void 0) {
28337 printNode = function printNode(n) {
28338 return String(n.key);
28343 printRow(this._root, '', true, function (v) {
28344 return out.push(v);
28346 return out.join('');
28349 Tree.prototype.update = function (key, newKey, newData) {
28350 var comparator = this._comparator;
28352 var _a = split$1(key, this._root, comparator),
28356 if (comparator(key, newKey) < 0) {
28357 right = insert(newKey, newData, right, comparator);
28359 left = insert(newKey, newData, left, comparator);
28362 this._root = merge$3(left, right, comparator);
28365 Tree.prototype.split = function (key) {
28366 return split$1(key, this._root, this._comparator);
28372 function loadRecursive(keys, values, start, end) {
28373 var size = end - start;
28376 var middle = start + Math.floor(size / 2);
28377 var key = keys[middle];
28378 var data = values[middle];
28379 var node = new Node$1(key, data);
28380 node.left = loadRecursive(keys, values, start, middle);
28381 node.right = loadRecursive(keys, values, middle + 1, end);
28388 function createList(keys, values) {
28389 var head = new Node$1(null, null);
28392 for (var i = 0; i < keys.length; i++) {
28393 p = p.next = new Node$1(keys[i], values[i]);
28400 function toList(root) {
28401 var current = root;
28404 var head = new Node$1(null, null);
28410 current = current.left;
28412 if (Q.length > 0) {
28413 current = p = p.next = Q.pop();
28414 current = current.right;
28415 } else done = true;
28419 p.next = null; // that'll work even if the tree was empty
28424 function sortedListToBST(list, start, end) {
28425 var size = end - start;
28428 var middle = start + Math.floor(size / 2);
28429 var left = sortedListToBST(list, start, middle);
28430 var root = list.head;
28432 list.head = list.head.next;
28433 root.right = sortedListToBST(list, middle + 1, end);
28440 function mergeLists(l1, l2, compare) {
28441 var head = new Node$1(null, null); // dummy
28447 while (p1 !== null && p2 !== null) {
28448 if (compare(p1.key, p2.key) < 0) {
28461 } else if (p2 !== null) {
28468 function sort(keys, values, left, right, compare) {
28469 if (left >= right) return;
28470 var pivot = keys[left + right >> 1];
28477 } while (compare(keys[i], pivot) < 0);
28481 } while (compare(keys[j], pivot) > 0);
28488 values[i] = values[j];
28492 sort(keys, values, left, j, compare);
28493 sort(keys, values, j + 1, right, compare);
28496 function _classCallCheck(instance, Constructor) {
28497 if (!(instance instanceof Constructor)) {
28498 throw new TypeError("Cannot call a class as a function");
28502 function _defineProperties(target, props) {
28503 for (var i = 0; i < props.length; i++) {
28504 var descriptor = props[i];
28505 descriptor.enumerable = descriptor.enumerable || false;
28506 descriptor.configurable = true;
28507 if ("value" in descriptor) descriptor.writable = true;
28508 Object.defineProperty(target, descriptor.key, descriptor);
28512 function _createClass(Constructor, protoProps, staticProps) {
28513 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
28514 if (staticProps) _defineProperties(Constructor, staticProps);
28515 return Constructor;
28518 * A bounding box has the format:
28520 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
28525 var isInBbox = function isInBbox(bbox, point) {
28526 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
28528 /* Returns either null, or a bbox (aka an ordered pair of points)
28529 * If there is only one point of overlap, a bbox with identical points
28530 * will be returned */
28533 var getBboxOverlap = function getBboxOverlap(b1, b2) {
28534 // check if the bboxes overlap at all
28535 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
28537 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
28538 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
28540 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
28541 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
28554 /* Javascript doesn't do integer math. Everything is
28555 * floating point with percision Number.EPSILON.
28557 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
28561 var epsilon = Number.EPSILON; // IE Polyfill
28563 if (epsilon === undefined) epsilon = Math.pow(2, -52);
28564 var EPSILON_SQ = epsilon * epsilon;
28565 /* FLP comparator */
28567 var cmp = function cmp(a, b) {
28568 // check if they're both 0
28569 if (-epsilon < a && a < epsilon) {
28570 if (-epsilon < b && b < epsilon) {
28573 } // check if they're flp equal
28578 if (ab * ab < EPSILON_SQ * a * b) {
28580 } // normal comparison
28583 return a < b ? -1 : 1;
28586 * This class rounds incoming values sufficiently so that
28587 * floating points problems are, for the most part, avoided.
28589 * Incoming points are have their x & y values tested against
28590 * all previously seen x & y values. If either is 'too close'
28591 * to a previously seen value, it's value is 'snapped' to the
28592 * previously seen value.
28594 * All points should be rounded by this class before being
28595 * stored in any data structures in the rest of this algorithm.
28599 var PtRounder = /*#__PURE__*/function () {
28600 function PtRounder() {
28601 _classCallCheck(this, PtRounder);
28606 _createClass(PtRounder, [{
28608 value: function reset() {
28609 this.xRounder = new CoordRounder();
28610 this.yRounder = new CoordRounder();
28614 value: function round(x, y) {
28616 x: this.xRounder.round(x),
28617 y: this.yRounder.round(y)
28625 var CoordRounder = /*#__PURE__*/function () {
28626 function CoordRounder() {
28627 _classCallCheck(this, CoordRounder);
28629 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
28632 } // Note: this can rounds input values backwards or forwards.
28633 // You might ask, why not restrict this to just rounding
28634 // forwards? Wouldn't that allow left endpoints to always
28635 // remain left endpoints during splitting (never change to
28636 // right). No - it wouldn't, because we snap intersections
28637 // to endpoints (to establish independence from the segment
28638 // angle for t-intersections).
28641 _createClass(CoordRounder, [{
28643 value: function round(coord) {
28644 var node = this.tree.add(coord);
28645 var prevNode = this.tree.prev(node);
28647 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
28648 this.tree.remove(coord);
28649 return prevNode.key;
28652 var nextNode = this.tree.next(node);
28654 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
28655 this.tree.remove(coord);
28656 return nextNode.key;
28663 return CoordRounder;
28664 }(); // singleton available by import
28667 var rounder = new PtRounder();
28668 /* Cross Product of two vectors with first point at origin */
28670 var crossProduct = function crossProduct(a, b) {
28671 return a.x * b.y - a.y * b.x;
28673 /* Dot Product of two vectors with first point at origin */
28676 var dotProduct = function dotProduct(a, b) {
28677 return a.x * b.x + a.y * b.y;
28679 /* Comparator for two vectors with same starting point */
28682 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
28684 x: endPt1.x - basePt.x,
28685 y: endPt1.y - basePt.y
28688 x: endPt2.x - basePt.x,
28689 y: endPt2.y - basePt.y
28691 var kross = crossProduct(v1, v2);
28692 return cmp(kross, 0);
28695 var length = function length(v) {
28696 return Math.sqrt(dotProduct(v, v));
28698 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
28701 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
28703 x: pBase.x - pShared.x,
28704 y: pBase.y - pShared.y
28707 x: pAngle.x - pShared.x,
28708 y: pAngle.y - pShared.y
28710 return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28712 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
28715 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
28717 x: pBase.x - pShared.x,
28718 y: pBase.y - pShared.y
28721 x: pAngle.x - pShared.x,
28722 y: pAngle.y - pShared.y
28724 return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28726 /* Get the x coordinate where the given line (defined by a point and vector)
28727 * crosses the horizontal line with the given y coordiante.
28728 * In the case of parrallel lines (including overlapping ones) returns null. */
28731 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
28732 if (v.y === 0) return null;
28734 x: pt.x + v.x / v.y * (y - pt.y),
28738 /* Get the y coordinate where the given line (defined by a point and vector)
28739 * crosses the vertical line with the given x coordiante.
28740 * In the case of parrallel lines (including overlapping ones) returns null. */
28743 var verticalIntersection = function verticalIntersection(pt, v, x) {
28744 if (v.x === 0) return null;
28747 y: pt.y + v.y / v.x * (x - pt.x)
28750 /* Get the intersection of two lines, each defined by a base point and a vector.
28751 * In the case of parrallel lines (including overlapping ones) returns null. */
28754 var intersection = function intersection(pt1, v1, pt2, v2) {
28755 // take some shortcuts for vertical and horizontal lines
28756 // this also ensures we don't calculate an intersection and then discover
28757 // it's actually outside the bounding box of the line
28758 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
28759 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
28760 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
28761 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
28762 // This algorithm is based on Schneider and Eberly.
28763 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
28765 var kross = crossProduct(v1, v2);
28766 if (kross == 0) return null;
28771 var d1 = crossProduct(ve, v1) / kross;
28772 var d2 = crossProduct(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
28774 var x1 = pt1.x + d2 * v1.x,
28775 x2 = pt2.x + d1 * v2.x;
28776 var y1 = pt1.y + d2 * v1.y,
28777 y2 = pt2.y + d1 * v2.y;
28778 var x = (x1 + x2) / 2;
28779 var y = (y1 + y2) / 2;
28786 var SweepEvent = /*#__PURE__*/function () {
28787 _createClass(SweepEvent, null, [{
28789 // for ordering sweep events in the sweep event queue
28790 value: function compare(a, b) {
28791 // favor event with a point that the sweep line hits first
28792 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
28793 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
28795 if (a.point !== b.point) a.link(b); // favor right events over left
28797 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
28798 // ordering of this case is the same as for their segments
28800 return Segment.compare(a.segment, b.segment);
28801 } // for ordering points in sweep line order
28804 key: "comparePoints",
28805 value: function comparePoints(aPt, bPt) {
28806 if (aPt.x < bPt.x) return -1;
28807 if (aPt.x > bPt.x) return 1;
28808 if (aPt.y < bPt.y) return -1;
28809 if (aPt.y > bPt.y) return 1;
28811 } // Warning: 'point' input will be modified and re-used (for performance)
28815 function SweepEvent(point, isLeft) {
28816 _classCallCheck(this, SweepEvent);
28818 if (point.events === undefined) point.events = [this];else point.events.push(this);
28819 this.point = point;
28820 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
28823 _createClass(SweepEvent, [{
28825 value: function link(other) {
28826 if (other.point === this.point) {
28827 throw new Error('Tried to link already linked events');
28830 var otherEvents = other.point.events;
28832 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
28833 var evt = otherEvents[i];
28834 this.point.events.push(evt);
28835 evt.point = this.point;
28838 this.checkForConsuming();
28840 /* Do a pass over our linked events and check to see if any pair
28841 * of segments match, and should be consumed. */
28844 key: "checkForConsuming",
28845 value: function checkForConsuming() {
28846 // FIXME: The loops in this method run O(n^2) => no good.
28847 // Maintain little ordered sweep event trees?
28848 // Can we maintaining an ordering that avoids the need
28849 // for the re-sorting with getLeftmostComparator in geom-out?
28850 // Compare each pair of events to see if other events also match
28851 var numEvents = this.point.events.length;
28853 for (var i = 0; i < numEvents; i++) {
28854 var evt1 = this.point.events[i];
28855 if (evt1.segment.consumedBy !== undefined) continue;
28857 for (var j = i + 1; j < numEvents; j++) {
28858 var evt2 = this.point.events[j];
28859 if (evt2.consumedBy !== undefined) continue;
28860 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
28861 evt1.segment.consume(evt2.segment);
28866 key: "getAvailableLinkedEvents",
28867 value: function getAvailableLinkedEvents() {
28868 // point.events is always of length 2 or greater
28871 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
28872 var evt = this.point.events[i];
28874 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
28882 * Returns a comparator function for sorting linked events that will
28883 * favor the event that will give us the smallest left-side angle.
28884 * All ring construction starts as low as possible heading to the right,
28885 * so by always turning left as sharp as possible we'll get polygons
28886 * without uncessary loops & holes.
28888 * The comparator function has a compute cache such that it avoids
28889 * re-computing already-computed values.
28893 key: "getLeftmostComparator",
28894 value: function getLeftmostComparator(baseEvent) {
28897 var cache = new Map();
28899 var fillCache = function fillCache(linkedEvent) {
28900 var nextEvent = linkedEvent.otherSE;
28901 cache.set(linkedEvent, {
28902 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
28903 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
28907 return function (a, b) {
28908 if (!cache.has(a)) fillCache(a);
28909 if (!cache.has(b)) fillCache(b);
28911 var _cache$get = cache.get(a),
28912 asine = _cache$get.sine,
28913 acosine = _cache$get.cosine;
28915 var _cache$get2 = cache.get(b),
28916 bsine = _cache$get2.sine,
28917 bcosine = _cache$get2.cosine; // both on or above x-axis
28920 if (asine >= 0 && bsine >= 0) {
28921 if (acosine < bcosine) return 1;
28922 if (acosine > bcosine) return -1;
28924 } // both below x-axis
28927 if (asine < 0 && bsine < 0) {
28928 if (acosine < bcosine) return -1;
28929 if (acosine > bcosine) return 1;
28931 } // one above x-axis, one below
28934 if (bsine < asine) return -1;
28935 if (bsine > asine) return 1;
28942 }(); // segments and sweep events when all else is identical
28947 var Segment = /*#__PURE__*/function () {
28948 _createClass(Segment, null, [{
28951 /* This compare() function is for ordering segments in the sweep
28952 * line tree, and does so according to the following criteria:
28954 * Consider the vertical line that lies an infinestimal step to the
28955 * right of the right-more of the two left endpoints of the input
28956 * segments. Imagine slowly moving a point up from negative infinity
28957 * in the increasing y direction. Which of the two segments will that
28958 * point intersect first? That segment comes 'before' the other one.
28960 * If neither segment would be intersected by such a line, (if one
28961 * or more of the segments are vertical) then the line to be considered
28962 * is directly on the right-more of the two left inputs.
28964 value: function compare(a, b) {
28965 var alx = a.leftSE.point.x;
28966 var blx = b.leftSE.point.x;
28967 var arx = a.rightSE.point.x;
28968 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
28970 if (brx < alx) return 1;
28971 if (arx < blx) return -1;
28972 var aly = a.leftSE.point.y;
28973 var bly = b.leftSE.point.y;
28974 var ary = a.rightSE.point.y;
28975 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
28978 // are the two segments in the same horizontal plane?
28979 if (bly < aly && bly < ary) return 1;
28980 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
28982 var aCmpBLeft = a.comparePoint(b.leftSE.point);
28983 if (aCmpBLeft < 0) return 1;
28984 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
28986 var bCmpARight = b.comparePoint(a.rightSE.point);
28987 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
28988 // left endpoint to be first (arbitrary?)
28991 } // is left endpoint of segment A the right-more?
28995 if (aly < bly && aly < bry) return -1;
28996 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
28998 var bCmpALeft = b.comparePoint(a.leftSE.point);
28999 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
29001 var aCmpBRight = a.comparePoint(b.rightSE.point);
29002 if (aCmpBRight < 0) return 1;
29003 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
29004 // left endpoint to be first (arbitrary?)
29007 } // if we get here, the two left endpoints are in the same
29008 // vertical plane, ie alx === blx
29009 // consider the lower left-endpoint to come first
29012 if (aly < bly) return -1;
29013 if (aly > bly) return 1; // left endpoints are identical
29014 // check for colinearity by using the left-more right endpoint
29015 // is the A right endpoint more left-more?
29018 var _bCmpARight = b.comparePoint(a.rightSE.point);
29020 if (_bCmpARight !== 0) return _bCmpARight;
29021 } // is the B right endpoint more left-more?
29025 var _aCmpBRight = a.comparePoint(b.rightSE.point);
29027 if (_aCmpBRight < 0) return 1;
29028 if (_aCmpBRight > 0) return -1;
29032 // are these two [almost] vertical segments with opposite orientation?
29033 // if so, the one with the lower right endpoint comes first
29034 var ay = ary - aly;
29035 var ax = arx - alx;
29036 var by = bry - bly;
29037 var bx = brx - blx;
29038 if (ay > ax && by < bx) return 1;
29039 if (ay < ax && by > bx) return -1;
29040 } // we have colinear segments with matching orientation
29041 // consider the one with more left-more right endpoint to be first
29044 if (arx > brx) return 1;
29045 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
29046 // vertical plane, ie arx === brx
29047 // consider the lower right-endpoint to come first
29049 if (ary < bry) return -1;
29050 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
29051 // fall back on creation order as consistent tie-breaker
29053 if (a.id < b.id) return -1;
29054 if (a.id > b.id) return 1; // identical segment, ie a === b
29058 /* Warning: a reference to ringWindings input will be stored,
29059 * and possibly will be later modified */
29063 function Segment(leftSE, rightSE, rings, windings) {
29064 _classCallCheck(this, Segment);
29066 this.id = ++segmentId;
29067 this.leftSE = leftSE;
29068 leftSE.segment = this;
29069 leftSE.otherSE = rightSE;
29070 this.rightSE = rightSE;
29071 rightSE.segment = this;
29072 rightSE.otherSE = leftSE;
29073 this.rings = rings;
29074 this.windings = windings; // left unset for performance, set later in algorithm
29075 // this.ringOut, this.consumedBy, this.prev
29078 _createClass(Segment, [{
29079 key: "replaceRightSE",
29081 /* When a segment is split, the rightSE is replaced with a new sweep event */
29082 value: function replaceRightSE(newRightSE) {
29083 this.rightSE = newRightSE;
29084 this.rightSE.segment = this;
29085 this.rightSE.otherSE = this.leftSE;
29086 this.leftSE.otherSE = this.rightSE;
29090 value: function bbox() {
29091 var y1 = this.leftSE.point.y;
29092 var y2 = this.rightSE.point.y;
29095 x: this.leftSE.point.x,
29096 y: y1 < y2 ? y1 : y2
29099 x: this.rightSE.point.x,
29100 y: y1 > y2 ? y1 : y2
29104 /* A vector from the left point to the right */
29108 value: function vector() {
29110 x: this.rightSE.point.x - this.leftSE.point.x,
29111 y: this.rightSE.point.y - this.leftSE.point.y
29115 key: "isAnEndpoint",
29116 value: function isAnEndpoint(pt) {
29117 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;
29119 /* Compare this segment with a point.
29121 * A point P is considered to be colinear to a segment if there
29122 * exists a distance D such that if we travel along the segment
29123 * from one * endpoint towards the other a distance D, we find
29124 * ourselves at point P.
29126 * Return value indicates:
29128 * 1: point lies above the segment (to the left of vertical)
29129 * 0: point is colinear to segment
29130 * -1: point lies below the segment (to the right of vertical)
29134 key: "comparePoint",
29135 value: function comparePoint(point) {
29136 if (this.isAnEndpoint(point)) return 0;
29137 var lPt = this.leftSE.point;
29138 var rPt = this.rightSE.point;
29139 var v = this.vector(); // Exactly vertical segments.
29141 if (lPt.x === rPt.x) {
29142 if (point.x === lPt.x) return 0;
29143 return point.x < lPt.x ? 1 : -1;
29144 } // Nearly vertical segments with an intersection.
29145 // Check to see where a point on the line with matching Y coordinate is.
29148 var yDist = (point.y - lPt.y) / v.y;
29149 var xFromYDist = lPt.x + yDist * v.x;
29150 if (point.x === xFromYDist) return 0; // General case.
29151 // Check to see where a point on the line with matching X coordinate is.
29153 var xDist = (point.x - lPt.x) / v.x;
29154 var yFromXDist = lPt.y + xDist * v.y;
29155 if (point.y === yFromXDist) return 0;
29156 return point.y < yFromXDist ? -1 : 1;
29159 * Given another segment, returns the first non-trivial intersection
29160 * between the two segments (in terms of sweep line ordering), if it exists.
29162 * A 'non-trivial' intersection is one that will cause one or both of the
29163 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
29165 * * endpoint of segA with endpoint of segB --> trivial
29166 * * endpoint of segA with point along segB --> non-trivial
29167 * * endpoint of segB with point along segA --> non-trivial
29168 * * point along segA with point along segB --> non-trivial
29170 * If no non-trivial intersection exists, return null
29171 * Else, return null.
29175 key: "getIntersection",
29176 value: function getIntersection(other) {
29177 // If bboxes don't overlap, there can't be any intersections
29178 var tBbox = this.bbox();
29179 var oBbox = other.bbox();
29180 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
29181 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
29182 // This will 'snap' intersections to endpoints if possible, and will
29183 // handle cases of colinearity.
29185 var tlp = this.leftSE.point;
29186 var trp = this.rightSE.point;
29187 var olp = other.leftSE.point;
29188 var orp = other.rightSE.point; // does each endpoint touch the other segment?
29189 // note that we restrict the 'touching' definition to only allow segments
29190 // to touch endpoints that lie forward from where we are in the sweep line pass
29192 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
29193 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
29194 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
29195 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
29197 if (touchesThisLSE && touchesOtherLSE) {
29198 // these two cases are for colinear segments with matching left
29199 // endpoints, and one segment being longer than the other
29200 if (touchesThisRSE && !touchesOtherRSE) return trp;
29201 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
29202 // or just on their left endpoint (one trivial intersection
29205 } // does this left endpoint matches (other doesn't)
29208 if (touchesThisLSE) {
29209 // check for segments that just intersect on opposing endpoints
29210 if (touchesOtherRSE) {
29211 if (tlp.x === orp.x && tlp.y === orp.y) return null;
29212 } // t-intersection on left endpoint
29216 } // does other left endpoint matches (this doesn't)
29219 if (touchesOtherLSE) {
29220 // check for segments that just intersect on opposing endpoints
29221 if (touchesThisRSE) {
29222 if (trp.x === olp.x && trp.y === olp.y) return null;
29223 } // t-intersection on left endpoint
29227 } // trivial intersection on right endpoints
29230 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
29232 if (touchesThisRSE) return trp;
29233 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
29234 // infinite lines laid over the segments
29236 var pt = intersection(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
29237 // they would have an endpoint intersection and that case was already handled above
29239 if (pt === null) return null; // is the intersection found between the lines not on the segments?
29241 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
29243 return rounder.round(pt.x, pt.y);
29246 * Split the given segment into multiple segments on the given points.
29247 * * Each existing segment will retain its leftSE and a new rightSE will be
29248 * generated for it.
29249 * * A new segment will be generated which will adopt the original segment's
29250 * rightSE, and a new leftSE will be generated for it.
29251 * * If there are more than two points given to split on, new segments
29252 * in the middle will be generated with new leftSE and rightSE's.
29253 * * An array of the newly generated SweepEvents will be returned.
29255 * Warning: input array of points is modified
29260 value: function split(point) {
29261 var newEvents = [];
29262 var alreadyLinked = point.events !== undefined;
29263 var newLeftSE = new SweepEvent(point, true);
29264 var newRightSE = new SweepEvent(point, false);
29265 var oldRightSE = this.rightSE;
29266 this.replaceRightSE(newRightSE);
29267 newEvents.push(newRightSE);
29268 newEvents.push(newLeftSE);
29269 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
29270 // sometimes one of the resulting new segments is vertical, in which
29271 // case its left and right events may need to be swapped
29273 if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
29274 newSeg.swapEvents();
29277 if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
29279 } // in the point we just used to create new sweep events with was already
29280 // linked to other events, we need to check if either of the affected
29281 // segments should be consumed
29284 if (alreadyLinked) {
29285 newLeftSE.checkForConsuming();
29286 newRightSE.checkForConsuming();
29291 /* Swap which event is left and right */
29295 value: function swapEvents() {
29296 var tmpEvt = this.rightSE;
29297 this.rightSE = this.leftSE;
29298 this.leftSE = tmpEvt;
29299 this.leftSE.isLeft = true;
29300 this.rightSE.isLeft = false;
29302 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
29303 this.windings[i] *= -1;
29306 /* Consume another segment. We take their rings under our wing
29307 * and mark them as consumed. Use for perfectly overlapping segments */
29311 value: function consume(other) {
29312 var consumer = this;
29313 var consumee = other;
29315 while (consumer.consumedBy) {
29316 consumer = consumer.consumedBy;
29319 while (consumee.consumedBy) {
29320 consumee = consumee.consumedBy;
29323 var cmp = Segment.compare(consumer, consumee);
29324 if (cmp === 0) return; // already consumed
29325 // the winner of the consumption is the earlier segment
29326 // according to sweep line ordering
29329 var tmp = consumer;
29330 consumer = consumee;
29332 } // make sure a segment doesn't consume it's prev
29335 if (consumer.prev === consumee) {
29336 var _tmp = consumer;
29337 consumer = consumee;
29341 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
29342 var ring = consumee.rings[i];
29343 var winding = consumee.windings[i];
29344 var index = consumer.rings.indexOf(ring);
29346 if (index === -1) {
29347 consumer.rings.push(ring);
29348 consumer.windings.push(winding);
29349 } else consumer.windings[index] += winding;
29352 consumee.rings = null;
29353 consumee.windings = null;
29354 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
29356 consumee.leftSE.consumedBy = consumer.leftSE;
29357 consumee.rightSE.consumedBy = consumer.rightSE;
29359 /* The first segment previous segment chain that is in the result */
29362 key: "prevInResult",
29363 value: function prevInResult() {
29364 if (this._prevInResult !== undefined) return this._prevInResult;
29365 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
29366 return this._prevInResult;
29369 key: "beforeState",
29370 value: function beforeState() {
29371 if (this._beforeState !== undefined) return this._beforeState;
29372 if (!this.prev) this._beforeState = {
29377 var seg = this.prev.consumedBy || this.prev;
29378 this._beforeState = seg.afterState();
29380 return this._beforeState;
29384 value: function afterState() {
29385 if (this._afterState !== undefined) return this._afterState;
29386 var beforeState = this.beforeState();
29387 this._afterState = {
29388 rings: beforeState.rings.slice(0),
29389 windings: beforeState.windings.slice(0),
29392 var ringsAfter = this._afterState.rings;
29393 var windingsAfter = this._afterState.windings;
29394 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
29396 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
29397 var ring = this.rings[i];
29398 var winding = this.windings[i];
29399 var index = ringsAfter.indexOf(ring);
29401 if (index === -1) {
29402 ringsAfter.push(ring);
29403 windingsAfter.push(winding);
29404 } else windingsAfter[index] += winding;
29405 } // calcualte polysAfter
29408 var polysAfter = [];
29409 var polysExclude = [];
29411 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
29412 if (windingsAfter[_i] === 0) continue; // non-zero rule
29414 var _ring = ringsAfter[_i];
29415 var poly = _ring.poly;
29416 if (polysExclude.indexOf(poly) !== -1) continue;
29417 if (_ring.isExterior) polysAfter.push(poly);else {
29418 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
29420 var _index = polysAfter.indexOf(_ring.poly);
29422 if (_index !== -1) polysAfter.splice(_index, 1);
29424 } // calculate multiPolysAfter
29427 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
29428 var mp = polysAfter[_i2].multiPoly;
29429 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
29432 return this._afterState;
29434 /* Is this segment part of the final result? */
29438 value: function isInResult() {
29439 // if we've been consumed, we're not in the result
29440 if (this.consumedBy) return false;
29441 if (this._isInResult !== undefined) return this._isInResult;
29442 var mpsBefore = this.beforeState().multiPolys;
29443 var mpsAfter = this.afterState().multiPolys;
29445 switch (operation.type) {
29448 // UNION - included iff:
29449 // * On one side of us there is 0 poly interiors AND
29450 // * On the other side there is 1 or more.
29451 var noBefores = mpsBefore.length === 0;
29452 var noAfters = mpsAfter.length === 0;
29453 this._isInResult = noBefores !== noAfters;
29457 case 'intersection':
29459 // INTERSECTION - included iff:
29460 // * on one side of us all multipolys are rep. with poly interiors AND
29461 // * on the other side of us, not all multipolys are repsented
29462 // with poly interiors
29466 if (mpsBefore.length < mpsAfter.length) {
29467 least = mpsBefore.length;
29468 most = mpsAfter.length;
29470 least = mpsAfter.length;
29471 most = mpsBefore.length;
29474 this._isInResult = most === operation.numMultiPolys && least < most;
29480 // XOR - included iff:
29481 // * the difference between the number of multipolys represented
29482 // with poly interiors on our two sides is an odd number
29483 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
29484 this._isInResult = diff % 2 === 1;
29490 // DIFFERENCE included iff:
29491 // * on exactly one side, we have just the subject
29492 var isJustSubject = function isJustSubject(mps) {
29493 return mps.length === 1 && mps[0].isSubject;
29496 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
29501 throw new Error("Unrecognized operation type found ".concat(operation.type));
29504 return this._isInResult;
29508 value: function fromRing(pt1, pt2, ring) {
29509 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
29511 var cmpPts = SweepEvent.comparePoints(pt1, pt2);
29517 } else if (cmpPts > 0) {
29521 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
29523 var leftSE = new SweepEvent(leftPt, true);
29524 var rightSE = new SweepEvent(rightPt, false);
29525 return new Segment(leftSE, rightSE, [ring], [winding]);
29532 var RingIn = /*#__PURE__*/function () {
29533 function RingIn(geomRing, poly, isExterior) {
29534 _classCallCheck(this, RingIn);
29536 if (!Array.isArray(geomRing) || geomRing.length === 0) {
29537 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29541 this.isExterior = isExterior;
29542 this.segments = [];
29544 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
29545 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29548 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
29559 var prevPoint = firstPoint;
29561 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
29562 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
29563 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29566 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
29568 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
29569 this.segments.push(Segment.fromRing(prevPoint, point, this));
29570 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
29571 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
29572 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
29573 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
29575 } // add segment from last to first if last is not the same as first
29578 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
29579 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
29583 _createClass(RingIn, [{
29584 key: "getSweepEvents",
29585 value: function getSweepEvents() {
29586 var sweepEvents = [];
29588 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
29589 var segment = this.segments[i];
29590 sweepEvents.push(segment.leftSE);
29591 sweepEvents.push(segment.rightSE);
29594 return sweepEvents;
29601 var PolyIn = /*#__PURE__*/function () {
29602 function PolyIn(geomPoly, multiPoly) {
29603 _classCallCheck(this, PolyIn);
29605 if (!Array.isArray(geomPoly)) {
29606 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29609 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
29613 x: this.exteriorRing.bbox.ll.x,
29614 y: this.exteriorRing.bbox.ll.y
29617 x: this.exteriorRing.bbox.ur.x,
29618 y: this.exteriorRing.bbox.ur.y
29621 this.interiorRings = [];
29623 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
29624 var ring = new RingIn(geomPoly[i], this, false);
29625 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
29626 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
29627 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
29628 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
29629 this.interiorRings.push(ring);
29632 this.multiPoly = multiPoly;
29635 _createClass(PolyIn, [{
29636 key: "getSweepEvents",
29637 value: function getSweepEvents() {
29638 var sweepEvents = this.exteriorRing.getSweepEvents();
29640 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29641 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
29643 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
29644 sweepEvents.push(ringSweepEvents[j]);
29648 return sweepEvents;
29655 var MultiPolyIn = /*#__PURE__*/function () {
29656 function MultiPolyIn(geom, isSubject) {
29657 _classCallCheck(this, MultiPolyIn);
29659 if (!Array.isArray(geom)) {
29660 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29664 // if the input looks like a polygon, convert it to a multipolygon
29665 if (typeof geom[0][0][0] === 'number') geom = [geom];
29666 } catch (ex) {// The input is either malformed or has empty arrays.
29667 // In either case, it will be handled later on.
29673 x: Number.POSITIVE_INFINITY,
29674 y: Number.POSITIVE_INFINITY
29677 x: Number.NEGATIVE_INFINITY,
29678 y: Number.NEGATIVE_INFINITY
29682 for (var i = 0, iMax = geom.length; i < iMax; i++) {
29683 var poly = new PolyIn(geom[i], this);
29684 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
29685 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
29686 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
29687 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
29688 this.polys.push(poly);
29691 this.isSubject = isSubject;
29694 _createClass(MultiPolyIn, [{
29695 key: "getSweepEvents",
29696 value: function getSweepEvents() {
29697 var sweepEvents = [];
29699 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29700 var polySweepEvents = this.polys[i].getSweepEvents();
29702 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
29703 sweepEvents.push(polySweepEvents[j]);
29707 return sweepEvents;
29711 return MultiPolyIn;
29714 var RingOut = /*#__PURE__*/function () {
29715 _createClass(RingOut, null, [{
29718 /* Given the segments from the sweep line pass, compute & return a series
29719 * of closed rings from all the segments marked to be part of the result */
29720 value: function factory(allSegments) {
29723 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
29724 var segment = allSegments[i];
29725 if (!segment.isInResult() || segment.ringOut) continue;
29726 var prevEvent = null;
29727 var event = segment.leftSE;
29728 var nextEvent = segment.rightSE;
29729 var events = [event];
29730 var startingPoint = event.point;
29731 var intersectionLEs = [];
29732 /* Walk the chain of linked events to form a closed ring */
29737 events.push(event);
29738 /* Is the ring complete? */
29740 if (event.point === startingPoint) break;
29743 var availableLEs = event.getAvailableLinkedEvents();
29744 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
29745 * part of the algorithm malfunctioned... please file a bug report. */
29747 if (availableLEs.length === 0) {
29748 var firstPt = events[0].point;
29749 var lastPt = events[events.length - 1].point;
29750 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, "]."));
29752 /* Only one way to go, so cotinue on the path */
29755 if (availableLEs.length === 1) {
29756 nextEvent = availableLEs[0].otherSE;
29759 /* We must have an intersection. Check for a completed loop */
29762 var indexLE = null;
29764 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
29765 if (intersectionLEs[j].point === event.point) {
29770 /* Found a completed loop. Cut that off and make a ring */
29773 if (indexLE !== null) {
29774 var intersectionLE = intersectionLEs.splice(indexLE)[0];
29775 var ringEvents = events.splice(intersectionLE.index);
29776 ringEvents.unshift(ringEvents[0].otherSE);
29777 ringsOut.push(new RingOut(ringEvents.reverse()));
29780 /* register the intersection */
29783 intersectionLEs.push({
29784 index: events.length,
29787 /* Choose the left-most option to continue the walk */
29789 var comparator = event.getLeftmostComparator(prevEvent);
29790 nextEvent = availableLEs.sort(comparator)[0].otherSE;
29795 ringsOut.push(new RingOut(events));
29802 function RingOut(events) {
29803 _classCallCheck(this, RingOut);
29805 this.events = events;
29807 for (var i = 0, iMax = events.length; i < iMax; i++) {
29808 events[i].segment.ringOut = this;
29814 _createClass(RingOut, [{
29816 value: function getGeom() {
29817 // Remove superfluous points (ie extra points along a straight line),
29818 var prevPt = this.events[0].point;
29819 var points = [prevPt];
29821 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
29822 var _pt = this.events[i].point;
29823 var _nextPt = this.events[i + 1].point;
29824 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
29827 } // ring was all (within rounding error of angle calc) colinear points
29830 if (points.length === 1) return null; // check if the starting point is necessary
29832 var pt = points[0];
29833 var nextPt = points[1];
29834 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
29835 points.push(points[0]);
29836 var step = this.isExteriorRing() ? 1 : -1;
29837 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
29838 var iEnd = this.isExteriorRing() ? points.length : -1;
29839 var orderedPoints = [];
29841 for (var _i = iStart; _i != iEnd; _i += step) {
29842 orderedPoints.push([points[_i].x, points[_i].y]);
29845 return orderedPoints;
29848 key: "isExteriorRing",
29849 value: function isExteriorRing() {
29850 if (this._isExteriorRing === undefined) {
29851 var enclosing = this.enclosingRing();
29852 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
29855 return this._isExteriorRing;
29858 key: "enclosingRing",
29859 value: function enclosingRing() {
29860 if (this._enclosingRing === undefined) {
29861 this._enclosingRing = this._calcEnclosingRing();
29864 return this._enclosingRing;
29866 /* Returns the ring that encloses this one, if any */
29869 key: "_calcEnclosingRing",
29870 value: function _calcEnclosingRing() {
29871 // start with the ealier sweep line event so that the prevSeg
29872 // chain doesn't lead us inside of a loop of ours
29873 var leftMostEvt = this.events[0];
29875 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
29876 var evt = this.events[i];
29877 if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
29880 var prevSeg = leftMostEvt.segment.prevInResult();
29881 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29884 // no segment found, thus no ring can enclose us
29885 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
29886 // segment must loop back around and enclose us
29888 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
29889 // segment must either loop around us or the ring of the prev prev
29890 // seg, which would make us and the ring of the prev peers
29892 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
29893 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
29894 return prevSeg.ringOut;
29895 } else return prevSeg.ringOut.enclosingRing();
29896 } // two segments are from the same ring, so this was a penisula
29897 // of that ring. iterate downward, keep searching
29900 prevSeg = prevPrevSeg.prevInResult();
29901 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29909 var PolyOut = /*#__PURE__*/function () {
29910 function PolyOut(exteriorRing) {
29911 _classCallCheck(this, PolyOut);
29913 this.exteriorRing = exteriorRing;
29914 exteriorRing.poly = this;
29915 this.interiorRings = [];
29918 _createClass(PolyOut, [{
29919 key: "addInterior",
29920 value: function addInterior(ring) {
29921 this.interiorRings.push(ring);
29926 value: function getGeom() {
29927 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
29929 if (geom[0] === null) return null;
29931 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29932 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
29934 if (ringGeom === null) continue;
29935 geom.push(ringGeom);
29945 var MultiPolyOut = /*#__PURE__*/function () {
29946 function MultiPolyOut(rings) {
29947 _classCallCheck(this, MultiPolyOut);
29949 this.rings = rings;
29950 this.polys = this._composePolys(rings);
29953 _createClass(MultiPolyOut, [{
29955 value: function getGeom() {
29958 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29959 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
29961 if (polyGeom === null) continue;
29962 geom.push(polyGeom);
29968 key: "_composePolys",
29969 value: function _composePolys(rings) {
29972 for (var i = 0, iMax = rings.length; i < iMax; i++) {
29973 var ring = rings[i];
29974 if (ring.poly) continue;
29975 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
29976 var enclosingRing = ring.enclosingRing();
29977 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
29978 enclosingRing.poly.addInterior(ring);
29986 return MultiPolyOut;
29989 * NOTE: We must be careful not to change any segments while
29990 * they are in the SplayTree. AFAIK, there's no way to tell
29991 * the tree to rebalance itself - thus before splitting
29992 * a segment that's in the tree, we remove it from the tree,
29993 * do the split, then re-insert it. (Even though splitting a
29994 * segment *shouldn't* change its correct position in the
29995 * sweep line tree, the reality is because of rounding errors,
29996 * it sometimes does.)
30000 var SweepLine = /*#__PURE__*/function () {
30001 function SweepLine(queue) {
30002 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
30004 _classCallCheck(this, SweepLine);
30006 this.queue = queue;
30007 this.tree = new Tree(comparator);
30008 this.segments = [];
30011 _createClass(SweepLine, [{
30013 value: function process(event) {
30014 var segment = event.segment;
30015 var newEvents = []; // if we've already been consumed by another segment,
30016 // clean up our body parts and get out
30018 if (event.consumedBy) {
30019 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
30023 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
30024 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.');
30025 var prevNode = node;
30026 var nextNode = node;
30027 var prevSeg = undefined;
30028 var nextSeg = undefined; // skip consumed segments still in tree
30030 while (prevSeg === undefined) {
30031 prevNode = this.tree.prev(prevNode);
30032 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
30033 } // skip consumed segments still in tree
30036 while (nextSeg === undefined) {
30037 nextNode = this.tree.next(nextNode);
30038 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
30041 if (event.isLeft) {
30042 // Check for intersections against the previous segment in the sweep line
30043 var prevMySplitter = null;
30046 var prevInter = prevSeg.getIntersection(segment);
30048 if (prevInter !== null) {
30049 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
30051 if (!prevSeg.isAnEndpoint(prevInter)) {
30052 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
30054 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
30055 newEvents.push(newEventsFromSplit[i]);
30059 } // Check for intersections against the next segment in the sweep line
30062 var nextMySplitter = null;
30065 var nextInter = nextSeg.getIntersection(segment);
30067 if (nextInter !== null) {
30068 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
30070 if (!nextSeg.isAnEndpoint(nextInter)) {
30071 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
30073 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
30074 newEvents.push(_newEventsFromSplit[_i]);
30078 } // For simplicity, even if we find more than one intersection we only
30079 // spilt on the 'earliest' (sweep-line style) of the intersections.
30080 // The other intersection will be handled in a future process().
30083 if (prevMySplitter !== null || nextMySplitter !== null) {
30084 var mySplitter = null;
30085 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
30086 var cmpSplitters = SweepEvent.comparePoints(prevMySplitter, nextMySplitter);
30087 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
30088 } // Rounding errors can cause changes in ordering,
30089 // so remove afected segments and right sweep events before splitting
30091 this.queue.remove(segment.rightSE);
30092 newEvents.push(segment.rightSE);
30094 var _newEventsFromSplit2 = segment.split(mySplitter);
30096 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
30097 newEvents.push(_newEventsFromSplit2[_i2]);
30101 if (newEvents.length > 0) {
30102 // We found some intersections, so re-do the current event to
30103 // make sure sweep line ordering is totally consistent for later
30104 // use with the segment 'prev' pointers
30105 this.tree.remove(segment);
30106 newEvents.push(event);
30108 // done with left event
30109 this.segments.push(segment);
30110 segment.prev = prevSeg;
30114 // since we're about to be removed from the sweep line, check for
30115 // intersections between our previous and next segments
30116 if (prevSeg && nextSeg) {
30117 var inter = prevSeg.getIntersection(nextSeg);
30119 if (inter !== null) {
30120 if (!prevSeg.isAnEndpoint(inter)) {
30121 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
30123 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
30124 newEvents.push(_newEventsFromSplit3[_i3]);
30128 if (!nextSeg.isAnEndpoint(inter)) {
30129 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
30131 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
30132 newEvents.push(_newEventsFromSplit4[_i4]);
30138 this.tree.remove(segment);
30143 /* Safely split a segment that is currently in the datastructures
30144 * IE - a segment other than the one that is currently being processed. */
30147 key: "_splitSafely",
30148 value: function _splitSafely(seg, pt) {
30149 // Rounding errors can cause changes in ordering,
30150 // so remove afected segments and right sweep events before splitting
30151 // removeNode() doesn't work, so have re-find the seg
30152 // https://github.com/w8r/splay-tree/pull/5
30153 this.tree.remove(seg);
30154 var rightSE = seg.rightSE;
30155 this.queue.remove(rightSE);
30156 var newEvents = seg.split(pt);
30157 newEvents.push(rightSE); // splitting can trigger consumption
30159 if (seg.consumedBy === undefined) this.tree.insert(seg);
30167 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
30168 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
30170 var Operation = /*#__PURE__*/function () {
30171 function Operation() {
30172 _classCallCheck(this, Operation);
30175 _createClass(Operation, [{
30177 value: function run(type, geom, moreGeoms) {
30178 operation.type = type;
30180 /* Convert inputs to MultiPoly objects */
30182 var multipolys = [new MultiPolyIn(geom, true)];
30184 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
30185 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
30188 operation.numMultiPolys = multipolys.length;
30189 /* BBox optimization for difference operation
30190 * If the bbox of a multipolygon that's part of the clipping doesn't
30191 * intersect the bbox of the subject at all, we can just drop that
30194 if (operation.type === 'difference') {
30195 // in place removal
30196 var subject = multipolys[0];
30199 while (_i < multipolys.length) {
30200 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
30203 /* BBox optimization for intersection operation
30204 * If we can find any pair of multipolygons whose bbox does not overlap,
30205 * then the result will be empty. */
30208 if (operation.type === 'intersection') {
30209 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
30210 // it could be optimized to O(n * ln(n))
30211 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
30212 var mpA = multipolys[_i2];
30214 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
30215 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
30219 /* Put segment endpoints in a priority queue */
30222 var queue = new Tree(SweepEvent.compare);
30224 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
30225 var sweepEvents = multipolys[_i3].getSweepEvents();
30227 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
30228 queue.insert(sweepEvents[_j]);
30230 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30231 // prevents an infinite loop, an otherwise common manifestation of bugs
30232 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
30236 /* Pass the sweep line over those endpoints */
30239 var sweepLine = new SweepLine(queue);
30240 var prevQueueSize = queue.size;
30241 var node = queue.pop();
30244 var evt = node.key;
30246 if (queue.size === prevQueueSize) {
30247 // prevents an infinite loop, an otherwise common manifestation of bugs
30248 var seg = evt.segment;
30249 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.');
30252 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30253 // prevents an infinite loop, an otherwise common manifestation of bugs
30254 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
30257 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
30258 // prevents an infinite loop, an otherwise common manifestation of bugs
30259 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
30262 var newEvents = sweepLine.process(evt);
30264 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
30265 var _evt = newEvents[_i4];
30266 if (_evt.consumedBy === undefined) queue.insert(_evt);
30269 prevQueueSize = queue.size;
30270 node = queue.pop();
30271 } // free some memory we don't need anymore
30275 /* Collect and compile segments we're keeping into a multipolygon */
30277 var ringsOut = RingOut.factory(sweepLine.segments);
30278 var result = new MultiPolyOut(ringsOut);
30279 return result.getGeom();
30284 }(); // singleton available by import
30287 var operation = new Operation();
30289 var union = function union(geom) {
30290 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
30291 moreGeoms[_key - 1] = arguments[_key];
30294 return operation.run('union', geom, moreGeoms);
30297 var intersection$1 = function intersection(geom) {
30298 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
30299 moreGeoms[_key2 - 1] = arguments[_key2];
30302 return operation.run('intersection', geom, moreGeoms);
30305 var xor = function xor(geom) {
30306 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
30307 moreGeoms[_key3 - 1] = arguments[_key3];
30310 return operation.run('xor', geom, moreGeoms);
30313 var difference = function difference(subjectGeom) {
30314 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
30315 clippingGeoms[_key4 - 1] = arguments[_key4];
30318 return operation.run('difference', subjectGeom, clippingGeoms);
30323 intersection: intersection$1,
30325 difference: difference
30328 var geojsonPrecision = createCommonjsModule(function (module) {
30330 function parse(t, coordinatePrecision, extrasPrecision) {
30331 function point(p) {
30332 return p.map(function (e, index) {
30334 return 1 * e.toFixed(coordinatePrecision);
30336 return 1 * e.toFixed(extrasPrecision);
30341 function multi(l) {
30342 return l.map(point);
30346 return p.map(multi);
30349 function multiPoly(m) {
30350 return m.map(poly);
30353 function geometry(obj) {
30358 switch (obj.type) {
30360 obj.coordinates = point(obj.coordinates);
30365 obj.coordinates = multi(obj.coordinates);
30369 case "MultiLineString":
30370 obj.coordinates = poly(obj.coordinates);
30373 case "MultiPolygon":
30374 obj.coordinates = multiPoly(obj.coordinates);
30377 case "GeometryCollection":
30378 obj.geometries = obj.geometries.map(geometry);
30386 function feature(obj) {
30387 obj.geometry = geometry(obj.geometry);
30391 function featureCollection(f) {
30392 f.features = f.features.map(feature);
30396 function geometryCollection(g) {
30397 g.geometries = g.geometries.map(geometry);
30409 case "GeometryCollection":
30410 return geometryCollection(t);
30412 case "FeatureCollection":
30413 return featureCollection(t);
30419 case "MultiPolygon":
30420 case "MultiLineString":
30421 return geometry(t);
30428 module.exports = parse;
30429 module.exports.parse = parse;
30433 var FORCED$3 = fails(function () {
30434 return new Date(NaN).toJSON() !== null
30435 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
30438 // `Date.prototype.toJSON` method
30439 // https://tc39.es/ecma262/#sec-date.prototype.tojson
30440 _export({ target: 'Date', proto: true, forced: FORCED$3 }, {
30441 // eslint-disable-next-line no-unused-vars -- required for `.length`
30442 toJSON: function toJSON(key) {
30443 var O = toObject(this);
30444 var pv = toPrimitive(O);
30445 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
30449 // `URL.prototype.toJSON` method
30450 // https://url.spec.whatwg.org/#dom-url-tojson
30451 _export({ target: 'URL', proto: true, enumerable: true }, {
30452 toJSON: function toJSON() {
30453 return URL.prototype.toString.call(this);
30457 function isObject$3(obj) {
30458 return _typeof(obj) === 'object' && obj !== null;
30461 function forEach(obj, cb) {
30462 if (Array.isArray(obj)) {
30464 } else if (isObject$3(obj)) {
30465 Object.keys(obj).forEach(function (key) {
30466 var val = obj[key];
30472 function getTreeDepth(obj) {
30475 if (Array.isArray(obj) || isObject$3(obj)) {
30476 forEach(obj, function (val) {
30477 if (Array.isArray(val) || isObject$3(val)) {
30478 var tmpDepth = getTreeDepth(val);
30480 if (tmpDepth > depth) {
30491 function stringify(obj, options) {
30492 options = options || {};
30493 var indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3);
30494 var addMargin = get(options, 'margins', false);
30495 var addArrayMargin = get(options, 'arrayMargins', false);
30496 var addObjectMargin = get(options, 'objectMargins', false);
30497 var maxLength = indent === '' ? Infinity : get(options, 'maxLength', 80);
30498 var maxNesting = get(options, 'maxNesting', Infinity);
30499 return function _stringify(obj, currentIndent, reserved) {
30500 if (obj && typeof obj.toJSON === 'function') {
30501 obj = obj.toJSON();
30504 var string = JSON.stringify(obj);
30506 if (string === undefined) {
30510 var length = maxLength - currentIndent.length - reserved;
30511 var treeDepth = getTreeDepth(obj);
30513 if (treeDepth <= maxNesting && string.length <= length) {
30514 var prettified = prettify(string, {
30515 addMargin: addMargin,
30516 addArrayMargin: addArrayMargin,
30517 addObjectMargin: addObjectMargin
30520 if (prettified.length <= length) {
30525 if (isObject$3(obj)) {
30526 var nextIndent = currentIndent + indent;
30530 var comma = function comma(array, index) {
30531 return index === array.length - 1 ? 0 : 1;
30534 if (Array.isArray(obj)) {
30535 for (var index = 0; index < obj.length; index++) {
30536 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
30541 Object.keys(obj).forEach(function (key, index, array) {
30542 var keyPart = JSON.stringify(key) + ': ';
30544 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
30546 if (value !== undefined) {
30547 items.push(keyPart + value);
30553 if (items.length > 0) {
30554 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
30560 } // Note: This regex matches even invalid JSON strings, but since we’re
30561 // working on the output of `JSON.stringify` we know that only valid strings
30562 // are present (unless the user supplied a weird `options.indent` but in
30563 // that case we don’t care since the output would be invalid anyway).
30566 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
30568 function prettify(string, options) {
30569 options = options || {};
30579 if (options.addMargin || options.addObjectMargin) {
30580 tokens['{'] = '{ ';
30581 tokens['}'] = ' }';
30584 if (options.addMargin || options.addArrayMargin) {
30585 tokens['['] = '[ ';
30586 tokens[']'] = ' ]';
30589 return string.replace(stringOrChar, function (match, string) {
30590 return string ? match : tokens[match];
30594 function get(options, name, defaultValue) {
30595 return name in options ? options[name] : defaultValue;
30598 var jsonStringifyPrettyCompact = stringify;
30600 var _default = /*#__PURE__*/function () {
30603 // `fc` Optional FeatureCollection of known features
30605 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
30606 // Each feature must have a filename-like `id`, for example: `something.geojson`
30609 // "type": "FeatureCollection"
30612 // "type": "Feature",
30613 // "id": "philly_metro.geojson",
30614 // "properties": { … },
30615 // "geometry": { … }
30619 function _default(fc) {
30622 _classCallCheck$1(this, _default);
30624 // The _cache retains resolved features, so if you ask for the same thing multiple times
30625 // we don't repeat the expensive resolving/clipping operations.
30627 // Each feature has a stable identifier that is used as the cache key.
30628 // The identifiers look like:
30629 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
30630 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
30631 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
30632 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
30633 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
30634 // When strict mode = false, return `null` for invalid locations or locationSets.
30636 this._strict = true; // process input FeatureCollection
30638 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
30639 fc.features.forEach(function (feature) {
30640 feature.properties = feature.properties || {};
30641 var props = feature.properties; // Get `id` from either `id` or `properties`
30643 var id = feature.id || props.id;
30644 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
30646 id = id.toLowerCase();
30648 props.id = id; // Ensure `area` property exists
30651 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
30653 props.area = Number(area.toFixed(2));
30656 _this._cache[id] = feature;
30658 } // Replace CountryCoder world geometry to be a polygon covering the world.
30661 var world = _cloneDeep(feature$1('Q2'));
30665 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
30668 world.properties.id = 'Q2';
30669 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
30671 this._cache.Q2 = world;
30672 } // validateLocation
30673 // `location` The location to validate
30675 // Pass a `location` value to validate
30677 // Returns a result like:
30679 // type: 'point', 'geojson', or 'countrycoder'
30680 // location: the queried location
30681 // id: the stable identifier for the feature
30683 // or `null` if the location is invalid
30687 _createClass$1(_default, [{
30688 key: "validateLocation",
30689 value: function validateLocation(location) {
30690 if (Array.isArray(location) && (location.length === 2 || location.length === 3)) {
30691 // [lon, lat] or [lon, lat, radius] point?
30692 var lon = location[0];
30693 var lat = location[1];
30694 var radius = location[2];
30696 if (Number.isFinite(lon) && lon >= -180 && lon <= 180 && Number.isFinite(lat) && lat >= -90 && lat <= 90 && (location.length === 2 || Number.isFinite(radius) && radius > 0)) {
30697 var id = '[' + location.toString() + ']';
30700 location: location,
30704 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
30705 // a .geojson filename?
30706 var _id = location.toLowerCase();
30708 if (this._cache[_id]) {
30711 location: location,
30715 } else if (typeof location === 'string' || typeof location === 'number') {
30716 // a country-coder value?
30717 var feature = feature$1(location);
30720 // Use wikidata QID as the identifier, since that seems to be the one
30721 // property that everything in CountryCoder is guaranteed to have.
30722 var _id2 = feature.properties.wikidata;
30724 type: 'countrycoder',
30725 location: location,
30731 if (this._strict) {
30732 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
30736 } // resolveLocation
30737 // `location` The location to resolve
30739 // Pass a `location` value to resolve
30741 // Returns a result like:
30743 // type: 'point', 'geojson', or 'countrycoder'
30744 // location: the queried location
30745 // id: a stable identifier for the feature
30746 // feature: the resolved GeoJSON feature
30748 // or `null` if the location is invalid
30752 key: "resolveLocation",
30753 value: function resolveLocation(location) {
30754 var valid = this.validateLocation(location);
30755 if (!valid) return null;
30756 var id = valid.id; // Return a result from cache if we can
30758 if (this._cache[id]) {
30759 return Object.assign(valid, {
30760 feature: this._cache[id]
30762 } // A [lon,lat] coordinate pair?
30765 if (valid.type === 'point') {
30766 var lon = location[0];
30767 var lat = location[1];
30768 var radius = location[2] || 25; // km
30772 var area = Math.PI * radius * radius;
30773 var feature = this._cache[id] = geojsonPrecision({
30778 area: Number(area.toFixed(2))
30780 geometry: circleToPolygon([lon, lat], radius * 1000, EDGES) // km to m
30783 return Object.assign(valid, {
30785 }); // A .geojson filename?
30786 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
30787 var _feature = _cloneDeep(feature$1(id));
30789 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
30790 // CountryCoder includes higher level features which are made up of members.
30791 // These features don't have their own geometry, but CountryCoder provides an
30792 // `aggregateFeature` method to combine these members into a MultiPolygon.
30793 // In the past, Turf/JSTS/martinez could not handle the aggregated features,
30794 // so we'd iteratively union them all together. (this was slow)
30795 // But now mfogel/polygon-clipping handles these MultiPolygons like a boss.
30796 // This approach also has the benefit of removing all the internal boaders and
30797 // simplifying the regional polygons a lot.
30799 if (Array.isArray(props.members)) {
30800 var aggregate = aggregateFeature(id);
30801 aggregate.geometry.coordinates = _clip([aggregate], 'UNION').geometry.coordinates;
30802 _feature.geometry = aggregate.geometry;
30803 } // Ensure `area` property exists
30807 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
30810 props.area = Number(_area.toFixed(2));
30811 } // Ensure `id` property exists
30816 this._cache[id] = _feature;
30817 return Object.assign(valid, {
30822 if (this._strict) {
30823 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
30827 } // validateLocationSet
30828 // `locationSet` the locationSet to validate
30830 // Pass a locationSet Object to validate like:
30832 // include: [ Array of locations ],
30833 // exclude: [ Array of locations ]
30836 // Returns a result like:
30838 // type: 'locationset'
30839 // locationSet: the queried locationSet
30840 // id: the stable identifier for the feature
30842 // or `null` if the locationSet is invalid
30846 key: "validateLocationSet",
30847 value: function validateLocationSet(locationSet) {
30848 locationSet = locationSet || {};
30849 var validator = this.validateLocation.bind(this);
30850 var include = (locationSet.include || []).map(validator).filter(Boolean);
30851 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
30853 if (!include.length) {
30854 if (this._strict) {
30855 throw new Error("validateLocationSet: LocationSet includes nothing.");
30857 // non-strict mode, replace an empty locationSet with one that includes "the world"
30858 locationSet.include = ['Q2'];
30860 type: 'countrycoder',
30865 } // Generate stable identifier
30868 include.sort(_sortLocations);
30869 var id = '+[' + include.map(function (d) {
30871 }).join(',') + ']';
30873 if (exclude.length) {
30874 exclude.sort(_sortLocations);
30875 id += '-[' + exclude.map(function (d) {
30877 }).join(',') + ']';
30881 type: 'locationset',
30882 locationSet: locationSet,
30885 } // resolveLocationSet
30886 // `locationSet` the locationSet to resolve
30888 // Pass a locationSet Object to validate like:
30890 // include: [ Array of locations ],
30891 // exclude: [ Array of locations ]
30894 // Returns a result like:
30896 // type: 'locationset'
30897 // locationSet: the queried locationSet
30898 // id: the stable identifier for the feature
30899 // feature: the resolved GeoJSON feature
30901 // or `null` if the locationSet is invalid
30905 key: "resolveLocationSet",
30906 value: function resolveLocationSet(locationSet) {
30907 locationSet = locationSet || {};
30908 var valid = this.validateLocationSet(locationSet);
30909 if (!valid) return null;
30910 var id = valid.id; // Return a result from cache if we can
30912 if (this._cache[id]) {
30913 return Object.assign(valid, {
30914 feature: this._cache[id]
30918 var resolver = this.resolveLocation.bind(this);
30919 var includes = (locationSet.include || []).map(resolver).filter(Boolean);
30920 var excludes = (locationSet.exclude || []).map(resolver).filter(Boolean); // Return quickly if it's a single included location..
30922 if (includes.length === 1 && excludes.length === 0) {
30923 return Object.assign(valid, {
30924 feature: includes[0].feature
30926 } // Calculate unions
30929 var includeGeoJSON = _clip(includes.map(function (d) {
30933 var excludeGeoJSON = _clip(excludes.map(function (d) {
30935 }), 'UNION'); // Calculate difference, update `area` and return result
30938 var resultGeoJSON = excludeGeoJSON ? _clip([includeGeoJSON, excludeGeoJSON], 'DIFFERENCE') : includeGeoJSON;
30939 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
30941 resultGeoJSON.id = id;
30942 resultGeoJSON.properties = {
30944 area: Number(area.toFixed(2))
30946 this._cache[id] = resultGeoJSON;
30947 return Object.assign(valid, {
30948 feature: resultGeoJSON
30955 value: function strict(val) {
30956 if (val === undefined) {
30958 return this._strict;
30961 this._strict = val;
30965 // convenience method to access the internal cache
30969 value: function cache() {
30970 return this._cache;
30972 // convenience method to prettyStringify the given object
30976 value: function stringify(obj, options) {
30977 return jsonStringifyPrettyCompact(obj, options);
30982 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
30984 function _clip(features, which) {
30985 if (!Array.isArray(features) || !features.length) return null;
30987 UNION: index.union,
30988 DIFFERENCE: index.difference
30990 var args = features.map(function (feature) {
30991 return feature.geometry.coordinates;
30993 var coords = fn.apply(null, args);
30998 type: whichType(coords),
30999 coordinates: coords
31001 }; // is this a Polygon or a MultiPolygon?
31003 function whichType(coords) {
31004 var a = Array.isArray(coords);
31005 var b = a && Array.isArray(coords[0]);
31006 var c = b && Array.isArray(coords[0][0]);
31007 var d = c && Array.isArray(coords[0][0][0]);
31008 return d ? 'MultiPolygon' : 'Polygon';
31012 function _cloneDeep(obj) {
31013 return JSON.parse(JSON.stringify(obj));
31014 } // Sorting the location lists is ok because they end up unioned together.
31015 // This sorting makes it possible to generate a deterministic id.
31018 function _sortLocations(a, b) {
31024 var aRank = rank[a.type];
31025 var bRank = rank[b.type];
31026 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
31029 // `Number.MAX_SAFE_INTEGER` constant
31030 // https://tc39.es/ecma262/#sec-number.max_safe_integer
31031 _export({ target: 'Number', stat: true }, {
31032 MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
31035 var aesJs = createCommonjsModule(function (module, exports) {
31038 function checkInt(value) {
31039 return parseInt(value) === value;
31042 function checkInts(arrayish) {
31043 if (!checkInt(arrayish.length)) {
31047 for (var i = 0; i < arrayish.length; i++) {
31048 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
31056 function coerceArray(arg, copy) {
31057 // ArrayBuffer view
31058 if (arg.buffer && arg.name === 'Uint8Array') {
31063 arg = Array.prototype.slice.call(arg);
31068 } // It's an array; check it is a valid representation of a byte
31071 if (Array.isArray(arg)) {
31072 if (!checkInts(arg)) {
31073 throw new Error('Array contains invalid value: ' + arg);
31076 return new Uint8Array(arg);
31077 } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
31080 if (checkInt(arg.length) && checkInts(arg)) {
31081 return new Uint8Array(arg);
31084 throw new Error('unsupported array-like object');
31087 function createArray(length) {
31088 return new Uint8Array(length);
31091 function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
31092 if (sourceStart != null || sourceEnd != null) {
31093 if (sourceArray.slice) {
31094 sourceArray = sourceArray.slice(sourceStart, sourceEnd);
31096 sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
31100 targetArray.set(sourceArray, targetStart);
31103 var convertUtf8 = function () {
31104 function toBytes(text) {
31107 text = encodeURI(text);
31109 while (i < text.length) {
31110 var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
31113 result.push(parseInt(text.substr(i, 2), 16));
31114 i += 2; // otherwise, just the actual byte
31120 return coerceArray(result);
31123 function fromBytes(bytes) {
31127 while (i < bytes.length) {
31131 result.push(String.fromCharCode(c));
31133 } else if (c > 191 && c < 224) {
31134 result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
31137 result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
31142 return result.join('');
31147 fromBytes: fromBytes
31151 var convertHex = function () {
31152 function toBytes(text) {
31155 for (var i = 0; i < text.length; i += 2) {
31156 result.push(parseInt(text.substr(i, 2), 16));
31160 } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
31163 var Hex = '0123456789abcdef';
31165 function fromBytes(bytes) {
31168 for (var i = 0; i < bytes.length; i++) {
31170 result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
31173 return result.join('');
31178 fromBytes: fromBytes
31180 }(); // Number of rounds by keysize
31183 var numberOfRounds = {
31187 }; // Round constant words
31189 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)
31191 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];
31192 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
31194 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];
31195 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];
31196 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];
31197 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
31199 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];
31200 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];
31201 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];
31202 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
31204 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];
31205 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];
31206 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];
31207 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];
31209 function convertToInt32(bytes) {
31212 for (var i = 0; i < bytes.length; i += 4) {
31213 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
31219 var AES = function AES(key) {
31220 if (!(this instanceof AES)) {
31221 throw Error('AES must be instanitated with `new`');
31224 Object.defineProperty(this, 'key', {
31225 value: coerceArray(key, true)
31231 AES.prototype._prepare = function () {
31232 var rounds = numberOfRounds[this.key.length];
31234 if (rounds == null) {
31235 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
31236 } // encryption round keys
31239 this._Ke = []; // decryption round keys
31243 for (var i = 0; i <= rounds; i++) {
31244 this._Ke.push([0, 0, 0, 0]);
31246 this._Kd.push([0, 0, 0, 0]);
31249 var roundKeyCount = (rounds + 1) * 4;
31250 var KC = this.key.length / 4; // convert the key into ints
31252 var tk = convertToInt32(this.key); // copy values into round key arrays
31256 for (var i = 0; i < KC; i++) {
31258 this._Ke[index][i % 4] = tk[i];
31259 this._Kd[rounds - index][i % 4] = tk[i];
31260 } // key expansion (fips-197 section 5.2)
31263 var rconpointer = 0;
31267 while (t < roundKeyCount) {
31269 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
31270 rconpointer += 1; // key expansion (for non-256 bit)
31273 for (var i = 1; i < KC; i++) {
31274 tk[i] ^= tk[i - 1];
31275 } // key expansion for 256-bit keys is "slightly different" (fips-197)
31278 for (var i = 1; i < KC / 2; i++) {
31279 tk[i] ^= tk[i - 1];
31282 tt = tk[KC / 2 - 1];
31283 tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
31285 for (var i = KC / 2 + 1; i < KC; i++) {
31286 tk[i] ^= tk[i - 1];
31288 } // copy values into round key arrays
31295 while (i < KC && t < roundKeyCount) {
31298 this._Ke[r][c] = tk[i];
31299 this._Kd[rounds - r][c] = tk[i++];
31302 } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
31305 for (var r = 1; r < rounds; r++) {
31306 for (var c = 0; c < 4; c++) {
31307 tt = this._Kd[r][c];
31308 this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
31313 AES.prototype.encrypt = function (plaintext) {
31314 if (plaintext.length != 16) {
31315 throw new Error('invalid plaintext size (must be 16 bytes)');
31318 var rounds = this._Ke.length - 1;
31319 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31321 var t = convertToInt32(plaintext);
31323 for (var i = 0; i < 4; i++) {
31324 t[i] ^= this._Ke[0][i];
31325 } // apply round transforms
31328 for (var r = 1; r < rounds; r++) {
31329 for (var i = 0; i < 4; i++) {
31330 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];
31334 } // the last round is special
31337 var result = createArray(16),
31340 for (var i = 0; i < 4; i++) {
31341 tt = this._Ke[rounds][i];
31342 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31343 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31344 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31345 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
31351 AES.prototype.decrypt = function (ciphertext) {
31352 if (ciphertext.length != 16) {
31353 throw new Error('invalid ciphertext size (must be 16 bytes)');
31356 var rounds = this._Kd.length - 1;
31357 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31359 var t = convertToInt32(ciphertext);
31361 for (var i = 0; i < 4; i++) {
31362 t[i] ^= this._Kd[0][i];
31363 } // apply round transforms
31366 for (var r = 1; r < rounds; r++) {
31367 for (var i = 0; i < 4; i++) {
31368 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];
31372 } // the last round is special
31375 var result = createArray(16),
31378 for (var i = 0; i < 4; i++) {
31379 tt = this._Kd[rounds][i];
31380 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31381 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31382 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31383 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
31389 * Mode Of Operation - Electonic Codebook (ECB)
31393 var ModeOfOperationECB = function ModeOfOperationECB(key) {
31394 if (!(this instanceof ModeOfOperationECB)) {
31395 throw Error('AES must be instanitated with `new`');
31398 this.description = "Electronic Code Block";
31400 this._aes = new AES(key);
31403 ModeOfOperationECB.prototype.encrypt = function (plaintext) {
31404 plaintext = coerceArray(plaintext);
31406 if (plaintext.length % 16 !== 0) {
31407 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31410 var ciphertext = createArray(plaintext.length);
31411 var block = createArray(16);
31413 for (var i = 0; i < plaintext.length; i += 16) {
31414 copyArray(plaintext, block, 0, i, i + 16);
31415 block = this._aes.encrypt(block);
31416 copyArray(block, ciphertext, i);
31422 ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
31423 ciphertext = coerceArray(ciphertext);
31425 if (ciphertext.length % 16 !== 0) {
31426 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31429 var plaintext = createArray(ciphertext.length);
31430 var block = createArray(16);
31432 for (var i = 0; i < ciphertext.length; i += 16) {
31433 copyArray(ciphertext, block, 0, i, i + 16);
31434 block = this._aes.decrypt(block);
31435 copyArray(block, plaintext, i);
31441 * Mode Of Operation - Cipher Block Chaining (CBC)
31445 var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
31446 if (!(this instanceof ModeOfOperationCBC)) {
31447 throw Error('AES must be instanitated with `new`');
31450 this.description = "Cipher Block Chaining";
31454 iv = createArray(16);
31455 } else if (iv.length != 16) {
31456 throw new Error('invalid initialation vector size (must be 16 bytes)');
31459 this._lastCipherblock = coerceArray(iv, true);
31460 this._aes = new AES(key);
31463 ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
31464 plaintext = coerceArray(plaintext);
31466 if (plaintext.length % 16 !== 0) {
31467 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31470 var ciphertext = createArray(plaintext.length);
31471 var block = createArray(16);
31473 for (var i = 0; i < plaintext.length; i += 16) {
31474 copyArray(plaintext, block, 0, i, i + 16);
31476 for (var j = 0; j < 16; j++) {
31477 block[j] ^= this._lastCipherblock[j];
31480 this._lastCipherblock = this._aes.encrypt(block);
31481 copyArray(this._lastCipherblock, ciphertext, i);
31487 ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
31488 ciphertext = coerceArray(ciphertext);
31490 if (ciphertext.length % 16 !== 0) {
31491 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31494 var plaintext = createArray(ciphertext.length);
31495 var block = createArray(16);
31497 for (var i = 0; i < ciphertext.length; i += 16) {
31498 copyArray(ciphertext, block, 0, i, i + 16);
31499 block = this._aes.decrypt(block);
31501 for (var j = 0; j < 16; j++) {
31502 plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
31505 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
31511 * Mode Of Operation - Cipher Feedback (CFB)
31515 var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
31516 if (!(this instanceof ModeOfOperationCFB)) {
31517 throw Error('AES must be instanitated with `new`');
31520 this.description = "Cipher Feedback";
31524 iv = createArray(16);
31525 } else if (iv.length != 16) {
31526 throw new Error('invalid initialation vector size (must be 16 size)');
31529 if (!segmentSize) {
31533 this.segmentSize = segmentSize;
31534 this._shiftRegister = coerceArray(iv, true);
31535 this._aes = new AES(key);
31538 ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
31539 if (plaintext.length % this.segmentSize != 0) {
31540 throw new Error('invalid plaintext size (must be segmentSize bytes)');
31543 var encrypted = coerceArray(plaintext, true);
31546 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
31547 xorSegment = this._aes.encrypt(this._shiftRegister);
31549 for (var j = 0; j < this.segmentSize; j++) {
31550 encrypted[i + j] ^= xorSegment[j];
31551 } // Shift the register
31554 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31555 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31561 ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
31562 if (ciphertext.length % this.segmentSize != 0) {
31563 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
31566 var plaintext = coerceArray(ciphertext, true);
31569 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
31570 xorSegment = this._aes.encrypt(this._shiftRegister);
31572 for (var j = 0; j < this.segmentSize; j++) {
31573 plaintext[i + j] ^= xorSegment[j];
31574 } // Shift the register
31577 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31578 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31584 * Mode Of Operation - Output Feedback (OFB)
31588 var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
31589 if (!(this instanceof ModeOfOperationOFB)) {
31590 throw Error('AES must be instanitated with `new`');
31593 this.description = "Output Feedback";
31597 iv = createArray(16);
31598 } else if (iv.length != 16) {
31599 throw new Error('invalid initialation vector size (must be 16 bytes)');
31602 this._lastPrecipher = coerceArray(iv, true);
31603 this._lastPrecipherIndex = 16;
31604 this._aes = new AES(key);
31607 ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
31608 var encrypted = coerceArray(plaintext, true);
31610 for (var i = 0; i < encrypted.length; i++) {
31611 if (this._lastPrecipherIndex === 16) {
31612 this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
31613 this._lastPrecipherIndex = 0;
31616 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
31620 }; // Decryption is symetric
31623 ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
31625 * Counter object for CTR common mode of operation
31628 var Counter = function Counter(initialValue) {
31629 if (!(this instanceof Counter)) {
31630 throw Error('Counter must be instanitated with `new`');
31631 } // We allow 0, but anything false-ish uses the default 1
31634 if (initialValue !== 0 && !initialValue) {
31638 if (typeof initialValue === 'number') {
31639 this._counter = createArray(16);
31640 this.setValue(initialValue);
31642 this.setBytes(initialValue);
31646 Counter.prototype.setValue = function (value) {
31647 if (typeof value !== 'number' || parseInt(value) != value) {
31648 throw new Error('invalid counter value (must be an integer)');
31649 } // We cannot safely handle numbers beyond the safe range for integers
31652 if (value > Number.MAX_SAFE_INTEGER) {
31653 throw new Error('integer value out of safe range');
31656 for (var index = 15; index >= 0; --index) {
31657 this._counter[index] = value % 256;
31658 value = parseInt(value / 256);
31662 Counter.prototype.setBytes = function (bytes) {
31663 bytes = coerceArray(bytes, true);
31665 if (bytes.length != 16) {
31666 throw new Error('invalid counter bytes size (must be 16 bytes)');
31669 this._counter = bytes;
31672 Counter.prototype.increment = function () {
31673 for (var i = 15; i >= 0; i--) {
31674 if (this._counter[i] === 255) {
31675 this._counter[i] = 0;
31677 this._counter[i]++;
31683 * Mode Of Operation - Counter (CTR)
31687 var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
31688 if (!(this instanceof ModeOfOperationCTR)) {
31689 throw Error('AES must be instanitated with `new`');
31692 this.description = "Counter";
31695 if (!(counter instanceof Counter)) {
31696 counter = new Counter(counter);
31699 this._counter = counter;
31700 this._remainingCounter = null;
31701 this._remainingCounterIndex = 16;
31702 this._aes = new AES(key);
31705 ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
31706 var encrypted = coerceArray(plaintext, true);
31708 for (var i = 0; i < encrypted.length; i++) {
31709 if (this._remainingCounterIndex === 16) {
31710 this._remainingCounter = this._aes.encrypt(this._counter._counter);
31711 this._remainingCounterIndex = 0;
31713 this._counter.increment();
31716 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
31720 }; // Decryption is symetric
31723 ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
31725 // See:https://tools.ietf.org/html/rfc2315
31727 function pkcs7pad(data) {
31728 data = coerceArray(data, true);
31729 var padder = 16 - data.length % 16;
31730 var result = createArray(data.length + padder);
31731 copyArray(data, result);
31733 for (var i = data.length; i < result.length; i++) {
31734 result[i] = padder;
31740 function pkcs7strip(data) {
31741 data = coerceArray(data, true);
31743 if (data.length < 16) {
31744 throw new Error('PKCS#7 invalid length');
31747 var padder = data[data.length - 1];
31750 throw new Error('PKCS#7 padding byte out of range');
31753 var length = data.length - padder;
31755 for (var i = 0; i < padder; i++) {
31756 if (data[length + i] !== padder) {
31757 throw new Error('PKCS#7 invalid padding byte');
31761 var result = createArray(length);
31762 copyArray(data, result, 0, 0, length);
31764 } ///////////////////////
31766 // The block cipher
31773 ecb: ModeOfOperationECB,
31774 cbc: ModeOfOperationCBC,
31775 cfb: ModeOfOperationCFB,
31776 ofb: ModeOfOperationOFB,
31777 ctr: ModeOfOperationCTR
31790 coerceArray: coerceArray,
31791 createArray: createArray,
31792 copyArray: copyArray
31797 module.exports = aesjs; // RequireJS/AMD
31798 // http://www.requirejs.org/docs/api.html
31799 // https://github.com/amdjs/amdjs-api/wiki/AMD
31804 // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
31805 // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16));
31806 // This default signing key is built into iD and can be used to mask/unmask sensitive values.
31808 var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
31809 function utilAesEncrypt(text, key) {
31810 key = key || DEFAULT_128;
31811 var textBytes = aesJs.utils.utf8.toBytes(text);
31812 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31813 var encryptedBytes = aesCtr.encrypt(textBytes);
31814 var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
31815 return encryptedHex;
31817 function utilAesDecrypt(encryptedHex, key) {
31818 key = key || DEFAULT_128;
31819 var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
31820 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31821 var decryptedBytes = aesCtr.decrypt(encryptedBytes);
31822 var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
31826 function utilCleanTags(tags) {
31829 for (var k in tags) {
31833 if (v !== undefined) {
31834 out[k] = cleanValue(k, v);
31840 function cleanValue(k, v) {
31841 function keepSpaces(k) {
31842 return /_hours|_times|:conditional$/.test(k);
31846 return /^(description|note|fixme)$/.test(k);
31849 if (skip(k)) return v;
31850 var cleaned = v.split(';').map(function (s) {
31852 }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
31853 // It is only intended to prevent obvious copy-paste errors. (#2323)
31854 // clean website- and email-like tags
31856 if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
31857 cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
31866 function utilDetect(refresh) {
31867 if (_detected && !refresh) return _detected;
31869 var ua = navigator.userAgent;
31873 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
31876 _detected.browser = m[1];
31877 _detected.version = m[2];
31880 if (!_detected.browser) {
31881 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
31884 _detected.browser = 'msie';
31885 _detected.version = m[1];
31889 if (!_detected.browser) {
31890 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
31893 _detected.browser = 'Opera';
31894 _detected.version = m[2];
31898 if (!_detected.browser) {
31899 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
31902 _detected.browser = m[1];
31903 _detected.version = m[2];
31904 m = ua.match(/version\/([\.\d]+)/i);
31905 if (m !== null) _detected.version = m[1];
31909 if (!_detected.browser) {
31910 _detected.browser = navigator.appName;
31911 _detected.version = navigator.appVersion;
31912 } // keep major.minor version only..
31915 _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
31916 // Legacy Opera has incomplete svg style support. See #715
31918 _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
31920 if (_detected.browser.toLowerCase() === 'msie') {
31921 _detected.ie = true;
31922 _detected.browser = 'Internet Explorer';
31923 _detected.support = parseFloat(_detected.version) >= 11;
31925 _detected.ie = false;
31926 _detected.support = true;
31929 _detected.filedrop = window.FileReader && 'ondrop' in window;
31930 _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31931 _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31934 if (/Win/.test(ua)) {
31935 _detected.os = 'win';
31936 _detected.platform = 'Windows';
31937 } else if (/Mac/.test(ua)) {
31938 _detected.os = 'mac';
31939 _detected.platform = 'Macintosh';
31940 } else if (/X11/.test(ua) || /Linux/.test(ua)) {
31941 _detected.os = 'linux';
31942 _detected.platform = 'Linux';
31944 _detected.os = 'win';
31945 _detected.platform = 'Unknown';
31948 _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
31949 // so assume any "mac" with multitouch is actually iOS
31950 navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
31952 // An array of locales requested by the browser in priority order.
31954 _detected.browserLocales = Array.from(new Set( // remove duplicates
31955 [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
31956 navigator.userLanguage]) // remove any undefined values
31957 .filter(Boolean)));
31960 var loc = window.top.location;
31961 var origin = loc.origin;
31964 // for unpatched IE11
31965 origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
31968 _detected.host = origin + loc.pathname;
31972 // Like selection.property('value', ...), but avoids no-op value sets,
31973 // which can result in layout/repaint thrashing in some situations.
31974 function utilGetSetValue(selection, value) {
31975 function d3_selection_value(value) {
31976 function valueNull() {
31980 function valueConstant() {
31981 if (this.value !== value) {
31982 this.value = value;
31986 function valueFunction() {
31987 var x = value.apply(this, arguments);
31989 if (x === null || x === undefined) {
31991 } else if (this.value !== x) {
31996 return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
31999 if (arguments.length === 1) {
32000 return selection.property('value');
32003 return selection.each(d3_selection_value(value));
32006 function utilKeybinding(namespace) {
32007 var _keybindings = {};
32009 function testBindings(d3_event, isCapturing) {
32010 var didMatch = false;
32011 var bindings = Object.keys(_keybindings).map(function (id) {
32012 return _keybindings[id];
32014 var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
32015 // so we don't strictly match on the shift key, but we prioritize
32016 // shifted keybindings first, and fallback to unshifted only if no match.
32017 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
32018 // priority match shifted keybindings first
32020 for (i = 0; i < bindings.length; i++) {
32021 binding = bindings[i];
32022 if (!binding.event.modifiers.shiftKey) continue; // no shift
32024 if (!!binding.capture !== isCapturing) continue;
32026 if (matches(d3_event, binding, true)) {
32027 binding.callback(d3_event);
32028 didMatch = true; // match a max of one binding per event
32034 if (didMatch) return; // then unshifted keybindings
32036 for (i = 0; i < bindings.length; i++) {
32037 binding = bindings[i];
32038 if (binding.event.modifiers.shiftKey) continue; // shift
32040 if (!!binding.capture !== isCapturing) continue;
32042 if (matches(d3_event, binding, false)) {
32043 binding.callback(d3_event);
32048 function matches(d3_event, binding, testShift) {
32049 var event = d3_event;
32050 var isMatch = false;
32051 var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
32053 if (event.key !== undefined) {
32054 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
32058 if (binding.event.key === undefined) {
32060 } else if (Array.isArray(binding.event.key)) {
32061 if (binding.event.key.map(function (s) {
32062 return s.toLowerCase();
32063 }).indexOf(event.key.toLowerCase()) === -1) {
32067 if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) {
32071 } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
32072 // - browser doesn't support `KeyboardEvent.key`
32073 // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
32076 if (!isMatch && tryKeyCode) {
32077 isMatch = event.keyCode === binding.event.keyCode;
32080 if (!isMatch) return false; // test modifier keys
32082 if (!(event.ctrlKey && event.altKey)) {
32083 // if both are set, assume AltGr and skip it - #4096
32084 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
32085 if (event.altKey !== binding.event.modifiers.altKey) return false;
32088 if (event.metaKey !== binding.event.modifiers.metaKey) return false;
32089 if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
32094 function capture(d3_event) {
32095 testBindings(d3_event, true);
32098 function bubble(d3_event) {
32099 var tagName = select(d3_event.target).node().tagName;
32101 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
32105 testBindings(d3_event, false);
32108 function keybinding(selection) {
32109 selection = selection || select(document);
32110 selection.on('keydown.capture.' + namespace, capture, true);
32111 selection.on('keydown.bubble.' + namespace, bubble, false);
32113 } // was: keybinding.off()
32116 keybinding.unbind = function (selection) {
32118 selection = selection || select(document);
32119 selection.on('keydown.capture.' + namespace, null);
32120 selection.on('keydown.bubble.' + namespace, null);
32124 keybinding.clear = function () {
32127 }; // Remove one or more keycode bindings.
32130 keybinding.off = function (codes, capture) {
32131 var arr = utilArrayUniq([].concat(codes));
32133 for (var i = 0; i < arr.length; i++) {
32134 var id = arr[i] + (capture ? '-capture' : '-bubble');
32135 delete _keybindings[id];
32139 }; // Add one or more keycode bindings.
32142 keybinding.on = function (codes, callback, capture) {
32143 if (typeof callback !== 'function') {
32144 return keybinding.off(codes, capture);
32147 var arr = utilArrayUniq([].concat(codes));
32149 for (var i = 0; i < arr.length; i++) {
32150 var id = arr[i] + (capture ? '-capture' : '-bubble');
32154 callback: callback,
32169 if (_keybindings[id]) {
32170 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
32173 _keybindings[id] = binding;
32174 var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
32176 for (var j = 0; j < matches.length; j++) {
32177 // Normalise matching errors
32178 if (matches[j] === '++') matches[j] = '+';
32180 if (matches[j] in utilKeybinding.modifierCodes) {
32181 var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
32182 binding.event.modifiers[prop] = true;
32184 binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
32186 if (matches[j] in utilKeybinding.keyCodes) {
32187 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
32199 * See https://github.com/keithamus/jwerty
32202 utilKeybinding.modifierCodes = {
32206 // CTRL key, on Mac: ⌃
32209 // ALT key, on Mac: ⌥ (Alt)
32213 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
32220 utilKeybinding.modifierProperties = {
32226 utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
32227 utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
32228 utilKeybinding.keys = {
32229 // Backspace key, on Mac: ⌫ (Backspace)
32231 backspace: 'Backspace',
32232 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32245 'pause-break': 'Pause',
32246 // Caps Lock key, ⇪
32249 'caps-lock': 'CapsLock',
32250 // Escape key, on Mac: ⎋, on Windows: Esc
32251 '⎋': ['Escape', 'Esc'],
32252 escape: ['Escape', 'Esc'],
32253 esc: ['Escape', 'Esc'],
32255 space: [' ', 'Spacebar'],
32256 // Page-Up key, or pgup, on Mac: ↖
32259 'page-up': 'PageUp',
32260 // Page-Down key, or pgdown, on Mac: ↘
32262 pgdown: 'PageDown',
32263 'page-down': 'PageDown',
32264 // END key, on Mac: ⇟
32267 // HOME key, on Mac: ⇞
32270 // Insert key, or ins
32273 // Delete key, on Mac: ⌦ (Delete)
32274 '⌦': ['Delete', 'Del'],
32275 del: ['Delete', 'Del'],
32276 'delete': ['Delete', 'Del'],
32277 // Left Arrow Key, or ←
32278 '←': ['ArrowLeft', 'Left'],
32279 left: ['ArrowLeft', 'Left'],
32280 'arrow-left': ['ArrowLeft', 'Left'],
32281 // Up Arrow Key, or ↑
32282 '↑': ['ArrowUp', 'Up'],
32283 up: ['ArrowUp', 'Up'],
32284 'arrow-up': ['ArrowUp', 'Up'],
32285 // Right Arrow Key, or →
32286 '→': ['ArrowRight', 'Right'],
32287 right: ['ArrowRight', 'Right'],
32288 'arrow-right': ['ArrowRight', 'Right'],
32289 // Up Arrow Key, or ↓
32290 '↓': ['ArrowDown', 'Down'],
32291 down: ['ArrowDown', 'Down'],
32292 'arrow-down': ['ArrowDown', 'Down'],
32293 // odities, stuff for backward compatibility (browsers and code):
32294 // Num-Multiply, or *
32295 '*': ['*', 'Multiply'],
32296 star: ['*', 'Multiply'],
32297 asterisk: ['*', 'Multiply'],
32298 multiply: ['*', 'Multiply'],
32301 'plus': ['+', 'Add'],
32302 // Num-Subtract, or -
32303 '-': ['-', 'Subtract'],
32304 subtract: ['-', 'Subtract'],
32305 'dash': ['-', 'Subtract'],
32312 // Period, or ., or full-stop
32315 // Slash, or /, or forward-slash
32317 'forward-slash': '/',
32318 // Tick, or `, or back-quote
32321 // Open bracket, or [
32322 'open-bracket': '[',
32323 // Back slash, or \
32324 'back-slash': '\\',
32325 // Close backet, or ]
32326 'close-bracket': ']',
32327 // Apostrophe, or Quote, or '
32368 utilKeybinding.keyCodes = {
32369 // Backspace key, on Mac: ⌫ (Backspace)
32372 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32386 // Caps Lock key, ⇪
32390 // Escape key, on Mac: ⎋, on Windows: Esc
32396 // Page-Up key, or pgup, on Mac: ↖
32400 // Page-Down key, or pgdown, on Mac: ↘
32404 // END key, on Mac: ⇟
32407 // HOME key, on Mac: ⇞
32410 // Insert key, or ins
32413 // Delete key, on Mac: ⌦ (Delete)
32417 // Left Arrow Key, or ←
32421 // Up Arrow Key, or ↑
32425 // Right Arrow Key, or →
32429 // Up Arrow Key, or ↓
32433 // odities, printing characters that come out wrong:
32436 // Num-Multiply, or *
32444 // Num-Subtract, or -
32447 // Vertical Bar / Pipe
32462 // Dash / Underscore key
32464 // Period, or ., or full-stop
32468 // Slash, or /, or forward-slash
32471 'forward-slash': 191,
32472 // Tick, or `, or back-quote
32476 // Open bracket, or [
32478 'open-bracket': 219,
32479 // Back slash, or \
32482 // Close backet, or ]
32484 'close-bracket': 221,
32485 // Apostrophe, or Quote, or '
32494 while (++i < 106) {
32495 utilKeybinding.keyCodes['num-' + n] = i;
32504 utilKeybinding.keyCodes[n] = i;
32512 while (++i < 136) {
32513 utilKeybinding.keyCodes['f' + n] = i;
32521 utilKeybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
32524 function utilObjectOmit(obj, omitKeys) {
32525 return Object.keys(obj).reduce(function (result, key) {
32526 if (omitKeys.indexOf(key) === -1) {
32527 result[key] = obj[key]; // keep
32534 // Copies a variable number of methods from source to target.
32535 function utilRebind(target, source) {
32537 n = arguments.length,
32541 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
32545 } // Method is assumed to be a standard D3 getter-setter:
32546 // If passed with no arguments, gets the value.
32547 // If passed with arguments, sets the value and returns the target.
32549 function d3_rebind(target, source, method) {
32550 return function () {
32551 var value = method.apply(source, arguments);
32552 return value === source ? target : value;
32556 // A per-domain session mutex backed by a cookie and dead man's
32557 // switch. If the session crashes, the mutex will auto-release
32558 // after 5 seconds.
32559 // This accepts a string and returns an object that complies with utilSessionMutexType
32560 function utilSessionMutex(name) {
32565 var expires = new Date();
32566 expires.setSeconds(expires.getSeconds() + 5);
32567 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
32570 mutex.lock = function () {
32571 if (intervalID) return true;
32572 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
32573 if (cookie) return false;
32575 intervalID = window.setInterval(renew, 4000);
32579 mutex.unlock = function () {
32580 if (!intervalID) return;
32581 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
32582 clearInterval(intervalID);
32586 mutex.locked = function () {
32587 return !!intervalID;
32593 function utilTiler() {
32594 var _size = [256, 256];
32596 var _tileSize = 256;
32597 var _zoomExtent = [0, 20];
32598 var _translate = [_size[0] / 2, _size[1] / 2];
32600 var _skipNullIsland = false;
32602 function clamp(num, min, max) {
32603 return Math.max(min, Math.min(num, max));
32606 function nearNullIsland(tile) {
32612 var center = Math.pow(2, z - 1);
32613 var width = Math.pow(2, z - 6);
32614 var min = center - width / 2;
32615 var max = center + width / 2 - 1;
32616 return x >= min && x <= max && y >= min && y <= max;
32623 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
32624 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
32626 var tileMax = Math.pow(2, z0) - 1;
32627 var log2ts = Math.log(_tileSize) * Math.LOG2E;
32628 var k = Math.pow(2, z - z0 + log2ts);
32629 var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
32630 var cols = range$1(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
32631 var rows = range$1(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
32634 for (var i = 0; i < rows.length; i++) {
32637 for (var j = 0; j < cols.length; j++) {
32640 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
32641 tiles.unshift([x, y, z0]); // tiles in view at beginning
32643 tiles.push([x, y, z0]); // tiles in margin at the end
32648 tiles.translate = origin;
32653 * getTiles() returns an array of tiles that cover the map view
32657 tiler.getTiles = function (projection) {
32658 var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
32659 this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
32660 var tiles = tiler();
32661 var ts = tiles.scale;
32662 return tiles.map(function (tile) {
32663 if (_skipNullIsland && nearNullIsland(tile)) {
32667 var x = tile[0] * ts - origin[0];
32668 var y = tile[1] * ts - origin[1];
32670 id: tile.toString(),
32672 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
32674 }).filter(Boolean);
32677 * getGeoJSON() returns a FeatureCollection for debugging tiles
32681 tiler.getGeoJSON = function (projection) {
32682 var features = tiler.getTiles(projection).map(function (tile) {
32691 coordinates: [tile.extent.polygon()]
32696 type: 'FeatureCollection',
32701 tiler.tileSize = function (val) {
32702 if (!arguments.length) return _tileSize;
32707 tiler.zoomExtent = function (val) {
32708 if (!arguments.length) return _zoomExtent;
32713 tiler.size = function (val) {
32714 if (!arguments.length) return _size;
32719 tiler.scale = function (val) {
32720 if (!arguments.length) return _scale;
32725 tiler.translate = function (val) {
32726 if (!arguments.length) return _translate;
32729 }; // number to extend the rows/columns beyond those covering the viewport
32732 tiler.margin = function (val) {
32733 if (!arguments.length) return _margin;
32738 tiler.skipNullIsland = function (val) {
32739 if (!arguments.length) return _skipNullIsland;
32740 _skipNullIsland = val;
32747 function utilTriggerEvent(target, type) {
32748 target.each(function () {
32749 var evt = document.createEvent('HTMLEvents');
32750 evt.initEvent(type, true, true);
32751 this.dispatchEvent(evt);
32755 var _mainLocations = coreLocations(); // singleton
32756 // `coreLocations` maintains an internal index of all the boundaries/geofences used by iD.
32757 // It's used by presets, community index, background imagery, to know where in the world these things are valid.
32758 // These geofences should be defined by `locationSet` objects:
32760 // let locationSet = {
32761 // include: [ Array of locations ],
32762 // exclude: [ Array of locations ]
32765 // For more info see the location-conflation and country-coder projects, see:
32766 // https://github.com/ideditor/location-conflation
32767 // https://github.com/ideditor/country-coder
32770 function coreLocations() {
32772 var _resolvedFeatures = {}; // cache of *resolved* locationSet features
32774 var _loco = new _default(); // instance of a location-conflation resolver
32777 var _wp; // instance of a which-polygon index
32778 // pre-resolve the worldwide locationSet
32786 resolveLocationSet(world);
32790 var _deferred = new Set();
32792 var _inProcess; // Returns a Promise to process the queue
32795 function processQueue() {
32796 if (!_queue.length) return Promise.resolve(); // console.log(`queue length ${_queue.length}`);
32798 var chunk = _queue.pop();
32800 return new Promise(function (resolvePromise) {
32801 var handle = window.requestIdleCallback(function () {
32802 _deferred["delete"](handle); // const t0 = performance.now();
32805 chunk.forEach(resolveLocationSet); // const t1 = performance.now();
32806 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
32811 _deferred.add(handle);
32812 }).then(function () {
32813 return processQueue();
32815 } // Pass an Object with a `locationSet` property,
32816 // Performs the locationSet resolution, caches the result, and sets a `locationSetID` property on the object.
32819 function resolveLocationSet(obj) {
32820 if (obj.locationSetID) return; // work was done already
32823 var locationSet = obj.locationSet;
32825 if (!locationSet) {
32826 throw new Error('object missing locationSet property');
32829 if (!locationSet.include) {
32830 // missing `include`, default to worldwide include
32831 locationSet.include = ['Q2']; // https://github.com/openstreetmap/iD/pull/8305#discussion_r662344647
32834 var resolved = _loco.resolveLocationSet(locationSet);
32836 var locationSetID = resolved.id;
32837 obj.locationSetID = locationSetID;
32839 if (!resolved.feature.geometry.coordinates.length || !resolved.feature.properties.area) {
32840 throw new Error("locationSet ".concat(locationSetID, " resolves to an empty feature."));
32843 if (!_resolvedFeatures[locationSetID]) {
32844 // First time seeing this locationSet feature
32845 var feature = JSON.parse(JSON.stringify(resolved.feature)); // deep clone
32847 feature.id = locationSetID; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
32849 feature.properties.id = locationSetID;
32850 _resolvedFeatures[locationSetID] = feature; // insert into cache
32853 obj.locationSet = {
32855 }; // default worldwide
32857 obj.locationSetID = '+[Q2]';
32859 } // Rebuilds the whichPolygon index with whatever features have been resolved.
32862 function rebuildIndex() {
32863 _wp = whichPolygon_1({
32864 features: Object.values(_resolvedFeatures)
32867 // `mergeCustomGeoJSON`
32868 // Accepts an FeatureCollection-like object containing custom locations
32869 // Each feature must have a filename-like `id`, for example: `something.geojson`
32872 // "type": "FeatureCollection"
32875 // "type": "Feature",
32876 // "id": "philly_metro.geojson",
32877 // "properties": { … },
32878 // "geometry": { … }
32885 _this.mergeCustomGeoJSON = function (fc) {
32886 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
32887 fc.features.forEach(function (feature) {
32888 feature.properties = feature.properties || {};
32889 var props = feature.properties; // Get `id` from either `id` or `properties`
32891 var id = feature.id || props.id;
32892 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
32894 id = id.toLowerCase();
32896 props.id = id; // Ensure `area` property exists
32899 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
32901 props.area = Number(area.toFixed(2));
32904 _loco._cache[id] = feature;
32908 // `mergeLocationSets`
32909 // Accepts an Array of Objects containing `locationSet` properties.
32910 // The locationSets will be resolved and indexed in the background.
32912 // { id: 'preset1', locationSet: {…} },
32913 // { id: 'preset2', locationSet: {…} },
32914 // { id: 'preset3', locationSet: {…} },
32917 // After resolving and indexing, the Objects will be decorated with a
32918 // `locationSetID` property.
32920 // { id: 'preset1', locationSet: {…}, locationSetID: '+[Q2]' },
32921 // { id: 'preset2', locationSet: {…}, locationSetID: '+[Q30]' },
32922 // { id: 'preset3', locationSet: {…}, locationSetID: '+[Q2]' },
32926 // Returns a Promise fulfilled when the resolving/indexing has been completed
32927 // This will take some seconds but happen in the background during browser idle time.
32931 _this.mergeLocationSets = function (objects) {
32932 if (!Array.isArray(objects)) return Promise.reject('nothing to do'); // Resolve all locationSets -> geojson, processing data in chunks
32934 // Because this will happen during idle callbacks, we want to choose a chunk size
32935 // that won't make the browser stutter too badly. LocationSets that are a simple
32936 // country coder include will resolve instantly, but ones that involve complex
32937 // include/exclude operations will take some milliseconds longer.
32939 // Some discussion and performance results on these tickets:
32940 // https://github.com/ideditor/location-conflation/issues/26
32941 // https://github.com/osmlab/name-suggestion-index/issues/4784#issuecomment-742003434
32943 _queue = _queue.concat(utilArrayChunk(objects, 200));
32946 _inProcess = processQueue().then(function () {
32956 // Returns a locationSetID for a given locationSet (fallback to `+[Q2]`, world)
32957 // (The locationset doesn't necessarily need to be resolved to compute its `id`)
32960 // `locationSet`: A locationSet, e.g. `{ include: ['us'] }`
32962 // The locationSetID, e.g. `+[Q30]`
32966 _this.locationSetID = function (locationSet) {
32970 locationSetID = _loco.validateLocationSet(locationSet).id;
32972 locationSetID = '+[Q2]'; // the world
32975 return locationSetID;
32978 // Returns the resolved GeoJSON feature for a given locationSetID (fallback to 'world')
32981 // `locationSetID`: id of the form like `+[Q30]` (United States)
32983 // A GeoJSON feature:
32985 // type: 'Feature',
32987 // properties: { id: '+[Q30]', area: 21817019.17, … },
32992 _this.feature = function (locationSetID) {
32993 return _resolvedFeatures[locationSetID] || _resolvedFeatures['+[Q2]'];
32996 // Find all the resolved locationSets valid at the given location.
32997 // Results include the area (in km²) to facilitate sorting.
33000 // `loc`: the [lon,lat] location to query, e.g. `[-74.4813, 40.7967]`
33002 // Object of locationSetIDs to areas (in km²)
33004 // "+[Q2]": 511207893.3958111,
33005 // "+[Q30]": 21817019.17,
33006 // "+[new_jersey.geojson]": 22390.77,
33012 _this.locationsAt = function (loc) {
33014 (_wp(loc, true) || []).forEach(function (prop) {
33015 return result[prop.id] = prop.area;
33020 // Execute a query directly against which-polygon
33021 // https://github.com/mapbox/which-polygon
33024 // `loc`: the [lon,lat] location to query,
33025 // `multi`: `true` to return all results, `false` to return first result
33027 // Array of GeoJSON *properties* for the locationSet features that exist at `loc`
33031 _this.query = function (loc, multi) {
33032 return _wp(loc, multi);
33033 }; // Direct access to the location-conflation resolver
33036 _this.loco = function () {
33038 }; // Direct access to the which-polygon index
33041 _this.wp = function () {
33048 var $findIndex = arrayIteration.findIndex;
33051 var FIND_INDEX = 'findIndex';
33052 var SKIPS_HOLES = true;
33054 // Shouldn't skip holes
33055 if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES = false; });
33057 // `Array.prototype.findIndex` method
33058 // https://tc39.es/ecma262/#sec-array.prototype.findindex
33059 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES }, {
33060 findIndex: function findIndex(callbackfn /* , that = undefined */) {
33061 return $findIndex(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
33065 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
33066 addToUnscopables(FIND_INDEX);
33068 var notARegexp = function (it) {
33069 if (isRegexp(it)) {
33070 throw TypeError("The method doesn't accept regular expressions");
33074 var MATCH = wellKnownSymbol('match');
33076 var correctIsRegexpLogic = function (METHOD_NAME) {
33079 '/./'[METHOD_NAME](regexp);
33082 regexp[MATCH] = false;
33083 return '/./'[METHOD_NAME](regexp);
33084 } catch (error2) { /* empty */ }
33088 // `String.prototype.includes` method
33089 // https://tc39.es/ecma262/#sec-string.prototype.includes
33090 _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
33091 includes: function includes(searchString /* , position = 0 */) {
33092 return !!~String(requireObjectCoercible(this))
33093 .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
33097 var _mainLocalizer = coreLocalizer(); // singleton
33100 var _t = _mainLocalizer.t;
33101 // coreLocalizer manages language and locale parameters including translated strings
33104 function coreLocalizer() {
33105 var localizer = {};
33106 var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
33107 // * `rtl` - right-to-left or left-to-right text direction
33108 // * `pct` - the percent of strings translated; 1 = 100%, full coverage
33111 // en: { rtl: false, pct: {…} },
33112 // de: { rtl: false, pct: {…} },
33116 var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
33118 // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33119 // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33123 var _localeStrings = {}; // the current locale
33125 var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
33127 var _localeCodes = ['en-US', 'en'];
33128 var _languageCode = 'en';
33129 var _textDirection = 'ltr';
33130 var _usesMetric = false;
33131 var _languageNames = {};
33132 var _scriptNames = {}; // getters for the current locale parameters
33134 localizer.localeCode = function () {
33135 return _localeCode;
33138 localizer.localeCodes = function () {
33139 return _localeCodes;
33142 localizer.languageCode = function () {
33143 return _languageCode;
33146 localizer.textDirection = function () {
33147 return _textDirection;
33150 localizer.usesMetric = function () {
33151 return _usesMetric;
33154 localizer.languageNames = function () {
33155 return _languageNames;
33158 localizer.scriptNames = function () {
33159 return _scriptNames;
33160 }; // The client app may want to manually set the locale, regardless of the
33161 // settings provided by the browser
33164 var _preferredLocaleCodes = [];
33166 localizer.preferredLocaleCodes = function (codes) {
33167 if (!arguments.length) return _preferredLocaleCodes;
33169 if (typeof codes === 'string') {
33170 // be generous and accept delimited strings as input
33171 _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
33173 _preferredLocaleCodes = codes;
33181 localizer.ensureLoaded = function () {
33182 if (_loadPromise) return _loadPromise;
33183 var filesToFetch = [// load the list of languages
33184 'languages', // load the list of supported locales
33187 general: 'locales',
33188 tagging: 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/translations'
33190 var fileMap = _mainFileFetcher.fileMap();
33192 for (var scopeId in localeDirs) {
33193 var key = "locales_index_".concat(scopeId);
33194 fileMap[key] = localeDirs[scopeId] + '/index.min.json';
33195 filesToFetch.push(key);
33198 return _loadPromise = Promise.all(filesToFetch.map(function (key) {
33199 return _mainFileFetcher.get(key);
33200 })).then(function (results) {
33201 _dataLanguages = results[0];
33202 _dataLocales = results[1];
33203 var indexes = results.slice(2);
33205 var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
33206 concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
33209 _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
33211 _localeCode = _localeCodes[0];
33212 var loadStringsPromises = [];
33213 indexes.forEach(function (index, i) {
33214 // Will always return the index for `en` if nothing else
33215 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
33216 return index[locale] && index[locale].pct === 1;
33217 }); // We only need to load locales up until we find one with full coverage
33220 _localeCodes.slice(0, fullCoverageIndex + 1).forEach(function (code) {
33221 var scopeId = Object.keys(localeDirs)[i];
33222 var directory = Object.values(localeDirs)[i];
33223 if (index[code]) loadStringsPromises.push(localizer.loadLocale(code, scopeId, directory));
33226 return Promise.all(loadStringsPromises);
33227 }).then(function () {
33228 updateForCurrentLocale();
33229 })["catch"](function (err) {
33230 return console.error(err);
33231 }); // eslint-disable-line
33232 }; // Returns the locales from `requestedLocales` supported by iD that we should use
33235 function localesToUseFrom(requestedLocales) {
33236 var supportedLocales = _dataLocales;
33239 for (var i in requestedLocales) {
33240 var locale = requestedLocales[i];
33241 if (supportedLocales[locale]) toUse.push(locale);
33243 if (locale.includes('-')) {
33244 // Full locale ('es-ES'), add fallback to the base ('es')
33245 var langPart = locale.split('-')[0];
33246 if (supportedLocales[langPart]) toUse.push(langPart);
33248 } // remove duplicates
33251 return utilArrayUniq(toUse);
33254 function updateForCurrentLocale() {
33255 if (!_localeCode) return;
33256 _languageCode = _localeCode.split('-')[0];
33257 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
33258 var hash = utilStringQs(window.location.hash);
33260 if (hash.rtl === 'true') {
33261 _textDirection = 'rtl';
33262 } else if (hash.rtl === 'false') {
33263 _textDirection = 'ltr';
33265 _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
33268 var locale = _localeCode;
33269 if (locale.toLowerCase() === 'en-us') locale = 'en';
33270 _languageNames = _localeStrings.general[locale].languageNames;
33271 _scriptNames = _localeStrings.general[locale].scriptNames;
33272 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
33275 // Returns a Promise to load the strings for the requested locale
33278 localizer.loadLocale = function (locale, scopeId, directory) {
33279 // US English is the default
33280 if (locale.toLowerCase() === 'en-us') locale = 'en';
33282 if (_localeStrings[scopeId] && _localeStrings[scopeId][locale]) {
33284 return Promise.resolve(locale);
33287 var fileMap = _mainFileFetcher.fileMap();
33288 var key = "locale_".concat(scopeId, "_").concat(locale);
33289 fileMap[key] = "".concat(directory, "/").concat(locale, ".min.json");
33290 return _mainFileFetcher.get(key).then(function (d) {
33291 if (!_localeStrings[scopeId]) _localeStrings[scopeId] = {};
33292 _localeStrings[scopeId][locale] = d[locale];
33297 localizer.pluralRule = function (number) {
33298 return pluralRule(number, _localeCode);
33299 }; // Returns the plural rule for the given `number` with the given `localeCode`.
33300 // One of: `zero`, `one`, `two`, `few`, `many`, `other`
33303 function pluralRule(number, localeCode) {
33304 // modern browsers have this functionality built-in
33305 var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
33308 return rules.select(number);
33309 } // fallback to basic one/other, as in English
33312 if (number === 1) return 'one';
33316 * Try to find that string in `locale` or the current `_localeCode` matching
33317 * the given `stringId`. If no string can be found in the requested locale,
33318 * we'll recurse down all the `_localeCodes` until one is found.
33320 * @param {string} stringId string identifier
33321 * @param {object?} replacements token replacements and default string
33322 * @param {string?} locale locale to use (defaults to currentLocale)
33323 * @return {string?} localized string
33327 localizer.tInfo = function (origStringId, replacements, locale) {
33328 var stringId = origStringId.trim();
33329 var scopeId = 'general';
33331 if (stringId[0] === '_') {
33332 var split = stringId.split('.');
33333 scopeId = split[0].slice(1);
33334 stringId = split.slice(1).join('.');
33337 locale = locale || _localeCode;
33338 var path = stringId.split('.').map(function (s) {
33339 return s.replace(/<TX_DOT>/g, '.');
33341 var stringsKey = locale; // US English is the default
33343 if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
33344 var result = _localeStrings && _localeStrings[scopeId] && _localeStrings[scopeId][stringsKey];
33346 while (result !== undefined && path.length) {
33347 result = result[path.pop()];
33350 if (result !== undefined) {
33351 if (replacements) {
33352 if (_typeof(result) === 'object' && Object.keys(result).length) {
33353 // If plural forms are provided, dig one level deeper based on the
33354 // first numeric token replacement provided.
33355 var number = Object.values(replacements).find(function (value) {
33356 return typeof value === 'number';
33359 if (number !== undefined) {
33360 var rule = pluralRule(number, locale);
33362 if (result[rule]) {
33363 result = result[rule];
33365 // We're pretty sure this should be a plural but no string
33366 // could be found for the given rule. Just pick the first
33367 // string and hope it makes sense.
33368 result = Object.values(result)[0];
33373 if (typeof result === 'string') {
33374 for (var key in replacements) {
33375 var value = replacements[key];
33377 if (typeof value === 'number') {
33378 if (value.toLocaleString) {
33379 // format numbers for the locale
33380 value = value.toLocaleString(locale, {
33383 minimumFractionDigits: 0
33386 value = value.toString();
33390 var token = "{".concat(key, "}");
33391 var regex = new RegExp(token, 'g');
33392 result = result.replace(regex, value);
33397 if (typeof result === 'string') {
33398 // found a localized string!
33404 } // no localized string found...
33405 // attempt to fallback to a lower-priority language
33408 var index = _localeCodes.indexOf(locale);
33410 if (index >= 0 && index < _localeCodes.length - 1) {
33411 // eventually this will be 'en' or another locale with 100% coverage
33412 var fallback = _localeCodes[index + 1];
33413 return localizer.tInfo(origStringId, replacements, fallback);
33416 if (replacements && 'default' in replacements) {
33417 // Fallback to a default value if one is specified in `replacements`
33419 text: replacements["default"],
33424 var missing = "Missing ".concat(locale, " translation: ").concat(origStringId);
33425 if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
33433 localizer.hasTextForStringId = function (stringId) {
33434 return !!localizer.tInfo(stringId, {
33435 "default": 'nothing found'
33437 }; // Returns only the localized text, discarding the locale info
33440 localizer.t = function (stringId, replacements, locale) {
33441 return localizer.tInfo(stringId, replacements, locale).text;
33442 }; // Returns the localized text wrapped in an HTML element encoding the locale info
33445 localizer.t.html = function (stringId, replacements, locale) {
33446 var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
33448 return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
33451 localizer.htmlForLocalizedText = function (text, localeCode) {
33452 return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
33455 localizer.languageName = function (code, options) {
33456 if (_languageNames[code]) {
33457 // name in locale language
33459 return _languageNames[code];
33460 } // sometimes we only want the local name
33463 if (options && options.localOnly) return null;
33464 var langInfo = _dataLanguages[code];
33467 if (langInfo.nativeName) {
33468 // name in native language
33469 // e.g. "Deutsch (de)"
33470 return localizer.t('translate.language_and_code', {
33471 language: langInfo.nativeName,
33474 } else if (langInfo.base && langInfo.script) {
33475 var base = langInfo.base; // the code of the language this is based on
33477 if (_languageNames[base]) {
33478 // base language name in locale language
33479 var scriptCode = langInfo.script;
33480 var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
33482 return localizer.t('translate.language_and_code', {
33483 language: _languageNames[base],
33486 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
33487 // e.g. "српски (sr-Cyrl)"
33488 return localizer.t('translate.language_and_code', {
33489 language: _dataLanguages[base].nativeName,
33496 return code; // if not found, use the code
33502 // `presetCollection` is a wrapper around an `Array` of presets `collection`,
33503 // and decorated with some extra methods for searching and matching geometry
33506 function presetCollection(collection) {
33507 var MAXRESULTS = 50;
33510 _this.collection = collection;
33512 _this.item = function (id) {
33513 if (_memo[id]) return _memo[id];
33515 var found = _this.collection.find(function (d) {
33516 return d.id === id;
33519 if (found) _memo[id] = found;
33523 _this.index = function (id) {
33524 return _this.collection.findIndex(function (d) {
33525 return d.id === id;
33529 _this.matchGeometry = function (geometry) {
33530 return presetCollection(_this.collection.filter(function (d) {
33531 return d.matchGeometry(geometry);
33535 _this.matchAllGeometry = function (geometries) {
33536 return presetCollection(_this.collection.filter(function (d) {
33537 return d && d.matchAllGeometry(geometries);
33541 _this.matchAnyGeometry = function (geometries) {
33542 return presetCollection(_this.collection.filter(function (d) {
33543 return geometries.some(function (geom) {
33544 return d.matchGeometry(geom);
33549 _this.fallback = function (geometry) {
33551 if (id === 'vertex') id = 'point';
33552 return _this.item(id);
33555 _this.search = function (value, geometry, loc) {
33556 if (!value) return _this; // don't remove diacritical characters since we're assuming the user is being intentional
33558 value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
33560 function leading(a) {
33561 var index = a.indexOf(value);
33562 return index === 0 || a[index - 1] === ' ';
33563 } // match at name beginning only
33566 function leadingStrict(a) {
33567 var index = a.indexOf(value);
33568 return index === 0;
33571 function sortPresets(nameProp) {
33572 return function sortNames(a, b) {
33573 var aCompare = a[nameProp]();
33574 var bCompare = b[nameProp](); // priority if search string matches preset name exactly - #4325
33576 if (value === aCompare) return -1;
33577 if (value === bCompare) return 1; // priority for higher matchScore
33579 var i = b.originalScore - a.originalScore;
33580 if (i !== 0) return i; // priority if search string appears earlier in preset name
33582 i = aCompare.indexOf(value) - bCompare.indexOf(value);
33583 if (i !== 0) return i; // priority for shorter preset names
33585 return aCompare.length - bCompare.length;
33589 var pool = _this.collection;
33591 if (Array.isArray(loc)) {
33592 var validLocations = _mainLocations.locationsAt(loc);
33593 pool = pool.filter(function (a) {
33594 return !a.locationSetID || validLocations[a.locationSetID];
33598 var searchable = pool.filter(function (a) {
33599 return a.searchable !== false && a.suggestion !== true;
33601 var suggestions = pool.filter(function (a) {
33602 return a.suggestion === true;
33603 }); // matches value to preset.name
33605 var leadingNames = searchable.filter(function (a) {
33606 return leading(a.searchName());
33607 }).sort(sortPresets('searchName')); // matches value to preset suggestion name
33609 var leadingSuggestions = suggestions.filter(function (a) {
33610 return leadingStrict(a.searchName());
33611 }).sort(sortPresets('searchName'));
33612 var leadingNamesStripped = searchable.filter(function (a) {
33613 return leading(a.searchNameStripped());
33614 }).sort(sortPresets('searchNameStripped'));
33615 var leadingSuggestionsStripped = suggestions.filter(function (a) {
33616 return leadingStrict(a.searchNameStripped());
33617 }).sort(sortPresets('searchNameStripped')); // matches value to preset.terms values
33619 var leadingTerms = searchable.filter(function (a) {
33620 return (a.terms() || []).some(leading);
33622 var leadingSuggestionTerms = suggestions.filter(function (a) {
33623 return (a.terms() || []).some(leading);
33624 }); // matches value to preset.tags values
33626 var leadingTagValues = searchable.filter(function (a) {
33627 return Object.values(a.tags || {}).filter(function (val) {
33628 return val !== '*';
33630 }); // finds close matches to value in preset.name
33632 var similarName = searchable.map(function (a) {
33635 dist: utilEditDistance(value, a.searchName())
33637 }).filter(function (a) {
33638 return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 3;
33639 }).sort(function (a, b) {
33640 return a.dist - b.dist;
33641 }).map(function (a) {
33643 }); // finds close matches to value to preset suggestion name
33645 var similarSuggestions = suggestions.map(function (a) {
33648 dist: utilEditDistance(value, a.searchName())
33650 }).filter(function (a) {
33651 return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 1;
33652 }).sort(function (a, b) {
33653 return a.dist - b.dist;
33654 }).map(function (a) {
33656 }); // finds close matches to value in preset.terms
33658 var similarTerms = searchable.filter(function (a) {
33659 return (a.terms() || []).some(function (b) {
33660 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
33663 var results = leadingNames.concat(leadingSuggestions, leadingNamesStripped, leadingSuggestionsStripped, leadingTerms, leadingSuggestionTerms, leadingTagValues, similarName, similarSuggestions, similarTerms).slice(0, MAXRESULTS - 1);
33666 if (typeof geometry === 'string') {
33667 results.push(_this.fallback(geometry));
33669 geometry.forEach(function (geom) {
33670 return results.push(_this.fallback(geom));
33675 return presetCollection(utilArrayUniq(results));
33681 // `presetCategory` builds a `presetCollection` of member presets,
33682 // decorated with some extra methods for searching and matching geometry
33685 function presetCategory(categoryID, category, allPresets) {
33686 var _this = Object.assign({}, category); // shallow copy
33689 var _searchName; // cache
33692 var _searchNameStripped; // cache
33695 _this.id = categoryID;
33696 _this.members = presetCollection((category.members || []).map(function (presetID) {
33697 return allPresets[presetID];
33698 }).filter(Boolean));
33699 _this.geometry = _this.members.collection.reduce(function (acc, preset) {
33700 for (var i in preset.geometry) {
33701 var geometry = preset.geometry[i];
33703 if (acc.indexOf(geometry) === -1) {
33704 acc.push(geometry);
33711 _this.matchGeometry = function (geom) {
33712 return _this.geometry.indexOf(geom) >= 0;
33715 _this.matchAllGeometry = function (geometries) {
33716 return _this.members.collection.some(function (preset) {
33717 return preset.matchAllGeometry(geometries);
33721 _this.matchScore = function () {
33725 _this.name = function () {
33726 return _t("_tagging.presets.categories.".concat(categoryID, ".name"), {
33727 'default': categoryID
33731 _this.nameLabel = function () {
33732 return _t.html("_tagging.presets.categories.".concat(categoryID, ".name"), {
33733 'default': categoryID
33737 _this.terms = function () {
33741 _this.searchName = function () {
33742 if (!_searchName) {
33743 _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33746 return _searchName;
33749 _this.searchNameStripped = function () {
33750 if (!_searchNameStripped) {
33751 _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33753 if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33755 _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33758 return _searchNameStripped;
33764 // `presetField` decorates a given `field` Object
33765 // with some extra methods for searching and matching geometry
33768 function presetField(fieldID, field) {
33769 var _this = Object.assign({}, field); // shallow copy
33772 _this.id = fieldID; // for use in classes, element ids, css selectors
33774 _this.safeid = utilSafeClassName(fieldID);
33776 _this.matchGeometry = function (geom) {
33777 return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
33780 _this.matchAllGeometry = function (geometries) {
33781 return !_this.geometry || geometries.every(function (geom) {
33782 return _this.geometry.indexOf(geom) !== -1;
33786 _this.t = function (scope, options) {
33787 return _t("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33790 _this.t.html = function (scope, options) {
33791 return _t.html("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33794 _this.hasTextForStringId = function (scope) {
33795 return _mainLocalizer.hasTextForStringId("_tagging.presets.fields.".concat(fieldID, ".").concat(scope));
33798 _this.title = function () {
33799 return _this.overrideLabel || _this.t('label', {
33804 _this.label = function () {
33805 return _this.overrideLabel || _this.t.html('label', {
33810 var _placeholder = _this.placeholder;
33812 _this.placeholder = function () {
33813 return _this.t('placeholder', {
33814 'default': _placeholder
33818 _this.originalTerms = (_this.terms || []).join();
33820 _this.terms = function () {
33821 return _this.t('terms', {
33822 'default': _this.originalTerms
33823 }).toLowerCase().trim().split(/\s*,+\s*/);
33826 _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
33830 // `Array.prototype.lastIndexOf` method
33831 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
33832 // eslint-disable-next-line es/no-array-prototype-lastindexof -- required for testing
33833 _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
33834 lastIndexOf: arrayLastIndexOf
33837 // `presetPreset` decorates a given `preset` Object
33838 // with some extra methods for searching and matching geometry
33841 function presetPreset(presetID, preset, addable, allFields, allPresets) {
33842 allFields = allFields || {};
33843 allPresets = allPresets || {};
33845 var _this = Object.assign({}, preset); // shallow copy
33848 var _addable = addable || false;
33850 var _resolvedFields; // cache
33853 var _resolvedMoreFields; // cache
33856 var _searchName; // cache
33859 var _searchNameStripped; // cache
33862 _this.id = presetID;
33863 _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
33865 _this.originalTerms = (_this.terms || []).join();
33866 _this.originalName = _this.name || '';
33867 _this.originalScore = _this.matchScore || 1;
33868 _this.originalReference = _this.reference || {};
33869 _this.originalFields = _this.fields || [];
33870 _this.originalMoreFields = _this.moreFields || [];
33872 _this.fields = function () {
33873 return _resolvedFields || (_resolvedFields = resolve('fields'));
33876 _this.moreFields = function () {
33877 return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
33880 _this.resetFields = function () {
33881 return _resolvedFields = _resolvedMoreFields = null;
33884 _this.tags = _this.tags || {};
33885 _this.addTags = _this.addTags || _this.tags;
33886 _this.removeTags = _this.removeTags || _this.addTags;
33887 _this.geometry = _this.geometry || [];
33889 _this.matchGeometry = function (geom) {
33890 return _this.geometry.indexOf(geom) >= 0;
33893 _this.matchAllGeometry = function (geoms) {
33894 return geoms.every(_this.matchGeometry);
33897 _this.matchScore = function (entityTags) {
33898 var tags = _this.tags;
33900 var score = 0; // match on tags
33902 for (var k in tags) {
33905 if (entityTags[k] === tags[k]) {
33906 score += _this.originalScore;
33907 } else if (tags[k] === '*' && k in entityTags) {
33908 score += _this.originalScore / 2;
33912 } // boost score for additional matches in addTags - #6802
33915 var addTags = _this.addTags;
33917 for (var _k in addTags) {
33918 if (!seen[_k] && entityTags[_k] === addTags[_k]) {
33919 score += _this.originalScore;
33926 _this.t = function (scope, options) {
33927 var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33928 return _t(textID, options);
33931 _this.t.html = function (scope, options) {
33932 var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33933 return _t.html(textID, options);
33936 _this.name = function () {
33937 return _this.t('name', {
33938 'default': _this.originalName
33942 _this.nameLabel = function () {
33943 return _this.t.html('name', {
33944 'default': _this.originalName
33948 _this.subtitle = function () {
33949 if (_this.suggestion) {
33950 var path = presetID.split('/');
33951 path.pop(); // remove brand name
33953 return _t('_tagging.presets.presets.' + path.join('/') + '.name');
33959 _this.subtitleLabel = function () {
33960 if (_this.suggestion) {
33961 var path = presetID.split('/');
33962 path.pop(); // remove brand name
33964 return _t.html('_tagging.presets.presets.' + path.join('/') + '.name');
33970 _this.terms = function () {
33971 return _this.t('terms', {
33972 'default': _this.originalTerms
33973 }).toLowerCase().trim().split(/\s*,+\s*/);
33976 _this.searchName = function () {
33977 if (!_searchName) {
33978 _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33981 return _searchName;
33984 _this.searchNameStripped = function () {
33985 if (!_searchNameStripped) {
33986 _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33988 if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33990 _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33993 return _searchNameStripped;
33996 _this.isFallback = function () {
33997 var tagCount = Object.keys(_this.tags).length;
33998 return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
34001 _this.addable = function (val) {
34002 if (!arguments.length) return _addable;
34007 _this.reference = function () {
34008 // Lookup documentation on Wikidata...
34009 var qid = _this.tags.wikidata || _this.tags['flag:wikidata'] || _this.tags['brand:wikidata'] || _this.tags['network:wikidata'] || _this.tags['operator:wikidata'];
34015 } // Lookup documentation on OSM Wikibase...
34018 var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
34019 var value = _this.originalReference.value || _this.tags[key];
34021 if (value === '*') {
34033 _this.unsetTags = function (tags, geometry, ignoringKeys, skipFieldDefaults) {
34034 // allow manually keeping some tags
34035 var removeTags = ignoringKeys ? utilObjectOmit(_this.removeTags, ignoringKeys) : _this.removeTags;
34036 tags = utilObjectOmit(tags, Object.keys(removeTags));
34038 if (geometry && !skipFieldDefaults) {
34039 _this.fields().forEach(function (field) {
34040 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
34041 delete tags[field.key];
34050 _this.setTags = function (tags, geometry, skipFieldDefaults) {
34051 var addTags = _this.addTags;
34052 tags = Object.assign({}, tags); // shallow copy
34054 for (var k in addTags) {
34055 if (addTags[k] === '*') {
34056 // if this tag is ancillary, don't override an existing value since any value is okay
34057 if (_this.tags[k] || !tags[k] || tags[k] === 'no') {
34061 tags[k] = addTags[k];
34063 } // Add area=yes if necessary.
34064 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
34065 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
34066 // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
34069 if (!addTags.hasOwnProperty('area')) {
34072 if (geometry === 'area') {
34073 var needsAreaTag = true;
34075 if (_this.geometry.indexOf('line') === -1) {
34076 for (var _k2 in addTags) {
34077 if (_k2 in osmAreaKeys) {
34078 needsAreaTag = false;
34084 if (needsAreaTag) {
34090 if (geometry && !skipFieldDefaults) {
34091 _this.fields().forEach(function (field) {
34092 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
34093 tags[field.key] = field["default"];
34099 }; // For a preset without fields, use the fields of the parent preset.
34100 // Replace {preset} placeholders with the fields of the specified presets.
34103 function resolve(which) {
34104 var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
34106 fieldIDs.forEach(function (fieldID) {
34107 var match = fieldID.match(/\{(.*)\}/);
34109 if (match !== null) {
34110 // a presetID wrapped in braces {}
34111 resolved = resolved.concat(inheritFields(match[1], which));
34112 } else if (allFields[fieldID]) {
34113 // a normal fieldID
34114 resolved.push(allFields[fieldID]);
34116 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
34118 }); // no fields resolved, so use the parent's if possible
34120 if (!resolved.length) {
34121 var endIndex = _this.id.lastIndexOf('/');
34123 var parentID = endIndex && _this.id.substring(0, endIndex);
34126 resolved = inheritFields(parentID, which);
34130 return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
34132 function inheritFields(presetID, which) {
34133 var parent = allPresets[presetID];
34134 if (!parent) return [];
34136 if (which === 'fields') {
34137 return parent.fields().filter(shouldInherit);
34138 } else if (which === 'moreFields') {
34139 return parent.moreFields();
34143 } // Skip `fields` for the keys which define the preset.
34144 // These are usually `typeCombo` fields like `shop=*`
34147 function shouldInherit(f) {
34148 if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
34149 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
34157 var _mainPresetIndex = presetIndex(); // singleton
34158 // `presetIndex` wraps a `presetCollection`
34159 // with methods for loading new data and returning defaults
34162 function presetIndex() {
34163 var dispatch = dispatch$8('favoritePreset', 'recentsChange');
34164 var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
34166 var POINT = presetPreset('point', {
34169 geometry: ['point', 'vertex'],
34172 var LINE = presetPreset('line', {
34175 geometry: ['line'],
34178 var AREA = presetPreset('area', {
34183 geometry: ['area'],
34186 var RELATION = presetPreset('relation', {
34189 geometry: ['relation'],
34193 var _this = presetCollection([POINT, LINE, AREA, RELATION]);
34202 point: presetCollection([POINT]),
34203 vertex: presetCollection([POINT]),
34204 line: presetCollection([LINE]),
34205 area: presetCollection([AREA]),
34206 relation: presetCollection([RELATION])
34209 var _categories = {};
34210 var _universal = [];
34211 var _addablePresetIDs = null; // Set of preset IDs that the user can add
34215 var _favorites; // Index of presets by (geometry, tag key).
34218 var _geometryIndex = {
34228 _this.ensureLoaded = function () {
34229 if (_loadPromise) return _loadPromise;
34230 return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
34232 categories: vals[0],
34238 osmSetAreaKeys(_this.areaKeys());
34239 osmSetPointTags(_this.pointTags());
34240 osmSetVertexTags(_this.vertexTags());
34242 }; // `merge` accepts an object containing new preset data (all properties optional):
34248 // featureCollection: {}
34252 _this.merge = function (d) {
34253 var newLocationSets = []; // Merge Fields
34256 Object.keys(d.fields).forEach(function (fieldID) {
34257 var f = d.fields[fieldID];
34261 f = presetField(fieldID, f);
34262 if (f.locationSet) newLocationSets.push(f);
34263 _fields[fieldID] = f;
34266 delete _fields[fieldID];
34273 Object.keys(d.presets).forEach(function (presetID) {
34274 var p = d.presets[presetID];
34278 var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
34280 p = presetPreset(presetID, p, isAddable, _fields, _presets);
34281 if (p.locationSet) newLocationSets.push(p);
34282 _presets[presetID] = p;
34284 // remove (but not if it's a fallback)
34285 var existing = _presets[presetID];
34287 if (existing && !existing.isFallback()) {
34288 delete _presets[presetID];
34292 } // Merge Categories
34295 if (d.categories) {
34296 Object.keys(d.categories).forEach(function (categoryID) {
34297 var c = d.categories[categoryID];
34301 c = presetCategory(categoryID, c, _presets);
34302 if (c.locationSet) newLocationSets.push(c);
34303 _categories[categoryID] = c;
34306 delete _categories[categoryID];
34309 } // Rebuild _this.collection after changing presets and categories
34312 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
34315 Object.keys(d.defaults).forEach(function (geometry) {
34316 var def = d.defaults[geometry];
34318 if (Array.isArray(def)) {
34320 _defaults[geometry] = presetCollection(def.map(function (id) {
34321 return _presets[id] || _categories[id];
34322 }).filter(Boolean));
34325 delete _defaults[geometry];
34328 } // Rebuild universal fields array
34331 _universal = Object.values(_fields).filter(function (field) {
34332 return field.universal;
34333 }); // Reset all the preset fields - they'll need to be resolved again
34335 Object.values(_presets).forEach(function (preset) {
34336 return preset.resetFields();
34337 }); // Rebuild geometry index
34347 _this.collection.forEach(function (preset) {
34348 (preset.geometry || []).forEach(function (geometry) {
34349 var g = _geometryIndex[geometry];
34351 for (var key in preset.tags) {
34352 (g[key] = g[key] || []).push(preset);
34355 }); // Merge Custom Features
34358 if (d.featureCollection && Array.isArray(d.featureCollection.features)) {
34359 _mainLocations.mergeCustomGeoJSON(d.featureCollection);
34360 } // Resolve all locationSet features.
34363 if (newLocationSets.length) {
34364 _mainLocations.mergeLocationSets(newLocationSets);
34370 _this.match = function (entity, resolver) {
34371 return resolver["transient"](entity, 'presetMatch', function () {
34372 var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
34374 if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
34375 geometry = 'point';
34378 var entityExtent = entity.extent(resolver);
34379 return _this.matchTags(entity.tags, geometry, entityExtent.center());
34383 _this.matchTags = function (tags, geometry, loc) {
34384 var geometryMatches = _geometryIndex[geometry];
34388 var validLocations;
34390 if (Array.isArray(loc)) {
34391 validLocations = _mainLocations.locationsAt(loc);
34394 for (var k in tags) {
34395 // If any part of an address is present, allow fallback to "Address" preset - #4353
34396 if (/^addr:/.test(k) && geometryMatches['addr:*']) {
34397 address = geometryMatches['addr:*'][0];
34400 var keyMatches = geometryMatches[k];
34401 if (!keyMatches) continue;
34403 for (var i = 0; i < keyMatches.length; i++) {
34404 var candidate = keyMatches[i]; // discard candidate preset if location is not valid at `loc`
34406 if (validLocations && candidate.locationSetID) {
34407 if (!validLocations[candidate.locationSetID]) continue;
34410 var score = candidate.matchScore(tags);
34412 if (score > best) {
34419 if (address && (!match || match.isFallback())) {
34423 return match || _this.fallback(geometry);
34426 _this.allowsVertex = function (entity, resolver) {
34427 if (entity.type !== 'node') return false;
34428 if (Object.keys(entity.tags).length === 0) return true;
34429 return resolver["transient"](entity, 'vertexMatch', function () {
34430 // address lines allow vertices to act as standalone points
34431 if (entity.isOnAddressLine(resolver)) return true;
34432 var geometries = osmNodeGeometriesForTags(entity.tags);
34433 if (geometries.vertex) return true;
34434 if (geometries.point) return false; // allow vertices for unspecified points
34438 }; // Because of the open nature of tagging, iD will never have a complete
34439 // list of tags used in OSM, so we want it to have logic like "assume
34440 // that a closed way with an amenity tag is an area, unless the amenity
34441 // is one of these specific types". This function computes a structure
34442 // that allows testing of such conditions, based on the presets designated
34443 // as as supporting (or not supporting) the area geometry.
34445 // The returned object L is a keeplist/discardlist of tags. A closed way
34446 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
34447 // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
34448 // and the subkeys form the discardlist.
34451 _this.areaKeys = function () {
34452 // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
34453 var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
34454 var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
34456 var presets = _this.collection.filter(function (p) {
34457 return !p.suggestion && !p.replacement;
34461 presets.forEach(function (p) {
34462 var keys = p.tags && Object.keys(p.tags);
34463 var key = keys && keys.length && keys[0]; // pick the first tag
34466 if (ignore.indexOf(key) !== -1) return;
34468 if (p.geometry.indexOf('area') !== -1) {
34469 // probably an area..
34470 areaKeys[key] = areaKeys[key] || {};
34474 presets.forEach(function (p) {
34477 for (key in p.addTags) {
34478 // examine all addTags to get a better sense of what can be tagged on lines - #6800
34479 var value = p.addTags[key];
34481 if (key in areaKeys && // probably an area...
34482 p.geometry.indexOf('line') !== -1 && // but sometimes a line
34484 areaKeys[key][value] = true;
34491 _this.pointTags = function () {
34492 return _this.collection.reduce(function (pointTags, d) {
34493 // ignore name-suggestion-index, deprecated, and generic presets
34494 if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
34496 var keys = d.tags && Object.keys(d.tags);
34497 var key = keys && keys.length && keys[0]; // pick the first tag
34499 if (!key) return pointTags; // if this can be a point
34501 if (d.geometry.indexOf('point') !== -1) {
34502 pointTags[key] = pointTags[key] || {};
34503 pointTags[key][d.tags[key]] = true;
34510 _this.vertexTags = function () {
34511 return _this.collection.reduce(function (vertexTags, d) {
34512 // ignore name-suggestion-index, deprecated, and generic presets
34513 if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
34515 var keys = d.tags && Object.keys(d.tags);
34516 var key = keys && keys.length && keys[0]; // pick the first tag
34518 if (!key) return vertexTags; // if this can be a vertex
34520 if (d.geometry.indexOf('vertex') !== -1) {
34521 vertexTags[key] = vertexTags[key] || {};
34522 vertexTags[key][d.tags[key]] = true;
34529 _this.field = function (id) {
34530 return _fields[id];
34533 _this.universal = function () {
34537 _this.defaults = function (geometry, n, startWithRecents, loc) {
34540 if (startWithRecents) {
34541 recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
34546 if (_addablePresetIDs) {
34547 defaults = Array.from(_addablePresetIDs).map(function (id) {
34548 var preset = _this.item(id);
34550 if (preset && preset.matchGeometry(geometry)) return preset;
34552 }).filter(Boolean);
34554 defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
34557 var result = presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
34559 if (Array.isArray(loc)) {
34560 var validLocations = _mainLocations.locationsAt(loc);
34561 result.collection = result.collection.filter(function (a) {
34562 return !a.locationSetID || validLocations[a.locationSetID];
34567 }; // pass a Set of addable preset ids
34570 _this.addablePresetIDs = function (val) {
34571 if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
34573 if (Array.isArray(val)) val = new Set(val);
34574 _addablePresetIDs = val;
34576 if (_addablePresetIDs) {
34577 // reset all presets
34578 _this.collection.forEach(function (p) {
34579 // categories aren't addable
34580 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
34583 _this.collection.forEach(function (p) {
34584 if (p.addable) p.addable(true);
34591 _this.recent = function () {
34592 return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
34597 function RibbonItem(preset, source) {
34599 item.preset = preset;
34600 item.source = source;
34602 item.isFavorite = function () {
34603 return item.source === 'favorite';
34606 item.isRecent = function () {
34607 return item.source === 'recent';
34610 item.matches = function (preset) {
34611 return item.preset.id === preset.id;
34614 item.minified = function () {
34616 pID: item.preset.id
34623 function ribbonItemForMinified(d, source) {
34625 var preset = _this.item(d.pID);
34627 if (!preset) return null;
34628 return RibbonItem(preset, source);
34634 _this.getGenericRibbonItems = function () {
34635 return ['point', 'line', 'area'].map(function (id) {
34636 return RibbonItem(_this.item(id), 'generic');
34640 _this.getAddable = function () {
34641 if (!_addablePresetIDs) return [];
34642 return _addablePresetIDs.map(function (id) {
34643 var preset = _this.item(id);
34645 if (preset) return RibbonItem(preset, 'addable');
34647 }).filter(Boolean);
34650 function setRecents(items) {
34652 var minifiedItems = items.map(function (d) {
34653 return d.minified();
34655 corePreferences('preset_recents', JSON.stringify(minifiedItems));
34656 dispatch.call('recentsChange');
34659 _this.getRecents = function () {
34661 // fetch from local storage
34662 _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
34663 var item = ribbonItemForMinified(d, 'recent');
34664 if (item && item.preset.addable()) acc.push(item);
34672 _this.addRecent = function (preset, besidePreset, after) {
34673 var recents = _this.getRecents();
34675 var beforeItem = _this.recentMatching(besidePreset);
34677 var toIndex = recents.indexOf(beforeItem);
34678 if (after) toIndex += 1;
34679 var newItem = RibbonItem(preset, 'recent');
34680 recents.splice(toIndex, 0, newItem);
34681 setRecents(recents);
34684 _this.removeRecent = function (preset) {
34685 var item = _this.recentMatching(preset);
34688 var items = _this.getRecents();
34690 items.splice(items.indexOf(item), 1);
34695 _this.recentMatching = function (preset) {
34696 var items = _this.getRecents();
34698 for (var i in items) {
34699 if (items[i].matches(preset)) {
34707 _this.moveItem = function (items, fromIndex, toIndex) {
34708 if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
34709 items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
34713 _this.moveRecent = function (item, beforeItem) {
34714 var recents = _this.getRecents();
34716 var fromIndex = recents.indexOf(item);
34717 var toIndex = recents.indexOf(beforeItem);
34719 var items = _this.moveItem(recents, fromIndex, toIndex);
34721 if (items) setRecents(items);
34724 _this.setMostRecent = function (preset) {
34725 if (preset.searchable === false) return;
34727 var items = _this.getRecents();
34729 var item = _this.recentMatching(preset);
34732 items.splice(items.indexOf(item), 1);
34734 item = RibbonItem(preset, 'recent');
34735 } // remove the last recent (first in, first out)
34738 while (items.length >= MAXRECENTS) {
34743 items.unshift(item);
34747 function setFavorites(items) {
34748 _favorites = items;
34749 var minifiedItems = items.map(function (d) {
34750 return d.minified();
34752 corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
34754 dispatch.call('favoritePreset');
34757 _this.addFavorite = function (preset, besidePreset, after) {
34758 var favorites = _this.getFavorites();
34760 var beforeItem = _this.favoriteMatching(besidePreset);
34762 var toIndex = favorites.indexOf(beforeItem);
34763 if (after) toIndex += 1;
34764 var newItem = RibbonItem(preset, 'favorite');
34765 favorites.splice(toIndex, 0, newItem);
34766 setFavorites(favorites);
34769 _this.toggleFavorite = function (preset) {
34770 var favs = _this.getFavorites();
34772 var favorite = _this.favoriteMatching(preset);
34775 favs.splice(favs.indexOf(favorite), 1);
34777 // only allow 10 favorites
34778 if (favs.length === 10) {
34779 // remove the last favorite (last in, first out)
34784 favs.push(RibbonItem(preset, 'favorite'));
34787 setFavorites(favs);
34790 _this.removeFavorite = function (preset) {
34791 var item = _this.favoriteMatching(preset);
34794 var items = _this.getFavorites();
34796 items.splice(items.indexOf(item), 1);
34797 setFavorites(items);
34801 _this.getFavorites = function () {
34803 // fetch from local storage
34804 var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
34806 if (!rawFavorites) {
34808 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
34811 _favorites = rawFavorites.reduce(function (output, d) {
34812 var item = ribbonItemForMinified(d, 'favorite');
34813 if (item && item.preset.addable()) output.push(item);
34821 _this.favoriteMatching = function (preset) {
34822 var favs = _this.getFavorites();
34824 for (var index in favs) {
34825 if (favs[index].matches(preset)) {
34826 return favs[index];
34833 return utilRebind(_this, dispatch, 'on');
34836 function utilTagText(entity) {
34837 var obj = entity && entity.tags || {};
34838 return Object.keys(obj).map(function (k) {
34839 return k + '=' + obj[k];
34842 function utilTotalExtent(array, graph) {
34843 var extent = geoExtent();
34846 for (var i = 0; i < array.length; i++) {
34848 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
34851 extent._extend(entity.extent(graph));
34857 function utilTagDiff(oldTags, newTags) {
34859 var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
34860 keys.forEach(function (k) {
34861 var oldVal = oldTags[k];
34862 var newVal = newTags[k];
34864 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
34870 display: '- ' + k + '=' + oldVal
34874 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
34880 display: '+ ' + k + '=' + newVal
34886 function utilEntitySelector(ids) {
34887 return ids.length ? '.' + ids.join(',.') : 'nothing';
34888 } // returns an selector to select entity ids for:
34889 // - entityIDs passed in
34890 // - shallow descendant entityIDs for any of those entities that are relations
34892 function utilEntityOrMemberSelector(ids, graph) {
34893 var seen = new Set(ids);
34894 ids.forEach(collectShallowDescendants);
34895 return utilEntitySelector(Array.from(seen));
34897 function collectShallowDescendants(id) {
34898 var entity = graph.hasEntity(id);
34899 if (!entity || entity.type !== 'relation') return;
34900 entity.members.map(function (member) {
34902 }).forEach(function (id) {
34906 } // returns an selector to select entity ids for:
34907 // - entityIDs passed in
34908 // - deep descendant entityIDs for any of those entities that are relations
34910 function utilEntityOrDeepMemberSelector(ids, graph) {
34911 return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
34912 } // returns an selector to select entity ids for:
34913 // - entityIDs passed in
34914 // - deep descendant entityIDs for any of those entities that are relations
34916 function utilEntityAndDeepMemberIDs(ids, graph) {
34917 var seen = new Set();
34918 ids.forEach(collectDeepDescendants);
34919 return Array.from(seen);
34921 function collectDeepDescendants(id) {
34922 if (seen.has(id)) return;
34924 var entity = graph.hasEntity(id);
34925 if (!entity || entity.type !== 'relation') return;
34926 entity.members.map(function (member) {
34928 }).forEach(collectDeepDescendants); // recurse
34930 } // returns an selector to select entity ids for:
34931 // - deep descendant entityIDs for any of those entities that are relations
34933 function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
34934 var idsSet = new Set(ids);
34935 var seen = new Set();
34936 var returners = new Set();
34937 ids.forEach(collectDeepDescendants);
34938 return utilEntitySelector(Array.from(returners));
34940 function collectDeepDescendants(id) {
34941 if (seen.has(id)) return;
34944 if (!idsSet.has(id)) {
34948 var entity = graph.hasEntity(id);
34949 if (!entity || entity.type !== 'relation') return;
34950 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
34951 entity.members.map(function (member) {
34953 }).forEach(collectDeepDescendants); // recurse
34955 } // Adds or removes highlight styling for the specified entities
34957 function utilHighlightEntities(ids, highlighted, context) {
34958 context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
34959 } // returns an Array that is the union of:
34960 // - nodes for any nodeIDs passed in
34961 // - child nodes of any wayIDs passed in
34962 // - descendant member and child nodes of relationIDs passed in
34964 function utilGetAllNodes(ids, graph) {
34965 var seen = new Set();
34966 var nodes = new Set();
34967 ids.forEach(collectNodes);
34968 return Array.from(nodes);
34970 function collectNodes(id) {
34971 if (seen.has(id)) return;
34973 var entity = graph.hasEntity(id);
34974 if (!entity) return;
34976 if (entity.type === 'node') {
34978 } else if (entity.type === 'way') {
34979 entity.nodes.forEach(collectNodes);
34981 entity.members.map(function (member) {
34983 }).forEach(collectNodes); // recurse
34987 function utilDisplayName(entity) {
34988 var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
34989 var name = entity.tags[localizedNameKey] || entity.tags.name || '';
34990 if (name) return name;
34992 direction: entity.tags.direction,
34993 from: entity.tags.from,
34994 network: entity.tags.cycle_network || entity.tags.network,
34995 ref: entity.tags.ref,
34996 to: entity.tags.to,
34997 via: entity.tags.via
34999 var keyComponents = [];
35001 if (tags.network) {
35002 keyComponents.push('network');
35006 keyComponents.push('ref');
35007 } // Routes may need more disambiguation based on direction or destination
35010 if (entity.tags.route) {
35011 if (tags.direction) {
35012 keyComponents.push('direction');
35013 } else if (tags.from && tags.to) {
35014 keyComponents.push('from');
35015 keyComponents.push('to');
35018 keyComponents.push('via');
35023 if (keyComponents.length) {
35024 name = _t('inspector.display_name.' + keyComponents.join('_'), tags);
35029 function utilDisplayNameForPath(entity) {
35030 var name = utilDisplayName(entity);
35031 var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
35033 if (!isFirefox && name && rtlRegex.test(name)) {
35034 name = fixRTLTextForSvg(name);
35039 function utilDisplayType(id) {
35041 n: _t('inspector.node'),
35042 w: _t('inspector.way'),
35043 r: _t('inspector.relation')
35045 } // `utilDisplayLabel`
35046 // Returns a string suitable for display
35047 // By default returns something like name/ref, fallback to preset type, fallback to OSM type
35048 // "Main Street" or "Tertiary Road"
35049 // If `verbose=true`, include both preset name and feature name.
35050 // "Tertiary Road Main Street"
35053 function utilDisplayLabel(entity, graphOrGeometry, verbose) {
35055 var displayName = utilDisplayName(entity);
35056 var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
35057 var presetName = preset && (preset.suggestion ? preset.subtitle() : preset.name());
35060 result = [presetName, displayName].filter(Boolean).join(' ');
35062 result = displayName || presetName;
35063 } // Fallback to the OSM type (node/way/relation)
35066 return result || utilDisplayType(entity.id);
35068 function utilEntityRoot(entityType) {
35074 } // Returns a single object containing the tags of all the given entities.
35077 // highway: 'service',
35078 // service: 'parking_aisle'
35082 // highway: 'service',
35083 // service: 'driveway',
35088 // highway: 'service',
35089 // service: [ 'driveway', 'parking_aisle' ],
35090 // width: [ '3', undefined ]
35093 function utilCombinedTags(entityIDs, graph) {
35095 var tagCounts = {};
35096 var allKeys = new Set();
35097 var entities = entityIDs.map(function (entityID) {
35098 return graph.hasEntity(entityID);
35099 }).filter(Boolean); // gather the aggregate keys
35101 entities.forEach(function (entity) {
35102 var keys = Object.keys(entity.tags).filter(Boolean);
35103 keys.forEach(function (key) {
35107 entities.forEach(function (entity) {
35108 allKeys.forEach(function (key) {
35109 var value = entity.tags[key]; // purposely allow `undefined`
35111 if (!tags.hasOwnProperty(key)) {
35112 // first value, set as raw
35115 if (!Array.isArray(tags[key])) {
35116 if (tags[key] !== value) {
35117 // first alternate value, replace single value with array
35118 tags[key] = [tags[key], value];
35122 if (tags[key].indexOf(value) === -1) {
35123 // subsequent alternate value, add to array
35124 tags[key].push(value);
35129 var tagHash = key + '=' + value;
35130 if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
35131 tagCounts[tagHash] += 1;
35135 for (var key in tags) {
35136 if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
35138 tags[key] = tags[key].sort(function (val1, val2) {
35139 var key = key; // capture
35141 var count2 = tagCounts[key + '=' + val2];
35142 var count1 = tagCounts[key + '=' + val1];
35144 if (count2 !== count1) {
35145 return count2 - count1;
35148 if (val2 && val1) {
35149 return val1.localeCompare(val2);
35152 return val1 ? 1 : -1;
35158 function utilStringQs(str) {
35159 var i = 0; // advance past any leading '?' or '#' characters
35161 while (i < str.length && (str[i] === '?' || str[i] === '#')) {
35165 str = str.slice(i);
35166 return str.split('&').reduce(function (obj, pair) {
35167 var parts = pair.split('=');
35169 if (parts.length === 2) {
35170 obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
35176 function utilQsString(obj, noencode) {
35177 // encode everything except special characters used in certain hash parameters:
35178 // "/" in map states, ":", ",", {" and "}" in background
35179 function softEncode(s) {
35180 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
35183 return Object.keys(obj).sort().map(function (key) {
35184 return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
35187 function utilPrefixDOMProperty(property) {
35188 var prefixes = ['webkit', 'ms', 'moz', 'o'];
35190 var n = prefixes.length;
35191 var s = document.body;
35192 if (property in s) return property;
35193 property = property.substr(0, 1).toUpperCase() + property.substr(1);
35196 if (prefixes[i] + property in s) {
35197 return prefixes[i] + property;
35203 function utilPrefixCSSProperty(property) {
35204 var prefixes = ['webkit', 'ms', 'Moz', 'O'];
35206 var n = prefixes.length;
35207 var s = document.body.style;
35209 if (property.toLowerCase() in s) {
35210 return property.toLowerCase();
35214 if (prefixes[i] + property in s) {
35215 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
35221 var transformProperty;
35222 function utilSetTransform(el, x, y, scale) {
35223 var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
35224 var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
35225 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
35226 } // Calculates Levenshtein distance between two strings
35227 // see: https://en.wikipedia.org/wiki/Levenshtein_distance
35228 // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
35230 function utilEditDistance(a, b) {
35231 a = remove$6(a.toLowerCase());
35232 b = remove$6(b.toLowerCase());
35233 if (a.length === 0) return b.length;
35234 if (b.length === 0) return a.length;
35238 for (i = 0; i <= b.length; i++) {
35242 for (j = 0; j <= a.length; j++) {
35246 for (i = 1; i <= b.length; i++) {
35247 for (j = 1; j <= a.length; j++) {
35248 if (b.charAt(i - 1) === a.charAt(j - 1)) {
35249 matrix[i][j] = matrix[i - 1][j - 1];
35251 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
35252 Math.min(matrix[i][j - 1] + 1, // insertion
35253 matrix[i - 1][j] + 1)); // deletion
35258 return matrix[b.length][a.length];
35259 } // a d3.mouse-alike which
35260 // 1. Only works on HTML elements, not SVG
35261 // 2. Does not cause style recalculation
35263 function utilFastMouse(container) {
35264 var rect = container.getBoundingClientRect();
35265 var rectLeft = rect.left;
35266 var rectTop = rect.top;
35267 var clientLeft = +container.clientLeft;
35268 var clientTop = +container.clientTop;
35269 return function (e) {
35270 return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
35273 function utilAsyncMap(inputs, func, callback) {
35274 var remaining = inputs.length;
35277 inputs.forEach(function (d, i) {
35278 func(d, function done(err, data) {
35282 if (!remaining) callback(errors, results);
35285 } // wraps an index to an interval [0..length-1]
35287 function utilWrap(index, length) {
35289 index += Math.ceil(-index / length) * length;
35292 return index % length;
35295 * a replacement for functor
35297 * @param {*} value any value
35298 * @returns {Function} a function that returns that value or the value if it's a function
35301 function utilFunctor(value) {
35302 if (typeof value === 'function') return value;
35303 return function () {
35307 function utilNoAuto(selection) {
35308 var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
35309 return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
35310 .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
35311 } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
35312 // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
35314 function utilHashcode(str) {
35317 if (str.length === 0) {
35321 for (var i = 0; i < str.length; i++) {
35322 var _char = str.charCodeAt(i);
35324 hash = (hash << 5) - hash + _char;
35325 hash = hash & hash; // Convert to 32bit integer
35329 } // Returns version of `str` with all runs of special characters replaced by `_`;
35330 // suitable for HTML ids, classes, selectors, etc.
35332 function utilSafeClassName(str) {
35333 return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
35334 } // Returns string based on `val` that is highly unlikely to collide with an id
35335 // used previously or that's present elsewhere in the document. Useful for preventing
35336 // browser-provided autofills or when embedding iD on pages with unknown elements.
35338 function utilUniqueDomId(val) {
35339 return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
35340 } // Returns the length of `str` in unicode characters. This can be less than
35341 // `String.length()` since a single unicode character can be composed of multiple
35342 // JavaScript UTF-16 code units.
35344 function utilUnicodeCharsCount(str) {
35345 // Native ES2015 implementations of `Array.from` split strings into unicode characters
35346 return Array.from(str).length;
35347 } // Returns a new string representing `str` cut from its start to `limit` length
35348 // in unicode characters. Note that this runs the risk of splitting graphemes.
35350 function utilUnicodeCharsTruncated(str, limit) {
35351 return Array.from(str).slice(0, limit).join('');
35352 } // Variation of d3.json (https://github.com/d3/d3-fetch/blob/master/src/json.js)
35354 function utilFetchJson(resourse, init) {
35355 return fetch(resourse, init).then(function (response) {
35356 // fetch in PhantomJS tests may return ok=false and status=0 even if it's okay
35357 if (!response.ok && response.status !== 0 || !response.json) throw new Error(response.status + ' ' + response.statusText);
35358 if (response.status === 204 || response.status === 205) return;
35359 return response.json();
35363 function osmEntity(attrs) {
35364 // For prototypal inheritance.
35365 if (this instanceof osmEntity) return; // Create the appropriate subtype.
35367 if (attrs && attrs.type) {
35368 return osmEntity[attrs.type].apply(this, arguments);
35369 } else if (attrs && attrs.id) {
35370 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
35371 } // Initialize a generic Entity (used only in tests).
35374 return new osmEntity().initialize(arguments);
35377 osmEntity.id = function (type) {
35378 return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
35381 osmEntity.id.next = {
35388 osmEntity.id.fromOSM = function (type, id) {
35389 return type[0] + id;
35392 osmEntity.id.toOSM = function (id) {
35393 return id.slice(1);
35396 osmEntity.id.type = function (id) {
35403 }; // A function suitable for use as the second argument to d3.selection#data().
35406 osmEntity.key = function (entity) {
35407 return entity.id + 'v' + (entity.v || 0);
35410 var _deprecatedTagValuesByKey;
35412 osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
35413 if (!_deprecatedTagValuesByKey) {
35414 _deprecatedTagValuesByKey = {};
35415 dataDeprecated.forEach(function (d) {
35416 var oldKeys = Object.keys(d.old);
35418 if (oldKeys.length === 1) {
35419 var oldKey = oldKeys[0];
35420 var oldValue = d.old[oldKey];
35422 if (oldValue !== '*') {
35423 if (!_deprecatedTagValuesByKey[oldKey]) {
35424 _deprecatedTagValuesByKey[oldKey] = [oldValue];
35426 _deprecatedTagValuesByKey[oldKey].push(oldValue);
35433 return _deprecatedTagValuesByKey;
35436 osmEntity.prototype = {
35438 initialize: function initialize(sources) {
35439 for (var i = 0; i < sources.length; ++i) {
35440 var source = sources[i];
35442 for (var prop in source) {
35443 if (Object.prototype.hasOwnProperty.call(source, prop)) {
35444 if (source[prop] === undefined) {
35447 this[prop] = source[prop];
35453 if (!this.id && this.type) {
35454 this.id = osmEntity.id(this.type);
35457 if (!this.hasOwnProperty('visible')) {
35458 this.visible = true;
35462 Object.freeze(this);
35463 Object.freeze(this.tags);
35464 if (this.loc) Object.freeze(this.loc);
35465 if (this.nodes) Object.freeze(this.nodes);
35466 if (this.members) Object.freeze(this.members);
35471 copy: function copy(resolver, copies) {
35472 if (copies[this.id]) return copies[this.id];
35473 var copy = osmEntity(this, {
35478 copies[this.id] = copy;
35481 osmId: function osmId() {
35482 return osmEntity.id.toOSM(this.id);
35484 isNew: function isNew() {
35485 return this.osmId() < 0;
35487 update: function update(attrs) {
35488 return osmEntity(this, attrs, {
35489 v: 1 + (this.v || 0)
35492 mergeTags: function mergeTags(tags) {
35493 var merged = Object.assign({}, this.tags); // shallow copy
35495 var changed = false;
35497 for (var k in tags) {
35498 var t1 = merged[k];
35504 } else if (t1 !== t2) {
35506 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
35511 return changed ? this.update({
35515 intersects: function intersects(extent, resolver) {
35516 return this.extent(resolver).intersects(extent);
35518 hasNonGeometryTags: function hasNonGeometryTags() {
35519 return Object.keys(this.tags).some(function (k) {
35520 return k !== 'area';
35523 hasParentRelations: function hasParentRelations(resolver) {
35524 return resolver.parentRelations(this).length > 0;
35526 hasInterestingTags: function hasInterestingTags() {
35527 return Object.keys(this.tags).some(osmIsInterestingTag);
35529 isHighwayIntersection: function isHighwayIntersection() {
35532 isDegenerate: function isDegenerate() {
35535 deprecatedTags: function deprecatedTags(dataDeprecated) {
35536 var tags = this.tags; // if there are no tags, none can be deprecated
35538 if (Object.keys(tags).length === 0) return [];
35539 var deprecated = [];
35540 dataDeprecated.forEach(function (d) {
35541 var oldKeys = Object.keys(d.old);
35544 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
35545 if (!tags[replaceKey] || d.old[replaceKey]) return false;
35546 var replaceValue = d.replace[replaceKey];
35547 if (replaceValue === '*') return false;
35548 if (replaceValue === tags[replaceKey]) return false;
35550 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
35552 if (hasExistingValues) return;
35555 var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
35556 if (!tags[oldKey]) return false;
35557 if (d.old[oldKey] === '*') return true;
35558 if (d.old[oldKey] === tags[oldKey]) return true;
35559 var vals = tags[oldKey].split(';').filter(Boolean);
35561 if (vals.length === 0) {
35563 } else if (vals.length > 1) {
35564 return vals.indexOf(d.old[oldKey]) !== -1;
35566 if (tags[oldKey] === d.old[oldKey]) {
35567 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
35568 var replaceKeys = Object.keys(d.replace);
35569 return !replaceKeys.every(function (replaceKey) {
35570 return tags[replaceKey] === d.replace[replaceKey];
35581 if (matchesDeprecatedTags) {
35582 deprecated.push(d);
35589 function osmLanes(entity) {
35590 if (entity.type !== 'way') return null;
35591 if (!entity.tags.highway) return null;
35592 var tags = entity.tags;
35593 var isOneWay = entity.isOneWay();
35594 var laneCount = getLaneCount(tags, isOneWay);
35595 var maxspeed = parseMaxspeed(tags);
35596 var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
35597 var forward = laneDirections.forward;
35598 var backward = laneDirections.backward;
35599 var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
35601 var turnLanes = {};
35602 turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
35603 turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
35604 turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
35605 var maxspeedLanes = {};
35606 maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
35607 maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
35608 maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
35610 psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
35611 psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
35612 psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
35614 busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
35615 busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
35616 busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
35617 var taxiLanes = {};
35618 taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
35619 taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
35620 taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
35622 hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
35623 hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
35624 hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
35626 hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
35627 hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
35628 hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
35629 var bicyclewayLanes = {};
35630 bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
35631 bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
35632 bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
35637 }; // map forward/backward/unspecified of each lane type to lanesObj
35639 mapToLanesObj(lanesObj, turnLanes, 'turnLane');
35640 mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
35641 mapToLanesObj(lanesObj, psvLanes, 'psv');
35642 mapToLanesObj(lanesObj, busLanes, 'bus');
35643 mapToLanesObj(lanesObj, taxiLanes, 'taxi');
35644 mapToLanesObj(lanesObj, hovLanes, 'hov');
35645 mapToLanesObj(lanesObj, hgvLanes, 'hgv');
35646 mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
35652 backward: backward,
35653 bothways: bothways,
35654 turnLanes: turnLanes,
35655 maxspeed: maxspeed,
35656 maxspeedLanes: maxspeedLanes,
35657 psvLanes: psvLanes,
35658 busLanes: busLanes,
35659 taxiLanes: taxiLanes,
35660 hovLanes: hovLanes,
35661 hgvLanes: hgvLanes,
35662 bicyclewayLanes: bicyclewayLanes
35668 function getLaneCount(tags, isOneWay) {
35672 count = parseInt(tags.lanes, 10);
35679 switch (tags.highway) {
35682 count = isOneWay ? 2 : 4;
35686 count = isOneWay ? 1 : 2;
35693 function parseMaxspeed(tags) {
35694 var maxspeed = tags.maxspeed;
35695 if (!maxspeed) return;
35696 var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
35697 if (!maxspeedRegex.test(maxspeed)) return;
35698 return parseInt(maxspeed, 10);
35701 function parseLaneDirections(tags, isOneWay, laneCount) {
35702 var forward = parseInt(tags['lanes:forward'], 10);
35703 var backward = parseInt(tags['lanes:backward'], 10);
35704 var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
35706 if (parseInt(tags.oneway, 10) === -1) {
35709 backward = laneCount;
35710 } else if (isOneWay) {
35711 forward = laneCount;
35714 } else if (isNaN(forward) && isNaN(backward)) {
35715 backward = Math.floor((laneCount - bothways) / 2);
35716 forward = laneCount - bothways - backward;
35717 } else if (isNaN(forward)) {
35718 if (backward > laneCount - bothways) {
35719 backward = laneCount - bothways;
35722 forward = laneCount - bothways - backward;
35723 } else if (isNaN(backward)) {
35724 if (forward > laneCount - bothways) {
35725 forward = laneCount - bothways;
35728 backward = laneCount - bothways - forward;
35733 backward: backward,
35738 function parseTurnLanes(tag) {
35740 var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
35741 return tag.split('|').map(function (s) {
35742 if (s === '') s = 'none';
35743 return s.split(';').map(function (d) {
35744 return validValues.indexOf(d) === -1 ? 'unknown' : d;
35749 function parseMaxspeedLanes(tag, maxspeed) {
35751 return tag.split('|').map(function (s) {
35752 if (s === 'none') return s;
35753 var m = parseInt(s, 10);
35754 if (s === '' || m === maxspeed) return null;
35755 return isNaN(m) ? 'unknown' : m;
35759 function parseMiscLanes(tag) {
35761 var validValues = ['yes', 'no', 'designated'];
35762 return tag.split('|').map(function (s) {
35763 if (s === '') s = 'no';
35764 return validValues.indexOf(s) === -1 ? 'unknown' : s;
35768 function parseBicycleWay(tag) {
35770 var validValues = ['yes', 'no', 'designated', 'lane'];
35771 return tag.split('|').map(function (s) {
35772 if (s === '') s = 'no';
35773 return validValues.indexOf(s) === -1 ? 'unknown' : s;
35777 function mapToLanesObj(lanesObj, data, key) {
35778 if (data.forward) {
35779 data.forward.forEach(function (l, i) {
35780 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
35781 lanesObj.forward[i][key] = l;
35785 if (data.backward) {
35786 data.backward.forEach(function (l, i) {
35787 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
35788 lanesObj.backward[i][key] = l;
35792 if (data.unspecified) {
35793 data.unspecified.forEach(function (l, i) {
35794 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
35795 lanesObj.unspecified[i][key] = l;
35800 function osmWay() {
35801 if (!(this instanceof osmWay)) {
35802 return new osmWay().initialize(arguments);
35803 } else if (arguments.length) {
35804 this.initialize(arguments);
35807 osmEntity.way = osmWay;
35808 osmWay.prototype = Object.create(osmEntity.prototype);
35809 Object.assign(osmWay.prototype, {
35812 copy: function copy(resolver, copies) {
35813 if (copies[this.id]) return copies[this.id];
35814 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
35815 var nodes = this.nodes.map(function (id) {
35816 return resolver.entity(id).copy(resolver, copies).id;
35818 copy = copy.update({
35821 copies[this.id] = copy;
35824 extent: function extent(resolver) {
35825 return resolver["transient"](this, 'extent', function () {
35826 var extent = geoExtent();
35828 for (var i = 0; i < this.nodes.length; i++) {
35829 var node = resolver.hasEntity(this.nodes[i]);
35832 extent._extend(node.extent());
35839 first: function first() {
35840 return this.nodes[0];
35842 last: function last() {
35843 return this.nodes[this.nodes.length - 1];
35845 contains: function contains(node) {
35846 return this.nodes.indexOf(node) >= 0;
35848 affix: function affix(node) {
35849 if (this.nodes[0] === node) return 'prefix';
35850 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
35852 layer: function layer() {
35853 // explicit layer tag, clamp between -10, 10..
35854 if (isFinite(this.tags.layer)) {
35855 return Math.max(-10, Math.min(+this.tags.layer, 10));
35856 } // implied layer tag..
35859 if (this.tags.covered === 'yes') return -1;
35860 if (this.tags.location === 'overground') return 1;
35861 if (this.tags.location === 'underground') return -1;
35862 if (this.tags.location === 'underwater') return -10;
35863 if (this.tags.power === 'line') return 10;
35864 if (this.tags.power === 'minor_line') return 10;
35865 if (this.tags.aerialway) return 10;
35866 if (this.tags.bridge) return 1;
35867 if (this.tags.cutting) return -1;
35868 if (this.tags.tunnel) return -1;
35869 if (this.tags.waterway) return -1;
35870 if (this.tags.man_made === 'pipeline') return -10;
35871 if (this.tags.boundary) return -10;
35874 // the approximate width of the line based on its tags except its `width` tag
35875 impliedLineWidthMeters: function impliedLineWidthMeters() {
35876 var averageWidths = {
35878 // width is for single lane
35905 // width includes ties and rail bed, not just track gauge
35928 for (var key in averageWidths) {
35929 if (this.tags[key] && averageWidths[key][this.tags[key]]) {
35930 var width = averageWidths[key][this.tags[key]];
35932 if (key === 'highway') {
35933 var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
35934 if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
35935 return width * laneCount;
35944 isOneWay: function isOneWay() {
35945 // explicit oneway tag..
35950 'reversible': true,
35951 'alternating': true,
35956 if (values[this.tags.oneway] !== undefined) {
35957 return values[this.tags.oneway];
35958 } // implied oneway tag..
35961 for (var key in this.tags) {
35962 if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) {
35969 // Some identifier for tag that implies that this way is "sided",
35970 // i.e. the right side is the 'inside' (e.g. the right side of a
35971 // natural=cliff is lower).
35972 sidednessIdentifier: function sidednessIdentifier() {
35973 for (var key in this.tags) {
35974 var value = this.tags[key];
35976 if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
35977 if (osmRightSideIsInsideTags[key][value] === true) {
35980 // if the map's value is something other than a
35981 // literal true, we should use it so we can
35982 // special case some keys (e.g. natural=coastline
35983 // is handled differently to other naturals).
35984 return osmRightSideIsInsideTags[key][value];
35991 isSided: function isSided() {
35992 if (this.tags.two_sided === 'yes') {
35996 return this.sidednessIdentifier() !== null;
35998 lanes: function lanes() {
35999 return osmLanes(this);
36001 isClosed: function isClosed() {
36002 return this.nodes.length > 1 && this.first() === this.last();
36004 isConvex: function isConvex(resolver) {
36005 if (!this.isClosed() || this.isDegenerate()) return null;
36006 var nodes = utilArrayUniq(resolver.childNodes(this));
36007 var coords = nodes.map(function (n) {
36013 for (var i = 0; i < coords.length; i++) {
36014 var o = coords[(i + 1) % coords.length];
36016 var b = coords[(i + 2) % coords.length];
36017 var res = geoVecCross(a, b, o);
36018 curr = res > 0 ? 1 : res < 0 ? -1 : 0;
36022 } else if (prev && curr !== prev) {
36031 // returns an object with the tag that implies this is an area, if any
36032 tagSuggestingArea: function tagSuggestingArea() {
36033 return osmTagSuggestingArea(this.tags);
36035 isArea: function isArea() {
36036 if (this.tags.area === 'yes') return true;
36037 if (!this.isClosed() || this.tags.area === 'no') return false;
36038 return this.tagSuggestingArea() !== null;
36040 isDegenerate: function isDegenerate() {
36041 return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
36043 areAdjacent: function areAdjacent(n1, n2) {
36044 for (var i = 0; i < this.nodes.length; i++) {
36045 if (this.nodes[i] === n1) {
36046 if (this.nodes[i - 1] === n2) return true;
36047 if (this.nodes[i + 1] === n2) return true;
36053 geometry: function geometry(graph) {
36054 return graph["transient"](this, 'geometry', function () {
36055 return this.isArea() ? 'area' : 'line';
36058 // returns an array of objects representing the segments between the nodes in this way
36059 segments: function segments(graph) {
36060 function segmentExtent(graph) {
36061 var n1 = graph.hasEntity(this.nodes[0]);
36062 var n2 = graph.hasEntity(this.nodes[1]);
36063 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])]]);
36066 return graph["transient"](this, 'segments', function () {
36069 for (var i = 0; i < this.nodes.length - 1; i++) {
36071 id: this.id + '-' + i,
36074 nodes: [this.nodes[i], this.nodes[i + 1]],
36075 extent: segmentExtent
36082 // If this way is not closed, append the beginning node to the end of the nodelist to close it.
36083 close: function close() {
36084 if (this.isClosed() || !this.nodes.length) return this;
36085 var nodes = this.nodes.slice();
36086 nodes = nodes.filter(noRepeatNodes);
36087 nodes.push(nodes[0]);
36088 return this.update({
36092 // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
36093 unclose: function unclose() {
36094 if (!this.isClosed()) return this;
36095 var nodes = this.nodes.slice();
36096 var connector = this.first();
36097 var i = nodes.length - 1; // remove trailing connectors..
36099 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36100 nodes.splice(i, 1);
36101 i = nodes.length - 1;
36104 nodes = nodes.filter(noRepeatNodes);
36105 return this.update({
36109 // Adds a node (id) in front of the node which is currently at position index.
36110 // If index is undefined, the node will be added to the end of the way for linear ways,
36111 // or just before the final connecting node for circular ways.
36112 // Consecutive duplicates are eliminated including existing ones.
36113 // Circularity is always preserved when adding a node.
36114 addNode: function addNode(id, index) {
36115 var nodes = this.nodes.slice();
36116 var isClosed = this.isClosed();
36117 var max = isClosed ? nodes.length - 1 : nodes.length;
36119 if (index === undefined) {
36123 if (index < 0 || index > max) {
36124 throw new RangeError('index ' + index + ' out of range 0..' + max);
36125 } // If this is a closed way, remove all connector nodes except the first one
36126 // (there may be duplicates) and adjust index if necessary..
36130 var connector = this.first(); // leading connectors..
36134 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36135 nodes.splice(i, 1);
36136 if (index > i) index--;
36137 } // trailing connectors..
36140 i = nodes.length - 1;
36142 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36143 nodes.splice(i, 1);
36144 if (index > i) index--;
36145 i = nodes.length - 1;
36149 nodes.splice(index, 0, id);
36150 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36152 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36153 nodes.push(nodes[0]);
36156 return this.update({
36160 // Replaces the node which is currently at position index with the given node (id).
36161 // Consecutive duplicates are eliminated including existing ones.
36162 // Circularity is preserved when updating a node.
36163 updateNode: function updateNode(id, index) {
36164 var nodes = this.nodes.slice();
36165 var isClosed = this.isClosed();
36166 var max = nodes.length - 1;
36168 if (index === undefined || index < 0 || index > max) {
36169 throw new RangeError('index ' + index + ' out of range 0..' + max);
36170 } // If this is a closed way, remove all connector nodes except the first one
36171 // (there may be duplicates) and adjust index if necessary..
36175 var connector = this.first(); // leading connectors..
36179 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36180 nodes.splice(i, 1);
36181 if (index > i) index--;
36182 } // trailing connectors..
36185 i = nodes.length - 1;
36187 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36188 nodes.splice(i, 1);
36189 if (index === i) index = 0; // update leading connector instead
36191 i = nodes.length - 1;
36195 nodes.splice(index, 1, id);
36196 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36198 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36199 nodes.push(nodes[0]);
36202 return this.update({
36206 // Replaces each occurrence of node id needle with replacement.
36207 // Consecutive duplicates are eliminated including existing ones.
36208 // Circularity is preserved.
36209 replaceNode: function replaceNode(needleID, replacementID) {
36210 var nodes = this.nodes.slice();
36211 var isClosed = this.isClosed();
36213 for (var i = 0; i < nodes.length; i++) {
36214 if (nodes[i] === needleID) {
36215 nodes[i] = replacementID;
36219 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36221 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36222 nodes.push(nodes[0]);
36225 return this.update({
36229 // Removes each occurrence of node id.
36230 // Consecutive duplicates are eliminated including existing ones.
36231 // Circularity is preserved.
36232 removeNode: function removeNode(id) {
36233 var nodes = this.nodes.slice();
36234 var isClosed = this.isClosed();
36235 nodes = nodes.filter(function (node) {
36236 return node !== id;
36237 }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36239 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36240 nodes.push(nodes[0]);
36243 return this.update({
36247 asJXON: function asJXON(changeset_id) {
36250 '@id': this.osmId(),
36251 '@version': this.version || 0,
36252 nd: this.nodes.map(function (id) {
36255 ref: osmEntity.id.toOSM(id)
36259 tag: Object.keys(this.tags).map(function (k) {
36270 if (changeset_id) {
36271 r.way['@changeset'] = changeset_id;
36276 asGeoJSON: function asGeoJSON(resolver) {
36277 return resolver["transient"](this, 'GeoJSON', function () {
36278 var coordinates = resolver.childNodes(this).map(function (n) {
36282 if (this.isArea() && this.isClosed()) {
36285 coordinates: [coordinates]
36289 type: 'LineString',
36290 coordinates: coordinates
36295 area: function area(resolver) {
36296 return resolver["transient"](this, 'area', function () {
36297 var nodes = resolver.childNodes(this);
36300 coordinates: [nodes.map(function (n) {
36305 if (!this.isClosed() && nodes.length) {
36306 json.coordinates[0].push(nodes[0].loc);
36309 var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
36310 // that OpenStreetMap polygons are not hemisphere-spanning.
36312 if (area > 2 * Math.PI) {
36313 json.coordinates[0] = json.coordinates[0].reverse();
36314 area = d3_geoArea(json);
36317 return isNaN(area) ? 0 : area;
36320 }); // Filter function to eliminate consecutive duplicates.
36322 function noRepeatNodes(node, i, arr) {
36323 return i === 0 || node !== arr[i - 1];
36327 // 1. Relation tagged with `type=multipolygon` and no interesting tags.
36328 // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
36329 // 3. No members without a role.
36331 // Old multipolygons are no longer recommended but are still rendered as areas by iD.
36333 function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
36334 if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
36340 for (var memberIndex in entity.members) {
36341 var member = entity.members[memberIndex];
36343 if (!member.role || member.role === 'outer') {
36344 if (outerMember) return false;
36345 if (member.type !== 'way') return false;
36346 if (!graph.hasEntity(member.id)) return false;
36347 outerMember = graph.entity(member.id);
36349 if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
36355 return outerMember;
36356 } // For fixing up rendering of multipolygons with tags on the outer member.
36357 // https://github.com/openstreetmap/iD/issues/613
36359 function osmIsOldMultipolygonOuterMember(entity, graph) {
36360 if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) {
36364 var parents = graph.parentRelations(entity);
36365 if (parents.length !== 1) return false;
36366 var parent = parents[0];
36368 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36372 var members = parent.members,
36375 for (var i = 0; i < members.length; i++) {
36376 member = members[i];
36378 if (member.id === entity.id && member.role && member.role !== 'outer') {
36379 // Not outer member
36383 if (member.id !== entity.id && (!member.role || member.role === 'outer')) {
36384 // Not a simple multipolygon
36391 function osmOldMultipolygonOuterMember(entity, graph) {
36392 if (entity.type !== 'way') return false;
36393 var parents = graph.parentRelations(entity);
36394 if (parents.length !== 1) return false;
36395 var parent = parents[0];
36397 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36401 var members = parent.members,
36405 for (var i = 0; i < members.length; i++) {
36406 member = members[i];
36408 if (!member.role || member.role === 'outer') {
36409 if (outerMember) return false; // Not a simple multipolygon
36411 outerMember = member;
36415 if (!outerMember) return false;
36416 var outerEntity = graph.hasEntity(outerMember.id);
36418 if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) {
36422 return outerEntity;
36423 } // Join `toJoin` array into sequences of connecting ways.
36424 // Segments which share identical start/end nodes will, as much as possible,
36425 // be connected with each other.
36427 // The return value is a nested array. Each constituent array contains elements
36428 // of `toJoin` which have been determined to connect.
36430 // Each consitituent array also has a `nodes` property whose value is an
36431 // ordered array of member nodes, with appropriate order reversal and
36432 // start/end coordinate de-duplication.
36434 // Members of `toJoin` must have, at minimum, `type` and `id` properties.
36435 // Thus either an array of `osmWay`s or a relation member array may be used.
36437 // If an member is an `osmWay`, its tags and childnodes may be reversed via
36438 // `actionReverse` in the output.
36440 // The returned sequences array also has an `actions` array property, containing
36441 // any reversal actions that should be applied to the graph, should the calling
36442 // code attempt to actually join the given ways.
36444 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
36445 // false) and non-way members are ignored.
36448 function osmJoinWays(toJoin, graph) {
36449 function resolve(member) {
36450 return graph.childNodes(graph.entity(member.id));
36453 function reverse(item) {
36454 var action = actionReverse(item.id, {
36455 reverseOneway: true
36457 sequences.actions.push(action);
36458 return item instanceof osmWay ? action(graph).entity(item.id) : item;
36459 } // make a copy containing only the items to join
36462 toJoin = toJoin.filter(function (member) {
36463 return member.type === 'way' && graph.hasEntity(member.id);
36464 }); // Are the things we are joining relation members or `osmWays`?
36465 // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
36468 var joinAsMembers = true;
36470 for (i = 0; i < toJoin.length; i++) {
36471 if (toJoin[i] instanceof osmWay) {
36472 joinAsMembers = false;
36477 var sequences = [];
36478 sequences.actions = [];
36480 while (toJoin.length) {
36481 // start a new sequence
36482 var item = toJoin.shift();
36483 var currWays = [item];
36484 var currNodes = resolve(item).slice(); // add to it
36486 while (toJoin.length) {
36487 var start = currNodes[0];
36488 var end = currNodes[currNodes.length - 1];
36490 var nodes = null; // Find the next way/member to join.
36492 for (i = 0; i < toJoin.length; i++) {
36494 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
36495 // Strongly prefer to generate a forward path that preserves the order
36496 // of the members array. For multipolygons and most relations, member
36497 // order does not matter - but for routes, it does. (see #4589)
36498 // If we started this sequence backwards (i.e. next member way attaches to
36499 // the start node and not the end node), reverse the initial way before continuing.
36501 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
36502 currWays[0] = reverse(currWays[0]);
36503 currNodes.reverse();
36504 start = currNodes[0];
36505 end = currNodes[currNodes.length - 1];
36508 if (nodes[0] === end) {
36509 fn = currNodes.push; // join to end
36511 nodes = nodes.slice(1);
36513 } else if (nodes[nodes.length - 1] === end) {
36514 fn = currNodes.push; // join to end
36516 nodes = nodes.slice(0, -1).reverse();
36517 item = reverse(item);
36519 } else if (nodes[nodes.length - 1] === start) {
36520 fn = currNodes.unshift; // join to beginning
36522 nodes = nodes.slice(0, -1);
36524 } else if (nodes[0] === start) {
36525 fn = currNodes.unshift; // join to beginning
36527 nodes = nodes.slice(1).reverse();
36528 item = reverse(item);
36536 // couldn't find a joinable way/member
36540 fn.apply(currWays, [item]);
36541 fn.apply(currNodes, nodes);
36542 toJoin.splice(i, 1);
36545 currWays.nodes = currNodes;
36546 sequences.push(currWays);
36552 function actionAddMember(relationId, member, memberIndex, insertPair) {
36553 return function action(graph) {
36554 var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
36556 var isPTv2 = /stop|platform/.test(member.role);
36558 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
36559 // Try to perform sensible inserts based on how the ways join together
36560 graph = addWayMember(relation, graph);
36562 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36563 // Stops and Platforms for PTv2 should be ordered first.
36564 // hack: We do not currently have the ability to place them in the exactly correct order.
36565 if (isPTv2 && isNaN(memberIndex)) {
36569 graph = graph.replace(relation.addMember(member, memberIndex));
36573 }; // Add a way member into the relation "wherever it makes sense".
36574 // In this situation we were not supplied a memberIndex.
36576 function addWayMember(relation, graph) {
36577 var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
36579 var PTv2members = [];
36582 for (i = 0; i < relation.members.length; i++) {
36583 var m = relation.members[i];
36585 if (/stop|platform/.test(m.role)) {
36586 PTv2members.push(m);
36592 relation = relation.update({
36597 // We're adding a member that must stay paired with an existing member.
36598 // (This feature is used by `actionSplit`)
36600 // This is tricky because the members may exist multiple times in the
36601 // member list, and with different A-B/B-A ordering and different roles.
36602 // (e.g. a bus route that loops out and back - #4589).
36604 // Replace the existing member with a temporary way,
36605 // so that `osmJoinWays` can treat the pair like a single way.
36608 nodes: insertPair.nodes
36610 graph = graph.replace(tempWay);
36616 var tempRelation = relation.replaceMember({
36617 id: insertPair.originalID
36618 }, tempMember, true);
36619 groups = utilArrayGroupBy(tempRelation.members, 'type');
36620 groups.way = groups.way || [];
36622 // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
36623 groups = utilArrayGroupBy(relation.members, 'type');
36624 groups.way = groups.way || [];
36625 groups.way.push(member);
36628 members = withIndex(groups.way);
36629 var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
36630 // But will contain only the completed (downloaded) members
36632 for (i = 0; i < joined.length; i++) {
36633 var segment = joined[i];
36634 var nodes = segment.nodes.slice();
36635 var startIndex = segment[0].index; // j = array index in `members` where this segment starts
36637 for (j = 0; j < members.length; j++) {
36638 if (members[j].index === startIndex) {
36641 } // k = each member in segment
36644 for (k = 0; k < segment.length; k++) {
36646 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
36648 if (tempWay && item.id === tempWay.id) {
36649 if (nodes[0].id === insertPair.nodes[0]) {
36651 id: insertPair.originalID,
36655 id: insertPair.insertedID,
36661 id: insertPair.insertedID,
36665 id: insertPair.originalID,
36670 } // reorder `members` if necessary
36674 if (j + k >= members.length || item.index !== members[j + k].index) {
36675 moveMember(members, item.index, j + k);
36679 nodes.splice(0, way.nodes.length - 1);
36684 graph = graph.remove(tempWay);
36685 } // Final pass: skip dead items, split pairs, remove index properties
36688 var wayMembers = [];
36690 for (i = 0; i < members.length; i++) {
36692 if (item.index === -1) continue;
36695 wayMembers.push(item.pair[0]);
36696 wayMembers.push(item.pair[1]);
36698 wayMembers.push(utilObjectOmit(item, ['index']));
36700 } // Put stops and platforms first, then nodes, ways, relations
36701 // This is recommended for Public Transport v2 routes:
36702 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36705 var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
36706 return graph.replace(relation.update({
36707 members: newMembers
36708 })); // `moveMember()` changes the `members` array in place by splicing
36709 // the item with `.index = findIndex` to where it belongs,
36710 // and marking the old position as "dead" with `.index = -1`
36714 // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
36718 // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
36719 // members 0 1 2 3 x 5 4 6 7 8 9 moved
36723 // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
36724 // members 0 1 2 3 x 5 4 7 6 x 8 9 moved
36728 // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
36731 function moveMember(arr, findIndex, toIndex) {
36734 for (i = 0; i < arr.length; i++) {
36735 if (arr[i].index === findIndex) {
36740 var item = Object.assign({}, arr[i]); // shallow copy
36742 arr[i].index = -1; // mark as dead
36744 item.index = toIndex;
36745 arr.splice(toIndex, 0, item);
36746 } // This is the same as `Relation.indexedMembers`,
36747 // Except we don't want to index all the members, only the ways
36750 function withIndex(arr) {
36751 var result = new Array(arr.length);
36753 for (var i = 0; i < arr.length; i++) {
36754 result[i] = Object.assign({}, arr[i]); // shallow copy
36756 result[i].index = i;
36764 function actionAddMidpoint(midpoint, node) {
36765 return function (graph) {
36766 graph = graph.replace(node.move(midpoint.loc));
36767 var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
36768 parents.forEach(function (way) {
36769 for (var i = 0; i < way.nodes.length - 1; i++) {
36770 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
36771 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
36772 // turning them into self-intersections.
36782 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
36783 function actionAddVertex(wayId, nodeId, index) {
36784 return function (graph) {
36785 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
36789 function actionChangeMember(relationId, member, memberIndex) {
36790 return function (graph) {
36791 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
36795 function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
36796 return function action(graph) {
36797 var entity = graph.entity(entityID);
36798 var geometry = entity.geometry(graph);
36799 var tags = entity.tags; // preserve tags that the new preset might care about, if any
36801 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, newPreset && newPreset.addTags ? Object.keys(newPreset.addTags) : null);
36802 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
36803 return graph.replace(entity.update({
36809 function actionChangeTags(entityId, tags) {
36810 return function (graph) {
36811 var entity = graph.entity(entityId);
36812 return graph.replace(entity.update({
36818 function osmNode() {
36819 if (!(this instanceof osmNode)) {
36820 return new osmNode().initialize(arguments);
36821 } else if (arguments.length) {
36822 this.initialize(arguments);
36825 osmEntity.node = osmNode;
36826 osmNode.prototype = Object.create(osmEntity.prototype);
36827 Object.assign(osmNode.prototype, {
36830 extent: function extent() {
36831 return new geoExtent(this.loc);
36833 geometry: function geometry(graph) {
36834 return graph["transient"](this, 'geometry', function () {
36835 return graph.isPoi(this) ? 'point' : 'vertex';
36838 move: function move(loc) {
36839 return this.update({
36843 isDegenerate: function isDegenerate() {
36844 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);
36846 // Inspect tags and geometry to determine which direction(s) this node/vertex points
36847 directions: function directions(resolver, projection) {
36849 var i; // which tag to use?
36851 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
36852 // all-way stop tag on a highway intersection
36855 // generic direction tag
36856 val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
36858 var re = /:direction$/i;
36859 var keys = Object.keys(this.tags);
36861 for (i = 0; i < keys.length; i++) {
36862 if (re.test(keys[i])) {
36863 val = this.tags[keys[i]].toLowerCase();
36869 if (val === '') return [];
36873 northnortheast: 22,
36881 eastsoutheast: 112,
36885 southsoutheast: 157,
36889 southsouthwest: 202,
36893 westsouthwest: 247,
36897 westnorthwest: 292,
36901 northnorthwest: 337,
36904 var values = val.split(';');
36906 values.forEach(function (v) {
36907 // swap cardinal for numeric directions
36908 if (cardinal[v] !== undefined) {
36910 } // numeric direction - just add to results
36913 if (v !== '' && !isNaN(+v)) {
36916 } // string direction - inspect parent ways
36919 var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
36920 var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
36921 if (!lookForward && !lookBackward) return;
36923 resolver.parentWays(this).forEach(function (parent) {
36924 var nodes = parent.nodes;
36926 for (i = 0; i < nodes.length; i++) {
36927 if (nodes[i] === this.id) {
36928 // match current entity
36929 if (lookForward && i > 0) {
36930 nodeIds[nodes[i - 1]] = true; // look back to prev node
36933 if (lookBackward && i < nodes.length - 1) {
36934 nodeIds[nodes[i + 1]] = true; // look ahead to next node
36939 Object.keys(nodeIds).forEach(function (nodeId) {
36940 // +90 because geoAngle returns angle from X axis, not Y (north)
36941 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
36944 return utilArrayUniq(results);
36946 isEndpoint: function isEndpoint(resolver) {
36947 return resolver["transient"](this, 'isEndpoint', function () {
36949 return resolver.parentWays(this).filter(function (parent) {
36950 return !parent.isClosed() && !!parent.affix(id);
36954 isConnected: function isConnected(resolver) {
36955 return resolver["transient"](this, 'isConnected', function () {
36956 var parents = resolver.parentWays(this);
36958 if (parents.length > 1) {
36959 // vertex is connected to multiple parent ways
36960 for (var i in parents) {
36961 if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
36963 } else if (parents.length === 1) {
36964 var way = parents[0];
36965 var nodes = way.nodes.slice();
36967 if (way.isClosed()) {
36969 } // ignore connecting node if closed
36970 // return true if vertex appears multiple times (way is self intersecting)
36973 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
36979 parentIntersectionWays: function parentIntersectionWays(resolver) {
36980 return resolver["transient"](this, 'parentIntersectionWays', function () {
36981 return resolver.parentWays(this).filter(function (parent) {
36982 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
36986 isIntersection: function isIntersection(resolver) {
36987 return this.parentIntersectionWays(resolver).length > 1;
36989 isHighwayIntersection: function isHighwayIntersection(resolver) {
36990 return resolver["transient"](this, 'isHighwayIntersection', function () {
36991 return resolver.parentWays(this).filter(function (parent) {
36992 return parent.tags.highway && parent.geometry(resolver) === 'line';
36996 isOnAddressLine: function isOnAddressLine(resolver) {
36997 return resolver["transient"](this, 'isOnAddressLine', function () {
36998 return resolver.parentWays(this).filter(function (parent) {
36999 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
37003 asJXON: function asJXON(changeset_id) {
37006 '@id': this.osmId(),
37007 '@lon': this.loc[0],
37008 '@lat': this.loc[1],
37009 '@version': this.version || 0,
37010 tag: Object.keys(this.tags).map(function (k) {
37020 if (changeset_id) r.node['@changeset'] = changeset_id;
37023 asGeoJSON: function asGeoJSON() {
37026 coordinates: this.loc
37031 function actionCircularize(wayId, projection, maxAngle) {
37032 maxAngle = (maxAngle || 20) * Math.PI / 180;
37034 var action = function action(graph, t) {
37035 if (t === null || !isFinite(t)) t = 1;
37036 t = Math.min(Math.max(+t, 0), 1);
37037 var way = graph.entity(wayId);
37038 var origNodes = {};
37039 graph.childNodes(way).forEach(function (node) {
37040 if (!origNodes[node.id]) origNodes[node.id] = node;
37043 if (!way.isConvex(graph)) {
37044 graph = action.makeConvex(graph);
37047 var nodes = utilArrayUniq(graph.childNodes(way));
37048 var keyNodes = nodes.filter(function (n) {
37049 return graph.parentWays(n).length !== 1;
37051 var points = nodes.map(function (n) {
37052 return projection(n.loc);
37054 var keyPoints = keyNodes.map(function (n) {
37055 return projection(n.loc);
37057 var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
37058 var radius = d3_median(points, function (p) {
37059 return geoVecLength(centroid, p);
37061 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37062 var ids, i, j, k; // we need at least two key nodes for the algorithm to work
37064 if (!keyNodes.length) {
37065 keyNodes = [nodes[0]];
37066 keyPoints = [points[0]];
37069 if (keyNodes.length === 1) {
37070 var index = nodes.indexOf(keyNodes[0]);
37071 var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
37072 keyNodes.push(nodes[oppositeIndex]);
37073 keyPoints.push(points[oppositeIndex]);
37074 } // key points and nodes are those connected to the ways,
37075 // they are projected onto the circle, in between nodes are moved
37076 // to constant intervals between key nodes, extra in between nodes are
37077 // added if necessary.
37080 for (i = 0; i < keyPoints.length; i++) {
37081 var nextKeyNodeIndex = (i + 1) % keyNodes.length;
37082 var startNode = keyNodes[i];
37083 var endNode = keyNodes[nextKeyNodeIndex];
37084 var startNodeIndex = nodes.indexOf(startNode);
37085 var endNodeIndex = nodes.indexOf(endNode);
37086 var numberNewPoints = -1;
37087 var indexRange = endNodeIndex - startNodeIndex;
37088 var nearNodes = {};
37089 var inBetweenNodes = [];
37090 var startAngle, endAngle, totalAngle, eachAngle;
37091 var angle, loc, node, origNode;
37093 if (indexRange < 0) {
37094 indexRange += nodes.length;
37095 } // position this key node
37098 var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
37099 keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
37100 loc = projection.invert(keyPoints[i]);
37101 node = keyNodes[i];
37102 origNode = origNodes[node.id];
37103 node = node.move(geoVecInterp(origNode.loc, loc, t));
37104 graph = graph.replace(node); // figure out the between delta angle we want to match to
37106 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
37107 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
37108 totalAngle = endAngle - startAngle; // detects looping around -pi/pi
37110 if (totalAngle * sign > 0) {
37111 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
37116 eachAngle = totalAngle / (indexRange + numberNewPoints);
37117 } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
37120 for (j = 1; j < indexRange; j++) {
37121 angle = startAngle + j * eachAngle;
37122 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
37123 node = nodes[(j + startNodeIndex) % nodes.length];
37124 origNode = origNodes[node.id];
37125 nearNodes[node.id] = angle;
37126 node = node.move(geoVecInterp(origNode.loc, loc, t));
37127 graph = graph.replace(node);
37128 } // add new in between nodes if necessary
37131 for (j = 0; j < numberNewPoints; j++) {
37132 angle = startAngle + (indexRange + j) * eachAngle;
37133 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
37135 var min = Infinity;
37137 for (var nodeId in nearNodes) {
37138 var nearAngle = nearNodes[nodeId];
37139 var dist = Math.abs(nearAngle - angle);
37143 origNode = origNodes[nodeId];
37148 loc: geoVecInterp(origNode.loc, loc, t)
37150 graph = graph.replace(node);
37151 nodes.splice(endNodeIndex + j, 0, node);
37152 inBetweenNodes.push(node.id);
37153 } // Check for other ways that share these keyNodes..
37154 // If keyNodes are adjacent in both ways,
37155 // we can add inBetweenNodes to that shared way too..
37158 if (indexRange === 1 && inBetweenNodes.length) {
37159 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
37160 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
37161 var wayDirection1 = endIndex1 - startIndex1;
37163 if (wayDirection1 < -1) {
37167 var parentWays = graph.parentWays(keyNodes[i]);
37169 for (j = 0; j < parentWays.length; j++) {
37170 var sharedWay = parentWays[j];
37171 if (sharedWay === way) continue;
37173 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
37174 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
37175 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
37176 var wayDirection2 = endIndex2 - startIndex2;
37177 var insertAt = endIndex2;
37179 if (wayDirection2 < -1) {
37183 if (wayDirection1 !== wayDirection2) {
37184 inBetweenNodes.reverse();
37185 insertAt = startIndex2;
37188 for (k = 0; k < inBetweenNodes.length; k++) {
37189 sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
37192 graph = graph.replace(sharedWay);
37196 } // update the way to have all the new nodes
37199 ids = nodes.map(function (n) {
37206 graph = graph.replace(way);
37210 action.makeConvex = function (graph) {
37211 var way = graph.entity(wayId);
37212 var nodes = utilArrayUniq(graph.childNodes(way));
37213 var points = nodes.map(function (n) {
37214 return projection(n.loc);
37216 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37217 var hull = d3_polygonHull(points);
37218 var i, j; // D3 convex hulls go counterclockwise..
37225 for (i = 0; i < hull.length - 1; i++) {
37226 var startIndex = points.indexOf(hull[i]);
37227 var endIndex = points.indexOf(hull[i + 1]);
37228 var indexRange = endIndex - startIndex;
37230 if (indexRange < 0) {
37231 indexRange += nodes.length;
37232 } // move interior nodes to the surface of the convex hull..
37235 for (j = 1; j < indexRange; j++) {
37236 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
37237 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
37238 graph = graph.replace(node);
37245 action.disabled = function (graph) {
37246 if (!graph.entity(wayId).isClosed()) {
37247 return 'not_closed';
37248 } //disable when already circular
37251 var way = graph.entity(wayId);
37252 var nodes = utilArrayUniq(graph.childNodes(way));
37253 var points = nodes.map(function (n) {
37254 return projection(n.loc);
37256 var hull = d3_polygonHull(points);
37257 var epsilonAngle = Math.PI / 180;
37259 if (hull.length !== points.length || hull.length < 3) {
37263 var centroid = d3_polygonCentroid(points);
37264 var radius = geoVecLengthSquare(centroid, points[0]);
37265 var i, actualPoint; // compare distances between centroid and points
37267 for (i = 0; i < hull.length; i++) {
37268 actualPoint = hull[i];
37269 var actualDist = geoVecLengthSquare(actualPoint, centroid);
37270 var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
37272 if (diff > 0.05 * radius) {
37275 } //check if central angles are smaller than maxAngle
37278 for (i = 0; i < hull.length; i++) {
37279 actualPoint = hull[i];
37280 var nextPoint = hull[(i + 1) % hull.length];
37281 var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
37282 var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
37283 var angle = endAngle - startAngle;
37289 if (angle > Math.PI) {
37290 angle = 2 * Math.PI - angle;
37293 if (angle > maxAngle + epsilonAngle) {
37298 return 'already_circular';
37301 action.transitionable = true;
37305 function actionDeleteWay(wayID) {
37306 function canDeleteNode(node, graph) {
37307 // don't delete nodes still attached to ways or relations
37308 if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
37309 var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
37311 if (geometries.point) return false; // delete if this node only be a vertex
37313 if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
37314 // so only delete if there are no interesting tags
37316 return !node.hasInterestingTags();
37319 var action = function action(graph) {
37320 var way = graph.entity(wayID);
37321 graph.parentRelations(way).forEach(function (parent) {
37322 parent = parent.removeMembersWithID(wayID);
37323 graph = graph.replace(parent);
37325 if (parent.isDegenerate()) {
37326 graph = actionDeleteRelation(parent.id)(graph);
37329 new Set(way.nodes).forEach(function (nodeID) {
37330 graph = graph.replace(way.removeNode(nodeID));
37331 var node = graph.entity(nodeID);
37333 if (canDeleteNode(node, graph)) {
37334 graph = graph.remove(node);
37337 return graph.remove(way);
37343 function actionDeleteMultiple(ids) {
37345 way: actionDeleteWay,
37346 node: actionDeleteNode,
37347 relation: actionDeleteRelation
37350 var action = function action(graph) {
37351 ids.forEach(function (id) {
37352 if (graph.hasEntity(id)) {
37353 // It may have been deleted already.
37354 graph = actions[graph.entity(id).type](id)(graph);
37363 function actionDeleteRelation(relationID, allowUntaggedMembers) {
37364 function canDeleteEntity(entity, graph) {
37365 return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
37368 var action = function action(graph) {
37369 var relation = graph.entity(relationID);
37370 graph.parentRelations(relation).forEach(function (parent) {
37371 parent = parent.removeMembersWithID(relationID);
37372 graph = graph.replace(parent);
37374 if (parent.isDegenerate()) {
37375 graph = actionDeleteRelation(parent.id)(graph);
37378 var memberIDs = utilArrayUniq(relation.members.map(function (m) {
37381 memberIDs.forEach(function (memberID) {
37382 graph = graph.replace(relation.removeMembersWithID(memberID));
37383 var entity = graph.entity(memberID);
37385 if (canDeleteEntity(entity, graph)) {
37386 graph = actionDeleteMultiple([memberID])(graph);
37389 return graph.remove(relation);
37395 function actionDeleteNode(nodeId) {
37396 var action = function action(graph) {
37397 var node = graph.entity(nodeId);
37398 graph.parentWays(node).forEach(function (parent) {
37399 parent = parent.removeNode(nodeId);
37400 graph = graph.replace(parent);
37402 if (parent.isDegenerate()) {
37403 graph = actionDeleteWay(parent.id)(graph);
37406 graph.parentRelations(node).forEach(function (parent) {
37407 parent = parent.removeMembersWithID(nodeId);
37408 graph = graph.replace(parent);
37410 if (parent.isDegenerate()) {
37411 graph = actionDeleteRelation(parent.id)(graph);
37414 return graph.remove(node);
37421 // First choose a node to be the survivor, with preference given
37422 // to an existing (not new) node.
37424 // Tags and relation memberships of of non-surviving nodes are merged
37425 // to the survivor.
37427 // This is the inverse of `iD.actionDisconnect`.
37430 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
37431 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
37434 function actionConnect(nodeIDs) {
37435 var action = function action(graph) {
37439 var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
37441 for (i = 0; i < nodeIDs.length; i++) {
37442 survivor = graph.entity(nodeIDs[i]);
37443 if (survivor.version) break; // found one
37444 } // Replace all non-surviving nodes with the survivor and merge tags.
37447 for (i = 0; i < nodeIDs.length; i++) {
37448 node = graph.entity(nodeIDs[i]);
37449 if (node.id === survivor.id) continue;
37450 parents = graph.parentWays(node);
37452 for (j = 0; j < parents.length; j++) {
37453 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
37456 parents = graph.parentRelations(node);
37458 for (j = 0; j < parents.length; j++) {
37459 graph = graph.replace(parents[j].replaceMember(node, survivor));
37462 survivor = survivor.mergeTags(node.tags);
37463 graph = actionDeleteNode(node.id)(graph);
37466 graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
37468 parents = graph.parentWays(survivor);
37470 for (i = 0; i < parents.length; i++) {
37471 if (parents[i].isDegenerate()) {
37472 graph = actionDeleteWay(parents[i].id)(graph);
37479 action.disabled = function (graph) {
37481 var restrictionIDs = [];
37484 var relations, relation, role;
37485 var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
37487 for (i = 0; i < nodeIDs.length; i++) {
37488 survivor = graph.entity(nodeIDs[i]);
37489 if (survivor.version) break; // found one
37490 } // 1. disable if the nodes being connected have conflicting relation roles
37493 for (i = 0; i < nodeIDs.length; i++) {
37494 node = graph.entity(nodeIDs[i]);
37495 relations = graph.parentRelations(node);
37497 for (j = 0; j < relations.length; j++) {
37498 relation = relations[j];
37499 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
37501 if (relation.hasFromViaTo()) {
37502 restrictionIDs.push(relation.id);
37505 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
37508 seen[relation.id] = role;
37511 } // gather restrictions for parent ways
37514 for (i = 0; i < nodeIDs.length; i++) {
37515 node = graph.entity(nodeIDs[i]);
37516 var parents = graph.parentWays(node);
37518 for (j = 0; j < parents.length; j++) {
37519 var parent = parents[j];
37520 relations = graph.parentRelations(parent);
37522 for (k = 0; k < relations.length; k++) {
37523 relation = relations[k];
37525 if (relation.hasFromViaTo()) {
37526 restrictionIDs.push(relation.id);
37530 } // test restrictions
37533 restrictionIDs = utilArrayUniq(restrictionIDs);
37535 for (i = 0; i < restrictionIDs.length; i++) {
37536 relation = graph.entity(restrictionIDs[i]);
37537 if (!relation.isComplete(graph)) continue;
37538 var memberWays = relation.members.filter(function (m) {
37539 return m.type === 'way';
37540 }).map(function (m) {
37541 return graph.entity(m.id);
37543 memberWays = utilArrayUniq(memberWays);
37544 var f = relation.memberByRole('from');
37545 var t = relation.memberByRole('to');
37546 var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
37547 // (a key node is a node at the junction of ways)
37557 for (j = 0; j < relation.members.length; j++) {
37558 collectNodes(relation.members[j], nodes);
37561 nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
37562 nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
37563 var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
37564 nodes.from = nodes.from.filter(filter);
37565 nodes.via = nodes.via.filter(filter);
37566 nodes.to = nodes.to.filter(filter);
37567 var connectFrom = false;
37568 var connectVia = false;
37569 var connectTo = false;
37570 var connectKeyFrom = false;
37571 var connectKeyTo = false;
37573 for (j = 0; j < nodeIDs.length; j++) {
37574 var n = nodeIDs[j];
37576 if (nodes.from.indexOf(n) !== -1) {
37577 connectFrom = true;
37580 if (nodes.via.indexOf(n) !== -1) {
37584 if (nodes.to.indexOf(n) !== -1) {
37588 if (nodes.keyfrom.indexOf(n) !== -1) {
37589 connectKeyFrom = true;
37592 if (nodes.keyto.indexOf(n) !== -1) {
37593 connectKeyTo = true;
37597 if (connectFrom && connectTo && !isUturn) {
37598 return 'restriction';
37601 if (connectFrom && connectVia) {
37602 return 'restriction';
37605 if (connectTo && connectVia) {
37606 return 'restriction';
37607 } // connecting to a key node -
37608 // if both nodes are on a member way (i.e. part of the turn restriction),
37609 // the connecting node must be adjacent to the key node.
37612 if (connectKeyFrom || connectKeyTo) {
37613 if (nodeIDs.length !== 2) {
37614 return 'restriction';
37620 for (j = 0; j < memberWays.length; j++) {
37621 way = memberWays[j];
37623 if (way.contains(nodeIDs[0])) {
37627 if (way.contains(nodeIDs[1])) {
37633 // both nodes are part of the restriction
37636 for (j = 0; j < memberWays.length; j++) {
37637 way = memberWays[j];
37639 if (way.areAdjacent(n0, n1)) {
37646 return 'restriction';
37649 } // 2b. disable if nodes being connected will destroy a member way in a restriction
37650 // (to test, make a copy and try actually connecting the nodes)
37653 for (j = 0; j < memberWays.length; j++) {
37654 way = memberWays[j].update({}); // make copy
37656 for (k = 0; k < nodeIDs.length; k++) {
37657 if (nodeIDs[k] === survivor.id) continue;
37659 if (way.areAdjacent(nodeIDs[k], survivor.id)) {
37660 way = way.removeNode(nodeIDs[k]);
37662 way = way.replaceNode(nodeIDs[k], survivor.id);
37666 if (way.isDegenerate()) {
37667 return 'restriction';
37672 return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
37674 function hasDuplicates(n, i, arr) {
37675 return arr.indexOf(n) !== arr.lastIndexOf(n);
37678 function keyNodeFilter(froms, tos) {
37679 return function (n) {
37680 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
37684 function collectNodes(member, collection) {
37685 var entity = graph.hasEntity(member.id);
37686 if (!entity) return;
37687 var role = member.role || '';
37689 if (!collection[role]) {
37690 collection[role] = [];
37693 if (member.type === 'node') {
37694 collection[role].push(member.id);
37696 if (role === 'via') {
37697 collection.keyfrom.push(member.id);
37698 collection.keyto.push(member.id);
37700 } else if (member.type === 'way') {
37701 collection[role].push.apply(collection[role], entity.nodes);
37703 if (role === 'from' || role === 'via') {
37704 collection.keyfrom.push(entity.first());
37705 collection.keyfrom.push(entity.last());
37708 if (role === 'to' || role === 'via') {
37709 collection.keyto.push(entity.first());
37710 collection.keyto.push(entity.last());
37719 function actionCopyEntities(ids, fromGraph) {
37722 var action = function action(graph) {
37723 ids.forEach(function (id) {
37724 fromGraph.entity(id).copy(fromGraph, _copies);
37727 for (var id in _copies) {
37728 graph = graph.replace(_copies[id]);
37734 action.copies = function () {
37741 function actionDeleteMember(relationId, memberIndex) {
37742 return function (graph) {
37743 var relation = graph.entity(relationId).removeMember(memberIndex);
37744 graph = graph.replace(relation);
37746 if (relation.isDegenerate()) {
37747 graph = actionDeleteRelation(relation.id)(graph);
37754 function actionDiscardTags(difference, discardTags) {
37755 discardTags = discardTags || {};
37756 return function (graph) {
37757 difference.modified().forEach(checkTags);
37758 difference.created().forEach(checkTags);
37761 function checkTags(entity) {
37762 var keys = Object.keys(entity.tags);
37763 var didDiscard = false;
37766 for (var i = 0; i < keys.length; i++) {
37769 if (discardTags[k] || !entity.tags[k]) {
37772 tags[k] = entity.tags[k];
37777 graph = graph.replace(entity.update({
37786 // Optionally, disconnect only the given ways.
37788 // For testing convenience, accepts an ID to assign to the (first) new node.
37789 // Normally, this will be undefined and the way will automatically
37790 // be assigned a new ID.
37792 // This is the inverse of `iD.actionConnect`.
37795 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
37796 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
37799 function actionDisconnect(nodeId, newNodeId) {
37802 var action = function action(graph) {
37803 var node = graph.entity(nodeId);
37804 var connections = action.connections(graph);
37805 connections.forEach(function (connection) {
37806 var way = graph.entity(connection.wayID);
37807 var newNode = osmNode({
37812 graph = graph.replace(newNode);
37814 if (connection.index === 0 && way.isArea()) {
37815 // replace shared node with shared node..
37816 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
37817 } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
37818 // replace closing node with new new node..
37819 graph = graph.replace(way.unclose().addNode(newNode.id));
37821 // replace shared node with multiple new nodes..
37822 graph = graph.replace(way.updateNode(newNode.id, connection.index));
37828 action.connections = function (graph) {
37829 var candidates = [];
37830 var keeping = false;
37831 var parentWays = graph.parentWays(graph.entity(nodeId));
37834 for (var i = 0; i < parentWays.length; i++) {
37835 way = parentWays[i];
37837 if (wayIds && wayIds.indexOf(way.id) === -1) {
37842 if (way.isArea() && way.nodes[0] === nodeId) {
37848 for (var j = 0; j < way.nodes.length; j++) {
37849 waynode = way.nodes[j];
37851 if (waynode === nodeId) {
37852 if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
37865 return keeping ? candidates : candidates.slice(1);
37868 action.disabled = function (graph) {
37869 var connections = action.connections(graph);
37870 if (connections.length === 0) return 'not_connected';
37871 var parentWays = graph.parentWays(graph.entity(nodeId));
37872 var seenRelationIds = {};
37873 var sharedRelation;
37874 parentWays.forEach(function (way) {
37875 var relations = graph.parentRelations(way);
37876 relations.forEach(function (relation) {
37877 if (relation.id in seenRelationIds) {
37879 if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
37880 sharedRelation = relation;
37883 sharedRelation = relation;
37886 seenRelationIds[relation.id] = way.id;
37890 if (sharedRelation) return 'relation';
37893 action.limitWays = function (val) {
37894 if (!arguments.length) return wayIds;
37902 function actionExtract(entityID, projection) {
37903 var extractedNodeID;
37905 var action = function action(graph) {
37906 var entity = graph.entity(entityID);
37908 if (entity.type === 'node') {
37909 return extractFromNode(entity, graph);
37912 return extractFromWayOrRelation(entity, graph);
37915 function extractFromNode(node, graph) {
37916 extractedNodeID = node.id; // Create a new node to replace the one we will detach
37918 var replacement = osmNode({
37921 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
37923 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
37924 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
37925 }, graph); // Process any relations too
37927 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
37928 return accGraph.replace(parentRel.replaceMember(node, replacement));
37932 function extractFromWayOrRelation(entity, graph) {
37933 var fromGeometry = entity.geometry(graph);
37934 var keysToCopyAndRetain = ['source', 'wheelchair'];
37935 var keysToRetain = ['area'];
37936 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
37937 var extractedLoc = d3_geoPath(projection).centroid(entity.asGeoJSON(graph));
37938 extractedLoc = extractedLoc && projection.invert(extractedLoc);
37940 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
37941 extractedLoc = entity.extent(graph).center();
37944 var indoorAreaValues = {
37951 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
37952 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
37953 var entityTags = Object.assign({}, entity.tags); // shallow copy
37955 var pointTags = {};
37957 for (var key in entityTags) {
37958 if (entity.type === 'relation' && key === 'type') {
37962 if (keysToRetain.indexOf(key) !== -1) {
37967 // don't transfer building-related tags
37968 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
37969 } // leave `indoor` tag on the area
37972 if (isIndoorArea && key === 'indoor') {
37974 } // copy the tag from the entity to the point
37977 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
37979 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
37981 } else if (isIndoorArea && key === 'level') {
37982 // leave `level` on both features
37984 } // remove the tag from the entity
37987 delete entityTags[key];
37990 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
37991 // ensure that areas keep area geometry
37992 entityTags.area = 'yes';
37995 var replacement = osmNode({
37999 graph = graph.replace(replacement);
38000 extractedNodeID = replacement.id;
38001 return graph.replace(entity.update({
38006 action.getExtractedNodeID = function () {
38007 return extractedNodeID;
38014 // This is the inverse of `iD.actionSplit`.
38017 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
38018 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
38021 function actionJoin(ids) {
38022 function groupEntitiesByGeometry(graph) {
38023 var entities = ids.map(function (id) {
38024 return graph.entity(id);
38026 return Object.assign({
38028 }, utilArrayGroupBy(entities, function (entity) {
38029 return entity.geometry(graph);
38033 var action = function action(graph) {
38034 var ways = ids.map(graph.entity, graph);
38035 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
38036 // sort them first so they establish the overall order - #6033
38038 ways.sort(function (a, b) {
38039 var aSided = a.isSided();
38040 var bSided = b.isSided();
38041 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
38042 }); // Prefer to keep an existing way.
38044 for (var i = 0; i < ways.length; i++) {
38045 if (!ways[i].isNew()) {
38046 survivorID = ways[i].id;
38051 var sequences = osmJoinWays(ways, graph);
38052 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
38053 // `joined.actions` property will contain any actions we need to apply.
38055 graph = sequences.actions.reduce(function (g, action) {
38058 var survivor = graph.entity(survivorID);
38059 survivor = survivor.update({
38060 nodes: joined.nodes.map(function (n) {
38064 graph = graph.replace(survivor);
38065 joined.forEach(function (way) {
38066 if (way.id === survivorID) return;
38067 graph.parentRelations(way).forEach(function (parent) {
38068 graph = graph.replace(parent.replaceMember(way, survivor));
38070 survivor = survivor.mergeTags(way.tags);
38071 graph = graph.replace(survivor);
38072 graph = actionDeleteWay(way.id)(graph);
38073 }); // Finds if the join created a single-member multipolygon,
38074 // and if so turns it into a basic area instead
38076 function checkForSimpleMultipolygon() {
38077 if (!survivor.isClosed()) return;
38078 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
38079 // find multipolygons where the survivor is the only member
38080 return multipolygon.members.length === 1;
38081 }); // skip if this is the single member of multiple multipolygons
38083 if (multipolygons.length !== 1) return;
38084 var multipolygon = multipolygons[0];
38086 for (var key in survivor.tags) {
38087 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
38088 multipolygon.tags[key] !== survivor.tags[key]) return;
38091 survivor = survivor.mergeTags(multipolygon.tags);
38092 graph = graph.replace(survivor);
38093 graph = actionDeleteRelation(multipolygon.id, true
38094 /* allow untagged members */
38096 var tags = Object.assign({}, survivor.tags);
38098 if (survivor.geometry(graph) !== 'area') {
38099 // ensure the feature persists as an area
38103 delete tags.type; // remove type=multipolygon
38105 survivor = survivor.update({
38108 graph = graph.replace(survivor);
38111 checkForSimpleMultipolygon();
38113 }; // Returns the number of nodes the resultant way is expected to have
38116 action.resultingWayNodesLength = function (graph) {
38117 return ids.reduce(function (count, id) {
38118 return count + graph.entity(id).nodes.length;
38119 }, 0) - ids.length - 1;
38122 action.disabled = function (graph) {
38123 var geometries = groupEntitiesByGeometry(graph);
38125 if (ids.length < 2 || ids.length !== geometries.line.length) {
38126 return 'not_eligible';
38129 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
38131 if (joined.length > 1) {
38132 return 'not_adjacent';
38133 } // Loop through all combinations of path-pairs
38134 // to check potential intersections between all pairs
38137 for (var i = 0; i < ids.length - 1; i++) {
38138 for (var j = i + 1; j < ids.length; j++) {
38139 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
38142 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
38145 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
38146 // each other/the line, as opposed to crossing it
38148 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
38149 return n.loc.toString();
38150 }), intersections.map(function (n) {
38151 return n.toString();
38154 if (common.length !== intersections.length) {
38155 return 'paths_intersect';
38160 var nodeIds = joined[0].nodes.map(function (n) {
38165 var conflicting = false;
38166 joined[0].forEach(function (way) {
38167 var parents = graph.parentRelations(way);
38168 parents.forEach(function (parent) {
38169 if (parent.isRestriction() && parent.members.some(function (m) {
38170 return nodeIds.indexOf(m.id) >= 0;
38176 for (var k in way.tags) {
38177 if (!(k in tags)) {
38178 tags[k] = way.tags[k];
38179 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
38180 conflicting = true;
38186 return 'restriction';
38190 return 'conflicting_tags';
38197 function actionMerge(ids) {
38198 function groupEntitiesByGeometry(graph) {
38199 var entities = ids.map(function (id) {
38200 return graph.entity(id);
38202 return Object.assign({
38207 }, utilArrayGroupBy(entities, function (entity) {
38208 return entity.geometry(graph);
38212 var action = function action(graph) {
38213 var geometries = groupEntitiesByGeometry(graph);
38214 var target = geometries.area[0] || geometries.line[0];
38215 var points = geometries.point;
38216 points.forEach(function (point) {
38217 target = target.mergeTags(point.tags);
38218 graph = graph.replace(target);
38219 graph.parentRelations(point).forEach(function (parent) {
38220 graph = graph.replace(parent.replaceMember(point, target));
38222 var nodes = utilArrayUniq(graph.childNodes(target));
38223 var removeNode = point;
38225 for (var i = 0; i < nodes.length; i++) {
38226 var node = nodes[i];
38228 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
38230 } // Found an uninteresting child node on the target way.
38231 // Move orig point into its place to preserve point's history. #3683
38234 graph = graph.replace(point.update({
38238 target = target.replaceNode(node.id, point.id);
38239 graph = graph.replace(target);
38244 graph = graph.remove(removeNode);
38247 if (target.tags.area === 'yes') {
38248 var tags = Object.assign({}, target.tags); // shallow copy
38252 if (osmTagSuggestingArea(tags)) {
38253 // remove the `area` tag if area geometry is now implied - #3851
38254 target = target.update({
38257 graph = graph.replace(target);
38264 action.disabled = function (graph) {
38265 var geometries = groupEntitiesByGeometry(graph);
38267 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
38268 return 'not_eligible';
38276 // 1. move all the nodes to a common location
38277 // 2. `actionConnect` them
38279 function actionMergeNodes(nodeIDs, loc) {
38280 // If there is a single "interesting" node, use that as the location.
38281 // Otherwise return the average location of all the nodes.
38282 function chooseLoc(graph) {
38283 if (!nodeIDs.length) return null;
38285 var interestingCount = 0;
38286 var interestingLoc;
38288 for (var i = 0; i < nodeIDs.length; i++) {
38289 var node = graph.entity(nodeIDs[i]);
38291 if (node.hasInterestingTags()) {
38292 interestingLoc = ++interestingCount === 1 ? node.loc : null;
38295 sum = geoVecAdd(sum, node.loc);
38298 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
38301 var action = function action(graph) {
38302 if (nodeIDs.length < 2) return graph;
38306 toLoc = chooseLoc(graph);
38309 for (var i = 0; i < nodeIDs.length; i++) {
38310 var node = graph.entity(nodeIDs[i]);
38312 if (node.loc !== toLoc) {
38313 graph = graph.replace(node.move(toLoc));
38317 return actionConnect(nodeIDs)(graph);
38320 action.disabled = function (graph) {
38321 if (nodeIDs.length < 2) return 'not_eligible';
38323 for (var i = 0; i < nodeIDs.length; i++) {
38324 var entity = graph.entity(nodeIDs[i]);
38325 if (entity.type !== 'node') return 'not_eligible';
38328 return actionConnect(nodeIDs).disabled(graph);
38334 function osmChangeset() {
38335 if (!(this instanceof osmChangeset)) {
38336 return new osmChangeset().initialize(arguments);
38337 } else if (arguments.length) {
38338 this.initialize(arguments);
38341 osmEntity.changeset = osmChangeset;
38342 osmChangeset.prototype = Object.create(osmEntity.prototype);
38343 Object.assign(osmChangeset.prototype, {
38345 extent: function extent() {
38346 return new geoExtent();
38348 geometry: function geometry() {
38349 return 'changeset';
38351 asJXON: function asJXON() {
38355 tag: Object.keys(this.tags).map(function (k) {
38367 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
38368 // XML. Returns a string.
38369 osmChangeJXON: function osmChangeJXON(changes) {
38370 var changeset_id = this.id;
38372 function nest(x, order) {
38375 for (var i = 0; i < x.length; i++) {
38376 var tagName = Object.keys(x[i])[0];
38377 if (!groups[tagName]) groups[tagName] = [];
38378 groups[tagName].push(x[i][tagName]);
38382 order.forEach(function (o) {
38383 if (groups[o]) ordered[o] = groups[o];
38386 } // sort relations in a changeset by dependencies
38389 function sort(changes) {
38390 // find a referenced relation in the current changeset
38391 function resolve(item) {
38392 return relations.find(function (relation) {
38393 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
38395 } // a new item is an item that has not been already processed
38398 function isNew(item) {
38399 return !sorted[item['@id']] && !processing.find(function (proc) {
38400 return proc['@id'] === item['@id'];
38404 var processing = [];
38406 var relations = changes.relation;
38407 if (!relations) return changes;
38409 for (var i = 0; i < relations.length; i++) {
38410 var relation = relations[i]; // skip relation if already sorted
38412 if (!sorted[relation['@id']]) {
38413 processing.push(relation);
38416 while (processing.length > 0) {
38417 var next = processing[0],
38418 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
38420 if (deps.length === 0) {
38421 sorted[next['@id']] = next;
38422 processing.shift();
38424 processing = deps.concat(processing);
38429 changes.relation = Object.values(sorted);
38433 function rep(entity) {
38434 return entity.asJXON(changeset_id);
38440 '@generator': 'iD',
38441 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
38442 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
38443 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
38449 asGeoJSON: function asGeoJSON() {
38454 function osmNote() {
38455 if (!(this instanceof osmNote)) {
38456 return new osmNote().initialize(arguments);
38457 } else if (arguments.length) {
38458 this.initialize(arguments);
38462 osmNote.id = function () {
38463 return osmNote.id.next--;
38466 osmNote.id.next = -1;
38467 Object.assign(osmNote.prototype, {
38469 initialize: function initialize(sources) {
38470 for (var i = 0; i < sources.length; ++i) {
38471 var source = sources[i];
38473 for (var prop in source) {
38474 if (Object.prototype.hasOwnProperty.call(source, prop)) {
38475 if (source[prop] === undefined) {
38478 this[prop] = source[prop];
38485 this.id = osmNote.id().toString();
38490 extent: function extent() {
38491 return new geoExtent(this.loc);
38493 update: function update(attrs) {
38494 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
38496 isNew: function isNew() {
38497 return this.id < 0;
38499 move: function move(loc) {
38500 return this.update({
38506 function osmRelation() {
38507 if (!(this instanceof osmRelation)) {
38508 return new osmRelation().initialize(arguments);
38509 } else if (arguments.length) {
38510 this.initialize(arguments);
38513 osmEntity.relation = osmRelation;
38514 osmRelation.prototype = Object.create(osmEntity.prototype);
38516 osmRelation.creationOrder = function (a, b) {
38517 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
38518 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
38519 if (aId < 0 || bId < 0) return aId - bId;
38523 Object.assign(osmRelation.prototype, {
38526 copy: function copy(resolver, copies) {
38527 if (copies[this.id]) return copies[this.id];
38528 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
38529 var members = this.members.map(function (member) {
38530 return Object.assign({}, member, {
38531 id: resolver.entity(member.id).copy(resolver, copies).id
38534 copy = copy.update({
38537 copies[this.id] = copy;
38540 extent: function extent(resolver, memo) {
38541 return resolver["transient"](this, 'extent', function () {
38542 if (memo && memo[this.id]) return geoExtent();
38544 memo[this.id] = true;
38545 var extent = geoExtent();
38547 for (var i = 0; i < this.members.length; i++) {
38548 var member = resolver.hasEntity(this.members[i].id);
38551 extent._extend(member.extent(resolver, memo));
38558 geometry: function geometry(graph) {
38559 return graph["transient"](this, 'geometry', function () {
38560 return this.isMultipolygon() ? 'area' : 'relation';
38563 isDegenerate: function isDegenerate() {
38564 return this.members.length === 0;
38566 // Return an array of members, each extended with an 'index' property whose value
38567 // is the member index.
38568 indexedMembers: function indexedMembers() {
38569 var result = new Array(this.members.length);
38571 for (var i = 0; i < this.members.length; i++) {
38572 result[i] = Object.assign({}, this.members[i], {
38579 // Return the first member with the given role. A copy of the member object
38580 // is returned, extended with an 'index' property whose value is the member index.
38581 memberByRole: function memberByRole(role) {
38582 for (var i = 0; i < this.members.length; i++) {
38583 if (this.members[i].role === role) {
38584 return Object.assign({}, this.members[i], {
38590 // Same as memberByRole, but returns all members with the given role
38591 membersByRole: function membersByRole(role) {
38594 for (var i = 0; i < this.members.length; i++) {
38595 if (this.members[i].role === role) {
38596 result.push(Object.assign({}, this.members[i], {
38604 // Return the first member with the given id. A copy of the member object
38605 // is returned, extended with an 'index' property whose value is the member index.
38606 memberById: function memberById(id) {
38607 for (var i = 0; i < this.members.length; i++) {
38608 if (this.members[i].id === id) {
38609 return Object.assign({}, this.members[i], {
38615 // Return the first member with the given id and role. A copy of the member object
38616 // is returned, extended with an 'index' property whose value is the member index.
38617 memberByIdAndRole: function memberByIdAndRole(id, role) {
38618 for (var i = 0; i < this.members.length; i++) {
38619 if (this.members[i].id === id && this.members[i].role === role) {
38620 return Object.assign({}, this.members[i], {
38626 addMember: function addMember(member, index) {
38627 var members = this.members.slice();
38628 members.splice(index === undefined ? members.length : index, 0, member);
38629 return this.update({
38633 updateMember: function updateMember(member, index) {
38634 var members = this.members.slice();
38635 members.splice(index, 1, Object.assign({}, members[index], member));
38636 return this.update({
38640 removeMember: function removeMember(index) {
38641 var members = this.members.slice();
38642 members.splice(index, 1);
38643 return this.update({
38647 removeMembersWithID: function removeMembersWithID(id) {
38648 var members = this.members.filter(function (m) {
38649 return m.id !== id;
38651 return this.update({
38655 moveMember: function moveMember(fromIndex, toIndex) {
38656 var members = this.members.slice();
38657 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
38658 return this.update({
38662 // Wherever a member appears with id `needle.id`, replace it with a member
38663 // with id `replacement.id`, type `replacement.type`, and the original role,
38664 // By default, adding a duplicate member (by id and role) is prevented.
38665 // Return an updated relation.
38666 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
38667 if (!this.memberById(needle.id)) return this;
38670 for (var i = 0; i < this.members.length; i++) {
38671 var member = this.members[i];
38673 if (member.id !== needle.id) {
38674 members.push(member);
38675 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
38677 id: replacement.id,
38678 type: replacement.type,
38684 return this.update({
38688 asJXON: function asJXON(changeset_id) {
38691 '@id': this.osmId(),
38692 '@version': this.version || 0,
38693 member: this.members.map(function (member) {
38698 ref: osmEntity.id.toOSM(member.id)
38702 tag: Object.keys(this.tags).map(function (k) {
38713 if (changeset_id) {
38714 r.relation['@changeset'] = changeset_id;
38719 asGeoJSON: function asGeoJSON(resolver) {
38720 return resolver["transient"](this, 'GeoJSON', function () {
38721 if (this.isMultipolygon()) {
38723 type: 'MultiPolygon',
38724 coordinates: this.multipolygon(resolver)
38728 type: 'FeatureCollection',
38729 properties: this.tags,
38730 features: this.members.map(function (member) {
38731 return Object.assign({
38733 }, resolver.entity(member.id).asGeoJSON(resolver));
38739 area: function area(resolver) {
38740 return resolver["transient"](this, 'area', function () {
38741 return d3_geoArea(this.asGeoJSON(resolver));
38744 isMultipolygon: function isMultipolygon() {
38745 return this.tags.type === 'multipolygon';
38747 isComplete: function isComplete(resolver) {
38748 for (var i = 0; i < this.members.length; i++) {
38749 if (!resolver.hasEntity(this.members[i].id)) {
38756 hasFromViaTo: function hasFromViaTo() {
38757 return this.members.some(function (m) {
38758 return m.role === 'from';
38759 }) && this.members.some(function (m) {
38760 return m.role === 'via';
38761 }) && this.members.some(function (m) {
38762 return m.role === 'to';
38765 isRestriction: function isRestriction() {
38766 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
38768 isValidRestriction: function isValidRestriction() {
38769 if (!this.isRestriction()) return false;
38770 var froms = this.members.filter(function (m) {
38771 return m.role === 'from';
38773 var vias = this.members.filter(function (m) {
38774 return m.role === 'via';
38776 var tos = this.members.filter(function (m) {
38777 return m.role === 'to';
38779 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
38780 if (froms.some(function (m) {
38781 return m.type !== 'way';
38783 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
38784 if (tos.some(function (m) {
38785 return m.type !== 'way';
38787 if (vias.length === 0) return false;
38788 if (vias.length > 1 && vias.some(function (m) {
38789 return m.type !== 'way';
38793 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
38794 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
38796 // This corresponds to the structure needed for rendering a multipolygon path using a
38797 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
38799 // In the case of invalid geometries, this function will still return a result which
38800 // includes the nodes of all way members, but some Nds may be unclosed and some inner
38801 // rings not matched with the intended outer ring.
38803 multipolygon: function multipolygon(resolver) {
38804 var outers = this.members.filter(function (m) {
38805 return 'outer' === (m.role || 'outer');
38807 var inners = this.members.filter(function (m) {
38808 return 'inner' === m.role;
38810 outers = osmJoinWays(outers, resolver);
38811 inners = osmJoinWays(inners, resolver);
38813 var sequenceToLineString = function sequenceToLineString(sequence) {
38814 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
38815 // close unclosed parts to ensure correct area rendering - #2945
38816 sequence.nodes.push(sequence.nodes[0]);
38819 return sequence.nodes.map(function (node) {
38824 outers = outers.map(sequenceToLineString);
38825 inners = inners.map(sequenceToLineString);
38826 var result = outers.map(function (o) {
38827 // Heuristic for detecting counterclockwise winding order. Assumes
38828 // that OpenStreetMap polygons are not hemisphere-spanning.
38829 return [d3_geoArea({
38832 }) > 2 * Math.PI ? o.reverse() : o];
38835 function findOuter(inner) {
38838 for (o = 0; o < outers.length; o++) {
38841 if (geoPolygonContainsPolygon(outer, inner)) {
38846 for (o = 0; o < outers.length; o++) {
38849 if (geoPolygonIntersectsPolygon(outer, inner, false)) {
38855 for (var i = 0; i < inners.length; i++) {
38856 var inner = inners[i];
38860 coordinates: [inner]
38861 }) < 2 * Math.PI) {
38862 inner = inner.reverse();
38865 var o = findOuter(inners[i]);
38867 if (o !== undefined) {
38868 result[o].push(inners[i]);
38870 result.push([inners[i]]); // Invalid geometry
38878 var QAItem = /*#__PURE__*/function () {
38879 function QAItem(loc, service, itemType, id, props) {
38880 _classCallCheck$1(this, QAItem);
38882 // Store required properties
38884 this.service = service.title;
38885 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
38887 this.id = id ? id : "".concat(QAItem.id());
38888 this.update(props); // Some QA services have marker icons to differentiate issues
38890 if (service && typeof service.getIcon === 'function') {
38891 this.icon = service.getIcon(itemType);
38895 _createClass$1(QAItem, [{
38897 value: function update(props) {
38900 // You can't override this initial information
38901 var loc = this.loc,
38902 service = this.service,
38903 itemType = this.itemType,
38905 Object.keys(props).forEach(function (prop) {
38906 return _this[prop] = props[prop];
38909 this.service = service;
38910 this.itemType = itemType;
38913 } // Generic handling for newly created QAItems
38917 value: function id() {
38918 return this.nextId--;
38924 QAItem.nextId = -1;
38927 // Optionally, split only the given ways, if multiple ways share
38930 // This is the inverse of `iD.actionJoin`.
38932 // For testing convenience, accepts an ID to assign to the new way.
38933 // Normally, this will be undefined and the way will automatically
38934 // be assigned a new ID.
38937 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
38940 function actionSplit(nodeIds, newWayIds) {
38941 // accept single ID for backwards-compatiblity
38942 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
38944 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
38947 var _keepHistoryOn = 'longest'; // 'longest', 'first'
38948 // The IDs of the ways actually created by running this action
38950 var _createdWayIDs = [];
38952 function dist(graph, nA, nB) {
38953 var locA = graph.entity(nA).loc;
38954 var locB = graph.entity(nB).loc;
38955 var epsilon = 1e-6;
38956 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
38957 } // If the way is closed, we need to search for a partner node
38958 // to split the way at.
38960 // The following looks for a node that is both far away from
38961 // the initial node in terms of way segment length and nearby
38962 // in terms of beeline-distance. This assures that areas get
38963 // split on the most "natural" points (independent of the number
38965 // For example: bone-shaped areas get split across their waist
38966 // line, circles across the diameter.
38969 function splitArea(nodes, idxA, graph) {
38970 var lengths = new Array(nodes.length);
38976 function wrap(index) {
38977 return utilWrap(index, nodes.length);
38978 } // calculate lengths
38983 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
38984 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
38985 lengths[i] = length;
38990 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
38991 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
38993 if (length < lengths[i]) {
38994 lengths[i] = length;
38996 } // determine best opposite node to split
38999 for (i = 0; i < nodes.length; i++) {
39000 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
39011 function totalLengthBetweenNodes(graph, nodes) {
39012 var totalLength = 0;
39014 for (var i = 0; i < nodes.length - 1; i++) {
39015 totalLength += dist(graph, nodes[i], nodes[i + 1]);
39018 return totalLength;
39021 function split(graph, nodeId, wayA, newWayId) {
39022 var wayB = osmWay({
39025 }); // `wayB` is the NEW way
39027 var origNodes = wayA.nodes.slice();
39030 var isArea = wayA.isArea();
39031 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
39033 if (wayA.isClosed()) {
39034 var nodes = wayA.nodes.slice(0, -1);
39035 var idxA = nodes.indexOf(nodeId);
39036 var idxB = splitArea(nodes, idxA, graph);
39039 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
39040 nodesB = nodes.slice(idxB, idxA + 1);
39042 nodesA = nodes.slice(idxA, idxB + 1);
39043 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
39046 var idx = wayA.nodes.indexOf(nodeId, 1);
39047 nodesA = wayA.nodes.slice(0, idx + 1);
39048 nodesB = wayA.nodes.slice(idx);
39051 var lengthA = totalLengthBetweenNodes(graph, nodesA);
39052 var lengthB = totalLengthBetweenNodes(graph, nodesB);
39054 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
39055 // keep the history on the longer way, regardless of the node count
39056 wayA = wayA.update({
39059 wayB = wayB.update({
39062 var temp = lengthA;
39066 wayA = wayA.update({
39069 wayB = wayB.update({
39074 if (wayA.tags.step_count) {
39075 // divide up the the step count proportionally between the two ways
39076 var stepCount = parseFloat(wayA.tags.step_count);
39078 if (stepCount && // ensure a number
39079 isFinite(stepCount) && // ensure positive
39080 stepCount > 0 && // ensure integer
39081 Math.round(stepCount) === stepCount) {
39082 var tagsA = Object.assign({}, wayA.tags);
39083 var tagsB = Object.assign({}, wayB.tags);
39084 var ratioA = lengthA / (lengthA + lengthB);
39085 var countA = Math.round(stepCount * ratioA);
39086 tagsA.step_count = countA.toString();
39087 tagsB.step_count = (stepCount - countA).toString();
39088 wayA = wayA.update({
39091 wayB = wayB.update({
39097 graph = graph.replace(wayA);
39098 graph = graph.replace(wayB);
39099 graph.parentRelations(wayA).forEach(function (relation) {
39100 var member; // Turn restrictions - make sure:
39101 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
39102 // (whichever one is connected to the VIA node/ways)
39103 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
39105 if (relation.hasFromViaTo()) {
39106 var f = relation.memberByRole('from');
39107 var v = relation.membersByRole('via');
39108 var t = relation.memberByRole('to');
39109 var i; // 1. split a FROM/TO
39111 if (f.id === wayA.id || t.id === wayA.id) {
39114 if (v.length === 1 && v[0].type === 'node') {
39116 keepB = wayB.contains(v[0].id);
39118 // check via way(s)
39119 for (i = 0; i < v.length; i++) {
39120 if (v[i].type === 'way') {
39121 var wayVia = graph.hasEntity(v[i].id);
39123 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
39132 relation = relation.replaceMember(wayA, wayB);
39133 graph = graph.replace(relation);
39134 } // 2. split a VIA
39137 for (i = 0; i < v.length; i++) {
39138 if (v[i].type === 'way' && v[i].id === wayA.id) {
39144 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
39148 } // All other relations (Routes, Multipolygons, etc):
39149 // 1. Both `wayA` and `wayB` remain in the relation
39150 // 2. But must be inserted as a pair (see `actionAddMember` for details)
39153 if (relation === isOuter) {
39154 graph = graph.replace(relation.mergeTags(wayA.tags));
39155 graph = graph.replace(wayA.update({
39158 graph = graph.replace(wayB.update({
39166 role: relation.memberById(wayA.id).role
39169 originalID: wayA.id,
39170 insertedID: wayB.id,
39173 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
39177 if (!isOuter && isArea) {
39178 var multipolygon = osmRelation({
39179 tags: Object.assign({}, wayA.tags, {
39180 type: 'multipolygon'
39192 graph = graph.replace(multipolygon);
39193 graph = graph.replace(wayA.update({
39196 graph = graph.replace(wayB.update({
39201 _createdWayIDs.push(wayB.id);
39206 var action = function action(graph) {
39207 _createdWayIDs = [];
39208 var newWayIndex = 0;
39210 for (var i = 0; i < nodeIds.length; i++) {
39211 var nodeId = nodeIds[i];
39212 var candidates = action.waysForNode(nodeId, graph);
39214 for (var j = 0; j < candidates.length; j++) {
39215 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
39223 action.getCreatedWayIDs = function () {
39224 return _createdWayIDs;
39227 action.waysForNode = function (nodeId, graph) {
39228 var node = graph.entity(nodeId);
39229 var splittableParents = graph.parentWays(node).filter(isSplittable);
39232 // If the ways to split aren't specified, only split the lines.
39233 // If there are no lines to split, split the areas.
39234 var hasLine = splittableParents.some(function (parent) {
39235 return parent.geometry(graph) === 'line';
39239 return splittableParents.filter(function (parent) {
39240 return parent.geometry(graph) === 'line';
39245 return splittableParents;
39247 function isSplittable(parent) {
39248 // If the ways to split are specified, ignore everything else.
39249 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
39251 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
39253 for (var i = 1; i < parent.nodes.length - 1; i++) {
39254 if (parent.nodes[i] === nodeId) return true;
39261 action.ways = function (graph) {
39262 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
39263 return action.waysForNode(nodeId, graph);
39267 action.disabled = function (graph) {
39268 for (var i = 0; i < nodeIds.length; i++) {
39269 var nodeId = nodeIds[i];
39270 var candidates = action.waysForNode(nodeId, graph);
39272 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
39273 return 'not_eligible';
39278 action.limitWays = function (val) {
39279 if (!arguments.length) return _wayIDs;
39284 action.keepHistoryOn = function (val) {
39285 if (!arguments.length) return _keepHistoryOn;
39286 _keepHistoryOn = val;
39293 function coreGraph(other, mutable) {
39294 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
39296 if (other instanceof coreGraph) {
39297 var base = other.base();
39298 this.entities = Object.assign(Object.create(base.entities), other.entities);
39299 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
39300 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
39302 this.entities = Object.create({});
39303 this._parentWays = Object.create({});
39304 this._parentRels = Object.create({});
39305 this.rebase(other || [], [this]);
39308 this.transients = {};
39309 this._childNodes = {};
39310 this.frozen = !mutable;
39312 coreGraph.prototype = {
39313 hasEntity: function hasEntity(id) {
39314 return this.entities[id];
39316 entity: function entity(id) {
39317 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
39320 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
39324 throw new Error('entity ' + id + ' not found');
39329 geometry: function geometry(id) {
39330 return this.entity(id).geometry(this);
39332 "transient": function transient(entity, key, fn) {
39333 var id = entity.id;
39334 var transients = this.transients[id] || (this.transients[id] = {});
39336 if (transients[key] !== undefined) {
39337 return transients[key];
39340 transients[key] = fn.call(entity);
39341 return transients[key];
39343 parentWays: function parentWays(entity) {
39344 var parents = this._parentWays[entity.id];
39348 parents.forEach(function (id) {
39349 result.push(this.entity(id));
39355 isPoi: function isPoi(entity) {
39356 var parents = this._parentWays[entity.id];
39357 return !parents || parents.size === 0;
39359 isShared: function isShared(entity) {
39360 var parents = this._parentWays[entity.id];
39361 return parents && parents.size > 1;
39363 parentRelations: function parentRelations(entity) {
39364 var parents = this._parentRels[entity.id];
39368 parents.forEach(function (id) {
39369 result.push(this.entity(id));
39375 parentMultipolygons: function parentMultipolygons(entity) {
39376 return this.parentRelations(entity).filter(function (relation) {
39377 return relation.isMultipolygon();
39380 childNodes: function childNodes(entity) {
39381 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
39382 if (!entity.nodes) return [];
39385 for (var i = 0; i < entity.nodes.length; i++) {
39386 nodes[i] = this.entity(entity.nodes[i]);
39388 this._childNodes[entity.id] = nodes;
39389 return this._childNodes[entity.id];
39391 base: function base() {
39393 'entities': Object.getPrototypeOf(this.entities),
39394 'parentWays': Object.getPrototypeOf(this._parentWays),
39395 'parentRels': Object.getPrototypeOf(this._parentRels)
39398 // Unlike other graph methods, rebase mutates in place. This is because it
39399 // is used only during the history operation that merges newly downloaded
39400 // data into each state. To external consumers, it should appear as if the
39401 // graph always contained the newly downloaded data.
39402 rebase: function rebase(entities, stack, force) {
39403 var base = this.base();
39406 for (i = 0; i < entities.length; i++) {
39407 var entity = entities[i];
39408 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
39410 base.entities[entity.id] = entity;
39412 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
39415 if (entity.type === 'way') {
39416 for (j = 0; j < entity.nodes.length; j++) {
39417 id = entity.nodes[j];
39419 for (k = 1; k < stack.length; k++) {
39420 var ents = stack[k].entities;
39422 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
39430 for (i = 0; i < stack.length; i++) {
39431 stack[i]._updateRebased();
39434 _updateRebased: function _updateRebased() {
39435 var base = this.base();
39436 Object.keys(this._parentWays).forEach(function (child) {
39437 if (base.parentWays[child]) {
39438 base.parentWays[child].forEach(function (id) {
39439 if (!this.entities.hasOwnProperty(id)) {
39440 this._parentWays[child].add(id);
39445 Object.keys(this._parentRels).forEach(function (child) {
39446 if (base.parentRels[child]) {
39447 base.parentRels[child].forEach(function (id) {
39448 if (!this.entities.hasOwnProperty(id)) {
39449 this._parentRels[child].add(id);
39454 this.transients = {}; // this._childNodes is not updated, under the assumption that
39455 // ways are always downloaded with their child nodes.
39457 // Updates calculated properties (parentWays, parentRels) for the specified change
39458 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
39459 parentWays = parentWays || this._parentWays;
39460 parentRels = parentRels || this._parentRels;
39461 var type = entity && entity.type || oldentity && oldentity.type;
39462 var removed, added, i;
39464 if (type === 'way') {
39465 // Update parentWays
39466 if (oldentity && entity) {
39467 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
39468 added = utilArrayDifference(entity.nodes, oldentity.nodes);
39469 } else if (oldentity) {
39470 removed = oldentity.nodes;
39472 } else if (entity) {
39474 added = entity.nodes;
39477 for (i = 0; i < removed.length; i++) {
39478 // make a copy of prototype property, store as own property, and update..
39479 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
39480 parentWays[removed[i]]["delete"](oldentity.id);
39483 for (i = 0; i < added.length; i++) {
39484 // make a copy of prototype property, store as own property, and update..
39485 parentWays[added[i]] = new Set(parentWays[added[i]]);
39486 parentWays[added[i]].add(entity.id);
39488 } else if (type === 'relation') {
39489 // Update parentRels
39490 // diff only on the IDs since the same entity can be a member multiple times with different roles
39491 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
39494 var entityMemberIDs = entity ? entity.members.map(function (m) {
39498 if (oldentity && entity) {
39499 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
39500 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
39501 } else if (oldentity) {
39502 removed = oldentityMemberIDs;
39504 } else if (entity) {
39506 added = entityMemberIDs;
39509 for (i = 0; i < removed.length; i++) {
39510 // make a copy of prototype property, store as own property, and update..
39511 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
39512 parentRels[removed[i]]["delete"](oldentity.id);
39515 for (i = 0; i < added.length; i++) {
39516 // make a copy of prototype property, store as own property, and update..
39517 parentRels[added[i]] = new Set(parentRels[added[i]]);
39518 parentRels[added[i]].add(entity.id);
39522 replace: function replace(entity) {
39523 if (this.entities[entity.id] === entity) return this;
39524 return this.update(function () {
39525 this._updateCalculated(this.entities[entity.id], entity);
39527 this.entities[entity.id] = entity;
39530 remove: function remove(entity) {
39531 return this.update(function () {
39532 this._updateCalculated(entity, undefined);
39534 this.entities[entity.id] = undefined;
39537 revert: function revert(id) {
39538 var baseEntity = this.base().entities[id];
39539 var headEntity = this.entities[id];
39540 if (headEntity === baseEntity) return this;
39541 return this.update(function () {
39542 this._updateCalculated(headEntity, baseEntity);
39544 delete this.entities[id];
39547 update: function update() {
39548 var graph = this.frozen ? coreGraph(this, true) : this;
39550 for (var i = 0; i < arguments.length; i++) {
39551 arguments[i].call(graph, graph);
39554 if (this.frozen) graph.frozen = true;
39557 // Obliterates any existing entities
39558 load: function load(entities) {
39559 var base = this.base();
39560 this.entities = Object.create(base.entities);
39562 for (var i in entities) {
39563 this.entities[i] = entities[i];
39565 this._updateCalculated(base.entities[i], this.entities[i]);
39572 function osmTurn(turn) {
39573 if (!(this instanceof osmTurn)) {
39574 return new osmTurn(turn);
39577 Object.assign(this, turn);
39579 function osmIntersection(graph, startVertexId, maxDistance) {
39580 maxDistance = maxDistance || 30; // in meters
39582 var vgraph = coreGraph(); // virtual graph
39586 function memberOfRestriction(entity) {
39587 return graph.parentRelations(entity).some(function (r) {
39588 return r.isRestriction();
39592 function isRoad(way) {
39593 if (way.isArea() || way.isDegenerate()) return false;
39596 'motorway_link': true,
39598 'trunk_link': true,
39600 'primary_link': true,
39602 'secondary_link': true,
39604 'tertiary_link': true,
39605 'residential': true,
39606 'unclassified': true,
39607 'living_street': true,
39612 return roads[way.tags.highway];
39615 var startNode = graph.entity(startVertexId);
39616 var checkVertices = [startNode];
39619 var vertexIds = [];
39627 var parent; // `actions` will store whatever actions must be performed to satisfy
39628 // preconditions for adding a turn restriction to this intersection.
39629 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
39630 // - Reverse oneways so that they are drawn in the forward direction
39631 // - Split ways on key vertices
39633 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
39634 // for more key vertices and ways to include in the intersection..
39636 while (checkVertices.length) {
39637 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
39639 checkWays = graph.parentWays(vertex);
39640 var hasWays = false;
39642 for (i = 0; i < checkWays.length; i++) {
39643 way = checkWays[i];
39644 if (!isRoad(way) && !memberOfRestriction(way)) continue;
39645 ways.push(way); // it's a road, or it's already in a turn restriction
39647 hasWays = true; // check the way's children for more key vertices
39649 nodes = utilArrayUniq(graph.childNodes(way));
39651 for (j = 0; j < nodes.length; j++) {
39653 if (node === vertex) continue; // same thing
39655 if (vertices.indexOf(node) !== -1) continue; // seen it already
39657 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
39658 // a key vertex will have parents that are also roads
39660 var hasParents = false;
39661 parents = graph.parentWays(node);
39663 for (k = 0; k < parents.length; k++) {
39664 parent = parents[k];
39665 if (parent === way) continue; // same thing
39667 if (ways.indexOf(parent) !== -1) continue; // seen it already
39669 if (!isRoad(parent)) continue; // not a road
39676 checkVertices.push(node);
39682 vertices.push(vertex);
39686 vertices = utilArrayUniq(vertices);
39687 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
39688 // Everything done after this step should act on the virtual graph
39689 // Any actions that must be performed later to the main graph go in `actions` array
39691 ways.forEach(function (way) {
39692 graph.childNodes(way).forEach(function (node) {
39693 vgraph = vgraph.replace(node);
39695 vgraph = vgraph.replace(way);
39696 graph.parentRelations(way).forEach(function (relation) {
39697 if (relation.isRestriction()) {
39698 if (relation.isValidRestriction(graph)) {
39699 vgraph = vgraph.replace(relation);
39700 } else if (relation.isComplete(graph)) {
39701 actions.push(actionDeleteRelation(relation.id));
39705 }); // STEP 3: Force all oneways to be drawn in the forward direction
39707 ways.forEach(function (w) {
39708 var way = vgraph.entity(w.id);
39710 if (way.tags.oneway === '-1') {
39711 var action = actionReverse(way.id, {
39712 reverseOneway: true
39714 actions.push(action);
39715 vgraph = action(vgraph);
39717 }); // STEP 4: Split ways on key vertices
39719 var origCount = osmEntity.id.next.way;
39720 vertices.forEach(function (v) {
39721 // This is an odd way to do it, but we need to find all the ways that
39722 // will be split here, then split them one at a time to ensure that these
39723 // actions can be replayed on the main graph exactly in the same order.
39724 // (It is unintuitive, but the order of ways returned from graph.parentWays()
39725 // is arbitrary, depending on how the main graph and vgraph were built)
39726 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
39728 if (!splitAll.disabled(vgraph)) {
39729 splitAll.ways(vgraph).forEach(function (way) {
39730 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
39731 actions.push(splitOne);
39732 vgraph = splitOne(vgraph);
39735 }); // In here is where we should also split the intersection at nearby junction.
39736 // for https://github.com/mapbox/iD-internal/issues/31
39737 // nearbyVertices.forEach(function(v) {
39739 // Reasons why we reset the way id count here:
39740 // 1. Continuity with way ids created by the splits so that we can replay
39741 // these actions later if the user decides to create a turn restriction
39742 // 2. Avoids churning way ids just by hovering over a vertex
39743 // and displaying the turn restriction editor
39745 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
39747 vertexIds = vertices.map(function (v) {
39752 vertexIds.forEach(function (id) {
39753 var vertex = vgraph.entity(id);
39754 var parents = vgraph.parentWays(vertex);
39755 vertices.push(vertex);
39756 ways = ways.concat(parents);
39758 vertices = utilArrayUniq(vertices);
39759 ways = utilArrayUniq(ways);
39760 vertexIds = vertices.map(function (v) {
39763 wayIds = ways.map(function (w) {
39765 }); // STEP 6: Update the ways with some metadata that will be useful for
39766 // walking the intersection graph later and rendering turn arrows.
39768 function withMetadata(way, vertexIds) {
39769 var __oneWay = way.isOneWay(); // which affixes are key vertices?
39772 var __first = vertexIds.indexOf(way.first()) !== -1;
39774 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
39777 var __via = __first && __last;
39779 var __from = __first && !__oneWay || __last;
39781 var __to = __first || __last && !__oneWay;
39783 return way.update({
39794 wayIds.forEach(function (id) {
39795 var way = withMetadata(vgraph.entity(id), vertexIds);
39796 vgraph = vgraph.replace(way);
39798 }); // STEP 7: Simplify - This is an iterative process where we:
39799 // 1. Find trivial vertices with only 2 parents
39800 // 2. trim off the leaf way from those vertices and remove from vgraph
39803 var removeWayIds = [];
39804 var removeVertexIds = [];
39808 checkVertices = vertexIds.slice();
39810 for (i = 0; i < checkVertices.length; i++) {
39811 var vertexId = checkVertices[i];
39812 vertex = vgraph.hasEntity(vertexId);
39815 if (vertexIds.indexOf(vertexId) !== -1) {
39816 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39819 removeVertexIds.push(vertexId);
39823 parents = vgraph.parentWays(vertex);
39825 if (parents.length < 3) {
39826 if (vertexIds.indexOf(vertexId) !== -1) {
39827 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39831 if (parents.length === 2) {
39832 // vertex with 2 parents is trivial
39833 var a = parents[0];
39834 var b = parents[1];
39835 var aIsLeaf = a && !a.__via;
39836 var bIsLeaf = b && !b.__via;
39837 var leaf, survivor;
39839 if (aIsLeaf && !bIsLeaf) {
39842 } else if (!aIsLeaf && bIsLeaf) {
39847 if (leaf && survivor) {
39848 survivor = withMetadata(survivor, vertexIds); // update survivor way
39850 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
39852 removeWayIds.push(leaf.id);
39857 parents = vgraph.parentWays(vertex);
39859 if (parents.length < 2) {
39860 // vertex is no longer a key vertex
39861 if (vertexIds.indexOf(vertexId) !== -1) {
39862 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39865 removeVertexIds.push(vertexId);
39869 if (parents.length < 1) {
39870 // vertex is no longer attached to anything
39871 vgraph = vgraph.remove(vertex);
39874 } while (keepGoing);
39876 vertices = vertices.filter(function (vertex) {
39877 return removeVertexIds.indexOf(vertex.id) === -1;
39878 }).map(function (vertex) {
39879 return vgraph.entity(vertex.id);
39881 ways = ways.filter(function (way) {
39882 return removeWayIds.indexOf(way.id) === -1;
39883 }).map(function (way) {
39884 return vgraph.entity(way.id);
39885 }); // OK! Here is our intersection..
39887 var intersection = {
39890 vertices: vertices,
39892 }; // Get all the valid turns through this intersection given a starting way id.
39893 // This operates on the virtual graph for everything.
39895 // Basically, walk through all possible paths from starting way,
39896 // honoring the existing turn restrictions as we go (watch out for loops!)
39898 // For each path found, generate and return a `osmTurn` datastructure.
39901 intersection.turns = function (fromWayId, maxViaWay) {
39902 if (!fromWayId) return [];
39903 if (!maxViaWay) maxViaWay = 0;
39904 var vgraph = intersection.graph;
39905 var keyVertexIds = intersection.vertices.map(function (v) {
39908 var start = vgraph.entity(fromWayId);
39909 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
39910 // maxViaWay=1 from-*-via-*-to (1 via max)
39911 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
39913 var maxPathLength = maxViaWay * 2 + 3;
39916 return turns; // traverse the intersection graph and find all the valid paths
39918 function step(entity, currPath, currRestrictions, matchedRestriction) {
39919 currPath = (currPath || []).slice(); // shallow copy
39921 if (currPath.length >= maxPathLength) return;
39922 currPath.push(entity.id);
39923 currRestrictions = (currRestrictions || []).slice(); // shallow copy
39927 if (entity.type === 'node') {
39928 var parents = vgraph.parentWays(entity);
39929 var nextWays = []; // which ways can we step into?
39931 for (i = 0; i < parents.length; i++) {
39932 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
39934 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
39936 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
39938 var restrict = null;
39940 for (j = 0; j < currRestrictions.length; j++) {
39941 var restriction = currRestrictions[j];
39942 var f = restriction.memberByRole('from');
39943 var v = restriction.membersByRole('via');
39944 var t = restriction.memberByRole('to');
39945 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
39947 var matchesFrom = f.id === fromWayId;
39948 var matchesViaTo = false;
39949 var isAlongOnlyPath = false;
39951 if (t.id === way.id) {
39953 if (v.length === 1 && v[0].type === 'node') {
39955 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
39957 // match all VIA ways
39960 for (k = 2; k < currPath.length; k += 2) {
39961 // k = 2 skips FROM
39962 pathVias.push(currPath[k]); // (path goes way-node-way...)
39965 var restrictionVias = [];
39967 for (k = 0; k < v.length; k++) {
39968 if (v[k].type === 'way') {
39969 restrictionVias.push(v[k].id);
39973 var diff = utilArrayDifference(pathVias, restrictionVias);
39974 matchesViaTo = !diff.length;
39976 } else if (isOnly) {
39977 for (k = 0; k < v.length; k++) {
39978 // way doesn't match TO, but is one of the via ways along the path of an "only"
39979 if (v[k].type === 'way' && v[k].id === way.id) {
39980 isAlongOnlyPath = true;
39986 if (matchesViaTo) {
39989 id: restriction.id,
39990 direct: matchesFrom,
39997 id: restriction.id,
39998 direct: matchesFrom,
40005 // indirect - caused by a different nearby restriction
40006 if (isAlongOnlyPath) {
40008 id: restriction.id,
40014 } else if (isOnly) {
40016 id: restriction.id,
40023 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
40026 if (restrict && restrict.direct) break;
40035 nextWays.forEach(function (nextWay) {
40036 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
40039 // entity.type === 'way'
40040 if (currPath.length >= 3) {
40041 // this is a "complete" path..
40042 var turnPath = currPath.slice(); // shallow copy
40043 // an indirect restriction - only include the partial path (starting at FROM)
40045 if (matchedRestriction && matchedRestriction.direct === false) {
40046 for (i = 0; i < turnPath.length; i++) {
40047 if (turnPath[i] === matchedRestriction.from) {
40048 turnPath = turnPath.slice(i);
40054 var turn = pathToTurn(turnPath);
40057 if (matchedRestriction) {
40058 turn.restrictionID = matchedRestriction.id;
40059 turn.no = matchedRestriction.no;
40060 turn.only = matchedRestriction.only;
40061 turn.direct = matchedRestriction.direct;
40064 turns.push(osmTurn(turn));
40067 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
40070 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
40071 // which nodes can we step into?
40073 var n1 = vgraph.entity(entity.first());
40074 var n2 = vgraph.entity(entity.last());
40075 var dist = geoSphericalDistance(n1.loc, n2.loc);
40076 var nextNodes = [];
40078 if (currPath.length > 1) {
40079 if (dist > maxDistance) return; // the next node is too far
40081 if (!entity.__via) return; // this way is a leaf / can't be a via
40084 if (!entity.__oneWay && // bidirectional..
40085 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
40086 currPath.indexOf(n1.id) === -1) {
40087 // haven't seen it yet..
40088 nextNodes.push(n1); // can advance to first node
40091 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
40092 currPath.indexOf(n2.id) === -1) {
40093 // haven't seen it yet..
40094 nextNodes.push(n2); // can advance to last node
40097 nextNodes.forEach(function (nextNode) {
40098 // gather restrictions FROM this way
40099 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
40100 if (!r.isRestriction()) return false;
40101 var f = r.memberByRole('from');
40102 if (!f || f.id !== entity.id) return false;
40103 var isOnly = /^only_/.test(r.tags.restriction);
40104 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
40106 var isOnlyVia = false;
40107 var v = r.membersByRole('via');
40109 if (v.length === 1 && v[0].type === 'node') {
40111 isOnlyVia = v[0].id === nextNode.id;
40114 for (var i = 0; i < v.length; i++) {
40115 if (v[i].type !== 'way') continue;
40116 var viaWay = vgraph.entity(v[i].id);
40118 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
40127 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
40130 } // assumes path is alternating way-node-way of odd length
40133 function pathToTurn(path) {
40134 if (path.length < 3) return;
40135 var fromWayId, fromNodeId, fromVertexId;
40136 var toWayId, toNodeId, toVertexId;
40137 var viaWayIds, viaNodeId, isUturn;
40138 fromWayId = path[0];
40139 toWayId = path[path.length - 1];
40141 if (path.length === 3 && fromWayId === toWayId) {
40143 var way = vgraph.entity(fromWayId);
40144 if (way.__oneWay) return null;
40146 viaNodeId = fromVertexId = toVertexId = path[1];
40147 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
40150 fromVertexId = path[1];
40151 fromNodeId = adjacentNode(fromWayId, fromVertexId);
40152 toVertexId = path[path.length - 2];
40153 toNodeId = adjacentNode(toWayId, toVertexId);
40155 if (path.length === 3) {
40156 viaNodeId = path[1];
40158 viaWayIds = path.filter(function (entityId) {
40159 return entityId[0] === 'w';
40161 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
40166 key: path.join('_'),
40171 vertex: fromVertexId
40185 function adjacentNode(wayId, affixId) {
40186 var nodes = vgraph.entity(wayId).nodes;
40187 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
40192 return intersection;
40194 function osmInferRestriction(graph, turn, projection) {
40195 var fromWay = graph.entity(turn.from.way);
40196 var fromNode = graph.entity(turn.from.node);
40197 var fromVertex = graph.entity(turn.from.vertex);
40198 var toWay = graph.entity(turn.to.way);
40199 var toNode = graph.entity(turn.to.node);
40200 var toVertex = graph.entity(turn.to.vertex);
40201 var fromOneWay = fromWay.tags.oneway === 'yes';
40202 var toOneWay = toWay.tags.oneway === 'yes';
40203 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
40205 while (angle < 0) {
40209 if (fromNode === toNode) {
40210 return 'no_u_turn';
40213 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) {
40214 return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
40217 if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) {
40218 return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to)
40222 return 'no_right_turn';
40226 return 'no_left_turn';
40229 return 'no_straight_on';
40232 function actionMergePolygon(ids, newRelationId) {
40233 function groupEntities(graph) {
40234 var entities = ids.map(function (id) {
40235 return graph.entity(id);
40237 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
40238 if (entity.type === 'way' && entity.isClosed()) {
40239 return 'closedWay';
40240 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
40241 return 'multipolygon';
40246 return Object.assign({
40250 }, geometryGroups);
40253 var action = function action(graph) {
40254 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
40256 // Each element is itself an array of objects with an id property, and has a
40257 // locs property which is an array of the locations forming the polygon.
40259 var polygons = entities.multipolygon.reduce(function (polygons, m) {
40260 return polygons.concat(osmJoinWays(m.members, graph));
40261 }, []).concat(entities.closedWay.map(function (d) {
40265 member.nodes = graph.childNodes(d);
40267 })); // contained is an array of arrays of boolean values,
40268 // where contained[j][k] is true iff the jth way is
40269 // contained by the kth way.
40271 var contained = polygons.map(function (w, i) {
40272 return polygons.map(function (d, n) {
40273 if (i === n) return null;
40274 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
40276 }), w.nodes.map(function (n) {
40280 }); // Sort all polygons as either outer or inner ways
40285 while (polygons.length) {
40286 extractUncontained(polygons);
40287 polygons = polygons.filter(isContained);
40288 contained = contained.filter(isContained).map(filterContained);
40291 function isContained(d, i) {
40292 return contained[i].some(function (val) {
40297 function filterContained(d) {
40298 return d.filter(isContained);
40301 function extractUncontained(polygons) {
40302 polygons.forEach(function (d, i) {
40303 if (!isContained(d, i)) {
40304 d.forEach(function (member) {
40308 role: outer ? 'outer' : 'inner'
40314 } // Move all tags to one relation
40317 var relation = entities.multipolygon[0] || osmRelation({
40320 type: 'multipolygon'
40323 entities.multipolygon.slice(1).forEach(function (m) {
40324 relation = relation.mergeTags(m.tags);
40325 graph = graph.remove(m);
40327 entities.closedWay.forEach(function (way) {
40328 function isThisOuter(m) {
40329 return m.id === way.id && m.role !== 'inner';
40332 if (members.some(isThisOuter)) {
40333 relation = relation.mergeTags(way.tags);
40334 graph = graph.replace(way.update({
40339 return graph.replace(relation.update({
40341 tags: utilObjectOmit(relation.tags, ['area'])
40345 action.disabled = function (graph) {
40346 var entities = groupEntities(graph);
40348 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
40349 return 'not_eligible';
40352 if (!entities.multipolygon.every(function (r) {
40353 return r.isComplete(graph);
40355 return 'incomplete_relation';
40358 if (!entities.multipolygon.length) {
40359 var sharedMultipolygons = [];
40360 entities.closedWay.forEach(function (way, i) {
40362 sharedMultipolygons = graph.parentMultipolygons(way);
40364 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
40367 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
40368 return relation.members.length === entities.closedWay.length;
40371 if (sharedMultipolygons.length) {
40372 // don't create a new multipolygon if it'd be redundant
40373 return 'not_eligible';
40375 } else if (entities.closedWay.some(function (way) {
40376 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
40378 // don't add a way to a multipolygon again if it's already a member
40379 return 'not_eligible';
40386 var FORCED$2 = descriptors && fails(function () {
40387 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
40388 return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy';
40391 // `RegExp.prototype.flags` getter
40392 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
40393 if (FORCED$2) objectDefineProperty.f(RegExp.prototype, 'flags', {
40394 configurable: true,
40398 var fastDeepEqual = function equal(a, b) {
40399 if (a === b) return true;
40401 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
40402 if (a.constructor !== b.constructor) return false;
40403 var length, i, keys;
40405 if (Array.isArray(a)) {
40407 if (length != b.length) return false;
40409 for (i = length; i-- !== 0;) {
40410 if (!equal(a[i], b[i])) return false;
40416 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
40417 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
40418 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
40419 keys = Object.keys(a);
40420 length = keys.length;
40421 if (length !== Object.keys(b).length) return false;
40423 for (i = length; i-- !== 0;) {
40424 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
40427 for (i = length; i-- !== 0;) {
40429 if (!equal(a[key], b[key])) return false;
40433 } // true if both NaN, false otherwise
40436 return a !== a && b !== b;
40439 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
40440 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
40441 // http://www.cs.dartmouth.edu/~doug/
40442 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
40444 // Expects two arrays, finds longest common sequence
40446 function LCS(buffer1, buffer2) {
40447 var equivalenceClasses = {};
40449 for (var j = 0; j < buffer2.length; j++) {
40450 var item = buffer2[j];
40452 if (equivalenceClasses[item]) {
40453 equivalenceClasses[item].push(j);
40455 equivalenceClasses[item] = [j];
40464 var candidates = [NULLRESULT];
40466 for (var i = 0; i < buffer1.length; i++) {
40467 var _item = buffer1[i];
40468 var buffer2indices = equivalenceClasses[_item] || [];
40470 var c = candidates[0];
40472 for (var jx = 0; jx < buffer2indices.length; jx++) {
40473 var _j = buffer2indices[jx];
40476 for (s = r; s < candidates.length; s++) {
40477 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
40482 if (s < candidates.length) {
40483 var newCandidate = {
40486 chain: candidates[s]
40489 if (r === candidates.length) {
40490 candidates.push(c);
40498 if (r === candidates.length) {
40499 break; // no point in examining further (j)s
40505 } // At this point, we know the LCS: it's in the reverse of the
40506 // linked-list through .chain of candidates[candidates.length - 1].
40509 return candidates[candidates.length - 1];
40510 } // We apply the LCS to build a 'comm'-style picture of the
40511 // offsets and lengths of mismatched chunks in the input
40512 // buffers. This is used by diff3MergeRegions.
40515 function diffIndices(buffer1, buffer2) {
40516 var lcs = LCS(buffer1, buffer2);
40518 var tail1 = buffer1.length;
40519 var tail2 = buffer2.length;
40521 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
40522 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
40523 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
40524 tail1 = candidate.buffer1index;
40525 tail2 = candidate.buffer2index;
40527 if (mismatchLength1 || mismatchLength2) {
40529 buffer1: [tail1 + 1, mismatchLength1],
40530 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
40531 buffer2: [tail2 + 1, mismatchLength2],
40532 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
40539 } // We apply the LCS to build a JSON representation of a
40540 // independently derived from O, returns a fairly complicated
40541 // internal representation of merge decisions it's taken. The
40542 // interested reader may wish to consult
40544 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
40545 // 'A Formal Investigation of ' In Arvind and Prasad,
40546 // editors, Foundations of Software Technology and Theoretical
40547 // Computer Science (FSTTCS), December 2007.
40549 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
40553 function diff3MergeRegions(a, o, b) {
40554 // "hunks" are array subsets where `a` or `b` are different from `o`
40555 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
40558 function addHunk(h, ab) {
40561 oStart: h.buffer1[0],
40562 oLength: h.buffer1[1],
40563 // length of o to remove
40564 abStart: h.buffer2[0],
40565 abLength: h.buffer2[1] // length of a/b to insert
40566 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
40571 diffIndices(o, a).forEach(function (item) {
40572 return addHunk(item, 'a');
40574 diffIndices(o, b).forEach(function (item) {
40575 return addHunk(item, 'b');
40577 hunks.sort(function (x, y) {
40578 return x.oStart - y.oStart;
40581 var currOffset = 0;
40583 function advanceTo(endOffset) {
40584 if (endOffset > currOffset) {
40588 bufferStart: currOffset,
40589 bufferLength: endOffset - currOffset,
40590 bufferContent: o.slice(currOffset, endOffset)
40592 currOffset = endOffset;
40596 while (hunks.length) {
40597 var hunk = hunks.shift();
40598 var regionStart = hunk.oStart;
40599 var regionEnd = hunk.oStart + hunk.oLength;
40600 var regionHunks = [hunk];
40601 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
40603 while (hunks.length) {
40604 var nextHunk = hunks[0];
40605 var nextHunkStart = nextHunk.oStart;
40606 if (nextHunkStart > regionEnd) break; // no overlap
40608 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
40609 regionHunks.push(hunks.shift());
40612 if (regionHunks.length === 1) {
40613 // Only one hunk touches this region, meaning that there is no conflict here.
40614 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
40615 if (hunk.abLength > 0) {
40616 var buffer = hunk.ab === 'a' ? a : b;
40620 bufferStart: hunk.abStart,
40621 bufferLength: hunk.abLength,
40622 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
40626 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
40627 // Effectively merge all the `a` hunks into one giant hunk, then do the
40628 // same for the `b` hunks; then, correct for skew in the regions of `o`
40629 // that each side changed, and report appropriate spans for the three sides.
40631 a: [a.length, -1, o.length, -1],
40632 b: [b.length, -1, o.length, -1]
40635 while (regionHunks.length) {
40636 hunk = regionHunks.shift();
40637 var oStart = hunk.oStart;
40638 var oEnd = oStart + hunk.oLength;
40639 var abStart = hunk.abStart;
40640 var abEnd = abStart + hunk.abLength;
40641 var _b = bounds[hunk.ab];
40642 _b[0] = Math.min(abStart, _b[0]);
40643 _b[1] = Math.max(abEnd, _b[1]);
40644 _b[2] = Math.min(oStart, _b[2]);
40645 _b[3] = Math.max(oEnd, _b[3]);
40648 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
40649 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
40650 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
40651 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
40655 aLength: aEnd - aStart,
40656 aContent: a.slice(aStart, aEnd),
40657 oStart: regionStart,
40658 oLength: regionEnd - regionStart,
40659 oContent: o.slice(regionStart, regionEnd),
40661 bLength: bEnd - bStart,
40662 bContent: b.slice(bStart, bEnd)
40664 results.push(result);
40667 currOffset = regionEnd;
40670 advanceTo(o.length);
40672 } // Applies the output of diff3MergeRegions to actually
40673 // construct the merged buffer; the returned result alternates
40674 // between 'ok' and 'conflict' blocks.
40675 // A "false conflict" is where `a` and `b` both change the same from `o`
40678 function diff3Merge(a, o, b, options) {
40680 excludeFalseConflicts: true,
40681 stringSeparator: /\s+/
40683 options = Object.assign(defaults, options);
40684 var aString = typeof a === 'string';
40685 var oString = typeof o === 'string';
40686 var bString = typeof b === 'string';
40687 if (aString) a = a.split(options.stringSeparator);
40688 if (oString) o = o.split(options.stringSeparator);
40689 if (bString) b = b.split(options.stringSeparator);
40691 var regions = diff3MergeRegions(a, o, b);
40694 function flushOk() {
40695 if (okBuffer.length) {
40704 function isFalseConflict(a, b) {
40705 if (a.length !== b.length) return false;
40707 for (var i = 0; i < a.length; i++) {
40708 if (a[i] !== b[i]) return false;
40714 regions.forEach(function (region) {
40715 if (region.stable) {
40718 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
40720 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
40723 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
40728 a: region.aContent,
40729 aIndex: region.aStart,
40730 o: region.oContent,
40731 oIndex: region.oStart,
40732 b: region.bContent,
40733 bIndex: region.bStart
40743 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
40744 discardTags = discardTags || {};
40745 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
40747 var _conflicts = [];
40750 return typeof formatUser === 'function' ? formatUser(d) : d;
40753 function mergeLocation(remote, target) {
40754 function pointEqual(a, b) {
40755 var epsilon = 1e-6;
40756 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
40759 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
40763 if (_option === 'force_remote') {
40764 return target.update({
40769 _conflicts.push(_t('merge_remote_changes.conflict.location', {
40770 user: user(remote.user)
40776 function mergeNodes(base, remote, target) {
40777 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
40781 if (_option === 'force_remote') {
40782 return target.update({
40783 nodes: remote.nodes
40787 var ccount = _conflicts.length;
40788 var o = base.nodes || [];
40789 var a = target.nodes || [];
40790 var b = remote.nodes || [];
40792 var hunks = diff3Merge(a, o, b, {
40793 excludeFalseConflicts: true
40796 for (var i = 0; i < hunks.length; i++) {
40797 var hunk = hunks[i];
40800 nodes.push.apply(nodes, hunk.ok);
40802 // for all conflicts, we can assume c.a !== c.b
40803 // because `diff3Merge` called with `true` option to exclude false conflicts..
40804 var c = hunk.conflict;
40806 if (fastDeepEqual(c.o, c.a)) {
40807 // only changed remotely
40808 nodes.push.apply(nodes, c.b);
40809 } else if (fastDeepEqual(c.o, c.b)) {
40810 // only changed locally
40811 nodes.push.apply(nodes, c.a);
40813 // changed both locally and remotely
40814 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
40815 user: user(remote.user)
40823 return _conflicts.length === ccount ? target.update({
40828 function mergeChildren(targetWay, children, updates, graph) {
40829 function isUsed(node, targetWay) {
40830 var hasInterestingParent = graph.parentWays(node).some(function (way) {
40831 return way.id !== targetWay.id;
40833 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
40836 var ccount = _conflicts.length;
40838 for (var i = 0; i < children.length; i++) {
40839 var id = children[i];
40840 var node = graph.hasEntity(id); // remove unused childNodes..
40842 if (targetWay.nodes.indexOf(id) === -1) {
40843 if (node && !isUsed(node, targetWay)) {
40844 updates.removeIds.push(id);
40848 } // restore used childNodes..
40851 var local = localGraph.hasEntity(id);
40852 var remote = remoteGraph.hasEntity(id);
40855 if (_option === 'force_remote' && remote && remote.visible) {
40856 updates.replacements.push(remote);
40857 } else if (_option === 'force_local' && local) {
40858 target = osmEntity(local);
40861 target = target.update({
40862 version: remote.version
40866 updates.replacements.push(target);
40867 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
40868 target = osmEntity(local, {
40869 version: remote.version
40872 if (remote.visible) {
40873 target = mergeLocation(remote, target);
40875 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
40876 user: user(remote.user)
40880 if (_conflicts.length !== ccount) break;
40881 updates.replacements.push(target);
40888 function updateChildren(updates, graph) {
40889 for (var i = 0; i < updates.replacements.length; i++) {
40890 graph = graph.replace(updates.replacements[i]);
40893 if (updates.removeIds.length) {
40894 graph = actionDeleteMultiple(updates.removeIds)(graph);
40900 function mergeMembers(remote, target) {
40901 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
40905 if (_option === 'force_remote') {
40906 return target.update({
40907 members: remote.members
40911 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
40912 user: user(remote.user)
40918 function mergeTags(base, remote, target) {
40919 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
40923 if (_option === 'force_remote') {
40924 return target.update({
40929 var ccount = _conflicts.length;
40930 var o = base.tags || {};
40931 var a = target.tags || {};
40932 var b = remote.tags || {};
40933 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
40934 return !discardTags[k];
40936 var tags = Object.assign({}, a); // shallow copy
40938 var changed = false;
40940 for (var i = 0; i < keys.length; i++) {
40943 if (o[k] !== b[k] && a[k] !== b[k]) {
40944 // changed remotely..
40945 if (o[k] !== a[k]) {
40946 // changed locally..
40947 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
40951 user: user(remote.user)
40954 // unchanged locally, accept remote change..
40955 if (b.hasOwnProperty(k)) {
40966 return changed && _conflicts.length === ccount ? target.update({
40969 } // `graph.base()` is the common ancestor of the two graphs.
40970 // `localGraph` contains user's edits up to saving
40971 // `remoteGraph` contains remote edits to modified nodes
40972 // `graph` must be a descendent of `localGraph` and may include
40973 // some conflict resolution actions performed on it.
40975 // --- ... --- `localGraph` -- ... -- `graph`
40977 // `graph.base()` --- ... --- `remoteGraph`
40981 var action = function action(graph) {
40986 var base = graph.base().entities[id];
40987 var local = localGraph.entity(id);
40988 var remote = remoteGraph.entity(id);
40989 var target = osmEntity(local, {
40990 version: remote.version
40991 }); // delete/undelete
40993 if (!remote.visible) {
40994 if (_option === 'force_remote') {
40995 return actionDeleteMultiple([id])(graph);
40996 } else if (_option === 'force_local') {
40997 if (target.type === 'way') {
40998 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
40999 graph = updateChildren(updates, graph);
41002 return graph.replace(target);
41004 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
41005 user: user(remote.user)
41008 return graph; // do nothing
41013 if (target.type === 'node') {
41014 target = mergeLocation(remote, target);
41015 } else if (target.type === 'way') {
41016 // pull in any child nodes that may not be present locally..
41017 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
41018 target = mergeNodes(base, remote, target);
41019 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
41020 } else if (target.type === 'relation') {
41021 target = mergeMembers(remote, target);
41024 target = mergeTags(base, remote, target);
41026 if (!_conflicts.length) {
41027 graph = updateChildren(updates, graph).replace(target);
41033 action.withOption = function (opt) {
41038 action.conflicts = function () {
41045 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
41047 function actionMove(moveIDs, tryDelta, projection, cache) {
41048 var _delta = tryDelta;
41050 function setupCache(graph) {
41051 function canMove(nodeID) {
41052 // Allow movement of any node that is in the selectedIDs list..
41053 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
41055 var parents = graph.parentWays(graph.entity(nodeID));
41056 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
41058 var parentsMoving = parents.every(function (way) {
41059 return cache.moving[way.id];
41061 if (!parentsMoving) delete cache.moving[nodeID];
41062 return parentsMoving;
41065 function cacheEntities(ids) {
41066 for (var i = 0; i < ids.length; i++) {
41068 if (cache.moving[id]) continue;
41069 cache.moving[id] = true;
41070 var entity = graph.hasEntity(id);
41071 if (!entity) continue;
41073 if (entity.type === 'node') {
41074 cache.nodes.push(id);
41075 cache.startLoc[id] = entity.loc;
41076 } else if (entity.type === 'way') {
41077 cache.ways.push(id);
41078 cacheEntities(entity.nodes);
41080 cacheEntities(entity.members.map(function (member) {
41087 function cacheIntersections(ids) {
41088 function isEndpoint(way, id) {
41089 return !way.isClosed() && !!way.affix(id);
41092 for (var i = 0; i < ids.length; i++) {
41093 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
41095 var childNodes = graph.childNodes(graph.entity(id));
41097 for (var j = 0; j < childNodes.length; j++) {
41098 var node = childNodes[j];
41099 var parents = graph.parentWays(node);
41100 if (parents.length !== 2) continue;
41101 var moved = graph.entity(id);
41102 var unmoved = null;
41104 for (var k = 0; k < parents.length; k++) {
41105 var way = parents[k];
41107 if (!cache.moving[way.id]) {
41113 if (!unmoved) continue; // exclude ways that are overly connected..
41115 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
41116 if (moved.isArea() || unmoved.isArea()) continue;
41117 cache.intersections.push({
41120 unmovedId: unmoved.id,
41121 movedIsEP: isEndpoint(moved, node.id),
41122 unmovedIsEP: isEndpoint(unmoved, node.id)
41134 cache.intersections = [];
41135 cache.replacedVertex = {};
41136 cache.startLoc = {};
41139 cacheEntities(moveIDs);
41140 cacheIntersections(cache.ways);
41141 cache.nodes = cache.nodes.filter(canMove);
41144 } // Place a vertex where the moved vertex used to be, to preserve way shape..
41153 // * node '*' added to preserve shape
41155 // / b ---- e way `b,e` moved here:
41162 function replaceMovedVertex(nodeId, wayId, graph, delta) {
41163 var way = graph.entity(wayId);
41164 var moved = graph.entity(nodeId);
41165 var movedIndex = way.nodes.indexOf(nodeId);
41166 var len, prevIndex, nextIndex;
41168 if (way.isClosed()) {
41169 len = way.nodes.length - 1;
41170 prevIndex = (movedIndex + len - 1) % len;
41171 nextIndex = (movedIndex + len + 1) % len;
41173 len = way.nodes.length;
41174 prevIndex = movedIndex - 1;
41175 nextIndex = movedIndex + 1;
41178 var prev = graph.hasEntity(way.nodes[prevIndex]);
41179 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
41181 if (!prev || !next) return graph;
41182 var key = wayId + '_' + nodeId;
41183 var orig = cache.replacedVertex[key];
41187 cache.replacedVertex[key] = orig;
41188 cache.startLoc[orig.id] = cache.startLoc[nodeId];
41194 start = projection(cache.startLoc[nodeId]);
41195 end = projection.invert(geoVecAdd(start, delta));
41197 end = cache.startLoc[nodeId];
41200 orig = orig.move(end);
41201 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..
41203 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
41205 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
41206 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
41207 var d1 = geoPathLength(p1);
41208 var d2 = geoPathLength(p2);
41209 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
41211 if (way.isClosed() && insertAt === 0) insertAt = len;
41212 way = way.addNode(orig.id, insertAt);
41213 return graph.replace(orig).replace(way);
41214 } // Remove duplicate vertex that might have been added by
41215 // replaceMovedVertex. This is done after the unzorro checks.
41218 function removeDuplicateVertices(wayId, graph) {
41219 var way = graph.entity(wayId);
41220 var epsilon = 1e-6;
41223 function isInteresting(node, graph) {
41224 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41227 for (var i = 0; i < way.nodes.length; i++) {
41228 curr = graph.entity(way.nodes[i]);
41230 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
41231 if (!isInteresting(prev, graph)) {
41232 way = way.removeNode(prev.id);
41233 graph = graph.replace(way).remove(prev);
41234 } else if (!isInteresting(curr, graph)) {
41235 way = way.removeNode(curr.id);
41236 graph = graph.replace(way).remove(curr);
41244 } // Reorder nodes around intersections that have moved..
41246 // Start: way1.nodes: b,e (moving)
41247 // a - b - c ----- d way2.nodes: a,b,c,d (static)
41249 // e isEP1: true, isEP2, false
41251 // way1 `b,e` moved here:
41252 // a ----- c = b - d
41256 // reorder nodes way1.nodes: b,e
41257 // a ----- c - b - d way2.nodes: a,c,b,d
41263 function unZorroIntersection(intersection, graph) {
41264 var vertex = graph.entity(intersection.nodeId);
41265 var way1 = graph.entity(intersection.movedId);
41266 var way2 = graph.entity(intersection.unmovedId);
41267 var isEP1 = intersection.movedIsEP;
41268 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
41270 if (isEP1 && isEP2) return graph;
41271 var nodes1 = graph.childNodes(way1).filter(function (n) {
41272 return n !== vertex;
41274 var nodes2 = graph.childNodes(way2).filter(function (n) {
41275 return n !== vertex;
41277 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
41278 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
41279 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
41280 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
41281 var loc; // snap vertex to nearest edge (or some point between them)..
41283 if (!isEP1 && !isEP2) {
41284 var epsilon = 1e-6,
41287 for (var i = 0; i < maxIter; i++) {
41288 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
41289 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
41290 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
41291 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
41293 } else if (!isEP1) {
41299 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
41301 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
41302 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
41303 graph = graph.replace(way1);
41306 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
41307 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
41308 graph = graph.replace(way2);
41314 function cleanupIntersections(graph) {
41315 for (var i = 0; i < cache.intersections.length; i++) {
41316 var obj = cache.intersections[i];
41317 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
41318 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
41319 graph = unZorroIntersection(obj, graph);
41320 graph = removeDuplicateVertices(obj.movedId, graph);
41321 graph = removeDuplicateVertices(obj.unmovedId, graph);
41325 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
41328 function limitDelta(graph) {
41329 function moveNode(loc) {
41330 return geoVecAdd(projection(loc), _delta);
41333 for (var i = 0; i < cache.intersections.length; i++) {
41334 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
41336 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
41338 if (!obj.movedIsEP) continue;
41339 var node = graph.entity(obj.nodeId);
41340 var start = projection(node.loc);
41341 var end = geoVecAdd(start, _delta);
41342 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
41343 var movedPath = movedNodes.map(function (n) {
41344 return moveNode(n.loc);
41346 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
41347 var unmovedPath = unmovedNodes.map(function (n) {
41348 return projection(n.loc);
41350 var hits = geoPathIntersections(movedPath, unmovedPath);
41352 for (var j = 0; i < hits.length; i++) {
41353 if (geoVecEqual(hits[j], end)) continue;
41354 var edge = geoChooseEdge(unmovedNodes, end, projection);
41355 _delta = geoVecSubtract(projection(edge.loc), start);
41360 var action = function action(graph) {
41361 if (_delta[0] === 0 && _delta[1] === 0) return graph;
41364 if (cache.intersections.length) {
41368 for (var i = 0; i < cache.nodes.length; i++) {
41369 var node = graph.entity(cache.nodes[i]);
41370 var start = projection(node.loc);
41371 var end = geoVecAdd(start, _delta);
41372 graph = graph.replace(node.move(projection.invert(end)));
41375 if (cache.intersections.length) {
41376 graph = cleanupIntersections(graph);
41382 action.delta = function () {
41389 function actionMoveMember(relationId, fromIndex, toIndex) {
41390 return function (graph) {
41391 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
41395 function actionMoveNode(nodeID, toLoc) {
41396 var action = function action(graph, t) {
41397 if (t === null || !isFinite(t)) t = 1;
41398 t = Math.min(Math.max(+t, 0), 1);
41399 var node = graph.entity(nodeID);
41400 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
41403 action.transitionable = true;
41407 function actionNoop() {
41408 return function (graph) {
41413 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
41414 var epsilon = ep || 1e-4;
41415 var threshold = degThresh || 13; // degrees within right or straight to alter
41416 // We test normalized dot products so we can compare as cos(angle)
41418 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
41419 var upperThreshold = Math.cos(threshold * Math.PI / 180);
41421 var action = function action(graph, t) {
41422 if (t === null || !isFinite(t)) t = 1;
41423 t = Math.min(Math.max(+t, 0), 1);
41424 var way = graph.entity(wayID);
41425 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41427 if (way.tags.nonsquare) {
41428 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
41430 delete tags.nonsquare;
41436 graph = graph.replace(way);
41437 var isClosed = way.isClosed();
41438 var nodes = graph.childNodes(way).slice(); // shallow copy
41440 if (isClosed) nodes.pop();
41442 if (vertexID !== undefined) {
41443 nodes = nodeSubset(nodes, vertexID, isClosed);
41444 if (nodes.length !== 3) return graph;
41445 } // note: all geometry functions here use the unclosed node/point/coord list
41448 var nodeCount = {};
41454 var node, point, loc, score, motions, i, j;
41456 for (i = 0; i < nodes.length; i++) {
41458 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
41461 coord: projection(node.loc)
41465 if (points.length === 3) {
41466 // move only one vertex for right triangle
41467 for (i = 0; i < 1000; i++) {
41468 motions = points.map(calcMotion);
41469 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
41470 score = corner.dotp;
41472 if (score < epsilon) {
41477 node = graph.entity(nodes[corner.i].id);
41478 loc = projection.invert(points[corner.i].coord);
41479 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41481 var straights = [];
41482 var simplified = []; // Remove points from nearly straight sections..
41483 // This produces a simplified shape to orthogonalize
41485 for (i = 0; i < points.length; i++) {
41489 if (isClosed || i > 0 && i < points.length - 1) {
41490 var a = points[(i - 1 + points.length) % points.length];
41491 var b = points[(i + 1) % points.length];
41492 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
41495 if (dotp > upperThreshold) {
41496 straights.push(point);
41498 simplified.push(point);
41500 } // Orthogonalize the simplified shape
41503 var bestPoints = clonePoints(simplified);
41504 var originalPoints = clonePoints(simplified);
41507 for (i = 0; i < 1000; i++) {
41508 motions = simplified.map(calcMotion);
41510 for (j = 0; j < motions.length; j++) {
41511 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
41514 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
41516 if (newScore < score) {
41517 bestPoints = clonePoints(simplified);
41521 if (score < epsilon) {
41526 var bestCoords = bestPoints.map(function (p) {
41529 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
41531 for (i = 0; i < bestPoints.length; i++) {
41532 point = bestPoints[i];
41534 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
41535 node = graph.entity(point.id);
41536 loc = projection.invert(point.coord);
41537 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41539 } // move the nodes along straight segments
41542 for (i = 0; i < straights.length; i++) {
41543 point = straights[i];
41544 if (nodeCount[point.id] > 1) continue; // skip self-intersections
41546 node = graph.entity(point.id);
41548 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
41549 // remove uninteresting points..
41550 graph = actionDeleteNode(node.id)(graph);
41552 // move interesting points to the nearest edge..
41553 var choice = geoVecProject(point.coord, bestCoords);
41556 loc = projection.invert(choice.target);
41557 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41565 function clonePoints(array) {
41566 return array.map(function (p) {
41569 coord: [p.coord[0], p.coord[1]]
41574 function calcMotion(point, i, array) {
41575 // don't try to move the endpoints of a non-closed way.
41576 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)
41578 if (nodeCount[array[i].id] > 1) return [0, 0];
41579 var a = array[(i - 1 + array.length) % array.length].coord;
41580 var origin = point.coord;
41581 var b = array[(i + 1) % array.length].coord;
41582 var p = geoVecSubtract(a, origin);
41583 var q = geoVecSubtract(b, origin);
41584 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
41585 p = geoVecNormalize(p);
41586 q = geoVecNormalize(q);
41587 var dotp = p[0] * q[0] + p[1] * q[1];
41588 var val = Math.abs(dotp);
41590 if (val < lowerThreshold) {
41591 // nearly orthogonal
41594 var vec = geoVecNormalize(geoVecAdd(p, q));
41595 return geoVecScale(vec, 0.1 * dotp * scale);
41598 return [0, 0]; // do nothing
41600 }; // if we are only orthogonalizing one vertex,
41601 // get that vertex and the previous and next
41604 function nodeSubset(nodes, vertexID, isClosed) {
41605 var first = isClosed ? 0 : 1;
41606 var last = isClosed ? nodes.length : nodes.length - 1;
41608 for (var i = first; i < last; i++) {
41609 if (nodes[i].id === vertexID) {
41610 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
41617 action.disabled = function (graph) {
41618 var way = graph.entity(wayID);
41619 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41621 graph = graph.replace(way);
41622 var isClosed = way.isClosed();
41623 var nodes = graph.childNodes(way).slice(); // shallow copy
41625 if (isClosed) nodes.pop();
41626 var allowStraightAngles = false;
41628 if (vertexID !== undefined) {
41629 allowStraightAngles = true;
41630 nodes = nodeSubset(nodes, vertexID, isClosed);
41631 if (nodes.length !== 3) return 'end_vertex';
41634 var coords = nodes.map(function (n) {
41635 return projection(n.loc);
41637 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
41639 if (score === null) {
41640 return 'not_squarish';
41641 } else if (score === 0) {
41642 return 'square_enough';
41648 action.transitionable = true;
41653 // `turn` must be an `osmTurn` object
41654 // see osm/intersection.js, pathToTurn()
41656 // This specifies a restriction of type `restriction` when traveling from
41657 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
41658 // (The action does not check that these entities form a valid intersection.)
41660 // From, to, and via ways should be split before calling this action.
41661 // (old versions of the code would split the ways here, but we no longer do it)
41663 // For testing convenience, accepts a restrictionID to assign to the new
41664 // relation. Normally, this will be undefined and the relation will
41665 // automatically be assigned a new ID.
41668 function actionRestrictTurn(turn, restrictionType, restrictionID) {
41669 return function (graph) {
41670 var fromWay = graph.entity(turn.from.way);
41671 var toWay = graph.entity(turn.to.way);
41672 var viaNode = turn.via.node && graph.entity(turn.via.node);
41673 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
41674 return graph.entity(id);
41689 } else if (viaWays) {
41690 viaWays.forEach(function (viaWay) {
41704 return graph.replace(osmRelation({
41707 type: 'restriction',
41708 restriction: restrictionType
41715 function actionRevert(id) {
41716 var action = function action(graph) {
41717 var entity = graph.hasEntity(id),
41718 base = graph.base().entities[id];
41720 if (entity && !base) {
41721 // entity will be removed..
41722 if (entity.type === 'node') {
41723 graph.parentWays(entity).forEach(function (parent) {
41724 parent = parent.removeNode(id);
41725 graph = graph.replace(parent);
41727 if (parent.isDegenerate()) {
41728 graph = actionDeleteWay(parent.id)(graph);
41733 graph.parentRelations(entity).forEach(function (parent) {
41734 parent = parent.removeMembersWithID(id);
41735 graph = graph.replace(parent);
41737 if (parent.isDegenerate()) {
41738 graph = actionDeleteRelation(parent.id)(graph);
41743 return graph.revert(id);
41749 function actionRotate(rotateIds, pivot, angle, projection) {
41750 var action = function action(graph) {
41751 return graph.update(function (graph) {
41752 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
41753 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
41754 graph = graph.replace(node.move(projection.invert(point)));
41762 function actionScale(ids, pivotLoc, scaleFactor, projection) {
41763 return function (graph) {
41764 return graph.update(function (graph) {
41766 utilGetAllNodes(ids, graph).forEach(function (node) {
41767 point = projection(node.loc);
41768 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
41769 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
41770 graph = graph.replace(node.move(projection.invert(point)));
41776 /* Align nodes along their common axis */
41778 function actionStraightenNodes(nodeIDs, projection) {
41779 function positionAlongWay(a, o, b) {
41780 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41781 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
41784 function getEndpoints(points) {
41785 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
41786 // The shape's surrounding rectangle has 2 axes of symmetry.
41787 // Snap points to the long axis
41789 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
41790 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
41791 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
41792 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
41793 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
41802 var action = function action(graph, t) {
41803 if (t === null || !isFinite(t)) t = 1;
41804 t = Math.min(Math.max(+t, 0), 1);
41805 var nodes = nodeIDs.map(function (id) {
41806 return graph.entity(id);
41808 var points = nodes.map(function (n) {
41809 return projection(n.loc);
41811 var endpoints = getEndpoints(points);
41812 var startPoint = endpoints[0];
41813 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
41815 for (var i = 0; i < points.length; i++) {
41816 var node = nodes[i];
41817 var point = points[i];
41818 var u = positionAlongWay(point, startPoint, endPoint);
41819 var point2 = geoVecInterp(startPoint, endPoint, u);
41820 var loc2 = projection.invert(point2);
41821 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41827 action.disabled = function (graph) {
41828 var nodes = nodeIDs.map(function (id) {
41829 return graph.entity(id);
41831 var points = nodes.map(function (n) {
41832 return projection(n.loc);
41834 var endpoints = getEndpoints(points);
41835 var startPoint = endpoints[0];
41836 var endPoint = endpoints[1];
41837 var maxDistance = 0;
41839 for (var i = 0; i < points.length; i++) {
41840 var point = points[i];
41841 var u = positionAlongWay(point, startPoint, endPoint);
41842 var p = geoVecInterp(startPoint, endPoint, u);
41843 var dist = geoVecLength(p, point);
41845 if (!isNaN(dist) && dist > maxDistance) {
41846 maxDistance = dist;
41850 if (maxDistance < 0.0001) {
41851 return 'straight_enough';
41855 action.transitionable = true;
41860 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
41863 function actionStraightenWay(selectedIDs, projection) {
41864 function positionAlongWay(a, o, b) {
41865 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41866 } // Return all selected ways as a continuous, ordered array of nodes
41869 function allNodes(graph) {
41871 var startNodes = [];
41873 var remainingWays = [];
41874 var selectedWays = selectedIDs.filter(function (w) {
41875 return graph.entity(w).type === 'way';
41877 var selectedNodes = selectedIDs.filter(function (n) {
41878 return graph.entity(n).type === 'node';
41881 for (var i = 0; i < selectedWays.length; i++) {
41882 var way = graph.entity(selectedWays[i]);
41883 nodes = way.nodes.slice(0);
41884 remainingWays.push(nodes);
41885 startNodes.push(nodes[0]);
41886 endNodes.push(nodes[nodes.length - 1]);
41887 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
41888 // and need to be removed so currNode difference calculation below works)
41889 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
41892 startNodes = startNodes.filter(function (n) {
41893 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
41895 endNodes = endNodes.filter(function (n) {
41896 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
41897 }); // Choose the initial endpoint to start from
41899 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
41901 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
41903 var getNextWay = function getNextWay(currNode, remainingWays) {
41904 return remainingWays.filter(function (way) {
41905 return way[0] === currNode || way[way.length - 1] === currNode;
41907 }; // Add nodes to end of nodes array, until all ways are added
41910 while (remainingWays.length) {
41911 nextWay = getNextWay(currNode, remainingWays);
41912 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
41914 if (nextWay[0] !== currNode) {
41918 nodes = nodes.concat(nextWay);
41919 currNode = nodes[nodes.length - 1];
41920 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
41923 if (selectedNodes.length === 2) {
41924 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
41925 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
41926 var sortedStartEnd = [startNodeIdx, endNodeIdx];
41927 sortedStartEnd.sort(function (a, b) {
41930 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
41933 return nodes.map(function (n) {
41934 return graph.entity(n);
41938 function shouldKeepNode(node, graph) {
41939 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41942 var action = function action(graph, t) {
41943 if (t === null || !isFinite(t)) t = 1;
41944 t = Math.min(Math.max(+t, 0), 1);
41945 var nodes = allNodes(graph);
41946 var points = nodes.map(function (n) {
41947 return projection(n.loc);
41949 var startPoint = points[0];
41950 var endPoint = points[points.length - 1];
41954 for (i = 1; i < points.length - 1; i++) {
41955 var node = nodes[i];
41956 var point = points[i];
41958 if (t < 1 || shouldKeepNode(node, graph)) {
41959 var u = positionAlongWay(point, startPoint, endPoint);
41960 var p = geoVecInterp(startPoint, endPoint, u);
41961 var loc2 = projection.invert(p);
41962 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41965 if (toDelete.indexOf(node) === -1) {
41966 toDelete.push(node);
41971 for (i = 0; i < toDelete.length; i++) {
41972 graph = actionDeleteNode(toDelete[i].id)(graph);
41978 action.disabled = function (graph) {
41979 // check way isn't too bendy
41980 var nodes = allNodes(graph);
41981 var points = nodes.map(function (n) {
41982 return projection(n.loc);
41984 var startPoint = points[0];
41985 var endPoint = points[points.length - 1];
41986 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
41989 if (threshold === 0) {
41990 return 'too_bendy';
41993 var maxDistance = 0;
41995 for (i = 1; i < points.length - 1; i++) {
41996 var point = points[i];
41997 var u = positionAlongWay(point, startPoint, endPoint);
41998 var p = geoVecInterp(startPoint, endPoint, u);
41999 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
42001 if (isNaN(dist) || dist > threshold) {
42002 return 'too_bendy';
42003 } else if (dist > maxDistance) {
42004 maxDistance = dist;
42008 var keepingAllNodes = nodes.every(function (node, i) {
42009 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
42012 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
42014 return 'straight_enough';
42018 action.transitionable = true;
42023 // `turn` must be an `osmTurn` object with a `restrictionID` property.
42024 // see osm/intersection.js, pathToTurn()
42027 function actionUnrestrictTurn(turn) {
42028 return function (graph) {
42029 return actionDeleteRelation(turn.restrictionID)(graph);
42033 /* Reflect the given area around its axis of symmetry */
42035 function actionReflect(reflectIds, projection) {
42036 var _useLongAxis = true;
42038 var action = function action(graph, t) {
42039 if (t === null || !isFinite(t)) t = 1;
42040 t = Math.min(Math.max(+t, 0), 1);
42041 var nodes = utilGetAllNodes(reflectIds, graph);
42042 var points = nodes.map(function (n) {
42043 return projection(n.loc);
42045 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
42046 // The shape's surrounding rectangle has 2 axes of symmetry.
42047 // Reflect across the longer axis by default.
42049 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
42050 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
42051 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
42052 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
42054 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
42056 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
42062 } // reflect c across pq
42063 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
42066 var dx = q[0] - p[0];
42067 var dy = q[1] - p[1];
42068 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
42069 var b = 2 * dx * dy / (dx * dx + dy * dy);
42071 for (var i = 0; i < nodes.length; i++) {
42072 var node = nodes[i];
42073 var c = projection(node.loc);
42074 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]];
42075 var loc2 = projection.invert(c2);
42076 node = node.move(geoVecInterp(node.loc, loc2, t));
42077 graph = graph.replace(node);
42083 action.useLongAxis = function (val) {
42084 if (!arguments.length) return _useLongAxis;
42085 _useLongAxis = val;
42089 action.transitionable = true;
42093 function actionUpgradeTags(entityId, oldTags, replaceTags) {
42094 return function (graph) {
42095 var entity = graph.entity(entityId);
42096 var tags = Object.assign({}, entity.tags); // shallow copy
42101 for (var oldTagKey in oldTags) {
42102 if (!(oldTagKey in tags)) continue; // wildcard match
42104 if (oldTags[oldTagKey] === '*') {
42105 // note the value since we might need to transfer it
42106 transferValue = tags[oldTagKey];
42107 delete tags[oldTagKey]; // exact match
42108 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
42109 delete tags[oldTagKey]; // match is within semicolon-delimited values
42111 var vals = tags[oldTagKey].split(';').filter(Boolean);
42112 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
42114 if (vals.length === 1 || oldIndex === -1) {
42115 delete tags[oldTagKey];
42117 if (replaceTags && replaceTags[oldTagKey]) {
42118 // replacing a value within a semicolon-delimited value, note the index
42119 semiIndex = oldIndex;
42122 vals.splice(oldIndex, 1);
42123 tags[oldTagKey] = vals.join(';');
42129 for (var replaceKey in replaceTags) {
42130 var replaceValue = replaceTags[replaceKey];
42132 if (replaceValue === '*') {
42133 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
42134 // allow any pre-existing value except `no` (troll tag)
42137 // otherwise assume `yes` is okay
42138 tags[replaceKey] = 'yes';
42140 } else if (replaceValue === '$1') {
42141 tags[replaceKey] = transferValue;
42143 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
42144 // don't override preexisting values
42145 var existingVals = tags[replaceKey].split(';').filter(Boolean);
42147 if (existingVals.indexOf(replaceValue) === -1) {
42148 existingVals.splice(semiIndex, 0, replaceValue);
42149 tags[replaceKey] = existingVals.join(';');
42152 tags[replaceKey] = replaceValue;
42158 return graph.replace(entity.update({
42164 function behaviorEdit(context) {
42165 function behavior() {
42166 context.map().minzoom(context.minEditableZoom());
42169 behavior.off = function () {
42170 context.map().minzoom(0);
42177 The hover behavior adds the `.hover` class on pointerover to all elements to which
42178 the identical datum is bound, and removes it on pointerout.
42180 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
42181 representation may consist of several elements scattered throughout the DOM hierarchy.
42182 Only one of these elements can have the :hover pseudo-class, but all of them will
42183 have the .hover class.
42186 function behaviorHover(context) {
42187 var dispatch = dispatch$8('hover');
42189 var _selection = select(null);
42191 var _newNodeId = null;
42192 var _initialNodeID = null;
42198 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
42200 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42202 function keydown(d3_event) {
42203 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42204 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
42206 _selection.classed('hover-disabled', true);
42208 dispatch.call('hover', this, null);
42212 function keyup(d3_event) {
42213 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42214 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
42216 _selection.classed('hover-disabled', false);
42218 dispatch.call('hover', this, _targets);
42222 function behavior(selection) {
42223 _selection = selection;
42226 if (_initialNodeID) {
42227 _newNodeId = _initialNodeID;
42228 _initialNodeID = null;
42233 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
42234 .on(_pointerPrefix + 'down.hover', pointerover);
42236 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
42238 function eventTarget(d3_event) {
42239 var datum = d3_event.target && d3_event.target.__data__;
42240 if (_typeof(datum) !== 'object') return null;
42242 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
42243 return datum.properties.entity;
42249 function pointerover(d3_event) {
42250 // ignore mouse hovers with buttons pressed unless dragging
42251 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
42252 var target = eventTarget(d3_event);
42254 if (target && _targets.indexOf(target) === -1) {
42255 _targets.push(target);
42257 updateHover(d3_event, _targets);
42261 function pointerout(d3_event) {
42262 var target = eventTarget(d3_event);
42264 var index = _targets.indexOf(target);
42266 if (index !== -1) {
42267 _targets.splice(index);
42269 updateHover(d3_event, _targets);
42273 function allowsVertex(d) {
42274 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42277 function modeAllowsHover(target) {
42278 var mode = context.mode();
42280 if (mode.id === 'add-point') {
42281 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
42287 function updateHover(d3_event, targets) {
42288 _selection.selectAll('.hover').classed('hover', false);
42290 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42292 var mode = context.mode();
42294 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
42295 var node = targets.find(function (target) {
42296 return target instanceof osmEntity && target.type === 'node';
42298 _newNodeId = node && node.id;
42301 targets = targets.filter(function (datum) {
42302 if (datum instanceof osmEntity) {
42303 // If drawing a way, don't hover on a node that was just placed. #3974
42304 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
42311 for (var i in targets) {
42312 var datum = targets[i]; // What are we hovering over?
42314 if (datum.__featurehash__) {
42315 // hovering custom data
42316 selector += ', .data' + datum.__featurehash__;
42317 } else if (datum instanceof QAItem) {
42318 selector += ', .' + datum.service + '.itemId-' + datum.id;
42319 } else if (datum instanceof osmNote) {
42320 selector += ', .note-' + datum.id;
42321 } else if (datum instanceof osmEntity) {
42322 selector += ', .' + datum.id;
42324 if (datum.type === 'relation') {
42325 for (var j in datum.members) {
42326 selector += ', .' + datum.members[j].id;
42332 var suppressed = _altDisables && d3_event && d3_event.altKey;
42334 if (selector.trim().length) {
42335 // remove the first comma
42336 selector = selector.slice(1);
42338 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
42341 dispatch.call('hover', this, !suppressed && targets);
42345 behavior.off = function (selection) {
42346 selection.selectAll('.hover').classed('hover', false);
42347 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42348 selection.classed('hover-disabled', false);
42349 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
42350 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
42353 behavior.altDisables = function (val) {
42354 if (!arguments.length) return _altDisables;
42355 _altDisables = val;
42359 behavior.ignoreVertex = function (val) {
42360 if (!arguments.length) return _ignoreVertex;
42361 _ignoreVertex = val;
42365 behavior.initialNodeID = function (nodeId) {
42366 _initialNodeID = nodeId;
42370 return utilRebind(behavior, dispatch, 'on');
42373 var _disableSpace = false;
42374 var _lastSpace = null;
42375 function behaviorDraw(context) {
42376 var dispatch = dispatch$8('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
42377 var keybinding = utilKeybinding('draw');
42379 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
42381 var _edit = behaviorEdit(context);
42383 var _closeTolerance = 4;
42384 var _tolerance = 12;
42385 var _mouseLeave = false;
42386 var _lastMouse = null;
42388 var _lastPointerUpEvent;
42390 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
42393 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
42394 // - `mode/drag_node.js` `datum()`
42397 function datum(d3_event) {
42398 var mode = context.mode();
42399 var isNote = mode && mode.id.indexOf('note') !== -1;
42400 if (d3_event.altKey || isNote) return {};
42403 if (d3_event.type === 'keydown') {
42404 element = _lastMouse && _lastMouse.target;
42406 element = d3_event.target;
42407 } // When drawing, snap only to touch targets..
42408 // (this excludes area fills and active drawing elements)
42411 var d = element.__data__;
42412 return d && d.properties && d.properties.target ? d : {};
42415 function pointerdown(d3_event) {
42416 if (_downPointer) return;
42417 var pointerLocGetter = utilFastMouse(this);
42419 id: d3_event.pointerId || 'mouse',
42420 pointerLocGetter: pointerLocGetter,
42421 downTime: +new Date(),
42422 downLoc: pointerLocGetter(d3_event)
42424 dispatch.call('down', this, d3_event, datum(d3_event));
42427 function pointerup(d3_event) {
42428 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
42429 var downPointer = _downPointer;
42430 _downPointer = null;
42431 _lastPointerUpEvent = d3_event;
42432 if (downPointer.isCancelled) return;
42433 var t2 = +new Date();
42434 var p2 = downPointer.pointerLocGetter(d3_event);
42435 var dist = geoVecLength(downPointer.downLoc, p2);
42437 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
42438 // Prevent a quick second click
42439 select(window).on('click.draw-block', function () {
42440 d3_event.stopPropagation();
42442 context.map().dblclickZoomEnable(false);
42443 window.setTimeout(function () {
42444 context.map().dblclickZoomEnable(true);
42445 select(window).on('click.draw-block', null);
42447 click(d3_event, p2);
42451 function pointermove(d3_event) {
42452 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
42453 var p2 = _downPointer.pointerLocGetter(d3_event);
42455 var dist = geoVecLength(_downPointer.downLoc, p2);
42457 if (dist >= _closeTolerance) {
42458 _downPointer.isCancelled = true;
42459 dispatch.call('downcancel', this);
42463 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
42464 // events immediately after non-mouse pointerup events; detect and ignore them.
42466 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
42467 _lastMouse = d3_event;
42468 dispatch.call('move', this, d3_event, datum(d3_event));
42471 function pointercancel(d3_event) {
42472 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
42473 if (!_downPointer.isCancelled) {
42474 dispatch.call('downcancel', this);
42477 _downPointer = null;
42481 function mouseenter() {
42482 _mouseLeave = false;
42485 function mouseleave() {
42486 _mouseLeave = true;
42489 function allowsVertex(d) {
42490 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42492 // - `mode/drag_node.js` `doMove()`
42493 // - `behavior/draw.js` `click()`
42494 // - `behavior/draw_way.js` `move()`
42497 function click(d3_event, loc) {
42498 var d = datum(d3_event);
42499 var target = d && d.properties && d.properties.entity;
42500 var mode = context.mode();
42502 if (target && target.type === 'node' && allowsVertex(target)) {
42504 dispatch.call('clickNode', this, target, d);
42506 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
42508 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
42511 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
42512 dispatch.call('clickWay', this, choice.loc, edge, d);
42515 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
42516 var locLatLng = context.projection.invert(loc);
42517 dispatch.call('click', this, locLatLng, d);
42519 } // treat a spacebar press like a click
42522 function space(d3_event) {
42523 d3_event.preventDefault();
42524 d3_event.stopPropagation();
42525 var currSpace = context.map().mouse();
42527 if (_disableSpace && _lastSpace) {
42528 var dist = geoVecLength(_lastSpace, currSpace);
42530 if (dist > _tolerance) {
42531 _disableSpace = false;
42535 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
42537 _lastSpace = currSpace;
42538 _disableSpace = true;
42539 select(window).on('keyup.space-block', function () {
42540 d3_event.preventDefault();
42541 d3_event.stopPropagation();
42542 _disableSpace = false;
42543 select(window).on('keyup.space-block', null);
42544 }); // get the current mouse position
42546 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
42547 context.projection(context.map().center());
42548 click(d3_event, loc);
42551 function backspace(d3_event) {
42552 d3_event.preventDefault();
42553 dispatch.call('undo');
42556 function del(d3_event) {
42557 d3_event.preventDefault();
42558 dispatch.call('cancel');
42561 function ret(d3_event) {
42562 d3_event.preventDefault();
42563 dispatch.call('finish');
42566 function behavior(selection) {
42567 context.install(_hover);
42568 context.install(_edit);
42569 _downPointer = null;
42570 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
42571 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
42572 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
42573 select(document).call(keybinding);
42577 behavior.off = function (selection) {
42578 context.ui().sidebar.hover.cancel();
42579 context.uninstall(_hover);
42580 context.uninstall(_edit);
42581 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
42582 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
42584 select(document).call(keybinding.unbind);
42587 behavior.hover = function () {
42591 return utilRebind(behavior, dispatch, 'on');
42594 function initRange(domain, range) {
42595 switch (arguments.length) {
42600 this.range(domain);
42604 this.range(range).domain(domain);
42611 function constants(x) {
42612 return function () {
42617 function number(x) {
42622 function identity$1(x) {
42626 function normalize(a, b) {
42627 return (b -= a = +a) ? function (x) {
42628 return (x - a) / b;
42629 } : constants(isNaN(b) ? NaN : 0.5);
42632 function clamper(a, b) {
42634 if (a > b) t = a, a = b, b = t;
42635 return function (x) {
42636 return Math.max(a, Math.min(b, x));
42638 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
42639 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
42642 function bimap(domain, range, interpolate) {
42643 var d0 = domain[0],
42647 if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
42648 return function (x) {
42653 function polymap(domain, range, interpolate) {
42654 var j = Math.min(domain.length, range.length) - 1,
42657 i = -1; // Reverse descending domains.
42659 if (domain[j] < domain[0]) {
42660 domain = domain.slice().reverse();
42661 range = range.slice().reverse();
42665 d[i] = normalize(domain[i], domain[i + 1]);
42666 r[i] = interpolate(range[i], range[i + 1]);
42669 return function (x) {
42670 var i = bisectRight(domain, x, 1, j) - 1;
42671 return r[i](d[i](x));
42675 function copy$1(source, target) {
42676 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
42678 function transformer() {
42681 interpolate = interpolate$1,
42685 clamp = identity$1,
42690 function rescale() {
42691 var n = Math.min(domain.length, range.length);
42692 if (clamp !== identity$1) clamp = clamper(domain[0], domain[n - 1]);
42693 piecewise = n > 2 ? polymap : bimap;
42694 output = input = null;
42698 function scale(x) {
42699 return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
42702 scale.invert = function (y) {
42703 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
42706 scale.domain = function (_) {
42707 return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice();
42710 scale.range = function (_) {
42711 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
42714 scale.rangeRound = function (_) {
42715 return range = Array.from(_), interpolate = interpolateRound, rescale();
42718 scale.clamp = function (_) {
42719 return arguments.length ? (clamp = _ ? true : identity$1, rescale()) : clamp !== identity$1;
42722 scale.interpolate = function (_) {
42723 return arguments.length ? (interpolate = _, rescale()) : interpolate;
42726 scale.unknown = function (_) {
42727 return arguments.length ? (unknown = _, scale) : unknown;
42730 return function (t, u) {
42731 transform = t, untransform = u;
42735 function continuous() {
42736 return transformer()(identity$1, identity$1);
42739 function formatDecimal (x) {
42740 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
42741 } // Computes the decimal coefficient and exponent of the specified number x with
42742 // significant digits p, where x is positive and p is in [1, 21] or undefined.
42743 // For example, formatDecimalParts(1.23) returns ["123", 0].
42745 function formatDecimalParts(x, p) {
42746 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
42749 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
42750 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
42752 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
42755 function exponent (x) {
42756 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
42759 function formatGroup (grouping, thousands) {
42760 return function (value, width) {
42761 var i = value.length,
42767 while (i > 0 && g > 0) {
42768 if (length + g + 1 > width) g = Math.max(1, width - length);
42769 t.push(value.substring(i -= g, i + g));
42770 if ((length += g + 1) > width) break;
42771 g = grouping[j = (j + 1) % grouping.length];
42774 return t.reverse().join(thousands);
42778 function formatNumerals (numerals) {
42779 return function (value) {
42780 return value.replace(/[0-9]/g, function (i) {
42781 return numerals[+i];
42786 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
42787 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
42788 function formatSpecifier(specifier) {
42789 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
42791 return new FormatSpecifier({
42799 precision: match[8] && match[8].slice(1),
42804 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
42806 function FormatSpecifier(specifier) {
42807 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
42808 this.align = specifier.align === undefined ? ">" : specifier.align + "";
42809 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
42810 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
42811 this.zero = !!specifier.zero;
42812 this.width = specifier.width === undefined ? undefined : +specifier.width;
42813 this.comma = !!specifier.comma;
42814 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
42815 this.trim = !!specifier.trim;
42816 this.type = specifier.type === undefined ? "" : specifier.type + "";
42819 FormatSpecifier.prototype.toString = function () {
42820 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;
42823 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
42824 function formatTrim (s) {
42825 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
42832 if (i0 === 0) i0 = i;
42837 if (!+s[i]) break out;
42838 if (i0 > 0) i0 = 0;
42843 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
42846 var nativeToPrecision = 1.0.toPrecision;
42848 var FORCED$1 = fails(function () {
42850 return nativeToPrecision.call(1, undefined) !== '1';
42851 }) || !fails(function () {
42852 // V8 ~ Android 4.3-
42853 nativeToPrecision.call({});
42856 // `Number.prototype.toPrecision` method
42857 // https://tc39.es/ecma262/#sec-number.prototype.toprecision
42858 _export({ target: 'Number', proto: true, forced: FORCED$1 }, {
42859 toPrecision: function toPrecision(precision) {
42860 return precision === undefined
42861 ? nativeToPrecision.call(thisNumberValue(this))
42862 : nativeToPrecision.call(thisNumberValue(this), precision);
42866 var prefixExponent;
42867 function formatPrefixAuto (x, p) {
42868 var d = formatDecimalParts(x, p);
42869 if (!d) return x + "";
42870 var coefficient = d[0],
42872 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
42873 n = coefficient.length;
42874 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!
42877 function formatRounded (x, p) {
42878 var d = formatDecimalParts(x, p);
42879 if (!d) return x + "";
42880 var coefficient = d[0],
42882 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");
42885 var formatTypes = {
42886 "%": function _(x, p) {
42887 return (x * 100).toFixed(p);
42889 "b": function b(x) {
42890 return Math.round(x).toString(2);
42892 "c": function c(x) {
42895 "d": formatDecimal,
42896 "e": function e(x, p) {
42897 return x.toExponential(p);
42899 "f": function f(x, p) {
42900 return x.toFixed(p);
42902 "g": function g(x, p) {
42903 return x.toPrecision(p);
42905 "o": function o(x) {
42906 return Math.round(x).toString(8);
42908 "p": function p(x, _p) {
42909 return formatRounded(x * 100, _p);
42911 "r": formatRounded,
42912 "s": formatPrefixAuto,
42913 "X": function X(x) {
42914 return Math.round(x).toString(16).toUpperCase();
42916 "x": function x(_x) {
42917 return Math.round(_x).toString(16);
42921 function identity (x) {
42925 var map$1 = Array.prototype.map,
42926 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
42927 function formatLocale (locale) {
42928 var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map$1.call(locale.grouping, Number), locale.thousands + ""),
42929 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
42930 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
42931 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
42932 numerals = locale.numerals === undefined ? identity : formatNumerals(map$1.call(locale.numerals, String)),
42933 percent = locale.percent === undefined ? "%" : locale.percent + "",
42934 minus = locale.minus === undefined ? "−" : locale.minus + "",
42935 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
42937 function newFormat(specifier) {
42938 specifier = formatSpecifier(specifier);
42939 var fill = specifier.fill,
42940 align = specifier.align,
42941 sign = specifier.sign,
42942 symbol = specifier.symbol,
42943 zero = specifier.zero,
42944 width = specifier.width,
42945 comma = specifier.comma,
42946 precision = specifier.precision,
42947 trim = specifier.trim,
42948 type = specifier.type; // The "n" type is an alias for ",g".
42950 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
42951 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
42953 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
42954 // For SI-prefix, the suffix is lazily computed.
42956 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
42957 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
42958 // Is this an integer type?
42959 // Can this type generate exponential notation?
42961 var formatType = formatTypes[type],
42962 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
42963 // or clamp the specified precision to the supported range.
42964 // For significant precision, it must be in [1, 21].
42965 // For fixed precision, it must be in [0, 20].
42967 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
42969 function format(value) {
42970 var valuePrefix = prefix,
42971 valueSuffix = suffix,
42976 if (type === "c") {
42977 valueSuffix = formatType(value) + valueSuffix;
42980 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
42982 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
42984 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
42986 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
42988 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
42990 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
42991 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
42992 // grouped, and fractional or exponential “suffix” part that is not.
42995 i = -1, n = value.length;
42998 if (c = value.charCodeAt(i), 48 > c || c > 57) {
42999 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
43000 value = value.slice(0, i);
43005 } // If the fill character is not "0", grouping is applied before padding.
43008 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
43010 var length = valuePrefix.length + value.length + valueSuffix.length,
43011 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
43013 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
43017 value = valuePrefix + value + valueSuffix + padding;
43021 value = valuePrefix + padding + value + valueSuffix;
43025 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
43029 value = padding + valuePrefix + value + valueSuffix;
43033 return numerals(value);
43036 format.toString = function () {
43037 return specifier + "";
43043 function formatPrefix(specifier, value) {
43044 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
43045 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
43046 k = Math.pow(10, -e),
43047 prefix = prefixes[8 + e / 3];
43048 return function (value) {
43049 return f(k * value) + prefix;
43055 formatPrefix: formatPrefix
43065 currency: ["$", ""]
43067 function defaultLocale(definition) {
43068 locale = formatLocale(definition);
43069 format = locale.format;
43070 formatPrefix = locale.formatPrefix;
43074 function precisionFixed (step) {
43075 return Math.max(0, -exponent(Math.abs(step)));
43078 function precisionPrefix (step, value) {
43079 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
43082 function precisionRound (step, max) {
43083 step = Math.abs(step), max = Math.abs(max) - step;
43084 return Math.max(0, exponent(max) - exponent(step)) + 1;
43087 function tickFormat(start, stop, count, specifier) {
43088 var step = tickStep(start, stop, count),
43090 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
43092 switch (specifier.type) {
43095 var value = Math.max(Math.abs(start), Math.abs(stop));
43096 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
43097 return formatPrefix(specifier, value);
43106 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
43113 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
43118 return format(specifier);
43121 function linearish(scale) {
43122 var domain = scale.domain;
43124 scale.ticks = function (count) {
43126 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
43129 scale.tickFormat = function (count, specifier) {
43131 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
43134 scale.nice = function (count) {
43135 if (count == null) count = 10;
43138 var i1 = d.length - 1;
43145 if (stop < start) {
43146 step = start, start = stop, stop = step;
43147 step = i0, i0 = i1, i1 = step;
43150 while (maxIter-- > 0) {
43151 step = tickIncrement(start, stop, count);
43153 if (step === prestep) {
43157 } else if (step > 0) {
43158 start = Math.floor(start / step) * step;
43159 stop = Math.ceil(stop / step) * step;
43160 } else if (step < 0) {
43161 start = Math.ceil(start * step) / step;
43162 stop = Math.floor(stop * step) / step;
43175 function linear() {
43176 var scale = continuous();
43178 scale.copy = function () {
43179 return copy$1(scale, linear());
43182 initRange.apply(scale, arguments);
43183 return linearish(scale);
43186 // eslint-disable-next-line es/no-math-expm1 -- safe
43187 var $expm1 = Math.expm1;
43188 var exp$1 = Math.exp;
43190 // `Math.expm1` method implementation
43191 // https://tc39.es/ecma262/#sec-math.expm1
43192 var mathExpm1 = (!$expm1
43194 || $expm1(10) > 22025.465794806719 || $expm1(10) < 22025.4657948067165168
43196 || $expm1(-2e-17) != -2e-17
43197 ) ? function expm1(x) {
43198 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
43201 function quantize() {
43209 function scale(x) {
43210 return x != null && x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
43213 function rescale() {
43215 domain = new Array(n);
43218 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
43224 scale.domain = function (_) {
43227 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
43230 scale.range = function (_) {
43231 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
43234 scale.invertExtent = function (y) {
43235 var i = range.indexOf(y);
43236 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
43239 scale.unknown = function (_) {
43240 return arguments.length ? (unknown = _, scale) : scale;
43243 scale.thresholds = function () {
43244 return domain.slice();
43247 scale.copy = function () {
43248 return quantize().domain([x0, x1]).range(range).unknown(unknown);
43251 return initRange.apply(linearish(scale), arguments);
43254 // https://github.com/tc39/proposal-string-pad-start-end
43259 var ceil = Math.ceil;
43261 // `String.prototype.{ padStart, padEnd }` methods implementation
43262 var createMethod = function (IS_END) {
43263 return function ($this, maxLength, fillString) {
43264 var S = String(requireObjectCoercible($this));
43265 var stringLength = S.length;
43266 var fillStr = fillString === undefined ? ' ' : String(fillString);
43267 var intMaxLength = toLength(maxLength);
43268 var fillLen, stringFiller;
43269 if (intMaxLength <= stringLength || fillStr == '') return S;
43270 fillLen = intMaxLength - stringLength;
43271 stringFiller = stringRepeat.call(fillStr, ceil(fillLen / fillStr.length));
43272 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
43273 return IS_END ? S + stringFiller : stringFiller + S;
43278 // `String.prototype.padStart` method
43279 // https://tc39.es/ecma262/#sec-string.prototype.padstart
43280 start: createMethod(false),
43281 // `String.prototype.padEnd` method
43282 // https://tc39.es/ecma262/#sec-string.prototype.padend
43283 end: createMethod(true)
43286 var padStart = stringPad.start;
43288 var abs$1 = Math.abs;
43289 var DatePrototype = Date.prototype;
43290 var getTime = DatePrototype.getTime;
43291 var nativeDateToISOString = DatePrototype.toISOString;
43293 // `Date.prototype.toISOString` method implementation
43294 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43295 // PhantomJS / old WebKit fails here:
43296 var dateToIsoString = (fails(function () {
43297 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
43298 }) || !fails(function () {
43299 nativeDateToISOString.call(new Date(NaN));
43300 })) ? function toISOString() {
43301 if (!isFinite(getTime.call(this))) throw RangeError('Invalid time value');
43303 var year = date.getUTCFullYear();
43304 var milliseconds = date.getUTCMilliseconds();
43305 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
43306 return sign + padStart(abs$1(year), sign ? 6 : 4, 0) +
43307 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
43308 '-' + padStart(date.getUTCDate(), 2, 0) +
43309 'T' + padStart(date.getUTCHours(), 2, 0) +
43310 ':' + padStart(date.getUTCMinutes(), 2, 0) +
43311 ':' + padStart(date.getUTCSeconds(), 2, 0) +
43312 '.' + padStart(milliseconds, 3, 0) +
43314 } : nativeDateToISOString;
43316 // `Date.prototype.toISOString` method
43317 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43318 // PhantomJS / old WebKit has a broken implementations
43319 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
43320 toISOString: dateToIsoString
43323 function behaviorBreathe() {
43324 var duration = 800;
43326 var selector = '.selected.shadow, .selected .shadow';
43328 var _selected = select(null);
43336 function ratchetyInterpolator(a, b, steps, units) {
43339 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
43340 return function (t) {
43341 return String(sample(t)) + (units || '');
43345 function reset(selection) {
43346 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
43349 function setAnimationParams(transition, fromTo) {
43350 var toFrom = fromTo === 'from' ? 'to' : 'from';
43351 transition.styleTween('stroke-opacity', function (d) {
43352 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43353 }).styleTween('stroke-width', function (d) {
43354 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43355 }).styleTween('fill-opacity', function (d) {
43356 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43357 }).styleTween('r', function (d) {
43358 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43362 function calcAnimationParams(selection) {
43363 selection.call(reset).each(function (d) {
43364 var s = select(this);
43365 var tag = s.node().tagName;
43371 var width; // determine base opacity and width
43373 if (tag === 'circle') {
43374 opacity = parseFloat(s.style('fill-opacity') || 0.5);
43375 width = parseFloat(s.style('r') || 15.5);
43377 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
43378 width = parseFloat(s.style('stroke-width') || 10);
43379 } // calculate from/to interpolation params..
43383 p.from.opacity = opacity * 0.6;
43384 p.to.opacity = opacity * 1.25;
43385 p.from.width = width * 0.7;
43386 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
43391 function run(surface, fromTo) {
43392 var toFrom = fromTo === 'from' ? 'to' : 'from';
43393 var currSelected = surface.selectAll(selector);
43394 var currClassed = surface.attr('class');
43396 if (_done || currSelected.empty()) {
43397 _selected.call(reset);
43399 _selected = select(null);
43403 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
43404 _selected.call(reset);
43406 _classed = currClassed;
43407 _selected = currSelected.call(calcAnimationParams);
43410 var didCallNextRun = false;
43412 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
43413 // `end` event is called for each selected element, but we want
43414 // it to run only once
43415 if (!didCallNextRun) {
43416 surface.call(run, toFrom);
43417 didCallNextRun = true;
43418 } // if entity was deselected, remove breathe styling
43421 if (!select(this).classed('selected')) {
43422 reset(select(this));
43427 function behavior(surface) {
43429 _timer = timer(function () {
43430 // wait for elements to actually become selected
43431 if (surface.selectAll(selector).empty()) {
43435 surface.call(run, 'from');
43443 behavior.restartIfNeeded = function (surface) {
43444 if (_selected.empty()) {
43445 surface.call(run, 'from');
43453 behavior.off = function () {
43460 _selected.interrupt().call(reset);
43466 /* Creates a keybinding behavior for an operation */
43467 function behaviorOperation(context) {
43470 function keypress(d3_event) {
43471 // prevent operations during low zoom selection
43472 if (!context.map().withinEditableZoom()) return;
43473 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
43474 d3_event.preventDefault();
43476 var disabled = _operation.disabled();
43479 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
43481 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
43482 if (_operation.point) _operation.point(null);
43488 function behavior() {
43489 if (_operation && _operation.available()) {
43490 context.keybinding().on(_operation.keys, keypress);
43496 behavior.off = function () {
43497 context.keybinding().off(_operation.keys);
43500 behavior.which = function (_) {
43501 if (!arguments.length) return _operation;
43509 function operationCircularize(context, selectedIDs) {
43512 var _actions = selectedIDs.map(getAction).filter(Boolean);
43514 var _amount = _actions.length === 1 ? 'single' : 'multiple';
43516 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43520 function getAction(entityID) {
43521 var entity = context.entity(entityID);
43522 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
43525 _extent = entity.extent(context.graph());
43527 _extent = _extent.extend(entity.extent(context.graph()));
43530 return actionCircularize(entityID, context.projection);
43533 var operation = function operation() {
43534 if (!_actions.length) return;
43536 var combinedAction = function combinedAction(graph, t) {
43537 _actions.forEach(function (action) {
43538 if (!action.disabled(graph)) {
43539 graph = action(graph, t);
43546 combinedAction.transitionable = true;
43547 context.perform(combinedAction, operation.annotation());
43548 window.setTimeout(function () {
43549 context.validator().validate();
43550 }, 300); // after any transition
43553 operation.available = function () {
43554 return _actions.length && selectedIDs.length === _actions.length;
43555 }; // don't cache this because the visible extent could change
43558 operation.disabled = function () {
43559 if (!_actions.length) return '';
43561 var actionDisableds = _actions.map(function (action) {
43562 return action.disabled(context.graph());
43563 }).filter(Boolean);
43565 if (actionDisableds.length === _actions.length) {
43566 // none of the features can be circularized
43567 if (new Set(actionDisableds).size > 1) {
43568 return 'multiple_blockers';
43571 return actionDisableds[0];
43572 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
43573 return 'too_large';
43574 } else if (someMissing()) {
43575 return 'not_downloaded';
43576 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43577 return 'connected_to_hidden';
43582 function someMissing() {
43583 if (context.inIntro()) return false;
43584 var osm = context.connection();
43587 var missing = _coords.filter(function (loc) {
43588 return !osm.isDataLoaded(loc);
43591 if (missing.length) {
43592 missing.forEach(function (loc) {
43593 context.loadTileAtLoc(loc);
43603 operation.tooltip = function () {
43604 var disable = operation.disabled();
43605 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
43608 operation.annotation = function () {
43609 return _t('operations.circularize.annotation.feature', {
43614 operation.id = 'circularize';
43615 operation.keys = [_t('operations.circularize.key')];
43616 operation.title = _t('operations.circularize.title');
43617 operation.behavior = behaviorOperation(context).which(operation);
43621 // For example, ⌘Z -> Ctrl+Z
43623 var uiCmd = function uiCmd(code) {
43624 var detected = utilDetect();
43626 if (detected.os === 'mac') {
43630 if (detected.os === 'win') {
43631 if (code === '⌘⇧Z') return 'Ctrl+Y';
43643 for (var i = 0; i < code.length; i++) {
43644 if (code[i] in replacements) {
43645 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
43652 }; // return a display-focused string for a given keyboard code
43654 uiCmd.display = function (code) {
43655 if (code.length !== 1) return code;
43656 var detected = utilDetect();
43657 var mac = detected.os === 'mac';
43658 var replacements = {
43659 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
43660 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
43661 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
43662 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
43663 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
43664 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
43665 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
43666 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
43667 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
43668 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
43669 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
43670 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
43671 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
43673 return replacements[code] || code;
43676 function operationDelete(context, selectedIDs) {
43677 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43678 var action = actionDeleteMultiple(selectedIDs);
43679 var nodes = utilGetAllNodes(selectedIDs, context.graph());
43680 var coords = nodes.map(function (n) {
43683 var extent = utilTotalExtent(selectedIDs, context.graph());
43685 var operation = function operation() {
43686 var nextSelectedID;
43687 var nextSelectedLoc;
43689 if (selectedIDs.length === 1) {
43690 var id = selectedIDs[0];
43691 var entity = context.entity(id);
43692 var geometry = entity.geometry(context.graph());
43693 var parents = context.graph().parentWays(entity);
43694 var parent = parents[0]; // Select the next closest node in the way.
43696 if (geometry === 'vertex') {
43697 var nodes = parent.nodes;
43698 var i = nodes.indexOf(id);
43702 } else if (i === nodes.length - 1) {
43705 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
43706 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
43707 i = a < b ? i - 1 : i + 1;
43710 nextSelectedID = nodes[i];
43711 nextSelectedLoc = context.entity(nextSelectedID).loc;
43715 context.perform(action, operation.annotation());
43716 context.validator().validate();
43718 if (nextSelectedID && nextSelectedLoc) {
43719 if (context.hasEntity(nextSelectedID)) {
43720 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
43722 context.map().centerEase(nextSelectedLoc);
43723 context.enter(modeBrowse(context));
43726 context.enter(modeBrowse(context));
43730 operation.available = function () {
43734 operation.disabled = function () {
43735 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43736 return 'too_large';
43737 } else if (someMissing()) {
43738 return 'not_downloaded';
43739 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43740 return 'connected_to_hidden';
43741 } else if (selectedIDs.some(protectedMember)) {
43742 return 'part_of_relation';
43743 } else if (selectedIDs.some(incompleteRelation)) {
43744 return 'incomplete_relation';
43745 } else if (selectedIDs.some(hasWikidataTag)) {
43746 return 'has_wikidata_tag';
43751 function someMissing() {
43752 if (context.inIntro()) return false;
43753 var osm = context.connection();
43756 var missing = coords.filter(function (loc) {
43757 return !osm.isDataLoaded(loc);
43760 if (missing.length) {
43761 missing.forEach(function (loc) {
43762 context.loadTileAtLoc(loc);
43771 function hasWikidataTag(id) {
43772 var entity = context.entity(id);
43773 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
43776 function incompleteRelation(id) {
43777 var entity = context.entity(id);
43778 return entity.type === 'relation' && !entity.isComplete(context.graph());
43781 function protectedMember(id) {
43782 var entity = context.entity(id);
43783 if (entity.type !== 'way') return false;
43784 var parents = context.graph().parentRelations(entity);
43786 for (var i = 0; i < parents.length; i++) {
43787 var parent = parents[i];
43788 var type = parent.tags.type;
43789 var role = parent.memberById(id).role || 'outer';
43791 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
43800 operation.tooltip = function () {
43801 var disable = operation.disabled();
43802 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
43805 operation.annotation = function () {
43806 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
43807 n: selectedIDs.length
43811 operation.id = 'delete';
43812 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
43813 operation.title = _t('operations.delete.title');
43814 operation.behavior = behaviorOperation(context).which(operation);
43818 function operationOrthogonalize(context, selectedIDs) {
43823 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
43825 var _amount = _actions.length === 1 ? 'single' : 'multiple';
43827 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43831 function chooseAction(entityID) {
43832 var entity = context.entity(entityID);
43833 var geometry = entity.geometry(context.graph());
43836 _extent = entity.extent(context.graph());
43838 _extent = _extent.extend(entity.extent(context.graph()));
43839 } // square a line/area
43842 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
43843 if (_type && _type !== 'feature') return null;
43845 return actionOrthogonalize(entityID, context.projection); // square a single vertex
43846 } else if (geometry === 'vertex') {
43847 if (_type && _type !== 'corner') return null;
43849 var graph = context.graph();
43850 var parents = graph.parentWays(entity);
43852 if (parents.length === 1) {
43853 var way = parents[0];
43855 if (way.nodes.indexOf(entityID) !== -1) {
43856 return actionOrthogonalize(way.id, context.projection, entityID);
43864 var operation = function operation() {
43865 if (!_actions.length) return;
43867 var combinedAction = function combinedAction(graph, t) {
43868 _actions.forEach(function (action) {
43869 if (!action.disabled(graph)) {
43870 graph = action(graph, t);
43877 combinedAction.transitionable = true;
43878 context.perform(combinedAction, operation.annotation());
43879 window.setTimeout(function () {
43880 context.validator().validate();
43881 }, 300); // after any transition
43884 operation.available = function () {
43885 return _actions.length && selectedIDs.length === _actions.length;
43886 }; // don't cache this because the visible extent could change
43889 operation.disabled = function () {
43890 if (!_actions.length) return '';
43892 var actionDisableds = _actions.map(function (action) {
43893 return action.disabled(context.graph());
43894 }).filter(Boolean);
43896 if (actionDisableds.length === _actions.length) {
43897 // none of the features can be squared
43898 if (new Set(actionDisableds).size > 1) {
43899 return 'multiple_blockers';
43902 return actionDisableds[0];
43903 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
43904 return 'too_large';
43905 } else if (someMissing()) {
43906 return 'not_downloaded';
43907 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43908 return 'connected_to_hidden';
43913 function someMissing() {
43914 if (context.inIntro()) return false;
43915 var osm = context.connection();
43918 var missing = _coords.filter(function (loc) {
43919 return !osm.isDataLoaded(loc);
43922 if (missing.length) {
43923 missing.forEach(function (loc) {
43924 context.loadTileAtLoc(loc);
43934 operation.tooltip = function () {
43935 var disable = operation.disabled();
43936 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
43939 operation.annotation = function () {
43940 return _t('operations.orthogonalize.annotation.' + _type, {
43945 operation.id = 'orthogonalize';
43946 operation.keys = [_t('operations.orthogonalize.key')];
43947 operation.title = _t('operations.orthogonalize.title');
43948 operation.behavior = behaviorOperation(context).which(operation);
43952 function operationReflectShort(context, selectedIDs) {
43953 return operationReflect(context, selectedIDs, 'short');
43955 function operationReflectLong(context, selectedIDs) {
43956 return operationReflect(context, selectedIDs, 'long');
43958 function operationReflect(context, selectedIDs, axis) {
43959 axis = axis || 'long';
43960 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43961 var nodes = utilGetAllNodes(selectedIDs, context.graph());
43962 var coords = nodes.map(function (n) {
43965 var extent = utilTotalExtent(selectedIDs, context.graph());
43967 var operation = function operation() {
43968 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
43969 context.perform(action, operation.annotation());
43970 window.setTimeout(function () {
43971 context.validator().validate();
43972 }, 300); // after any transition
43975 operation.available = function () {
43976 return nodes.length >= 3;
43977 }; // don't cache this because the visible extent could change
43980 operation.disabled = function () {
43981 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43982 return 'too_large';
43983 } else if (someMissing()) {
43984 return 'not_downloaded';
43985 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43986 return 'connected_to_hidden';
43987 } else if (selectedIDs.some(incompleteRelation)) {
43988 return 'incomplete_relation';
43993 function someMissing() {
43994 if (context.inIntro()) return false;
43995 var osm = context.connection();
43998 var missing = coords.filter(function (loc) {
43999 return !osm.isDataLoaded(loc);
44002 if (missing.length) {
44003 missing.forEach(function (loc) {
44004 context.loadTileAtLoc(loc);
44013 function incompleteRelation(id) {
44014 var entity = context.entity(id);
44015 return entity.type === 'relation' && !entity.isComplete(context.graph());
44019 operation.tooltip = function () {
44020 var disable = operation.disabled();
44021 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
44024 operation.annotation = function () {
44025 return _t('operations.reflect.annotation.' + axis + '.feature', {
44026 n: selectedIDs.length
44030 operation.id = 'reflect-' + axis;
44031 operation.keys = [_t('operations.reflect.key.' + axis)];
44032 operation.title = _t('operations.reflect.title.' + axis);
44033 operation.behavior = behaviorOperation(context).which(operation);
44037 function operationMove(context, selectedIDs) {
44038 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44039 var nodes = utilGetAllNodes(selectedIDs, context.graph());
44040 var coords = nodes.map(function (n) {
44043 var extent = utilTotalExtent(selectedIDs, context.graph());
44045 var operation = function operation() {
44046 context.enter(modeMove(context, selectedIDs));
44049 operation.available = function () {
44050 return selectedIDs.length > 0;
44053 operation.disabled = function () {
44054 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44055 return 'too_large';
44056 } else if (someMissing()) {
44057 return 'not_downloaded';
44058 } else if (selectedIDs.some(context.hasHiddenConnections)) {
44059 return 'connected_to_hidden';
44060 } else if (selectedIDs.some(incompleteRelation)) {
44061 return 'incomplete_relation';
44066 function someMissing() {
44067 if (context.inIntro()) return false;
44068 var osm = context.connection();
44071 var missing = coords.filter(function (loc) {
44072 return !osm.isDataLoaded(loc);
44075 if (missing.length) {
44076 missing.forEach(function (loc) {
44077 context.loadTileAtLoc(loc);
44086 function incompleteRelation(id) {
44087 var entity = context.entity(id);
44088 return entity.type === 'relation' && !entity.isComplete(context.graph());
44092 operation.tooltip = function () {
44093 var disable = operation.disabled();
44094 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
44097 operation.annotation = function () {
44098 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
44099 n: selectedIDs.length
44103 operation.id = 'move';
44104 operation.keys = [_t('operations.move.key')];
44105 operation.title = _t('operations.move.title');
44106 operation.behavior = behaviorOperation(context).which(operation);
44107 operation.mouseOnly = true;
44111 function modeRotate(context, entityIDs) {
44112 var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeMove
44118 var keybinding = utilKeybinding('rotate');
44119 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];
44120 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
44121 n: entityIDs.length
44128 var _prevTransform;
44130 var _pivot; // use pointer events on supported platforms; fallback to mouse events
44133 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44135 function doRotate(d3_event) {
44138 if (context.graph() !== _prevGraph) {
44139 fn = context.perform;
44141 fn = context.replace;
44142 } // projection changed, recalculate _pivot
44145 var projection = context.projection;
44146 var currTransform = projection.transform();
44148 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
44149 var nodes = utilGetAllNodes(entityIDs, context.graph());
44150 var points = nodes.map(function (n) {
44151 return projection(n.loc);
44153 _pivot = getPivot(points);
44154 _prevAngle = undefined;
44157 var currMouse = context.map().mouse(d3_event);
44158 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
44159 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
44160 var delta = currAngle - _prevAngle;
44161 fn(actionRotate(entityIDs, _pivot, delta, projection));
44162 _prevTransform = currTransform;
44163 _prevAngle = currAngle;
44164 _prevGraph = context.graph();
44167 function getPivot(points) {
44170 if (points.length === 1) {
44171 _pivot = points[0];
44172 } else if (points.length === 2) {
44173 _pivot = geoVecInterp(points[0], points[1], 0.5);
44175 var polygonHull = d3_polygonHull(points);
44177 if (polygonHull.length === 2) {
44178 _pivot = geoVecInterp(points[0], points[1], 0.5);
44180 _pivot = d3_polygonCentroid(d3_polygonHull(points));
44187 function finish(d3_event) {
44188 d3_event.stopPropagation();
44189 context.replace(actionNoop(), annotation);
44190 context.enter(modeSelect(context, entityIDs));
44193 function cancel() {
44194 if (_prevGraph) context.pop(); // remove the rotate
44196 context.enter(modeSelect(context, entityIDs));
44199 function undone() {
44200 context.enter(modeBrowse(context));
44203 mode.enter = function () {
44205 context.features().forceVisible(entityIDs);
44206 behaviors.forEach(context.install);
44208 context.surface().on(_pointerPrefix + 'down.modeRotate', function (d3_event) {
44209 downEvent = d3_event;
44211 select(window).on(_pointerPrefix + 'move.modeRotate', doRotate, true).on(_pointerPrefix + 'up.modeRotate', function (d3_event) {
44212 if (!downEvent) return;
44213 var mapNode = context.container().select('.main-map').node();
44214 var pointGetter = utilFastMouse(mapNode);
44215 var p1 = pointGetter(downEvent);
44216 var p2 = pointGetter(d3_event);
44217 var dist = geoVecLength(p1, p2);
44218 if (dist <= _tolerancePx) finish(d3_event);
44221 context.history().on('undone.modeRotate', undone);
44222 keybinding.on('⎋', cancel).on('↩', finish);
44223 select(document).call(keybinding);
44226 mode.exit = function () {
44227 behaviors.forEach(context.uninstall);
44228 context.surface().on(_pointerPrefix + 'down.modeRotate', null);
44229 select(window).on(_pointerPrefix + 'move.modeRotate', null, true).on(_pointerPrefix + 'up.modeRotate', null, true);
44230 context.history().on('undone.modeRotate', null);
44231 select(document).call(keybinding.unbind);
44232 context.features().forceVisible([]);
44235 mode.selectedIDs = function () {
44236 if (!arguments.length) return entityIDs; // no assign
44244 function operationRotate(context, selectedIDs) {
44245 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44246 var nodes = utilGetAllNodes(selectedIDs, context.graph());
44247 var coords = nodes.map(function (n) {
44250 var extent = utilTotalExtent(selectedIDs, context.graph());
44252 var operation = function operation() {
44253 context.enter(modeRotate(context, selectedIDs));
44256 operation.available = function () {
44257 return nodes.length >= 2;
44260 operation.disabled = function () {
44261 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44262 return 'too_large';
44263 } else if (someMissing()) {
44264 return 'not_downloaded';
44265 } else if (selectedIDs.some(context.hasHiddenConnections)) {
44266 return 'connected_to_hidden';
44267 } else if (selectedIDs.some(incompleteRelation)) {
44268 return 'incomplete_relation';
44273 function someMissing() {
44274 if (context.inIntro()) return false;
44275 var osm = context.connection();
44278 var missing = coords.filter(function (loc) {
44279 return !osm.isDataLoaded(loc);
44282 if (missing.length) {
44283 missing.forEach(function (loc) {
44284 context.loadTileAtLoc(loc);
44293 function incompleteRelation(id) {
44294 var entity = context.entity(id);
44295 return entity.type === 'relation' && !entity.isComplete(context.graph());
44299 operation.tooltip = function () {
44300 var disable = operation.disabled();
44301 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
44304 operation.annotation = function () {
44305 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
44306 n: selectedIDs.length
44310 operation.id = 'rotate';
44311 operation.keys = [_t('operations.rotate.key')];
44312 operation.title = _t('operations.rotate.title');
44313 operation.behavior = behaviorOperation(context).which(operation);
44314 operation.mouseOnly = true;
44318 function modeMove(context, entityIDs, baseGraph) {
44319 var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeRotate
44325 var keybinding = utilKeybinding('move');
44326 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];
44327 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
44328 n: entityIDs.length
44337 var _nudgeInterval; // use pointer events on supported platforms; fallback to mouse events
44340 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44342 function doMove(nudge) {
44343 nudge = nudge || [0, 0];
44346 if (_prevGraph !== context.graph()) {
44348 _origin = context.map().mouseCoordinates();
44349 fn = context.perform;
44351 fn = context.overwrite;
44354 var currMouse = context.map().mouse();
44355 var origMouse = context.projection(_origin);
44356 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
44357 fn(actionMove(entityIDs, delta, context.projection, _cache));
44358 _prevGraph = context.graph();
44361 function startNudge(nudge) {
44362 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44363 _nudgeInterval = window.setInterval(function () {
44364 context.map().pan(nudge);
44369 function stopNudge() {
44370 if (_nudgeInterval) {
44371 window.clearInterval(_nudgeInterval);
44372 _nudgeInterval = null;
44378 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
44387 function finish(d3_event) {
44388 d3_event.stopPropagation();
44389 context.replace(actionNoop(), annotation);
44390 context.enter(modeSelect(context, entityIDs));
44394 function cancel() {
44396 while (context.graph() !== baseGraph) {
44398 } // reset to baseGraph
44401 context.enter(modeBrowse(context));
44403 if (_prevGraph) context.pop(); // remove the move
44405 context.enter(modeSelect(context, entityIDs));
44411 function undone() {
44412 context.enter(modeBrowse(context));
44415 mode.enter = function () {
44416 _origin = context.map().mouseCoordinates();
44419 context.features().forceVisible(entityIDs);
44420 behaviors.forEach(context.install);
44422 context.surface().on(_pointerPrefix + 'down.modeMove', function (d3_event) {
44423 downEvent = d3_event;
44425 select(window).on(_pointerPrefix + 'move.modeMove', move, true).on(_pointerPrefix + 'up.modeMove', function (d3_event) {
44426 if (!downEvent) return;
44427 var mapNode = context.container().select('.main-map').node();
44428 var pointGetter = utilFastMouse(mapNode);
44429 var p1 = pointGetter(downEvent);
44430 var p2 = pointGetter(d3_event);
44431 var dist = geoVecLength(p1, p2);
44432 if (dist <= _tolerancePx) finish(d3_event);
44435 context.history().on('undone.modeMove', undone);
44436 keybinding.on('⎋', cancel).on('↩', finish);
44437 select(document).call(keybinding);
44440 mode.exit = function () {
44442 behaviors.forEach(function (behavior) {
44443 context.uninstall(behavior);
44445 context.surface().on(_pointerPrefix + 'down.modeMove', null);
44446 select(window).on(_pointerPrefix + 'move.modeMove', null, true).on(_pointerPrefix + 'up.modeMove', null, true);
44447 context.history().on('undone.modeMove', null);
44448 select(document).call(keybinding.unbind);
44449 context.features().forceVisible([]);
44452 mode.selectedIDs = function () {
44453 if (!arguments.length) return entityIDs; // no assign
44461 function behaviorPaste(context) {
44462 function doPaste(d3_event) {
44463 // prevent paste during low zoom selection
44464 if (!context.map().withinEditableZoom()) return;
44465 d3_event.preventDefault();
44466 var baseGraph = context.graph();
44467 var mouse = context.map().mouse();
44468 var projection = context.projection;
44469 var viewport = geoExtent(projection.clipExtent()).polygon();
44470 if (!geoPointInPolygon(mouse, viewport)) return;
44471 var oldIDs = context.copyIDs();
44472 if (!oldIDs.length) return;
44473 var extent = geoExtent();
44474 var oldGraph = context.copyGraph();
44476 var action = actionCopyEntities(oldIDs, oldGraph);
44477 context.perform(action);
44478 var copies = action.copies();
44479 var originals = new Set();
44480 Object.values(copies).forEach(function (entity) {
44481 originals.add(entity.id);
44484 for (var id in copies) {
44485 var oldEntity = oldGraph.entity(id);
44486 var newEntity = copies[id];
44488 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
44491 var parents = context.graph().parentWays(newEntity);
44492 var parentCopied = parents.some(function (parent) {
44493 return originals.has(parent.id);
44496 if (!parentCopied) {
44497 newIDs.push(newEntity.id);
44499 } // Put pasted objects where mouse pointer is..
44502 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
44503 var delta = geoVecSubtract(mouse, copyPoint);
44504 context.perform(actionMove(newIDs, delta, projection));
44505 context.enter(modeMove(context, newIDs, baseGraph));
44508 function behavior() {
44509 context.keybinding().on(uiCmd('⌘V'), doPaste);
44513 behavior.off = function () {
44514 context.keybinding().off(uiCmd('⌘V'));
44520 // `String.prototype.repeat` method
44521 // https://tc39.es/ecma262/#sec-string.prototype.repeat
44522 _export({ target: 'String', proto: true }, {
44523 repeat: stringRepeat
44527 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
44529 * The `origin` function is expected to return an [x, y] tuple rather than an
44531 * The events are `start`, `move`, and `end`.
44532 (https://github.com/mbostock/d3/issues/563)
44533 * The `start` event is not dispatched until the first cursor movement occurs.
44534 (https://github.com/mbostock/d3/pull/368)
44535 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
44536 than `x`, `y`, `dx`, and `dy` properties.
44537 * The `end` event is not dispatched if no movement occurs.
44538 * An `off` function is available that unbinds the drag's internal event handlers.
44541 function behaviorDrag() {
44542 var dispatch = dispatch$8('start', 'move', 'end'); // see also behaviorSelect
44544 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
44546 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
44548 var _origin = null;
44549 var _selector = '';
44557 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
44560 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44562 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
44564 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
44565 var selection$1 = selection();
44566 var select = selection$1.style(d3_event_userSelectProperty);
44567 selection$1.style(d3_event_userSelectProperty, 'none');
44568 return function () {
44569 selection$1.style(d3_event_userSelectProperty, select);
44573 function pointerdown(d3_event) {
44574 if (_pointerId) return;
44575 _pointerId = d3_event.pointerId || 'mouse';
44576 _targetNode = this; // only force reflow once per drag
44578 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
44580 var startOrigin = pointerLocGetter(d3_event);
44581 var started = false;
44582 var selectEnable = d3_event_userSelectSuppress();
44583 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
44586 offset = _origin.call(_targetNode, _targetEntity);
44587 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
44592 d3_event.stopPropagation();
44594 function pointermove(d3_event) {
44595 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44596 var p = pointerLocGetter(d3_event);
44599 var dist = geoVecLength(startOrigin, p);
44600 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
44602 if (dist < tolerance) return;
44604 dispatch.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
44605 // a midpoint will convert the target to a node.
44608 d3_event.stopPropagation();
44609 d3_event.preventDefault();
44610 var dx = p[0] - startOrigin[0];
44611 var dy = p[1] - startOrigin[1];
44612 dispatch.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
44616 function pointerup(d3_event) {
44617 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44621 dispatch.call('end', this, d3_event, _targetEntity);
44622 d3_event.preventDefault();
44625 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44630 function behavior(selection) {
44631 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
44632 var delegate = pointerdown;
44635 delegate = function delegate(d3_event) {
44637 var target = d3_event.target;
44639 for (; target && target !== root; target = target.parentNode) {
44640 var datum = target.__data__;
44641 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
44643 if (_targetEntity && target[matchesSelector](_selector)) {
44644 return pointerdown.call(target, d3_event);
44650 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
44653 behavior.off = function (selection) {
44654 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
44657 behavior.selector = function (_) {
44658 if (!arguments.length) return _selector;
44663 behavior.origin = function (_) {
44664 if (!arguments.length) return _origin;
44669 behavior.cancel = function () {
44670 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44674 behavior.targetNode = function (_) {
44675 if (!arguments.length) return _targetNode;
44680 behavior.targetEntity = function (_) {
44681 if (!arguments.length) return _targetEntity;
44686 behavior.surface = function (_) {
44687 if (!arguments.length) return _surface;
44692 return utilRebind(behavior, dispatch, 'on');
44695 function modeDragNode(context) {
44700 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
44701 var edit = behaviorEdit(context);
44703 var _nudgeInterval;
44705 var _restoreSelectedIDs = [];
44706 var _wasMidpoint = false;
44707 var _isCancelled = false;
44715 function startNudge(d3_event, entity, nudge) {
44716 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44717 _nudgeInterval = window.setInterval(function () {
44718 context.map().pan(nudge);
44719 doMove(d3_event, entity, nudge);
44723 function stopNudge() {
44724 if (_nudgeInterval) {
44725 window.clearInterval(_nudgeInterval);
44726 _nudgeInterval = null;
44730 function moveAnnotation(entity) {
44731 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
44734 function connectAnnotation(nodeEntity, targetEntity) {
44735 var nodeGeometry = nodeEntity.geometry(context.graph());
44736 var targetGeometry = targetEntity.geometry(context.graph());
44738 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
44739 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
44740 var targetParentWayIDs = context.graph().parentWays(targetEntity);
44741 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
44743 if (sharedParentWays.length !== 0) {
44744 // if the nodes are next to each other, they are merged
44745 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
44746 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
44749 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
44753 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
44756 function shouldSnapToNode(target) {
44757 if (!_activeEntity) return false;
44758 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
44761 function origin(entity) {
44762 return context.projection(entity.loc);
44765 function keydown(d3_event) {
44766 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44767 if (context.surface().classed('nope')) {
44768 context.surface().classed('nope-suppressed', true);
44771 context.surface().classed('nope', false).classed('nope-disabled', true);
44775 function keyup(d3_event) {
44776 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44777 if (context.surface().classed('nope-suppressed')) {
44778 context.surface().classed('nope', true);
44781 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
44785 function start(d3_event, entity) {
44786 _wasMidpoint = entity.type === 'midpoint';
44787 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
44788 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
44790 if (_isCancelled) {
44792 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
44795 return drag.cancel();
44798 if (_wasMidpoint) {
44799 var midpoint = entity;
44800 entity = osmNode();
44801 context.perform(actionAddMidpoint(midpoint, entity));
44802 entity = context.entity(entity.id); // get post-action entity
44804 var vertex = context.surface().selectAll('.' + entity.id);
44805 drag.targetNode(vertex.node()).targetEntity(entity);
44807 context.perform(actionNoop());
44810 _activeEntity = entity;
44811 _startLoc = entity.loc;
44812 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
44813 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
44814 context.enter(mode);
44816 // - `behavior/draw.js` `datum()`
44819 function datum(d3_event) {
44820 if (!d3_event || d3_event.altKey) {
44823 // When dragging, snap only to touch targets..
44824 // (this excludes area fills and active drawing elements)
44825 var d = d3_event.target.__data__;
44826 return d && d.properties && d.properties.target ? d : {};
44830 function doMove(d3_event, entity, nudge) {
44831 nudge = nudge || [0, 0];
44832 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
44833 var currMouse = geoVecSubtract(currPoint, nudge);
44834 var loc = context.projection.invert(currMouse);
44837 if (!_nudgeInterval) {
44838 // If not nudging at the edge of the viewport, try to snap..
44840 // - `mode/drag_node.js` `doMove()`
44841 // - `behavior/draw.js` `click()`
44842 // - `behavior/draw_way.js` `move()`
44843 var d = datum(d3_event);
44844 target = d && d.properties && d.properties.entity;
44845 var targetLoc = target && target.loc;
44846 var targetNodes = d && d.properties && d.properties.nodes;
44849 // snap to node/vertex - a point target with `.loc`
44850 if (shouldSnapToNode(target)) {
44853 } else if (targetNodes) {
44854 // snap to way - a line target with `.nodes`
44855 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
44863 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
44865 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
44868 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
44869 } // Check if this drag causes the geometry to break..
44873 isInvalid = hasInvalidGeometry(entity, context.graph());
44876 var nope = context.surface().classed('nope');
44878 if (isInvalid === 'relation' || isInvalid === 'restriction') {
44880 // about to nope - show hint
44881 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
44882 relation: _mainPresetIndex.item('type/restriction').name()
44885 } else if (isInvalid) {
44886 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
44887 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
44890 // about to un-nope, remove hint
44891 context.ui().flash.duration(1).label('')();
44895 var nopeDisabled = context.surface().classed('nope-disabled');
44897 if (nopeDisabled) {
44898 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
44900 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
44904 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
44907 function hasRelationConflict(entity, target, edge, graph) {
44908 var testGraph = graph.update(); // copy
44909 // if snapping to way - add midpoint there and consider that the target..
44912 var midpoint = osmNode();
44913 var action = actionAddMidpoint({
44915 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
44917 testGraph = action(testGraph);
44919 } // can we connect to it?
44922 var ids = [entity.id, target.id];
44923 return actionConnect(ids).disabled(testGraph);
44926 function hasInvalidGeometry(entity, graph) {
44927 var parents = graph.parentWays(entity);
44930 for (i = 0; i < parents.length; i++) {
44931 var parent = parents[i];
44933 var activeIndex = null; // which multipolygon ring contains node being dragged
44934 // test any parent multipolygons for valid geometry
44936 var relations = graph.parentRelations(parent);
44938 for (j = 0; j < relations.length; j++) {
44939 if (!relations[j].isMultipolygon()) continue;
44940 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
44942 for (k = 0; k < rings.length; k++) {
44943 nodes = rings[k].nodes;
44945 if (nodes.find(function (n) {
44946 return n.id === entity.id;
44950 if (geoHasSelfIntersections(nodes, entity.id)) {
44951 return 'multipolygonMember';
44955 rings[k].coords = nodes.map(function (n) {
44958 } // test active ring for intersections with other rings in the multipolygon
44961 for (k = 0; k < rings.length; k++) {
44962 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
44964 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
44965 return 'multipolygonRing';
44968 } // If we still haven't tested this node's parent way for self-intersections.
44969 // (because it's not a member of a multipolygon), test it now.
44972 if (activeIndex === null) {
44973 nodes = parent.nodes.map(function (nodeID) {
44974 return graph.entity(nodeID);
44977 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
44978 return parent.geometry(graph);
44986 function move(d3_event, entity, point) {
44987 if (_isCancelled) return;
44988 d3_event.stopPropagation();
44989 context.surface().classed('nope-disabled', d3_event.altKey);
44990 _lastLoc = context.projection.invert(point);
44991 doMove(d3_event, entity);
44992 var nudge = geoViewportEdge(point, context.map().dimensions());
44995 startNudge(d3_event, entity, nudge);
45001 function end(d3_event, entity) {
45002 if (_isCancelled) return;
45003 var wasPoint = entity.geometry(context.graph()) === 'point';
45004 var d = datum(d3_event);
45005 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
45006 var target = d && d.properties && d.properties.entity; // entity to snap to
45010 context.perform(_actionBounceBack(entity.id, _startLoc));
45011 } else if (target && target.type === 'way') {
45012 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
45013 context.replace(actionAddMidpoint({
45015 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
45016 }, entity), connectAnnotation(entity, target));
45017 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
45018 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
45019 } else if (_wasMidpoint) {
45020 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
45022 context.replace(actionNoop(), moveAnnotation(entity));
45026 context.enter(modeSelect(context, [entity.id]));
45028 var reselection = _restoreSelectedIDs.filter(function (id) {
45029 return context.graph().hasEntity(id);
45032 if (reselection.length) {
45033 context.enter(modeSelect(context, reselection));
45035 context.enter(modeBrowse(context));
45040 function _actionBounceBack(nodeID, toLoc) {
45041 var moveNode = actionMoveNode(nodeID, toLoc);
45043 var action = function action(graph, t) {
45044 // last time through, pop off the bounceback perform.
45045 // it will then overwrite the initial perform with a moveNode that does nothing
45046 if (t === 1) context.pop();
45047 return moveNode(graph, t);
45050 action.transitionable = true;
45054 function cancel() {
45056 context.enter(modeBrowse(context));
45059 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);
45061 mode.enter = function () {
45062 context.install(hover);
45063 context.install(edit);
45064 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
45065 context.history().on('undone.drag-node', cancel);
45068 mode.exit = function () {
45069 context.ui().sidebar.hover.cancel();
45070 context.uninstall(hover);
45071 context.uninstall(edit);
45072 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
45073 context.history().on('undone.drag-node', null);
45074 _activeEntity = null;
45075 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
45079 mode.selectedIDs = function () {
45080 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
45085 mode.activeID = function () {
45086 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
45091 mode.restoreSelectedIDs = function (_) {
45092 if (!arguments.length) return _restoreSelectedIDs;
45093 _restoreSelectedIDs = _;
45097 mode.behavior = drag;
45101 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
45102 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
45103 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
45106 // `Promise.prototype.finally` method
45107 // https://tc39.es/ecma262/#sec-promise.prototype.finally
45108 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
45109 'finally': function (onFinally) {
45110 var C = speciesConstructor(this, getBuiltIn('Promise'));
45111 var isFunction = typeof onFinally == 'function';
45113 isFunction ? function (x) {
45114 return promiseResolve(C, onFinally()).then(function () { return x; });
45116 isFunction ? function (e) {
45117 return promiseResolve(C, onFinally()).then(function () { throw e; });
45123 // makes sure that native promise-based APIs `Promise#finally` properly works with patched `Promise#then`
45124 if (typeof nativePromiseConstructor == 'function') {
45125 var method = getBuiltIn('Promise').prototype['finally'];
45126 if (nativePromiseConstructor.prototype['finally'] !== method) {
45127 redefine(nativePromiseConstructor.prototype, 'finally', method, { unsafe: true });
45131 function quickselect(arr, k, left, right, compare) {
45132 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
45135 function quickselectStep(arr, k, left, right, compare) {
45136 while (right > left) {
45137 if (right - left > 600) {
45138 var n = right - left + 1;
45139 var m = k - left + 1;
45140 var z = Math.log(n);
45141 var s = 0.5 * Math.exp(2 * z / 3);
45142 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
45143 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
45144 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
45145 quickselectStep(arr, k, newLeft, newRight, compare);
45151 swap(arr, left, k);
45152 if (compare(arr[right], t) > 0) swap(arr, left, right);
45159 while (compare(arr[i], t) < 0) {
45163 while (compare(arr[j], t) > 0) {
45168 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
45170 swap(arr, j, right);
45172 if (j <= k) left = j + 1;
45173 if (k <= j) right = j - 1;
45177 function swap(arr, i, j) {
45183 function defaultCompare(a, b) {
45184 return a < b ? -1 : a > b ? 1 : 0;
45187 var RBush = /*#__PURE__*/function () {
45189 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
45191 _classCallCheck$1(this, RBush);
45193 // max entries in a node is 9 by default; min node fill is 40% for best performance
45194 this._maxEntries = Math.max(4, maxEntries);
45195 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
45199 _createClass$1(RBush, [{
45201 value: function all() {
45202 return this._all(this.data, []);
45206 value: function search(bbox) {
45207 var node = this.data;
45209 if (!intersects(bbox, node)) return result;
45210 var toBBox = this.toBBox;
45211 var nodesToSearch = [];
45214 for (var i = 0; i < node.children.length; i++) {
45215 var child = node.children[i];
45216 var childBBox = node.leaf ? toBBox(child) : child;
45218 if (intersects(bbox, childBBox)) {
45219 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
45223 node = nodesToSearch.pop();
45230 value: function collides(bbox) {
45231 var node = this.data;
45232 if (!intersects(bbox, node)) return false;
45233 var nodesToSearch = [];
45236 for (var i = 0; i < node.children.length; i++) {
45237 var child = node.children[i];
45238 var childBBox = node.leaf ? this.toBBox(child) : child;
45240 if (intersects(bbox, childBBox)) {
45241 if (node.leaf || contains(bbox, childBBox)) return true;
45242 nodesToSearch.push(child);
45246 node = nodesToSearch.pop();
45253 value: function load(data) {
45254 if (!(data && data.length)) return this;
45256 if (data.length < this._minEntries) {
45257 for (var i = 0; i < data.length; i++) {
45258 this.insert(data[i]);
45262 } // recursively build the tree with the given data from scratch using OMT algorithm
45265 var node = this._build(data.slice(), 0, data.length - 1, 0);
45267 if (!this.data.children.length) {
45268 // save as is if tree is empty
45270 } else if (this.data.height === node.height) {
45271 // split root if trees have the same height
45272 this._splitRoot(this.data, node);
45274 if (this.data.height < node.height) {
45275 // swap trees if inserted one is bigger
45276 var tmpNode = this.data;
45279 } // insert the small tree into the large tree at appropriate level
45282 this._insert(node, this.data.height - node.height - 1, true);
45289 value: function insert(item) {
45290 if (item) this._insert(item, this.data.height - 1);
45295 value: function clear() {
45296 this.data = createNode([]);
45301 value: function remove(item, equalsFn) {
45302 if (!item) return this;
45303 var node = this.data;
45304 var bbox = this.toBBox(item);
45307 var i, parent, goingUp; // depth-first iterative tree traversal
45309 while (node || path.length) {
45313 parent = path[path.length - 1];
45319 // check current node
45320 var index = findItem(item, node.children, equalsFn);
45322 if (index !== -1) {
45323 // item found, remove the item and condense tree upwards
45324 node.children.splice(index, 1);
45327 this._condense(path);
45333 if (!goingUp && !node.leaf && contains(node, bbox)) {
45339 node = node.children[0];
45340 } else if (parent) {
45343 node = parent.children[i];
45345 } else node = null; // nothing found
45353 value: function toBBox(item) {
45357 key: "compareMinX",
45358 value: function compareMinX(a, b) {
45359 return a.minX - b.minX;
45362 key: "compareMinY",
45363 value: function compareMinY(a, b) {
45364 return a.minY - b.minY;
45368 value: function toJSON() {
45373 value: function fromJSON(data) {
45379 value: function _all(node, result) {
45380 var nodesToSearch = [];
45383 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
45384 node = nodesToSearch.pop();
45391 value: function _build(items, left, right, height) {
45392 var N = right - left + 1;
45393 var M = this._maxEntries;
45397 // reached leaf level; return leaf
45398 node = createNode(items.slice(left, right + 1));
45399 calcBBox(node, this.toBBox);
45404 // target height of the bulk-loaded tree
45405 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
45407 M = Math.ceil(N / Math.pow(M, height - 1));
45410 node = createNode([]);
45412 node.height = height; // split the items into M mostly square tiles
45414 var N2 = Math.ceil(N / M);
45415 var N1 = N2 * Math.ceil(Math.sqrt(M));
45416 multiSelect(items, left, right, N1, this.compareMinX);
45418 for (var i = left; i <= right; i += N1) {
45419 var right2 = Math.min(i + N1 - 1, right);
45420 multiSelect(items, i, right2, N2, this.compareMinY);
45422 for (var j = i; j <= right2; j += N2) {
45423 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
45425 node.children.push(this._build(items, j, right3, height - 1));
45429 calcBBox(node, this.toBBox);
45433 key: "_chooseSubtree",
45434 value: function _chooseSubtree(bbox, node, level, path) {
45437 if (node.leaf || path.length - 1 === level) break;
45438 var minArea = Infinity;
45439 var minEnlargement = Infinity;
45440 var targetNode = void 0;
45442 for (var i = 0; i < node.children.length; i++) {
45443 var child = node.children[i];
45444 var area = bboxArea(child);
45445 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
45447 if (enlargement < minEnlargement) {
45448 minEnlargement = enlargement;
45449 minArea = area < minArea ? area : minArea;
45450 targetNode = child;
45451 } else if (enlargement === minEnlargement) {
45452 // otherwise choose one with the smallest area
45453 if (area < minArea) {
45455 targetNode = child;
45460 node = targetNode || node.children[0];
45467 value: function _insert(item, level, isNode) {
45468 var bbox = isNode ? item : this.toBBox(item);
45469 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
45471 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
45474 node.children.push(item);
45475 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
45477 while (level >= 0) {
45478 if (insertPath[level].children.length > this._maxEntries) {
45479 this._split(insertPath, level);
45483 } // adjust bboxes along the insertion path
45486 this._adjustParentBBoxes(bbox, insertPath, level);
45487 } // split overflowed node into two
45491 value: function _split(insertPath, level) {
45492 var node = insertPath[level];
45493 var M = node.children.length;
45494 var m = this._minEntries;
45496 this._chooseSplitAxis(node, m, M);
45498 var splitIndex = this._chooseSplitIndex(node, m, M);
45500 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
45501 newNode.height = node.height;
45502 newNode.leaf = node.leaf;
45503 calcBBox(node, this.toBBox);
45504 calcBBox(newNode, this.toBBox);
45505 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
45509 value: function _splitRoot(node, newNode) {
45511 this.data = createNode([node, newNode]);
45512 this.data.height = node.height + 1;
45513 this.data.leaf = false;
45514 calcBBox(this.data, this.toBBox);
45517 key: "_chooseSplitIndex",
45518 value: function _chooseSplitIndex(node, m, M) {
45520 var minOverlap = Infinity;
45521 var minArea = Infinity;
45523 for (var i = m; i <= M - m; i++) {
45524 var bbox1 = distBBox(node, 0, i, this.toBBox);
45525 var bbox2 = distBBox(node, i, M, this.toBBox);
45526 var overlap = intersectionArea(bbox1, bbox2);
45527 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
45529 if (overlap < minOverlap) {
45530 minOverlap = overlap;
45532 minArea = area < minArea ? area : minArea;
45533 } else if (overlap === minOverlap) {
45534 // otherwise choose distribution with minimum area
45535 if (area < minArea) {
45542 return index || M - m;
45543 } // sorts node children by the best axis for split
45546 key: "_chooseSplitAxis",
45547 value: function _chooseSplitAxis(node, m, M) {
45548 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
45549 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
45551 var xMargin = this._allDistMargin(node, m, M, compareMinX);
45553 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
45554 // otherwise it's already sorted by minY
45557 if (xMargin < yMargin) node.children.sort(compareMinX);
45558 } // total margin of all possible split distributions where each node is at least m full
45561 key: "_allDistMargin",
45562 value: function _allDistMargin(node, m, M, compare) {
45563 node.children.sort(compare);
45564 var toBBox = this.toBBox;
45565 var leftBBox = distBBox(node, 0, m, toBBox);
45566 var rightBBox = distBBox(node, M - m, M, toBBox);
45567 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
45569 for (var i = m; i < M - m; i++) {
45570 var child = node.children[i];
45571 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
45572 margin += bboxMargin(leftBBox);
45575 for (var _i = M - m - 1; _i >= m; _i--) {
45576 var _child = node.children[_i];
45577 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
45578 margin += bboxMargin(rightBBox);
45584 key: "_adjustParentBBoxes",
45585 value: function _adjustParentBBoxes(bbox, path, level) {
45586 // adjust bboxes along the given tree path
45587 for (var i = level; i >= 0; i--) {
45588 extend$1(path[i], bbox);
45593 value: function _condense(path) {
45594 // go through the path, removing empty nodes and updating bboxes
45595 for (var i = path.length - 1, siblings; i >= 0; i--) {
45596 if (path[i].children.length === 0) {
45598 siblings = path[i - 1].children;
45599 siblings.splice(siblings.indexOf(path[i]), 1);
45600 } else this.clear();
45601 } else calcBBox(path[i], this.toBBox);
45609 function findItem(item, items, equalsFn) {
45610 if (!equalsFn) return items.indexOf(item);
45612 for (var i = 0; i < items.length; i++) {
45613 if (equalsFn(item, items[i])) return i;
45617 } // calculate node's bbox from bboxes of its children
45620 function calcBBox(node, toBBox) {
45621 distBBox(node, 0, node.children.length, toBBox, node);
45622 } // min bounding rectangle of node children from k to p-1
45625 function distBBox(node, k, p, toBBox, destNode) {
45626 if (!destNode) destNode = createNode(null);
45627 destNode.minX = Infinity;
45628 destNode.minY = Infinity;
45629 destNode.maxX = -Infinity;
45630 destNode.maxY = -Infinity;
45632 for (var i = k; i < p; i++) {
45633 var child = node.children[i];
45634 extend$1(destNode, node.leaf ? toBBox(child) : child);
45640 function extend$1(a, b) {
45641 a.minX = Math.min(a.minX, b.minX);
45642 a.minY = Math.min(a.minY, b.minY);
45643 a.maxX = Math.max(a.maxX, b.maxX);
45644 a.maxY = Math.max(a.maxY, b.maxY);
45648 function compareNodeMinX(a, b) {
45649 return a.minX - b.minX;
45652 function compareNodeMinY(a, b) {
45653 return a.minY - b.minY;
45656 function bboxArea(a) {
45657 return (a.maxX - a.minX) * (a.maxY - a.minY);
45660 function bboxMargin(a) {
45661 return a.maxX - a.minX + (a.maxY - a.minY);
45664 function enlargedArea(a, b) {
45665 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));
45668 function intersectionArea(a, b) {
45669 var minX = Math.max(a.minX, b.minX);
45670 var minY = Math.max(a.minY, b.minY);
45671 var maxX = Math.min(a.maxX, b.maxX);
45672 var maxY = Math.min(a.maxY, b.maxY);
45673 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
45676 function contains(a, b) {
45677 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
45680 function intersects(a, b) {
45681 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
45684 function createNode(children) {
45686 children: children,
45694 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
45695 // combines selection algorithm with binary divide & conquer approach
45698 function multiSelect(arr, left, right, n, compare) {
45699 var stack = [left, right];
45701 while (stack.length) {
45702 right = stack.pop();
45703 left = stack.pop();
45704 if (right - left <= n) continue;
45705 var mid = left + Math.ceil((right - left) / n / 2) * n;
45706 quickselect(arr, mid, left, right, compare);
45707 stack.push(left, mid, mid, right);
45711 function responseText(response) {
45712 if (!response.ok) throw new Error(response.status + " " + response.statusText);
45713 return response.text();
45716 function d3_text (input, init) {
45717 return fetch(input, init).then(responseText);
45720 function responseJson(response) {
45721 if (!response.ok) throw new Error(response.status + " " + response.statusText);
45722 if (response.status === 204 || response.status === 205) return;
45723 return response.json();
45726 function d3_json (input, init) {
45727 return fetch(input, init).then(responseJson);
45730 function parser(type) {
45731 return function (input, init) {
45732 return d3_text(input, init).then(function (text) {
45733 return new DOMParser().parseFromString(text, type);
45738 var d3_xml = parser("application/xml");
45739 var svg = parser("image/svg+xml");
45741 var tiler$6 = utilTiler();
45742 var dispatch$7 = dispatch$8('loaded');
45743 var _tileZoom$3 = 14;
45744 var _krUrlRoot = 'https://www.keepright.at';
45747 localizeStrings: {}
45748 }; // This gets reassigned if reset
45752 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
45753 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];
45755 function abortRequest$6(controller) {
45757 controller.abort();
45761 function abortUnwantedRequests$3(cache, tiles) {
45762 Object.keys(cache.inflightTile).forEach(function (k) {
45763 var wanted = tiles.find(function (tile) {
45764 return k === tile.id;
45768 abortRequest$6(cache.inflightTile[k]);
45769 delete cache.inflightTile[k];
45774 function encodeIssueRtree$2(d) {
45782 } // Replace or remove QAItem from rtree
45785 function updateRtree$3(item, replace) {
45786 _cache$2.rtree.remove(item, function (a, b) {
45787 return a.data.id === b.data.id;
45791 _cache$2.rtree.insert(item);
45795 function tokenReplacements(d) {
45796 if (!(d instanceof QAItem)) return;
45797 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
45798 var replacements = {};
45799 var issueTemplate = _krData.errorTypes[d.whichType];
45801 if (!issueTemplate) {
45802 /* eslint-disable no-console */
45803 console.log('No Template: ', d.whichType);
45804 console.log(' ', d.description);
45805 /* eslint-enable no-console */
45808 } // some descriptions are just fixed text
45811 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
45813 var errorRegex = new RegExp(issueTemplate.regex, 'i');
45814 var errorMatch = errorRegex.exec(d.description);
45817 /* eslint-disable no-console */
45818 console.log('Unmatched: ', d.whichType);
45819 console.log(' ', d.description);
45820 console.log(' ', errorRegex);
45821 /* eslint-enable no-console */
45826 for (var i = 1; i < errorMatch.length; i++) {
45828 var capture = errorMatch[i];
45829 var idType = void 0;
45830 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
45832 if (idType && capture) {
45833 // link IDs if present in the capture
45834 capture = parseError(capture, idType);
45835 } else if (htmlRegex.test(capture)) {
45836 // escape any html in non-IDs
45837 capture = '\\' + capture + '\\';
45839 var compare = capture.toLowerCase();
45841 if (_krData.localizeStrings[compare]) {
45842 // some replacement strings can be localized
45843 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45847 replacements['var' + i] = capture;
45850 return replacements;
45853 function parseError(capture, idType) {
45854 var compare = capture.toLowerCase();
45856 if (_krData.localizeStrings[compare]) {
45857 // some replacement strings can be localized
45858 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45862 // link a string like "this node"
45864 capture = linkErrorObject(capture);
45868 capture = linkURL(capture);
45870 // link an entity ID
45875 capture = linkEntity(idType + capture);
45877 // some errors have more complex ID lists/variance
45880 capture = parse20(capture);
45884 capture = parse211(capture);
45888 capture = parse231(capture);
45892 capture = parse294(capture);
45896 capture = parse370(capture);
45902 function linkErrorObject(d) {
45903 return "<a class=\"error_object_link\">".concat(d, "</a>");
45906 function linkEntity(d) {
45907 return "<a class=\"error_entity_link\">".concat(d, "</a>");
45910 function linkURL(d) {
45911 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
45912 } // arbitrary node list of form: #ID, #ID, #ID...
45915 function parse211(capture) {
45917 var items = capture.split(', ');
45918 items.forEach(function (item) {
45919 // ID has # at the front
45920 var id = linkEntity('n' + item.slice(1));
45923 return newList.join(', ');
45924 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
45927 function parse231(capture) {
45928 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
45930 var items = capture.split('),');
45931 items.forEach(function (item) {
45932 var match = item.match(/\#(\d+)\((.+)\)?/);
45934 if (match !== null && match.length > 2) {
45935 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
45940 return newList.join(', ');
45941 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
45944 function parse294(capture) {
45946 var items = capture.split(',');
45947 items.forEach(function (item) {
45948 // item of form "from/to node/relation #ID"
45949 item = item.split(' '); // to/from role is more clear in quotes
45951 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
45953 var idType = item[1].slice(0, 1); // ID has # at the front
45955 var id = item[2].slice(1);
45956 id = linkEntity(idType + id);
45957 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
45959 return newList.join(', ');
45960 } // may or may not include the string "(including the name 'name')"
45963 function parse370(capture) {
45964 if (!capture) return '';
45965 var match = capture.match(/\(including the name (\'.+\')\)/);
45967 if (match && match.length) {
45968 return _t('QA.keepRight.errorTypes.370.including_the_name', {
45974 } // arbitrary node list of form: #ID,#ID,#ID...
45977 function parse20(capture) {
45979 var items = capture.split(',');
45980 items.forEach(function (item) {
45981 // ID has # at the front
45982 var id = linkEntity('n' + item.slice(1));
45985 return newList.join(', ');
45989 var serviceKeepRight = {
45990 title: 'keepRight',
45991 init: function init() {
45992 _mainFileFetcher.get('keepRight').then(function (d) {
45993 return _krData = d;
46000 this.event = utilRebind(this, dispatch$7, 'on');
46002 reset: function reset() {
46004 Object.values(_cache$2.inflightTile).forEach(abortRequest$6);
46016 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
46017 loadIssues: function loadIssues(projection) {
46023 }; // determine the needed tiles to cover the view
46025 var tiles = tiler$6.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
46027 abortUnwantedRequests$3(_cache$2, tiles); // issue new requests..
46029 tiles.forEach(function (tile) {
46030 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
46032 var _tile$extent$rectangl = tile.extent.rectangle(),
46033 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46034 left = _tile$extent$rectangl2[0],
46035 top = _tile$extent$rectangl2[1],
46036 right = _tile$extent$rectangl2[2],
46037 bottom = _tile$extent$rectangl2[3];
46039 var params = Object.assign({}, options, {
46045 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
46046 var controller = new AbortController();
46047 _cache$2.inflightTile[tile.id] = controller;
46049 signal: controller.signal
46050 }).then(function (data) {
46051 delete _cache$2.inflightTile[tile.id];
46052 _cache$2.loadedTile[tile.id] = true;
46054 if (!data || !data.features || !data.features.length) {
46055 throw new Error('No Data');
46058 data.features.forEach(function (feature) {
46059 var _feature$properties = feature.properties,
46060 itemType = _feature$properties.error_type,
46061 id = _feature$properties.error_id,
46062 _feature$properties$c = _feature$properties.comment,
46063 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
46064 objectId = _feature$properties.object_id,
46065 objectType = _feature$properties.object_type,
46066 schema = _feature$properties.schema,
46067 title = _feature$properties.title;
46068 var loc = feature.geometry.coordinates,
46069 _feature$properties$d = feature.properties.description,
46070 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
46071 // Error 191 = "highway-highway"
46072 // Error 190 = "intersections without junctions" (parent)
46074 var issueTemplate = _krData.errorTypes[itemType];
46075 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
46077 var whichType = issueTemplate ? itemType : parentIssueType;
46078 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
46079 // This is done to make them easier to linkify and translate.
46081 switch (whichType) {
46083 description = "This feature has a FIXME tag: ".concat(description);
46088 description = description.replace('A turn-', 'This turn-');
46096 description = "This turn-restriction~".concat(description);
46100 description = 'This highway is missing a maxspeed tag';
46106 description = "This feature~".concat(description);
46108 } // move markers slightly so it doesn't obscure the geometry,
46109 // then move markers away from other coincident markers
46112 var coincident = false;
46115 // first time, move marker up. after that, move marker right.
46116 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
46117 loc = geoVecAdd(loc, delta);
46118 var bbox = geoExtent(loc).bbox();
46119 coincident = _cache$2.rtree.search(bbox).length;
46120 } while (coincident);
46122 var d = new QAItem(loc, _this, itemType, id, {
46124 description: description,
46125 whichType: whichType,
46126 parentIssueType: parentIssueType,
46127 severity: whichTemplate.severity || 'error',
46128 objectId: objectId,
46129 objectType: objectType,
46133 d.replacements = tokenReplacements(d);
46134 _cache$2.data[id] = d;
46136 _cache$2.rtree.insert(encodeIssueRtree$2(d));
46138 dispatch$7.call('loaded');
46139 })["catch"](function () {
46140 delete _cache$2.inflightTile[tile.id];
46141 _cache$2.loadedTile[tile.id] = true;
46145 postUpdate: function postUpdate(d, callback) {
46148 if (_cache$2.inflightPost[d.id]) {
46150 message: 'Error update already inflight',
46161 params.st = d.newStatus;
46164 if (d.newComment !== undefined) {
46165 params.co = d.newComment;
46166 } // NOTE: This throws a CORS err, but it seems successful.
46167 // We don't care too much about the response, so this is fine.
46170 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
46171 var controller = new AbortController();
46172 _cache$2.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
46173 // (worst case scenario the request truly fails and issue will show up if iD restarts)
46176 signal: controller.signal
46177 })["finally"](function () {
46178 delete _cache$2.inflightPost[d.id];
46180 if (d.newStatus === 'ignore') {
46181 // ignore permanently (false positive)
46182 _this2.removeItem(d);
46183 } else if (d.newStatus === 'ignore_t') {
46184 // ignore temporarily (error fixed)
46185 _this2.removeItem(d);
46187 _cache$2.closed["".concat(d.schema, ":").concat(d.id)] = true;
46189 d = _this2.replaceItem(d.update({
46190 comment: d.newComment,
46191 newComment: undefined,
46192 newState: undefined
46196 if (callback) callback(null, d);
46199 // Get all cached QAItems covering the viewport
46200 getItems: function getItems(projection) {
46201 var viewport = projection.clipExtent();
46202 var min = [viewport[0][0], viewport[1][1]];
46203 var max = [viewport[1][0], viewport[0][1]];
46204 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46205 return _cache$2.rtree.search(bbox).map(function (d) {
46209 // Get a QAItem from cache
46210 // NOTE: Don't change method name until UI v3 is merged
46211 getError: function getError(id) {
46212 return _cache$2.data[id];
46214 // Replace a single QAItem in the cache
46215 replaceItem: function replaceItem(item) {
46216 if (!(item instanceof QAItem) || !item.id) return;
46217 _cache$2.data[item.id] = item;
46218 updateRtree$3(encodeIssueRtree$2(item), true); // true = replace
46222 // Remove a single QAItem from the cache
46223 removeItem: function removeItem(item) {
46224 if (!(item instanceof QAItem) || !item.id) return;
46225 delete _cache$2.data[item.id];
46226 updateRtree$3(encodeIssueRtree$2(item), false); // false = remove
46228 issueURL: function issueURL(item) {
46229 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
46231 // Get an array of issues closed during this session.
46232 // Used to populate `closed:keepright` changeset tag
46233 getClosedIDs: function getClosedIDs() {
46234 return Object.keys(_cache$2.closed).sort();
46238 var tiler$5 = utilTiler();
46239 var dispatch$6 = dispatch$8('loaded');
46240 var _tileZoom$2 = 14;
46241 var _impOsmUrls = {
46242 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
46243 mr: 'https://grab.community.improve-osm.org/missingGeoService',
46244 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
46246 var _impOsmData = {
46248 }; // This gets reassigned if reset
46252 function abortRequest$5(i) {
46253 Object.values(i).forEach(function (controller) {
46255 controller.abort();
46260 function abortUnwantedRequests$2(cache, tiles) {
46261 Object.keys(cache.inflightTile).forEach(function (k) {
46262 var wanted = tiles.find(function (tile) {
46263 return k === tile.id;
46267 abortRequest$5(cache.inflightTile[k]);
46268 delete cache.inflightTile[k];
46273 function encodeIssueRtree$1(d) {
46281 } // Replace or remove QAItem from rtree
46284 function updateRtree$2(item, replace) {
46285 _cache$1.rtree.remove(item, function (a, b) {
46286 return a.data.id === b.data.id;
46290 _cache$1.rtree.insert(item);
46294 function linkErrorObject(d) {
46295 return "<a class=\"error_object_link\">".concat(d, "</a>");
46298 function linkEntity(d) {
46299 return "<a class=\"error_entity_link\">".concat(d, "</a>");
46302 function pointAverage(points) {
46303 if (points.length) {
46304 var sum = points.reduce(function (acc, point) {
46305 return geoVecAdd(acc, [point.lon, point.lat]);
46307 return geoVecScale(sum, 1 / points.length);
46313 function relativeBearing(p1, p2) {
46314 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
46317 angle += 2 * Math.PI;
46318 } // Return degrees
46321 return angle * 180 / Math.PI;
46322 } // Assuming range [0,360)
46325 function cardinalDirection(bearing) {
46326 var dir = 45 * Math.round(bearing / 45);
46338 return _t("QA.improveOSM.directions.".concat(compass[dir]));
46339 } // Errors shouldn't obscure each other
46342 function preventCoincident$1(loc, bumpUp) {
46343 var coincident = false;
46346 // first time, move marker up. after that, move marker right.
46347 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
46348 loc = geoVecAdd(loc, delta);
46349 var bbox = geoExtent(loc).bbox();
46350 coincident = _cache$1.rtree.search(bbox).length;
46351 } while (coincident);
46356 var serviceImproveOSM = {
46357 title: 'improveOSM',
46358 init: function init() {
46359 _mainFileFetcher.get('qa_data').then(function (d) {
46360 return _impOsmData = d.improveOSM;
46367 this.event = utilRebind(this, dispatch$6, 'on');
46369 reset: function reset() {
46371 Object.values(_cache$1.inflightTile).forEach(abortRequest$5);
46383 loadIssues: function loadIssues(projection) {
46389 zoom: '19' // Use a high zoom so that clusters aren't returned
46391 }; // determine the needed tiles to cover the view
46393 var tiles = tiler$5.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
46395 abortUnwantedRequests$2(_cache$1, tiles); // issue new requests..
46397 tiles.forEach(function (tile) {
46398 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
46400 var _tile$extent$rectangl = tile.extent.rectangle(),
46401 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46402 east = _tile$extent$rectangl2[0],
46403 north = _tile$extent$rectangl2[1],
46404 west = _tile$extent$rectangl2[2],
46405 south = _tile$extent$rectangl2[3];
46407 var params = Object.assign({}, options, {
46412 }); // 3 separate requests to store for each tile
46415 Object.keys(_impOsmUrls).forEach(function (k) {
46416 // We exclude WATER from missing geometry as it doesn't seem useful
46417 // We use most confident one-way and turn restrictions only, still have false positives
46418 var kParams = Object.assign({}, params, k === 'mr' ? {
46419 type: 'PARKING,ROAD,BOTH,PATH'
46421 confidenceLevel: 'C1'
46423 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
46424 var controller = new AbortController();
46425 requests[k] = controller;
46427 signal: controller.signal
46428 }).then(function (data) {
46429 delete _cache$1.inflightTile[tile.id][k];
46431 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46432 delete _cache$1.inflightTile[tile.id];
46433 _cache$1.loadedTile[tile.id] = true;
46434 } // Road segments at high zoom == oneways
46437 if (data.roadSegments) {
46438 data.roadSegments.forEach(function (feature) {
46439 // Position error at the approximate middle of the segment
46440 var points = feature.points,
46441 wayId = feature.wayId,
46442 fromNodeId = feature.fromNodeId,
46443 toNodeId = feature.toNodeId;
46444 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
46445 var mid = points.length / 2;
46446 var loc; // Even number of points, find midpoint of the middle two
46447 // Odd number of points, use position of very middle point
46449 if (mid % 1 === 0) {
46450 loc = pointAverage([points[mid - 1], points[mid]]);
46452 mid = points[Math.floor(mid)];
46453 loc = [mid.lon, mid.lat];
46454 } // One-ways can land on same segment in opposite direction
46457 loc = preventCoincident$1(loc, false);
46458 var d = new QAItem(loc, _this, k, itemId, {
46460 // used as a category
46462 // used to post changes
46464 fromNodeId: fromNodeId,
46469 }); // Variables used in the description
46472 percentage: feature.percentOfTrips,
46473 num_trips: feature.numberOfTrips,
46474 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
46475 from_node: linkEntity('n' + feature.fromNodeId),
46476 to_node: linkEntity('n' + feature.toNodeId)
46478 _cache$1.data[d.id] = d;
46480 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46482 } // Tiles at high zoom == missing roads
46486 data.tiles.forEach(function (feature) {
46487 var type = feature.type,
46490 numberOfTrips = feature.numberOfTrips;
46491 var geoType = type.toLowerCase();
46492 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
46493 // Missing geometry could happen to land on another error
46495 var loc = pointAverage(feature.points);
46496 loc = preventCoincident$1(loc, false);
46497 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
46505 num_trips: numberOfTrips,
46506 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
46507 }; // -1 trips indicates data came from a 3rd party
46509 if (numberOfTrips === -1) {
46510 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
46513 _cache$1.data[d.id] = d;
46515 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46517 } // Entities at high zoom == turn restrictions
46520 if (data.entities) {
46521 data.entities.forEach(function (feature) {
46522 var point = feature.point,
46524 segments = feature.segments,
46525 numberOfPasses = feature.numberOfPasses,
46526 turnType = feature.turnType;
46527 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
46528 // We also want to bump the error up so node is accessible
46530 var loc = preventCoincident$1([point.lon, point.lat], true); // Elements are presented in a strange way
46532 var ids = id.split(',');
46533 var from_way = ids[0];
46534 var via_node = ids[3];
46535 var to_way = ids[2].split(':')[1];
46536 var d = new QAItem(loc, _this, k, itemId, {
46539 objectId: via_node,
46541 }); // Travel direction along from_way clarifies the turn restriction
46543 var _segments$0$points = _slicedToArray(segments[0].points, 2),
46544 p1 = _segments$0$points[0],
46545 p2 = _segments$0$points[1];
46547 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
46550 num_passed: numberOfPasses,
46551 num_trips: segments[0].numberOfTrips,
46552 turn_restriction: turnType.toLowerCase(),
46553 from_way: linkEntity('w' + from_way),
46554 to_way: linkEntity('w' + to_way),
46555 travel_direction: dir_of_travel,
46556 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
46558 _cache$1.data[d.id] = d;
46560 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46562 dispatch$6.call('loaded');
46565 })["catch"](function () {
46566 delete _cache$1.inflightTile[tile.id][k];
46568 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46569 delete _cache$1.inflightTile[tile.id];
46570 _cache$1.loadedTile[tile.id] = true;
46574 _cache$1.inflightTile[tile.id] = requests;
46577 getComments: function getComments(item) {
46580 // If comments already retrieved no need to do so again
46581 if (item.comments) {
46582 return Promise.resolve(item);
46585 var key = item.issueKey;
46588 if (key === 'ow') {
46589 qParams = item.identifier;
46590 } else if (key === 'mr') {
46591 qParams.tileX = item.identifier.x;
46592 qParams.tileY = item.identifier.y;
46593 } else if (key === 'tr') {
46594 qParams.targetId = item.identifier;
46597 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
46599 var cacheComments = function cacheComments(data) {
46600 // Assign directly for immediate use afterwards
46601 // comments are served newest to oldest
46602 item.comments = data.comments ? data.comments.reverse() : [];
46604 _this2.replaceItem(item);
46607 return d3_json(url).then(cacheComments).then(function () {
46611 postUpdate: function postUpdate(d, callback) {
46612 if (!serviceOsm.authenticated()) {
46613 // Username required in payload
46615 message: 'Not Authenticated',
46620 if (_cache$1.inflightPost[d.id]) {
46622 message: 'Error update already inflight',
46625 } // Payload can only be sent once username is established
46628 serviceOsm.userDetails(sendPayload.bind(this));
46630 function sendPayload(err, user) {
46634 return callback(err, d);
46637 var key = d.issueKey;
46638 var url = "".concat(_impOsmUrls[key], "/comment");
46640 username: user.display_name,
46641 targetIds: [d.identifier]
46645 payload.status = d.newStatus;
46646 payload.text = 'status changed';
46647 } // Comment take place of default text
46650 if (d.newComment) {
46651 payload.text = d.newComment;
46654 var controller = new AbortController();
46655 _cache$1.inflightPost[d.id] = controller;
46658 signal: controller.signal,
46659 body: JSON.stringify(payload)
46661 d3_json(url, options).then(function () {
46662 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
46664 if (!d.newStatus) {
46665 var now = new Date();
46666 var comments = d.comments ? d.comments : [];
46668 username: payload.username,
46669 text: payload.text,
46670 timestamp: now.getTime() / 1000
46673 _this3.replaceItem(d.update({
46674 comments: comments,
46675 newComment: undefined
46678 _this3.removeItem(d);
46680 if (d.newStatus === 'SOLVED') {
46681 // Keep track of the number of issues closed per type to tag the changeset
46682 if (!(d.issueKey in _cache$1.closed)) {
46683 _cache$1.closed[d.issueKey] = 0;
46686 _cache$1.closed[d.issueKey] += 1;
46690 if (callback) callback(null, d);
46691 })["catch"](function (err) {
46692 delete _cache$1.inflightPost[d.id];
46693 if (callback) callback(err.message);
46697 // Get all cached QAItems covering the viewport
46698 getItems: function getItems(projection) {
46699 var viewport = projection.clipExtent();
46700 var min = [viewport[0][0], viewport[1][1]];
46701 var max = [viewport[1][0], viewport[0][1]];
46702 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46703 return _cache$1.rtree.search(bbox).map(function (d) {
46707 // Get a QAItem from cache
46708 // NOTE: Don't change method name until UI v3 is merged
46709 getError: function getError(id) {
46710 return _cache$1.data[id];
46712 // get the name of the icon to display for this item
46713 getIcon: function getIcon(itemType) {
46714 return _impOsmData.icons[itemType];
46716 // Replace a single QAItem in the cache
46717 replaceItem: function replaceItem(issue) {
46718 if (!(issue instanceof QAItem) || !issue.id) return;
46719 _cache$1.data[issue.id] = issue;
46720 updateRtree$2(encodeIssueRtree$1(issue), true); // true = replace
46724 // Remove a single QAItem from the cache
46725 removeItem: function removeItem(issue) {
46726 if (!(issue instanceof QAItem) || !issue.id) return;
46727 delete _cache$1.data[issue.id];
46728 updateRtree$2(encodeIssueRtree$1(issue), false); // false = remove
46730 // Used to populate `closed:improveosm:*` changeset tags
46731 getClosedCounts: function getClosedCounts() {
46732 return _cache$1.closed;
46736 var defaults$5 = createCommonjsModule(function (module) {
46737 function getDefaults() {
46745 langPrefix: 'language-',
46753 smartypants: false,
46760 function changeDefaults(newDefaults) {
46761 module.exports.defaults = newDefaults;
46765 defaults: getDefaults(),
46766 getDefaults: getDefaults,
46767 changeDefaults: changeDefaults
46774 var escapeTest = /[&<>"']/;
46775 var escapeReplace = /[&<>"']/g;
46776 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
46777 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
46778 var escapeReplacements = {
46786 var getEscapeReplacement = function getEscapeReplacement(ch) {
46787 return escapeReplacements[ch];
46790 function escape$3(html, encode) {
46792 if (escapeTest.test(html)) {
46793 return html.replace(escapeReplace, getEscapeReplacement);
46796 if (escapeTestNoEncode.test(html)) {
46797 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
46804 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
46806 function unescape$2(html) {
46807 // explicitly match decimal, hex, and named HTML entities
46808 return html.replace(unescapeTest, function (_, n) {
46809 n = n.toLowerCase();
46810 if (n === 'colon') return ':';
46812 if (n.charAt(0) === '#') {
46813 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
46820 var caret = /(^|[^\[])\^/g;
46822 function edit$1(regex, opt) {
46823 regex = regex.source || regex;
46826 replace: function replace(name, val) {
46827 val = val.source || val;
46828 val = val.replace(caret, '$1');
46829 regex = regex.replace(name, val);
46832 getRegex: function getRegex() {
46833 return new RegExp(regex, opt);
46839 var nonWordAndColonTest = /[^\w:]/g;
46840 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
46842 function cleanUrl$1(sanitize, base, href) {
46847 prot = decodeURIComponent(unescape$2(href)).replace(nonWordAndColonTest, '').toLowerCase();
46852 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
46857 if (base && !originIndependentUrl.test(href)) {
46858 href = resolveUrl$1(base, href);
46862 href = encodeURI(href).replace(/%25/g, '%');
46871 var justDomain = /^[^:]+:\/*[^/]*$/;
46872 var protocol = /^([^:]+:)[\s\S]*$/;
46873 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
46875 function resolveUrl$1(base, href) {
46876 if (!baseUrls[' ' + base]) {
46877 // we can ignore everything in base after the last slash of its path component,
46878 // but we might need to add _that_
46879 // https://tools.ietf.org/html/rfc3986#section-3
46880 if (justDomain.test(base)) {
46881 baseUrls[' ' + base] = base + '/';
46883 baseUrls[' ' + base] = rtrim$1(base, '/', true);
46887 base = baseUrls[' ' + base];
46888 var relativeBase = base.indexOf(':') === -1;
46890 if (href.substring(0, 2) === '//') {
46891 if (relativeBase) {
46895 return base.replace(protocol, '$1') + href;
46896 } else if (href.charAt(0) === '/') {
46897 if (relativeBase) {
46901 return base.replace(domain, '$1') + href;
46903 return base + href;
46908 exec: function noopTest() {}
46911 function merge$2(obj) {
46916 for (; i < arguments.length; i++) {
46917 target = arguments[i];
46919 for (key in target) {
46920 if (Object.prototype.hasOwnProperty.call(target, key)) {
46921 obj[key] = target[key];
46929 function splitCells$1(tableRow, count) {
46930 // ensure that every cell-delimiting pipe has a space
46931 // before it to distinguish it from an escaped pipe
46932 var row = tableRow.replace(/\|/g, function (match, offset, str) {
46933 var escaped = false,
46936 while (--curr >= 0 && str[curr] === '\\') {
46937 escaped = !escaped;
46941 // odd number of slashes means | is escaped
46942 // so we leave it alone
46945 // add space before unescaped |
46949 cells = row.split(/ \|/);
46952 if (cells.length > count) {
46953 cells.splice(count);
46955 while (cells.length < count) {
46960 for (; i < cells.length; i++) {
46961 // leading or trailing whitespace is ignored per the gfm spec
46962 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
46966 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
46967 // /c*$/ is vulnerable to REDOS.
46968 // invert: Remove suffix of non-c chars instead. Default falsey.
46971 function rtrim$1(str, c, invert) {
46972 var l = str.length;
46976 } // Length of suffix matching the invert condition.
46979 var suffLen = 0; // Step left until we fail to match the invert condition.
46981 while (suffLen < l) {
46982 var currChar = str.charAt(l - suffLen - 1);
46984 if (currChar === c && !invert) {
46986 } else if (currChar !== c && invert) {
46993 return str.substr(0, l - suffLen);
46996 function findClosingBracket$1(str, b) {
46997 if (str.indexOf(b[1]) === -1) {
47001 var l = str.length;
47005 for (; i < l; i++) {
47006 if (str[i] === '\\') {
47008 } else if (str[i] === b[0]) {
47010 } else if (str[i] === b[1]) {
47022 function checkSanitizeDeprecation$1(opt) {
47023 if (opt && opt.sanitize && !opt.silent) {
47024 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');
47026 } // copied from https://stackoverflow.com/a/5450113/806777
47029 function repeatString$1(pattern, count) {
47036 while (count > 1) {
47042 pattern += pattern;
47045 return result + pattern;
47050 unescape: unescape$2,
47052 cleanUrl: cleanUrl$1,
47053 resolveUrl: resolveUrl$1,
47054 noopTest: noopTest$1,
47056 splitCells: splitCells$1,
47058 findClosingBracket: findClosingBracket$1,
47059 checkSanitizeDeprecation: checkSanitizeDeprecation$1,
47060 repeatString: repeatString$1
47063 var defaults$4 = defaults$5.defaults;
47064 var rtrim = helpers.rtrim,
47065 splitCells = helpers.splitCells,
47066 _escape = helpers.escape,
47067 findClosingBracket = helpers.findClosingBracket;
47069 function outputLink(cap, link, raw) {
47070 var href = link.href;
47071 var title = link.title ? _escape(link.title) : null;
47072 var text = cap[1].replace(/\\([\[\]])/g, '$1');
47074 if (cap[0].charAt(0) !== '!') {
47088 text: _escape(text)
47093 function indentCodeCompensation(raw, text) {
47094 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
47096 if (matchIndentToCode === null) {
47100 var indentToCode = matchIndentToCode[1];
47101 return text.split('\n').map(function (node) {
47102 var matchIndentInNode = node.match(/^\s+/);
47104 if (matchIndentInNode === null) {
47108 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
47109 indentInNode = _matchIndentInNode[0];
47111 if (indentInNode.length >= indentToCode.length) {
47112 return node.slice(indentToCode.length);
47123 var Tokenizer_1 = /*#__PURE__*/function () {
47124 function Tokenizer(options) {
47125 _classCallCheck$1(this, Tokenizer);
47127 this.options = options || defaults$4;
47130 _createClass$1(Tokenizer, [{
47132 value: function space(src) {
47133 var cap = this.rules.block.newline.exec(src);
47136 if (cap[0].length > 1) {
47150 value: function code(src) {
47151 var cap = this.rules.block.code.exec(src);
47154 var text = cap[0].replace(/^ {1,4}/gm, '');
47158 codeBlockStyle: 'indented',
47159 text: !this.options.pedantic ? rtrim(text, '\n') : text
47165 value: function fences(src) {
47166 var cap = this.rules.block.fences.exec(src);
47170 var text = indentCodeCompensation(raw, cap[3] || '');
47174 lang: cap[2] ? cap[2].trim() : cap[2],
47181 value: function heading(src) {
47182 var cap = this.rules.block.heading.exec(src);
47185 var text = cap[2].trim(); // remove trailing #s
47187 if (/#$/.test(text)) {
47188 var trimmed = rtrim(text, '#');
47190 if (this.options.pedantic) {
47191 text = trimmed.trim();
47192 } else if (!trimmed || / $/.test(trimmed)) {
47193 // CommonMark requires space before trailing #s
47194 text = trimmed.trim();
47201 depth: cap[1].length,
47208 value: function nptable(src) {
47209 var cap = this.rules.block.nptable.exec(src);
47214 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47215 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47216 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
47220 if (item.header.length === item.align.length) {
47221 var l = item.align.length;
47224 for (i = 0; i < l; i++) {
47225 if (/^ *-+: *$/.test(item.align[i])) {
47226 item.align[i] = 'right';
47227 } else if (/^ *:-+: *$/.test(item.align[i])) {
47228 item.align[i] = 'center';
47229 } else if (/^ *:-+ *$/.test(item.align[i])) {
47230 item.align[i] = 'left';
47232 item.align[i] = null;
47236 l = item.cells.length;
47238 for (i = 0; i < l; i++) {
47239 item.cells[i] = splitCells(item.cells[i], item.header.length);
47248 value: function hr(src) {
47249 var cap = this.rules.block.hr.exec(src);
47260 value: function blockquote(src) {
47261 var cap = this.rules.block.blockquote.exec(src);
47264 var text = cap[0].replace(/^ *> ?/gm, '');
47266 type: 'blockquote',
47274 value: function list(src) {
47275 var cap = this.rules.block.list.exec(src);
47280 var isordered = bull.length > 1;
47284 ordered: isordered,
47285 start: isordered ? +bull.slice(0, -1) : '',
47288 }; // Get each top-level item.
47290 var itemMatch = cap[0].match(this.rules.block.item);
47301 var l = itemMatch.length;
47302 bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
47304 for (var i = 0; i < l; i++) {
47305 item = itemMatch[i];
47308 if (!this.options.pedantic) {
47309 // Determine if current item contains the end of the list
47310 endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S'));
47313 addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length;
47314 list.raw = list.raw.substring(0, list.raw.length - addBack);
47315 item = item.substring(0, endMatch.index);
47319 } // Determine whether the next list item belongs here.
47320 // Backpedal if it does not belong in this list.
47324 bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
47326 if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) {
47327 // nested list or continuation
47328 itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]);
47332 } else if ( // different bullet style
47333 !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
47334 addBack = itemMatch.slice(i + 1).join('\n').length;
47335 list.raw = list.raw.substring(0, list.raw.length - addBack);
47340 } // Remove the list item's bullet
47341 // so it is seen as the next token.
47344 space = item.length;
47345 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
47346 // list item contains. Hacky.
47348 if (~item.indexOf('\n ')) {
47349 space -= item.length;
47350 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
47351 } // trim item newlines at end
47354 item = rtrim(item, '\n');
47358 } // Determine whether item is loose or not.
47359 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
47360 // for discount behavior.
47363 loose = next || /\n\n(?!\s*$)/.test(raw);
47366 next = raw.slice(-2) === '\n\n';
47367 if (!loose) loose = next;
47372 } // Check for task list items
47375 if (this.options.gfm) {
47376 istask = /^\[[ xX]\] /.test(item);
47377 ischecked = undefined;
47380 ischecked = item[1] !== ' ';
47381 item = item.replace(/^\[[ xX]\] +/, '');
47389 checked: ischecked,
47400 value: function html(src) {
47401 var cap = this.rules.block.html.exec(src);
47405 type: this.options.sanitize ? 'paragraph' : 'html',
47407 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
47408 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47414 value: function def(src) {
47415 var cap = this.rules.block.def.exec(src);
47418 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
47419 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
47431 value: function table(src) {
47432 var cap = this.rules.block.table.exec(src);
47437 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47438 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47439 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
47442 if (item.header.length === item.align.length) {
47444 var l = item.align.length;
47447 for (i = 0; i < l; i++) {
47448 if (/^ *-+: *$/.test(item.align[i])) {
47449 item.align[i] = 'right';
47450 } else if (/^ *:-+: *$/.test(item.align[i])) {
47451 item.align[i] = 'center';
47452 } else if (/^ *:-+ *$/.test(item.align[i])) {
47453 item.align[i] = 'left';
47455 item.align[i] = null;
47459 l = item.cells.length;
47461 for (i = 0; i < l; i++) {
47462 item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
47471 value: function lheading(src) {
47472 var cap = this.rules.block.lheading.exec(src);
47478 depth: cap[2].charAt(0) === '=' ? 1 : 2,
47485 value: function paragraph(src) {
47486 var cap = this.rules.block.paragraph.exec(src);
47492 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
47498 value: function text(src) {
47499 var cap = this.rules.block.text.exec(src);
47511 value: function escape(src) {
47512 var cap = this.rules.inline.escape.exec(src);
47518 text: _escape(cap[1])
47524 value: function tag(src, inLink, inRawBlock) {
47525 var cap = this.rules.inline.tag.exec(src);
47528 if (!inLink && /^<a /i.test(cap[0])) {
47530 } else if (inLink && /^<\/a>/i.test(cap[0])) {
47534 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47536 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47537 inRawBlock = false;
47541 type: this.options.sanitize ? 'text' : 'html',
47544 inRawBlock: inRawBlock,
47545 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47551 value: function link(src) {
47552 var cap = this.rules.inline.link.exec(src);
47555 var trimmedUrl = cap[2].trim();
47557 if (!this.options.pedantic && /^</.test(trimmedUrl)) {
47558 // commonmark requires matching angle brackets
47559 if (!/>$/.test(trimmedUrl)) {
47561 } // ending angle bracket cannot be escaped
47564 var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
47566 if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
47570 // find closing parenthesis
47571 var lastParenIndex = findClosingBracket(cap[2], '()');
47573 if (lastParenIndex > -1) {
47574 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
47575 var linkLen = start + cap[1].length + lastParenIndex;
47576 cap[2] = cap[2].substring(0, lastParenIndex);
47577 cap[0] = cap[0].substring(0, linkLen).trim();
47585 if (this.options.pedantic) {
47586 // split pedantic href and title
47587 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
47594 title = cap[3] ? cap[3].slice(1, -1) : '';
47597 href = href.trim();
47599 if (/^</.test(href)) {
47600 if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
47601 // pedantic allows starting angle bracket without ending angle bracket
47602 href = href.slice(1);
47604 href = href.slice(1, -1);
47608 return outputLink(cap, {
47609 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
47610 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
47616 value: function reflink(src, links) {
47619 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
47620 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
47621 link = links[link.toLowerCase()];
47623 if (!link || !link.href) {
47624 var text = cap[0].charAt(0);
47632 return outputLink(cap, link, cap[0]);
47637 value: function emStrong(src, maskedSrc) {
47638 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
47639 var match = this.rules.inline.emStrong.lDelim.exec(src);
47640 if (!match) return; // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
47642 if (match[3] && prevChar.match(/(?:[0-9A-Za-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08C7\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\u9FFC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7BF\uA7C2-\uA7CA\uA7F5-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDF00-\uDF1A\uDF30-\uDF3B]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDEE0-\uDEF2\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE96\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82C[\uDC00-\uDD1E\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD834[\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDD\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])/)) return;
47643 var nextChar = match[1] || match[2] || '';
47645 if (!nextChar || nextChar && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar))) {
47646 var lLength = match[0].length - 1;
47649 delimTotal = lLength,
47651 var endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
47652 endReg.lastIndex = 0; // Clip maskedSrc to same section of string as src (move to lexer?)
47654 maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
47656 while ((match = endReg.exec(maskedSrc)) != null) {
47657 rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
47658 if (!rDelim) continue; // skip single * in __abc*abc__
47660 rLength = rDelim.length;
47662 if (match[3] || match[4]) {
47663 // found another Left Delim
47664 delimTotal += rLength;
47666 } else if (match[5] || match[6]) {
47667 // either Left or Right Delim
47668 if (lLength % 3 && !((lLength + rLength) % 3)) {
47669 midDelimTotal += rLength;
47670 continue; // CommonMark Emphasis Rules 9-10
47674 delimTotal -= rLength;
47675 if (delimTotal > 0) continue; // Haven't found enough closing delimiters
47676 // Remove extra characters. *a*** -> *a*
47678 rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); // Create `em` if smallest delimiter has odd char count. *a***
47680 if (Math.min(lLength, rLength) % 2) {
47683 raw: src.slice(0, lLength + match.index + rLength + 1),
47684 text: src.slice(1, lLength + match.index + rLength)
47686 } // Create 'strong' if smallest delimiter has even char count. **a***
47691 raw: src.slice(0, lLength + match.index + rLength + 1),
47692 text: src.slice(2, lLength + match.index + rLength - 1)
47699 value: function codespan(src) {
47700 var cap = this.rules.inline.code.exec(src);
47703 var text = cap[2].replace(/\n/g, ' ');
47704 var hasNonSpaceChars = /[^ ]/.test(text);
47705 var hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
47707 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
47708 text = text.substring(1, text.length - 1);
47711 text = _escape(text, true);
47721 value: function br(src) {
47722 var cap = this.rules.inline.br.exec(src);
47733 value: function del(src) {
47734 var cap = this.rules.inline.del.exec(src);
47746 value: function autolink(src, mangle) {
47747 var cap = this.rules.inline.autolink.exec(src);
47752 if (cap[2] === '@') {
47753 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
47754 href = 'mailto:' + text;
47756 text = _escape(cap[1]);
47775 value: function url(src, mangle) {
47778 if (cap = this.rules.inline.url.exec(src)) {
47781 if (cap[2] === '@') {
47782 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
47783 href = 'mailto:' + text;
47785 // do extended autolink path validation
47789 prevCapZero = cap[0];
47790 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
47791 } while (prevCapZero !== cap[0]);
47793 text = _escape(cap[0]);
47795 if (cap[1] === 'www.') {
47796 href = 'http://' + text;
47817 value: function inlineText(src, inRawBlock, smartypants) {
47818 var cap = this.rules.inline.text.exec(src);
47824 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
47826 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
47841 var noopTest = helpers.noopTest,
47842 edit = helpers.edit,
47843 merge$1 = helpers.merge;
47845 * Block-Level Grammar
47849 newline: /^(?: *(?:\n|$))+/,
47850 code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
47851 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
47852 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
47853 heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
47854 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
47855 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
47856 html: '^ {0,3}(?:' // optional indentation
47857 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
47858 + '|comment[^\\n]*(\\n+|$)' // (2)
47859 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
47860 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
47861 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
47862 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
47863 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
47864 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
47866 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
47869 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
47870 // regex template, placeholders will be replaced according to different paragraph
47871 // interruption rules of commonmark and the original markdown spec:
47872 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,
47875 block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
47876 block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
47877 block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex();
47878 block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/;
47879 block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
47880 block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex();
47881 block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex();
47882 block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex();
47883 block$1._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul';
47884 block$1._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
47885 block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
47886 block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
47887 .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
47888 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks
47890 block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex();
47892 * Normal Block Grammar
47895 block$1.normal = merge$1({}, block$1);
47897 * GFM Block Grammar
47900 block$1.gfm = merge$1({}, block$1.normal, {
47901 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
47902 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
47903 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
47905 table: '^ *\\|(.+)\\n' // Header
47906 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
47907 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
47910 block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
47911 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47913 block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
47914 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47917 * Pedantic grammar (original John Gruber's loose markdown specification)
47920 block$1.pedantic = merge$1({}, block$1.normal, {
47921 html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
47922 + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(),
47923 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
47924 heading: /^(#{1,6})(.*)(?:\n+|$)/,
47926 // fences not supported
47927 paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex()
47930 * Inline-Level Grammar
47934 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
47935 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
47937 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
47938 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
47939 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
47940 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
47941 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
47943 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
47944 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
47945 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
47946 reflinkSearch: 'reflink|nolink(?!\\()',
47948 lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,
47949 // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right.
47950 // () Skip other delimiter (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a
47951 rDelimAst: /\_\_[^_*]*?\*[^_*]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
47952 rDelimUnd: /\*\*[^_*]*?\_[^_*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _
47955 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
47956 br: /^( {2,}|\\)\n(?!\s*$)/,
47958 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,
47959 punctuation: /^([\spunctuation])/
47960 }; // list of punctuation marks from CommonMark spec
47961 // without * and _ to handle the different emphasis markers * and _
47963 inline$1._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
47964 inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
47966 inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g;
47967 inline$1.escapedEmSt = /\\\*|\\_/g;
47968 inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex();
47969 inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex();
47970 inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47971 inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47972 inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
47973 inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
47974 inline$1._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
47975 inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex();
47976 inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
47977 inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex();
47978 inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
47979 inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
47980 inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
47981 inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex();
47982 inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex();
47983 inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex();
47985 * Normal Inline Grammar
47988 inline$1.normal = merge$1({}, inline$1);
47990 * Pedantic Inline Grammar
47993 inline$1.pedantic = merge$1({}, inline$1.normal, {
47996 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
47997 endAst: /\*\*(?!\*)/g,
48002 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
48003 endAst: /\*(?!\*)/g,
48006 link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(),
48007 reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex()
48010 * GFM Inline Grammar
48013 inline$1.gfm = merge$1({}, inline$1.normal, {
48014 escape: edit(inline$1.escape).replace('])', '~|])').getRegex(),
48015 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
48016 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
48017 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
48018 del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
48019 text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
48021 inline$1.gfm.url = edit(inline$1.gfm.url, 'i').replace('email', inline$1.gfm._extended_email).getRegex();
48023 * GFM + Line Breaks Inline Grammar
48026 inline$1.breaks = merge$1({}, inline$1.gfm, {
48027 br: edit(inline$1.br).replace('{2,}', '*').getRegex(),
48028 text: edit(inline$1.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
48035 var defaults$3 = defaults$5.defaults;
48036 var block = rules.block,
48037 inline = rules.inline;
48038 var repeatString = helpers.repeatString;
48040 * smartypants text replacement
48043 function smartypants(text) {
48044 return text // em-dashes
48045 .replace(/---/g, "\u2014") // en-dashes
48046 .replace(/--/g, "\u2013") // opening singles
48047 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
48048 .replace(/'/g, "\u2019") // opening doubles
48049 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
48050 .replace(/"/g, "\u201D") // ellipses
48051 .replace(/\.{3}/g, "\u2026");
48054 * mangle email addresses
48058 function mangle(text) {
48062 var l = text.length;
48064 for (i = 0; i < l; i++) {
48065 ch = text.charCodeAt(i);
48067 if (Math.random() > 0.5) {
48068 ch = 'x' + ch.toString(16);
48071 out += '&#' + ch + ';';
48081 var Lexer_1 = /*#__PURE__*/function () {
48082 function Lexer(options) {
48083 _classCallCheck$1(this, Lexer);
48086 this.tokens.links = Object.create(null);
48087 this.options = options || defaults$3;
48088 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
48089 this.tokenizer = this.options.tokenizer;
48090 this.tokenizer.options = this.options;
48092 block: block.normal,
48093 inline: inline.normal
48096 if (this.options.pedantic) {
48097 rules.block = block.pedantic;
48098 rules.inline = inline.pedantic;
48099 } else if (this.options.gfm) {
48100 rules.block = block.gfm;
48102 if (this.options.breaks) {
48103 rules.inline = inline.breaks;
48105 rules.inline = inline.gfm;
48109 this.tokenizer.rules = rules;
48116 _createClass$1(Lexer, [{
48122 function lex(src) {
48123 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
48124 this.blockTokens(src, this.tokens, true);
48125 this.inline(this.tokens);
48126 return this.tokens;
48133 key: "blockTokens",
48134 value: function blockTokens(src) {
48135 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48136 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
48138 if (this.options.pedantic) {
48139 src = src.replace(/^ +$/gm, '');
48142 var token, i, l, lastToken;
48146 if (token = this.tokenizer.space(src)) {
48147 src = src.substring(token.raw.length);
48150 tokens.push(token);
48157 if (token = this.tokenizer.code(src)) {
48158 src = src.substring(token.raw.length);
48159 lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
48161 if (lastToken && lastToken.type === 'paragraph') {
48162 lastToken.raw += '\n' + token.raw;
48163 lastToken.text += '\n' + token.text;
48165 tokens.push(token);
48172 if (token = this.tokenizer.fences(src)) {
48173 src = src.substring(token.raw.length);
48174 tokens.push(token);
48179 if (token = this.tokenizer.heading(src)) {
48180 src = src.substring(token.raw.length);
48181 tokens.push(token);
48183 } // table no leading pipe (gfm)
48186 if (token = this.tokenizer.nptable(src)) {
48187 src = src.substring(token.raw.length);
48188 tokens.push(token);
48193 if (token = this.tokenizer.hr(src)) {
48194 src = src.substring(token.raw.length);
48195 tokens.push(token);
48200 if (token = this.tokenizer.blockquote(src)) {
48201 src = src.substring(token.raw.length);
48202 token.tokens = this.blockTokens(token.text, [], top);
48203 tokens.push(token);
48208 if (token = this.tokenizer.list(src)) {
48209 src = src.substring(token.raw.length);
48210 l = token.items.length;
48212 for (i = 0; i < l; i++) {
48213 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
48216 tokens.push(token);
48221 if (token = this.tokenizer.html(src)) {
48222 src = src.substring(token.raw.length);
48223 tokens.push(token);
48228 if (top && (token = this.tokenizer.def(src))) {
48229 src = src.substring(token.raw.length);
48231 if (!this.tokens.links[token.tag]) {
48232 this.tokens.links[token.tag] = {
48242 if (token = this.tokenizer.table(src)) {
48243 src = src.substring(token.raw.length);
48244 tokens.push(token);
48249 if (token = this.tokenizer.lheading(src)) {
48250 src = src.substring(token.raw.length);
48251 tokens.push(token);
48253 } // top-level paragraph
48256 if (top && (token = this.tokenizer.paragraph(src))) {
48257 src = src.substring(token.raw.length);
48258 tokens.push(token);
48263 if (token = this.tokenizer.text(src)) {
48264 src = src.substring(token.raw.length);
48265 lastToken = tokens[tokens.length - 1];
48267 if (lastToken && lastToken.type === 'text') {
48268 lastToken.raw += '\n' + token.raw;
48269 lastToken.text += '\n' + token.text;
48271 tokens.push(token);
48278 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48280 if (this.options.silent) {
48281 console.error(errMsg);
48284 throw new Error(errMsg);
48293 value: function inline(tokens) {
48294 var i, j, k, l2, row, token;
48295 var l = tokens.length;
48297 for (i = 0; i < l; i++) {
48300 switch (token.type) {
48306 this.inlineTokens(token.text, token.tokens);
48317 l2 = token.header.length;
48319 for (j = 0; j < l2; j++) {
48320 token.tokens.header[j] = [];
48321 this.inlineTokens(token.header[j], token.tokens.header[j]);
48325 l2 = token.cells.length;
48327 for (j = 0; j < l2; j++) {
48328 row = token.cells[j];
48329 token.tokens.cells[j] = [];
48331 for (k = 0; k < row.length; k++) {
48332 token.tokens.cells[j][k] = [];
48333 this.inlineTokens(row[k], token.tokens.cells[j][k]);
48342 this.inline(token.tokens);
48348 l2 = token.items.length;
48350 for (j = 0; j < l2; j++) {
48351 this.inline(token.items[j].tokens);
48366 key: "inlineTokens",
48367 value: function inlineTokens(src) {
48368 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48369 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
48370 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
48371 var token, lastToken; // String with links masked to avoid interference with em and strong
48373 var maskedSrc = src;
48375 var keepPrevChar, prevChar; // Mask out reflinks
48377 if (this.tokens.links) {
48378 var links = Object.keys(this.tokens.links);
48380 if (links.length > 0) {
48381 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
48382 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
48383 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
48387 } // Mask out other blocks
48390 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
48391 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
48392 } // Mask out escaped em & strong delimiters
48395 while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) {
48396 maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
48400 if (!keepPrevChar) {
48404 keepPrevChar = false; // escape
48406 if (token = this.tokenizer.escape(src)) {
48407 src = src.substring(token.raw.length);
48408 tokens.push(token);
48413 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
48414 src = src.substring(token.raw.length);
48415 inLink = token.inLink;
48416 inRawBlock = token.inRawBlock;
48417 var _lastToken = tokens[tokens.length - 1];
48419 if (_lastToken && token.type === 'text' && _lastToken.type === 'text') {
48420 _lastToken.raw += token.raw;
48421 _lastToken.text += token.text;
48423 tokens.push(token);
48430 if (token = this.tokenizer.link(src)) {
48431 src = src.substring(token.raw.length);
48433 if (token.type === 'link') {
48434 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48437 tokens.push(token);
48439 } // reflink, nolink
48442 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
48443 src = src.substring(token.raw.length);
48444 var _lastToken2 = tokens[tokens.length - 1];
48446 if (token.type === 'link') {
48447 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48448 tokens.push(token);
48449 } else if (_lastToken2 && token.type === 'text' && _lastToken2.type === 'text') {
48450 _lastToken2.raw += token.raw;
48451 _lastToken2.text += token.text;
48453 tokens.push(token);
48460 if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
48461 src = src.substring(token.raw.length);
48462 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48463 tokens.push(token);
48468 if (token = this.tokenizer.codespan(src)) {
48469 src = src.substring(token.raw.length);
48470 tokens.push(token);
48475 if (token = this.tokenizer.br(src)) {
48476 src = src.substring(token.raw.length);
48477 tokens.push(token);
48482 if (token = this.tokenizer.del(src)) {
48483 src = src.substring(token.raw.length);
48484 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48485 tokens.push(token);
48490 if (token = this.tokenizer.autolink(src, mangle)) {
48491 src = src.substring(token.raw.length);
48492 tokens.push(token);
48497 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
48498 src = src.substring(token.raw.length);
48499 tokens.push(token);
48504 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
48505 src = src.substring(token.raw.length);
48507 if (token.raw.slice(-1) !== '_') {
48508 // Track prevChar before string of ____ started
48509 prevChar = token.raw.slice(-1);
48512 keepPrevChar = true;
48513 lastToken = tokens[tokens.length - 1];
48515 if (lastToken && lastToken.type === 'text') {
48516 lastToken.raw += token.raw;
48517 lastToken.text += token.text;
48519 tokens.push(token);
48526 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48528 if (this.options.silent) {
48529 console.error(errMsg);
48532 throw new Error(errMsg);
48541 get: function get() {
48548 * Static Lex Method
48553 value: function lex(src, options) {
48554 var lexer = new Lexer(options);
48555 return lexer.lex(src);
48558 * Static Lex Inline Method
48563 value: function lexInline(src, options) {
48564 var lexer = new Lexer(options);
48565 return lexer.inlineTokens(src);
48572 var defaults$2 = defaults$5.defaults;
48573 var cleanUrl = helpers.cleanUrl,
48574 escape$2 = helpers.escape;
48579 var Renderer_1 = /*#__PURE__*/function () {
48580 function Renderer(options) {
48581 _classCallCheck$1(this, Renderer);
48583 this.options = options || defaults$2;
48586 _createClass$1(Renderer, [{
48588 value: function code(_code, infostring, escaped) {
48589 var lang = (infostring || '').match(/\S*/)[0];
48591 if (this.options.highlight) {
48592 var out = this.options.highlight(_code, lang);
48594 if (out != null && out !== _code) {
48600 _code = _code.replace(/\n$/, '') + '\n';
48603 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48606 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48610 value: function blockquote(quote) {
48611 return '<blockquote>\n' + quote + '</blockquote>\n';
48615 value: function html(_html) {
48620 value: function heading(text, level, raw, slugger) {
48621 if (this.options.headerIds) {
48622 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
48626 return '<h' + level + '>' + text + '</h' + level + '>\n';
48630 value: function hr() {
48631 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
48635 value: function list(body, ordered, start) {
48636 var type = ordered ? 'ol' : 'ul',
48637 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
48638 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
48642 value: function listitem(text) {
48643 return '<li>' + text + '</li>\n';
48647 value: function checkbox(checked) {
48648 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
48652 value: function paragraph(text) {
48653 return '<p>' + text + '</p>\n';
48657 value: function table(header, body) {
48658 if (body) body = '<tbody>' + body + '</tbody>';
48659 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
48663 value: function tablerow(content) {
48664 return '<tr>\n' + content + '</tr>\n';
48668 value: function tablecell(content, flags) {
48669 var type = flags.header ? 'th' : 'td';
48670 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
48671 return tag + content + '</' + type + '>\n';
48672 } // span level renderer
48676 value: function strong(text) {
48677 return '<strong>' + text + '</strong>';
48681 value: function em(text) {
48682 return '<em>' + text + '</em>';
48686 value: function codespan(text) {
48687 return '<code>' + text + '</code>';
48691 value: function br() {
48692 return this.options.xhtml ? '<br/>' : '<br>';
48696 value: function del(text) {
48697 return '<del>' + text + '</del>';
48701 value: function link(href, title, text) {
48702 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48704 if (href === null) {
48708 var out = '<a href="' + escape$2(href) + '"';
48711 out += ' title="' + title + '"';
48714 out += '>' + text + '</a>';
48719 value: function image(href, title, text) {
48720 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48722 if (href === null) {
48726 var out = '<img src="' + href + '" alt="' + text + '"';
48729 out += ' title="' + title + '"';
48732 out += this.options.xhtml ? '/>' : '>';
48737 value: function text(_text) {
48747 * returns only the textual part of the token
48749 var TextRenderer_1 = /*#__PURE__*/function () {
48750 function TextRenderer() {
48751 _classCallCheck$1(this, TextRenderer);
48754 _createClass$1(TextRenderer, [{
48756 value: // no need for block level renderers
48757 function strong(text) {
48762 value: function em(text) {
48767 value: function codespan(text) {
48772 value: function del(text) {
48777 value: function html(text) {
48782 value: function text(_text) {
48787 value: function link(href, title, text) {
48792 value: function image(href, title, text) {
48797 value: function br() {
48802 return TextRenderer;
48806 * Slugger generates header id
48808 var Slugger_1 = /*#__PURE__*/function () {
48809 function Slugger() {
48810 _classCallCheck$1(this, Slugger);
48815 _createClass$1(Slugger, [{
48817 value: function serialize(value) {
48818 return value.toLowerCase().trim() // remove html tags
48819 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
48820 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
48823 * Finds the next safe (unique) slug to use
48827 key: "getNextSafeSlug",
48828 value: function getNextSafeSlug(originalSlug, isDryRun) {
48829 var slug = originalSlug;
48830 var occurenceAccumulator = 0;
48832 if (this.seen.hasOwnProperty(slug)) {
48833 occurenceAccumulator = this.seen[originalSlug];
48836 occurenceAccumulator++;
48837 slug = originalSlug + '-' + occurenceAccumulator;
48838 } while (this.seen.hasOwnProperty(slug));
48842 this.seen[originalSlug] = occurenceAccumulator;
48843 this.seen[slug] = 0;
48849 * Convert string to unique id
48850 * @param {object} options
48851 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
48856 value: function slug(value) {
48857 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
48858 var slug = this.serialize(value);
48859 return this.getNextSafeSlug(slug, options.dryrun);
48866 var defaults$1 = defaults$5.defaults;
48867 var unescape$1 = helpers.unescape;
48869 * Parsing & Compiling
48872 var Parser_1 = /*#__PURE__*/function () {
48873 function Parser(options) {
48874 _classCallCheck$1(this, Parser);
48876 this.options = options || defaults$1;
48877 this.options.renderer = this.options.renderer || new Renderer_1();
48878 this.renderer = this.options.renderer;
48879 this.renderer.options = this.options;
48880 this.textRenderer = new TextRenderer_1();
48881 this.slugger = new Slugger_1();
48884 * Static Parse Method
48888 _createClass$1(Parser, [{
48894 function parse(tokens) {
48895 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
48915 var l = tokens.length;
48917 for (i = 0; i < l; i++) {
48920 switch (token.type) {
48928 out += this.renderer.hr();
48934 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
48940 out += this.renderer.code(token.text, token.lang, token.escaped);
48946 header = ''; // header
48949 l2 = token.header.length;
48951 for (j = 0; j < l2; j++) {
48952 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
48954 align: token.align[j]
48958 header += this.renderer.tablerow(cell);
48960 l2 = token.cells.length;
48962 for (j = 0; j < l2; j++) {
48963 row = token.tokens.cells[j];
48967 for (k = 0; k < l3; k++) {
48968 cell += this.renderer.tablecell(this.parseInline(row[k]), {
48970 align: token.align[k]
48974 body += this.renderer.tablerow(cell);
48977 out += this.renderer.table(header, body);
48983 body = this.parse(token.tokens);
48984 out += this.renderer.blockquote(body);
48990 ordered = token.ordered;
48991 start = token.start;
48992 loose = token.loose;
48993 l2 = token.items.length;
48996 for (j = 0; j < l2; j++) {
48997 item = token.items[j];
48998 checked = item.checked;
49003 checkbox = this.renderer.checkbox(checked);
49006 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
49007 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
49009 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
49010 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
49013 item.tokens.unshift({
49019 itemBody += checkbox;
49023 itemBody += this.parse(item.tokens, loose);
49024 body += this.renderer.listitem(itemBody, task, checked);
49027 out += this.renderer.list(body, ordered, start);
49033 // TODO parse inline content if parameter markdown=1
49034 out += this.renderer.html(token.text);
49040 out += this.renderer.paragraph(this.parseInline(token.tokens));
49046 body = token.tokens ? this.parseInline(token.tokens) : token.text;
49048 while (i + 1 < l && tokens[i + 1].type === 'text') {
49049 token = tokens[++i];
49050 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
49053 out += top ? this.renderer.paragraph(body) : body;
49059 var errMsg = 'Token with "' + token.type + '" type was not found.';
49061 if (this.options.silent) {
49062 console.error(errMsg);
49065 throw new Error(errMsg);
49074 * Parse Inline Tokens
49078 key: "parseInline",
49079 value: function parseInline(tokens, renderer) {
49080 renderer = renderer || this.renderer;
49084 var l = tokens.length;
49086 for (i = 0; i < l; i++) {
49089 switch (token.type) {
49092 out += renderer.text(token.text);
49098 out += renderer.html(token.text);
49104 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
49110 out += renderer.image(token.href, token.title, token.text);
49116 out += renderer.strong(this.parseInline(token.tokens, renderer));
49122 out += renderer.em(this.parseInline(token.tokens, renderer));
49128 out += renderer.codespan(token.text);
49134 out += renderer.br();
49140 out += renderer.del(this.parseInline(token.tokens, renderer));
49146 out += renderer.text(token.text);
49152 var errMsg = 'Token with "' + token.type + '" type was not found.';
49154 if (this.options.silent) {
49155 console.error(errMsg);
49158 throw new Error(errMsg);
49168 value: function parse(tokens, options) {
49169 var parser = new Parser(options);
49170 return parser.parse(tokens);
49173 * Static Parse Inline Method
49177 key: "parseInline",
49178 value: function parseInline(tokens, options) {
49179 var parser = new Parser(options);
49180 return parser.parseInline(tokens);
49187 var merge = helpers.merge,
49188 checkSanitizeDeprecation = helpers.checkSanitizeDeprecation,
49189 escape$1 = helpers.escape;
49190 var getDefaults = defaults$5.getDefaults,
49191 changeDefaults = defaults$5.changeDefaults,
49192 defaults = defaults$5.defaults;
49197 function marked(src, opt, callback) {
49198 // throw error in case of non string input
49199 if (typeof src === 'undefined' || src === null) {
49200 throw new Error('marked(): input parameter is undefined or null');
49203 if (typeof src !== 'string') {
49204 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49207 if (typeof opt === 'function') {
49212 opt = merge({}, marked.defaults, opt || {});
49213 checkSanitizeDeprecation(opt);
49216 var highlight = opt.highlight;
49220 tokens = Lexer_1.lex(src, opt);
49222 return callback(e);
49225 var done = function done(err) {
49230 if (opt.walkTokens) {
49231 marked.walkTokens(tokens, opt.walkTokens);
49234 out = Parser_1.parse(tokens, opt);
49240 opt.highlight = highlight;
49241 return err ? callback(err) : callback(null, out);
49244 if (!highlight || highlight.length < 3) {
49248 delete opt.highlight;
49249 if (!tokens.length) return done();
49251 marked.walkTokens(tokens, function (token) {
49252 if (token.type === 'code') {
49254 setTimeout(function () {
49255 highlight(token.text, token.lang, function (err, code) {
49260 if (code != null && code !== token.text) {
49262 token.escaped = true;
49267 if (pending === 0) {
49275 if (pending === 0) {
49283 var _tokens = Lexer_1.lex(src, opt);
49285 if (opt.walkTokens) {
49286 marked.walkTokens(_tokens, opt.walkTokens);
49289 return Parser_1.parse(_tokens, opt);
49291 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49294 return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49305 marked.options = marked.setOptions = function (opt) {
49306 merge(marked.defaults, opt);
49307 changeDefaults(marked.defaults);
49311 marked.getDefaults = getDefaults;
49312 marked.defaults = defaults;
49317 marked.use = function (extension) {
49318 var opts = merge({}, extension);
49320 if (extension.renderer) {
49322 var renderer = marked.defaults.renderer || new Renderer_1();
49324 var _loop = function _loop(prop) {
49325 var prevRenderer = renderer[prop];
49327 renderer[prop] = function () {
49328 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
49329 args[_key] = arguments[_key];
49332 var ret = extension.renderer[prop].apply(renderer, args);
49334 if (ret === false) {
49335 ret = prevRenderer.apply(renderer, args);
49342 for (var prop in extension.renderer) {
49346 opts.renderer = renderer;
49350 if (extension.tokenizer) {
49352 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
49354 var _loop2 = function _loop2(prop) {
49355 var prevTokenizer = tokenizer[prop];
49357 tokenizer[prop] = function () {
49358 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
49359 args[_key2] = arguments[_key2];
49362 var ret = extension.tokenizer[prop].apply(tokenizer, args);
49364 if (ret === false) {
49365 ret = prevTokenizer.apply(tokenizer, args);
49372 for (var prop in extension.tokenizer) {
49376 opts.tokenizer = tokenizer;
49380 if (extension.walkTokens) {
49381 var walkTokens = marked.defaults.walkTokens;
49383 opts.walkTokens = function (token) {
49384 extension.walkTokens(token);
49392 marked.setOptions(opts);
49395 * Run callback for every token
49399 marked.walkTokens = function (tokens, callback) {
49400 var _iterator = _createForOfIteratorHelper(tokens),
49404 for (_iterator.s(); !(_step = _iterator.n()).done;) {
49405 var token = _step.value;
49408 switch (token.type) {
49411 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
49415 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
49416 var cell = _step2.value;
49417 marked.walkTokens(cell, callback);
49425 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
49429 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
49430 var row = _step3.value;
49432 var _iterator4 = _createForOfIteratorHelper(row),
49436 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
49437 var _cell = _step4.value;
49438 marked.walkTokens(_cell, callback);
49457 marked.walkTokens(token.items, callback);
49463 if (token.tokens) {
49464 marked.walkTokens(token.tokens, callback);
49480 marked.parseInline = function (src, opt) {
49481 // throw error in case of non string input
49482 if (typeof src === 'undefined' || src === null) {
49483 throw new Error('marked.parseInline(): input parameter is undefined or null');
49486 if (typeof src !== 'string') {
49487 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49490 opt = merge({}, marked.defaults, opt || {});
49491 checkSanitizeDeprecation(opt);
49494 var tokens = Lexer_1.lexInline(src, opt);
49496 if (opt.walkTokens) {
49497 marked.walkTokens(tokens, opt.walkTokens);
49500 return Parser_1.parseInline(tokens, opt);
49502 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49505 return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49516 marked.Parser = Parser_1;
49517 marked.parser = Parser_1.parse;
49518 marked.Renderer = Renderer_1;
49519 marked.TextRenderer = TextRenderer_1;
49520 marked.Lexer = Lexer_1;
49521 marked.lexer = Lexer_1.lex;
49522 marked.Tokenizer = Tokenizer_1;
49523 marked.Slugger = Slugger_1;
49524 marked.parse = marked;
49525 var marked_1 = marked;
49527 var tiler$4 = utilTiler();
49528 var dispatch$5 = dispatch$8('loaded');
49529 var _tileZoom$1 = 14;
49530 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
49531 var _osmoseData = {
49534 }; // This gets reassigned if reset
49538 function abortRequest$4(controller) {
49540 controller.abort();
49544 function abortUnwantedRequests$1(cache, tiles) {
49545 Object.keys(cache.inflightTile).forEach(function (k) {
49546 var wanted = tiles.find(function (tile) {
49547 return k === tile.id;
49551 abortRequest$4(cache.inflightTile[k]);
49552 delete cache.inflightTile[k];
49557 function encodeIssueRtree(d) {
49565 } // Replace or remove QAItem from rtree
49568 function updateRtree$1(item, replace) {
49569 _cache.rtree.remove(item, function (a, b) {
49570 return a.data.id === b.data.id;
49574 _cache.rtree.insert(item);
49576 } // Issues shouldn't obscure each other
49579 function preventCoincident(loc) {
49580 var coincident = false;
49583 // first time, move marker up. after that, move marker right.
49584 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
49585 loc = geoVecAdd(loc, delta);
49586 var bbox = geoExtent(loc).bbox();
49587 coincident = _cache.rtree.search(bbox).length;
49588 } while (coincident);
49593 var serviceOsmose = {
49595 init: function init() {
49596 _mainFileFetcher.get('qa_data').then(function (d) {
49597 _osmoseData = d.osmose;
49598 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
49599 return s.split('-')[0];
49600 }).reduce(function (unique, item) {
49601 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
49609 this.event = utilRebind(this, dispatch$5, 'on');
49611 reset: function reset() {
49616 Object.values(_cache.inflightTile).forEach(abortRequest$4); // Strings and colors are static and should not be re-populated
49618 _strings = _cache.strings;
49619 _colors = _cache.colors;
49628 rtree: new RBush(),
49633 loadIssues: function loadIssues(projection) {
49637 // Tiles return a maximum # of issues
49638 // So we want to filter our request for only types iD supports
49639 item: _osmoseData.items
49640 }; // determine the needed tiles to cover the view
49642 var tiles = tiler$4.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
49644 abortUnwantedRequests$1(_cache, tiles); // issue new requests..
49646 tiles.forEach(function (tile) {
49647 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
49649 var _tile$xyz = _slicedToArray(tile.xyz, 3),
49654 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
49655 var controller = new AbortController();
49656 _cache.inflightTile[tile.id] = controller;
49658 signal: controller.signal
49659 }).then(function (data) {
49660 delete _cache.inflightTile[tile.id];
49661 _cache.loadedTile[tile.id] = true;
49663 if (data.features) {
49664 data.features.forEach(function (issue) {
49665 var _issue$properties = issue.properties,
49666 item = _issue$properties.item,
49667 cl = _issue$properties["class"],
49668 id = _issue$properties.uuid;
49669 /* Osmose issues are uniquely identified by a unique
49670 `item` and `class` combination (both integer values) */
49672 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
49674 if (itemType in _osmoseData.icons) {
49675 var loc = issue.geometry.coordinates; // lon, lat
49677 loc = preventCoincident(loc);
49678 var d = new QAItem(loc, _this, itemType, id, {
49680 }); // Setting elems here prevents UI detail requests
49682 if (item === 8300 || item === 8360) {
49686 _cache.data[d.id] = d;
49688 _cache.rtree.insert(encodeIssueRtree(d));
49693 dispatch$5.call('loaded');
49694 })["catch"](function () {
49695 delete _cache.inflightTile[tile.id];
49696 _cache.loadedTile[tile.id] = true;
49700 loadIssueDetail: function loadIssueDetail(issue) {
49703 // Issue details only need to be fetched once
49704 if (issue.elems !== undefined) {
49705 return Promise.resolve(issue);
49708 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
49710 var cacheDetails = function cacheDetails(data) {
49711 // Associated elements used for highlighting
49712 // Assign directly for immediate use in the callback
49713 issue.elems = data.elems.map(function (e) {
49714 return e.type.substring(0, 1) + e.id;
49715 }); // Some issues have instance specific detail in a subtitle
49717 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
49719 _this2.replaceItem(issue);
49722 return d3_json(url).then(cacheDetails).then(function () {
49726 loadStrings: function loadStrings() {
49727 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
49728 var items = Object.keys(_osmoseData.icons);
49730 if (locale in _cache.strings && Object.keys(_cache.strings[locale]).length === items.length) {
49731 return Promise.resolve(_cache.strings[locale]);
49732 } // May be partially populated already if some requests were successful
49735 if (!(locale in _cache.strings)) {
49736 _cache.strings[locale] = {};
49737 } // Only need to cache strings for supported issue types
49738 // Using multiple individual item + class requests to reduce fetched data size
49741 var allRequests = items.map(function (itemType) {
49742 // No need to request data we already have
49743 if (itemType in _cache.strings[locale]) return null;
49745 var cacheData = function cacheData(data) {
49746 // Bunch of nested single value arrays of objects
49747 var _data$categories = _slicedToArray(data.categories, 1),
49748 _data$categories$ = _data$categories[0],
49749 cat = _data$categories$ === void 0 ? {
49751 } : _data$categories$;
49753 var _cat$items = _slicedToArray(cat.items, 1),
49754 _cat$items$ = _cat$items[0],
49755 item = _cat$items$ === void 0 ? {
49759 var _item$class = _slicedToArray(item["class"], 1),
49760 _item$class$ = _item$class[0],
49761 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
49765 /* eslint-disable no-console */
49766 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
49767 /* eslint-enable no-console */
49770 } // Cache served item colors to automatically style issue markers later
49773 var itemInt = item.item,
49774 color = item.color;
49776 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
49777 _cache.colors[itemInt] = color;
49778 } // Value of root key will be null if no string exists
49779 // If string exists, value is an object with key 'auto' for string
49782 var title = cl.title,
49783 detail = cl.detail,
49785 trap = cl.trap; // Osmose titles shouldn't contain markdown
49787 var issueStrings = {};
49788 if (title) issueStrings.title = title.auto;
49789 if (detail) issueStrings.detail = marked_1(detail.auto);
49790 if (trap) issueStrings.trap = marked_1(trap.auto);
49791 if (fix) issueStrings.fix = marked_1(fix.auto);
49792 _cache.strings[locale][itemType] = issueStrings;
49795 var _itemType$split = itemType.split('-'),
49796 _itemType$split2 = _slicedToArray(_itemType$split, 2),
49797 item = _itemType$split2[0],
49798 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
49801 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
49802 return d3_json(url).then(cacheData);
49803 }).filter(Boolean);
49804 return Promise.all(allRequests).then(function () {
49805 return _cache.strings[locale];
49808 getStrings: function getStrings(itemType) {
49809 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
49810 // No need to fallback to English, Osmose API handles this for us
49811 return locale in _cache.strings ? _cache.strings[locale][itemType] : {};
49813 getColor: function getColor(itemType) {
49814 return itemType in _cache.colors ? _cache.colors[itemType] : '#FFFFFF';
49816 postUpdate: function postUpdate(issue, callback) {
49819 if (_cache.inflightPost[issue.id]) {
49821 message: 'Issue update already inflight',
49824 } // UI sets the status to either 'done' or 'false'
49827 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
49828 var controller = new AbortController();
49830 var after = function after() {
49831 delete _cache.inflightPost[issue.id];
49833 _this3.removeItem(issue);
49835 if (issue.newStatus === 'done') {
49836 // Keep track of the number of issues closed per `item` to tag the changeset
49837 if (!(issue.item in _cache.closed)) {
49838 _cache.closed[issue.item] = 0;
49841 _cache.closed[issue.item] += 1;
49844 if (callback) callback(null, issue);
49847 _cache.inflightPost[issue.id] = controller;
49849 signal: controller.signal
49850 }).then(after)["catch"](function (err) {
49851 delete _cache.inflightPost[issue.id];
49852 if (callback) callback(err.message);
49855 // Get all cached QAItems covering the viewport
49856 getItems: function getItems(projection) {
49857 var viewport = projection.clipExtent();
49858 var min = [viewport[0][0], viewport[1][1]];
49859 var max = [viewport[1][0], viewport[0][1]];
49860 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
49861 return _cache.rtree.search(bbox).map(function (d) {
49865 // Get a QAItem from cache
49866 // NOTE: Don't change method name until UI v3 is merged
49867 getError: function getError(id) {
49868 return _cache.data[id];
49870 // get the name of the icon to display for this item
49871 getIcon: function getIcon(itemType) {
49872 return _osmoseData.icons[itemType];
49874 // Replace a single QAItem in the cache
49875 replaceItem: function replaceItem(item) {
49876 if (!(item instanceof QAItem) || !item.id) return;
49877 _cache.data[item.id] = item;
49878 updateRtree$1(encodeIssueRtree(item), true); // true = replace
49882 // Remove a single QAItem from the cache
49883 removeItem: function removeItem(item) {
49884 if (!(item instanceof QAItem) || !item.id) return;
49885 delete _cache.data[item.id];
49886 updateRtree$1(encodeIssueRtree(item), false); // false = remove
49888 // Used to populate `closed:osmose:*` changeset tags
49889 getClosedCounts: function getClosedCounts() {
49890 return _cache.closed;
49892 itemURL: function itemURL(item) {
49893 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
49897 /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
49898 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
49900 var eLen = nBytes * 8 - mLen - 1;
49901 var eMax = (1 << eLen) - 1;
49902 var eBias = eMax >> 1;
49904 var i = isLE ? nBytes - 1 : 0;
49905 var d = isLE ? -1 : 1;
49906 var s = buffer[offset + i];
49908 e = s & (1 << -nBits) - 1;
49912 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49914 m = e & (1 << -nBits) - 1;
49918 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49922 } else if (e === eMax) {
49923 return m ? NaN : (s ? -1 : 1) * Infinity;
49925 m = m + Math.pow(2, mLen);
49929 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
49932 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
49934 var eLen = nBytes * 8 - mLen - 1;
49935 var eMax = (1 << eLen) - 1;
49936 var eBias = eMax >> 1;
49937 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
49938 var i = isLE ? 0 : nBytes - 1;
49939 var d = isLE ? 1 : -1;
49940 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
49941 value = Math.abs(value);
49943 if (isNaN(value) || value === Infinity) {
49944 m = isNaN(value) ? 1 : 0;
49947 e = Math.floor(Math.log(value) / Math.LN2);
49949 if (value * (c = Math.pow(2, -e)) < 1) {
49954 if (e + eBias >= 1) {
49957 value += rt * Math.pow(2, 1 - eBias);
49960 if (value * c >= 2) {
49965 if (e + eBias >= eMax) {
49968 } else if (e + eBias >= 1) {
49969 m = (value * c - 1) * Math.pow(2, mLen);
49972 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
49977 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
49982 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
49984 buffer[offset + i - d] |= s * 128;
49994 function Pbf(buf) {
49995 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
49998 this.length = this.buf.length;
50001 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50003 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50005 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50007 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50009 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50010 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50011 // data structures (which currently switch structure types at 12 bytes or more)
50013 var TEXT_DECODER_MIN_LENGTH = 12;
50014 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50016 destroy: function destroy() {
50019 // === READING =================================================================
50020 readFields: function readFields(readField, result, end) {
50021 end = end || this.length;
50023 while (this.pos < end) {
50024 var val = this.readVarint(),
50026 startPos = this.pos;
50027 this.type = val & 0x7;
50028 readField(tag, result, this);
50029 if (this.pos === startPos) this.skip(val);
50034 readMessage: function readMessage(readField, result) {
50035 return this.readFields(readField, result, this.readVarint() + this.pos);
50037 readFixed32: function readFixed32() {
50038 var val = readUInt32(this.buf, this.pos);
50042 readSFixed32: function readSFixed32() {
50043 var val = readInt32(this.buf, this.pos);
50047 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50048 readFixed64: function readFixed64() {
50049 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50053 readSFixed64: function readSFixed64() {
50054 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50058 readFloat: function readFloat() {
50059 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
50063 readDouble: function readDouble() {
50064 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
50068 readVarint: function readVarint(isSigned) {
50069 var buf = this.buf,
50072 b = buf[this.pos++];
50074 if (b < 0x80) return val;
50075 b = buf[this.pos++];
50076 val |= (b & 0x7f) << 7;
50077 if (b < 0x80) return val;
50078 b = buf[this.pos++];
50079 val |= (b & 0x7f) << 14;
50080 if (b < 0x80) return val;
50081 b = buf[this.pos++];
50082 val |= (b & 0x7f) << 21;
50083 if (b < 0x80) return val;
50085 val |= (b & 0x0f) << 28;
50086 return readVarintRemainder(val, isSigned, this);
50088 readVarint64: function readVarint64() {
50089 // for compatibility with v2.0.1
50090 return this.readVarint(true);
50092 readSVarint: function readSVarint() {
50093 var num = this.readVarint();
50094 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50096 readBoolean: function readBoolean() {
50097 return Boolean(this.readVarint());
50099 readString: function readString() {
50100 var end = this.readVarint() + this.pos;
50101 var pos = this.pos;
50104 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50105 // longer strings are fast with the built-in browser TextDecoder API
50106 return readUtf8TextDecoder(this.buf, pos, end);
50107 } // short strings are fast with our custom implementation
50110 return readUtf8(this.buf, pos, end);
50112 readBytes: function readBytes() {
50113 var end = this.readVarint() + this.pos,
50114 buffer = this.buf.subarray(this.pos, end);
50118 // verbose for performance reasons; doesn't affect gzipped size
50119 readPackedVarint: function readPackedVarint(arr, isSigned) {
50120 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50121 var end = readPackedEnd(this);
50124 while (this.pos < end) {
50125 arr.push(this.readVarint(isSigned));
50130 readPackedSVarint: function readPackedSVarint(arr) {
50131 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50132 var end = readPackedEnd(this);
50135 while (this.pos < end) {
50136 arr.push(this.readSVarint());
50141 readPackedBoolean: function readPackedBoolean(arr) {
50142 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50143 var end = readPackedEnd(this);
50146 while (this.pos < end) {
50147 arr.push(this.readBoolean());
50152 readPackedFloat: function readPackedFloat(arr) {
50153 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50154 var end = readPackedEnd(this);
50157 while (this.pos < end) {
50158 arr.push(this.readFloat());
50163 readPackedDouble: function readPackedDouble(arr) {
50164 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50165 var end = readPackedEnd(this);
50168 while (this.pos < end) {
50169 arr.push(this.readDouble());
50174 readPackedFixed32: function readPackedFixed32(arr) {
50175 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50176 var end = readPackedEnd(this);
50179 while (this.pos < end) {
50180 arr.push(this.readFixed32());
50185 readPackedSFixed32: function readPackedSFixed32(arr) {
50186 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50187 var end = readPackedEnd(this);
50190 while (this.pos < end) {
50191 arr.push(this.readSFixed32());
50196 readPackedFixed64: function readPackedFixed64(arr) {
50197 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
50198 var end = readPackedEnd(this);
50201 while (this.pos < end) {
50202 arr.push(this.readFixed64());
50207 readPackedSFixed64: function readPackedSFixed64(arr) {
50208 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
50209 var end = readPackedEnd(this);
50212 while (this.pos < end) {
50213 arr.push(this.readSFixed64());
50218 skip: function skip(val) {
50219 var type = val & 0x7;
50220 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);
50222 // === WRITING =================================================================
50223 writeTag: function writeTag(tag, type) {
50224 this.writeVarint(tag << 3 | type);
50226 realloc: function realloc(min) {
50227 var length = this.length || 16;
50229 while (length < this.pos + min) {
50233 if (length !== this.length) {
50234 var buf = new Uint8Array(length);
50237 this.length = length;
50240 finish: function finish() {
50241 this.length = this.pos;
50243 return this.buf.subarray(0, this.length);
50245 writeFixed32: function writeFixed32(val) {
50247 writeInt32(this.buf, val, this.pos);
50250 writeSFixed32: function writeSFixed32(val) {
50252 writeInt32(this.buf, val, this.pos);
50255 writeFixed64: function writeFixed64(val) {
50257 writeInt32(this.buf, val & -1, this.pos);
50258 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50261 writeSFixed64: function writeSFixed64(val) {
50263 writeInt32(this.buf, val & -1, this.pos);
50264 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50267 writeVarint: function writeVarint(val) {
50270 if (val > 0xfffffff || val < 0) {
50271 writeBigVarint(val, this);
50276 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
50277 if (val <= 0x7f) return;
50278 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50279 if (val <= 0x7f) return;
50280 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50281 if (val <= 0x7f) return;
50282 this.buf[this.pos++] = val >>> 7 & 0x7f;
50284 writeSVarint: function writeSVarint(val) {
50285 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
50287 writeBoolean: function writeBoolean(val) {
50288 this.writeVarint(Boolean(val));
50290 writeString: function writeString(str) {
50292 this.realloc(str.length * 4);
50293 this.pos++; // reserve 1 byte for short string length
50295 var startPos = this.pos; // write the string directly to the buffer and see how much was written
50297 this.pos = writeUtf8(this.buf, str, this.pos);
50298 var len = this.pos - startPos;
50299 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50301 this.pos = startPos - 1;
50302 this.writeVarint(len);
50305 writeFloat: function writeFloat(val) {
50307 ieee754.write(this.buf, val, this.pos, true, 23, 4);
50310 writeDouble: function writeDouble(val) {
50312 ieee754.write(this.buf, val, this.pos, true, 52, 8);
50315 writeBytes: function writeBytes(buffer) {
50316 var len = buffer.length;
50317 this.writeVarint(len);
50320 for (var i = 0; i < len; i++) {
50321 this.buf[this.pos++] = buffer[i];
50324 writeRawMessage: function writeRawMessage(fn, obj) {
50325 this.pos++; // reserve 1 byte for short message length
50326 // write the message directly to the buffer and see how much was written
50328 var startPos = this.pos;
50330 var len = this.pos - startPos;
50331 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50333 this.pos = startPos - 1;
50334 this.writeVarint(len);
50337 writeMessage: function writeMessage(tag, fn, obj) {
50338 this.writeTag(tag, Pbf.Bytes);
50339 this.writeRawMessage(fn, obj);
50341 writePackedVarint: function writePackedVarint(tag, arr) {
50342 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
50344 writePackedSVarint: function writePackedSVarint(tag, arr) {
50345 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
50347 writePackedBoolean: function writePackedBoolean(tag, arr) {
50348 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
50350 writePackedFloat: function writePackedFloat(tag, arr) {
50351 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
50353 writePackedDouble: function writePackedDouble(tag, arr) {
50354 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
50356 writePackedFixed32: function writePackedFixed32(tag, arr) {
50357 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
50359 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
50360 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
50362 writePackedFixed64: function writePackedFixed64(tag, arr) {
50363 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
50365 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
50366 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
50368 writeBytesField: function writeBytesField(tag, buffer) {
50369 this.writeTag(tag, Pbf.Bytes);
50370 this.writeBytes(buffer);
50372 writeFixed32Field: function writeFixed32Field(tag, val) {
50373 this.writeTag(tag, Pbf.Fixed32);
50374 this.writeFixed32(val);
50376 writeSFixed32Field: function writeSFixed32Field(tag, val) {
50377 this.writeTag(tag, Pbf.Fixed32);
50378 this.writeSFixed32(val);
50380 writeFixed64Field: function writeFixed64Field(tag, val) {
50381 this.writeTag(tag, Pbf.Fixed64);
50382 this.writeFixed64(val);
50384 writeSFixed64Field: function writeSFixed64Field(tag, val) {
50385 this.writeTag(tag, Pbf.Fixed64);
50386 this.writeSFixed64(val);
50388 writeVarintField: function writeVarintField(tag, val) {
50389 this.writeTag(tag, Pbf.Varint);
50390 this.writeVarint(val);
50392 writeSVarintField: function writeSVarintField(tag, val) {
50393 this.writeTag(tag, Pbf.Varint);
50394 this.writeSVarint(val);
50396 writeStringField: function writeStringField(tag, str) {
50397 this.writeTag(tag, Pbf.Bytes);
50398 this.writeString(str);
50400 writeFloatField: function writeFloatField(tag, val) {
50401 this.writeTag(tag, Pbf.Fixed32);
50402 this.writeFloat(val);
50404 writeDoubleField: function writeDoubleField(tag, val) {
50405 this.writeTag(tag, Pbf.Fixed64);
50406 this.writeDouble(val);
50408 writeBooleanField: function writeBooleanField(tag, val) {
50409 this.writeVarintField(tag, Boolean(val));
50413 function readVarintRemainder(l, s, p) {
50418 h = (b & 0x70) >> 4;
50419 if (b < 0x80) return toNum(l, h, s);
50421 h |= (b & 0x7f) << 3;
50422 if (b < 0x80) return toNum(l, h, s);
50424 h |= (b & 0x7f) << 10;
50425 if (b < 0x80) return toNum(l, h, s);
50427 h |= (b & 0x7f) << 17;
50428 if (b < 0x80) return toNum(l, h, s);
50430 h |= (b & 0x7f) << 24;
50431 if (b < 0x80) return toNum(l, h, s);
50433 h |= (b & 0x01) << 31;
50434 if (b < 0x80) return toNum(l, h, s);
50435 throw new Error('Expected varint not more than 10 bytes');
50438 function readPackedEnd(pbf) {
50439 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
50442 function toNum(low, high, isSigned) {
50444 return high * 0x100000000 + (low >>> 0);
50447 return (high >>> 0) * 0x100000000 + (low >>> 0);
50450 function writeBigVarint(val, pbf) {
50454 low = val % 0x100000000 | 0;
50455 high = val / 0x100000000 | 0;
50457 low = ~(-val % 0x100000000);
50458 high = ~(-val / 0x100000000);
50460 if (low ^ 0xffffffff) {
50464 high = high + 1 | 0;
50468 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
50469 throw new Error('Given varint doesn\'t fit into 10 bytes');
50473 writeBigVarintLow(low, high, pbf);
50474 writeBigVarintHigh(high, pbf);
50477 function writeBigVarintLow(low, high, pbf) {
50478 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50480 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50482 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50484 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50486 pbf.buf[pbf.pos] = low & 0x7f;
50489 function writeBigVarintHigh(high, pbf) {
50490 var lsb = (high & 0x07) << 4;
50491 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
50493 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50495 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50497 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50499 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50501 pbf.buf[pbf.pos++] = high & 0x7f;
50504 function makeRoomForExtraLength(startPos, len, pbf) {
50505 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
50507 pbf.realloc(extraLen);
50509 for (var i = pbf.pos - 1; i >= startPos; i--) {
50510 pbf.buf[i + extraLen] = pbf.buf[i];
50514 function _writePackedVarint(arr, pbf) {
50515 for (var i = 0; i < arr.length; i++) {
50516 pbf.writeVarint(arr[i]);
50520 function _writePackedSVarint(arr, pbf) {
50521 for (var i = 0; i < arr.length; i++) {
50522 pbf.writeSVarint(arr[i]);
50526 function _writePackedFloat(arr, pbf) {
50527 for (var i = 0; i < arr.length; i++) {
50528 pbf.writeFloat(arr[i]);
50532 function _writePackedDouble(arr, pbf) {
50533 for (var i = 0; i < arr.length; i++) {
50534 pbf.writeDouble(arr[i]);
50538 function _writePackedBoolean(arr, pbf) {
50539 for (var i = 0; i < arr.length; i++) {
50540 pbf.writeBoolean(arr[i]);
50544 function _writePackedFixed(arr, pbf) {
50545 for (var i = 0; i < arr.length; i++) {
50546 pbf.writeFixed32(arr[i]);
50550 function _writePackedSFixed(arr, pbf) {
50551 for (var i = 0; i < arr.length; i++) {
50552 pbf.writeSFixed32(arr[i]);
50556 function _writePackedFixed2(arr, pbf) {
50557 for (var i = 0; i < arr.length; i++) {
50558 pbf.writeFixed64(arr[i]);
50562 function _writePackedSFixed2(arr, pbf) {
50563 for (var i = 0; i < arr.length; i++) {
50564 pbf.writeSFixed64(arr[i]);
50566 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
50569 function readUInt32(buf, pos) {
50570 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
50573 function writeInt32(buf, val, pos) {
50575 buf[pos + 1] = val >>> 8;
50576 buf[pos + 2] = val >>> 16;
50577 buf[pos + 3] = val >>> 24;
50580 function readInt32(buf, pos) {
50581 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
50584 function readUtf8(buf, pos, end) {
50590 var c = null; // codepoint
50592 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
50593 if (i + bytesPerSequence > end) break;
50596 if (bytesPerSequence === 1) {
50600 } else if (bytesPerSequence === 2) {
50603 if ((b1 & 0xC0) === 0x80) {
50604 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
50610 } else if (bytesPerSequence === 3) {
50614 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
50615 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
50617 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
50621 } else if (bytesPerSequence === 4) {
50626 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
50627 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
50629 if (c <= 0xFFFF || c >= 0x110000) {
50637 bytesPerSequence = 1;
50638 } else if (c > 0xFFFF) {
50640 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
50641 c = 0xDC00 | c & 0x3FF;
50644 str += String.fromCharCode(c);
50645 i += bytesPerSequence;
50651 function readUtf8TextDecoder(buf, pos, end) {
50652 return utf8TextDecoder.decode(buf.subarray(pos, end));
50655 function writeUtf8(buf, str, pos) {
50656 for (var i = 0, c, lead; i < str.length; i++) {
50657 c = str.charCodeAt(i); // code point
50659 if (c > 0xD7FF && c < 0xE000) {
50668 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
50672 if (c > 0xDBFF || i + 1 === str.length) {
50693 buf[pos++] = c >> 0x6 | 0xC0;
50696 buf[pos++] = c >> 0xC | 0xE0;
50698 buf[pos++] = c >> 0x12 | 0xF0;
50699 buf[pos++] = c >> 0xC & 0x3F | 0x80;
50702 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
50705 buf[pos++] = c & 0x3F | 0x80;
50712 var pointGeometry = Point;
50714 * A standalone point geometry with useful accessor, comparison, and
50715 * modification methods.
50718 * @param {Number} x the x-coordinate. this could be longitude or screen
50719 * pixels, or any other sort of unit.
50720 * @param {Number} y the y-coordinate. this could be latitude or screen
50721 * pixels, or any other sort of unit.
50723 * var point = new Point(-77, 38);
50726 function Point(x, y) {
50731 Point.prototype = {
50733 * Clone this point, returning a new point that can be modified
50734 * without affecting the old one.
50735 * @return {Point} the clone
50737 clone: function clone() {
50738 return new Point(this.x, this.y);
50742 * Add this point's x & y coordinates to another point,
50743 * yielding a new point.
50744 * @param {Point} p the other point
50745 * @return {Point} output point
50747 add: function add(p) {
50748 return this.clone()._add(p);
50752 * Subtract this point's x & y coordinates to from point,
50753 * yielding a new point.
50754 * @param {Point} p the other point
50755 * @return {Point} output point
50757 sub: function sub(p) {
50758 return this.clone()._sub(p);
50762 * Multiply this point's x & y coordinates by point,
50763 * yielding a new point.
50764 * @param {Point} p the other point
50765 * @return {Point} output point
50767 multByPoint: function multByPoint(p) {
50768 return this.clone()._multByPoint(p);
50772 * Divide this point's x & y coordinates by point,
50773 * yielding a new point.
50774 * @param {Point} p the other point
50775 * @return {Point} output point
50777 divByPoint: function divByPoint(p) {
50778 return this.clone()._divByPoint(p);
50782 * Multiply this point's x & y coordinates by a factor,
50783 * yielding a new point.
50784 * @param {Point} k factor
50785 * @return {Point} output point
50787 mult: function mult(k) {
50788 return this.clone()._mult(k);
50792 * Divide this point's x & y coordinates by a factor,
50793 * yielding a new point.
50794 * @param {Point} k factor
50795 * @return {Point} output point
50797 div: function div(k) {
50798 return this.clone()._div(k);
50802 * Rotate this point around the 0, 0 origin by an angle a,
50804 * @param {Number} a angle to rotate around, in radians
50805 * @return {Point} output point
50807 rotate: function rotate(a) {
50808 return this.clone()._rotate(a);
50812 * Rotate this point around p point by an angle a,
50814 * @param {Number} a angle to rotate around, in radians
50815 * @param {Point} p Point to rotate around
50816 * @return {Point} output point
50818 rotateAround: function rotateAround(a, p) {
50819 return this.clone()._rotateAround(a, p);
50823 * Multiply this point by a 4x1 transformation matrix
50824 * @param {Array<Number>} m transformation matrix
50825 * @return {Point} output point
50827 matMult: function matMult(m) {
50828 return this.clone()._matMult(m);
50832 * Calculate this point but as a unit vector from 0, 0, meaning
50833 * that the distance from the resulting point to the 0, 0
50834 * coordinate will be equal to 1 and the angle from the resulting
50835 * point to the 0, 0 coordinate will be the same as before.
50836 * @return {Point} unit vector point
50838 unit: function unit() {
50839 return this.clone()._unit();
50843 * Compute a perpendicular point, where the new y coordinate
50844 * is the old x coordinate and the new x coordinate is the old y
50845 * coordinate multiplied by -1
50846 * @return {Point} perpendicular point
50848 perp: function perp() {
50849 return this.clone()._perp();
50853 * Return a version of this point with the x & y coordinates
50854 * rounded to integers.
50855 * @return {Point} rounded point
50857 round: function round() {
50858 return this.clone()._round();
50862 * Return the magitude of this point: this is the Euclidean
50863 * distance from the 0, 0 coordinate to this point's x and y
50865 * @return {Number} magnitude
50867 mag: function mag() {
50868 return Math.sqrt(this.x * this.x + this.y * this.y);
50872 * Judge whether this point is equal to another point, returning
50874 * @param {Point} other the other point
50875 * @return {boolean} whether the points are equal
50877 equals: function equals(other) {
50878 return this.x === other.x && this.y === other.y;
50882 * Calculate the distance from this point to another point
50883 * @param {Point} p the other point
50884 * @return {Number} distance
50886 dist: function dist(p) {
50887 return Math.sqrt(this.distSqr(p));
50891 * Calculate the distance from this point to another point,
50892 * without the square root step. Useful if you're comparing
50893 * relative distances.
50894 * @param {Point} p the other point
50895 * @return {Number} distance
50897 distSqr: function distSqr(p) {
50898 var dx = p.x - this.x,
50900 return dx * dx + dy * dy;
50904 * Get the angle from the 0, 0 coordinate to this point, in radians
50906 * @return {Number} angle
50908 angle: function angle() {
50909 return Math.atan2(this.y, this.x);
50913 * Get the angle from this point to another point, in radians
50914 * @param {Point} b the other point
50915 * @return {Number} angle
50917 angleTo: function angleTo(b) {
50918 return Math.atan2(this.y - b.y, this.x - b.x);
50922 * Get the angle between this point and another point, in radians
50923 * @param {Point} b the other point
50924 * @return {Number} angle
50926 angleWith: function angleWith(b) {
50927 return this.angleWithSep(b.x, b.y);
50931 * Find the angle of the two vectors, solving the formula for
50932 * the cross product a x b = |a||b|sin(θ) for θ.
50933 * @param {Number} x the x-coordinate
50934 * @param {Number} y the y-coordinate
50935 * @return {Number} the angle in radians
50937 angleWithSep: function angleWithSep(x, y) {
50938 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
50940 _matMult: function _matMult(m) {
50941 var x = m[0] * this.x + m[1] * this.y,
50942 y = m[2] * this.x + m[3] * this.y;
50947 _add: function _add(p) {
50952 _sub: function _sub(p) {
50957 _mult: function _mult(k) {
50962 _div: function _div(k) {
50967 _multByPoint: function _multByPoint(p) {
50972 _divByPoint: function _divByPoint(p) {
50977 _unit: function _unit() {
50978 this._div(this.mag());
50982 _perp: function _perp() {
50988 _rotate: function _rotate(angle) {
50989 var cos = Math.cos(angle),
50990 sin = Math.sin(angle),
50991 x = cos * this.x - sin * this.y,
50992 y = sin * this.x + cos * this.y;
50997 _rotateAround: function _rotateAround(angle, p) {
50998 var cos = Math.cos(angle),
50999 sin = Math.sin(angle),
51000 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51001 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51006 _round: function _round() {
51007 this.x = Math.round(this.x);
51008 this.y = Math.round(this.y);
51013 * Construct a point from an array if necessary, otherwise if the input
51014 * is already a Point, or an unknown type, return it unchanged
51015 * @param {Array<Number>|Point|*} a any kind of input value
51016 * @return {Point} constructed point, or passed-through value.
51019 * var point = Point.convert([0, 1]);
51020 * // is equivalent to
51021 * var point = new Point(0, 1);
51024 Point.convert = function (a) {
51025 if (a instanceof Point) {
51029 if (Array.isArray(a)) {
51030 return new Point(a[0], a[1]);
51036 var vectortilefeature = VectorTileFeature$1;
51038 function VectorTileFeature$1(pbf, end, extent, keys, values) {
51040 this.properties = {};
51041 this.extent = extent;
51042 this.type = 0; // Private
51045 this._geometry = -1;
51047 this._values = values;
51048 pbf.readFields(readFeature, this, end);
51051 function readFeature(tag, feature, pbf) {
51052 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;
51055 function readTag(pbf, feature) {
51056 var end = pbf.readVarint() + pbf.pos;
51058 while (pbf.pos < end) {
51059 var key = feature._keys[pbf.readVarint()],
51060 value = feature._values[pbf.readVarint()];
51062 feature.properties[key] = value;
51066 VectorTileFeature$1.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51068 VectorTileFeature$1.prototype.loadGeometry = function () {
51069 var pbf = this._pbf;
51070 pbf.pos = this._geometry;
51071 var end = pbf.readVarint() + pbf.pos,
51079 while (pbf.pos < end) {
51081 var cmdLen = pbf.readVarint();
51082 cmd = cmdLen & 0x7;
51083 length = cmdLen >> 3;
51088 if (cmd === 1 || cmd === 2) {
51089 x += pbf.readSVarint();
51090 y += pbf.readSVarint();
51094 if (line) lines.push(line);
51098 line.push(new pointGeometry(x, y));
51099 } else if (cmd === 7) {
51100 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51102 line.push(line[0].clone()); // closePolygon
51105 throw new Error('unknown command ' + cmd);
51109 if (line) lines.push(line);
51113 VectorTileFeature$1.prototype.bbox = function () {
51114 var pbf = this._pbf;
51115 pbf.pos = this._geometry;
51116 var end = pbf.readVarint() + pbf.pos,
51126 while (pbf.pos < end) {
51128 var cmdLen = pbf.readVarint();
51129 cmd = cmdLen & 0x7;
51130 length = cmdLen >> 3;
51135 if (cmd === 1 || cmd === 2) {
51136 x += pbf.readSVarint();
51137 y += pbf.readSVarint();
51138 if (x < x1) x1 = x;
51139 if (x > x2) x2 = x;
51140 if (y < y1) y1 = y;
51141 if (y > y2) y2 = y;
51142 } else if (cmd !== 7) {
51143 throw new Error('unknown command ' + cmd);
51147 return [x1, y1, x2, y2];
51150 VectorTileFeature$1.prototype.toGeoJSON = function (x, y, z) {
51151 var size = this.extent * Math.pow(2, z),
51152 x0 = this.extent * x,
51153 y0 = this.extent * y,
51154 coords = this.loadGeometry(),
51155 type = VectorTileFeature$1.types[this.type],
51159 function project(line) {
51160 for (var j = 0; j < line.length; j++) {
51162 y2 = 180 - (p.y + y0) * 360 / size;
51163 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51167 switch (this.type) {
51171 for (i = 0; i < coords.length; i++) {
51172 points[i] = coords[i][0];
51180 for (i = 0; i < coords.length; i++) {
51181 project(coords[i]);
51187 coords = classifyRings(coords);
51189 for (i = 0; i < coords.length; i++) {
51190 for (j = 0; j < coords[i].length; j++) {
51191 project(coords[i][j]);
51198 if (coords.length === 1) {
51199 coords = coords[0];
51201 type = 'Multi' + type;
51208 coordinates: coords
51210 properties: this.properties
51213 if ('id' in this) {
51214 result.id = this.id;
51218 }; // classifies an array of rings into polygons with outer rings and holes
51221 function classifyRings(rings) {
51222 var len = rings.length;
51223 if (len <= 1) return [rings];
51228 for (var i = 0; i < len; i++) {
51229 var area = signedArea(rings[i]);
51230 if (area === 0) continue;
51231 if (ccw === undefined) ccw = area < 0;
51233 if (ccw === area < 0) {
51234 if (polygon) polygons.push(polygon);
51235 polygon = [rings[i]];
51237 polygon.push(rings[i]);
51241 if (polygon) polygons.push(polygon);
51245 function signedArea(ring) {
51248 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
51251 sum += (p2.x - p1.x) * (p1.y + p2.y);
51257 var vectortilelayer = VectorTileLayer$1;
51259 function VectorTileLayer$1(pbf, end) {
51263 this.extent = 4096;
51264 this.length = 0; // Private
51269 this._features = [];
51270 pbf.readFields(readLayer, this, end);
51271 this.length = this._features.length;
51274 function readLayer(tag, layer, pbf) {
51275 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));
51278 function readValueMessage(pbf) {
51280 end = pbf.readVarint() + pbf.pos;
51282 while (pbf.pos < end) {
51283 var tag = pbf.readVarint() >> 3;
51284 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;
51288 } // return feature `i` from this layer as a `VectorTileFeature`
51291 VectorTileLayer$1.prototype.feature = function (i) {
51292 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
51293 this._pbf.pos = this._features[i];
51295 var end = this._pbf.readVarint() + this._pbf.pos;
51297 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
51300 var vectortile = VectorTile$1;
51302 function VectorTile$1(pbf, end) {
51303 this.layers = pbf.readFields(readTile, {}, end);
51306 function readTile(tag, layers, pbf) {
51308 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
51309 if (layer.length) layers[layer.name] = layer;
51313 var VectorTile = vectortile;
51314 var VectorTileFeature = vectortilefeature;
51315 var VectorTileLayer = vectortilelayer;
51317 VectorTile: VectorTile,
51318 VectorTileFeature: VectorTileFeature,
51319 VectorTileLayer: VectorTileLayer
51322 var accessToken = 'MLY|4100327730013843|5bb78b81720791946a9a7b956c57b7cf';
51323 var apiUrl = 'https://graph.mapillary.com/';
51324 var baseTileUrl = 'https://tiles.mapillary.com/maps/vtp';
51325 var mapFeatureTileUrl = "".concat(baseTileUrl, "/mly_map_feature_point/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51326 var tileUrl = "".concat(baseTileUrl, "/mly1_public/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51327 var trafficSignTileUrl = "".concat(baseTileUrl, "/mly_map_feature_traffic_sign/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51328 var viewercss = 'mapillary-js/mapillary.css';
51329 var viewerjs = 'mapillary-js/mapillary.js';
51330 var minZoom$1 = 14;
51331 var dispatch$4 = dispatch$8('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged');
51333 var _loadViewerPromise$2;
51335 var _mlyActiveImage;
51339 var _mlyFallback = false;
51341 var _mlyHighlightedDetection;
51343 var _mlyShowFeatureDetections = false;
51344 var _mlyShowSignDetections = false;
51348 var _mlyViewerFilter = ['all']; // Load all data for the specified type from Mapillary vector tiles
51350 function loadTiles$2(which, url, maxZoom, projection) {
51351 var tiler = utilTiler().zoomExtent([minZoom$1, maxZoom]).skipNullIsland(true);
51352 var tiles = tiler.getTiles(projection);
51353 tiles.forEach(function (tile) {
51354 loadTile$1(which, url, tile);
51356 } // Load all data for the specified type from one vector tile
51359 function loadTile$1(which, url, tile) {
51360 var cache = _mlyCache.requests;
51361 var tileId = "".concat(tile.id, "-").concat(which);
51362 if (cache.loaded[tileId] || cache.inflight[tileId]) return;
51363 var controller = new AbortController();
51364 cache.inflight[tileId] = controller;
51365 var requestUrl = url.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]).replace('{z}', tile.xyz[2]);
51366 fetch(requestUrl, {
51367 signal: controller.signal
51368 }).then(function (response) {
51369 if (!response.ok) {
51370 throw new Error(response.status + ' ' + response.statusText);
51373 cache.loaded[tileId] = true;
51374 delete cache.inflight[tileId];
51375 return response.arrayBuffer();
51376 }).then(function (data) {
51378 throw new Error('No Data');
51381 loadTileDataToCache(data, tile, which);
51383 if (which === 'images') {
51384 dispatch$4.call('loadedImages');
51385 } else if (which === 'signs') {
51386 dispatch$4.call('loadedSigns');
51387 } else if (which === 'points') {
51388 dispatch$4.call('loadedMapFeatures');
51390 })["catch"](function () {
51391 cache.loaded[tileId] = true;
51392 delete cache.inflight[tileId];
51394 } // Load the data from the vector tile into cache
51397 function loadTileDataToCache(data, tile, which) {
51398 var vectorTile = new VectorTile(new pbf(data));
51399 var features, cache, layer, i, feature, loc, d;
51401 if (vectorTile.layers.hasOwnProperty('image')) {
51403 cache = _mlyCache.images;
51404 layer = vectorTile.layers.image;
51406 for (i = 0; i < layer.length; i++) {
51407 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51408 loc = feature.geometry.coordinates;
51411 captured_at: feature.properties.captured_at,
51412 ca: feature.properties.compass_angle,
51413 id: feature.properties.id,
51414 is_pano: feature.properties.is_pano,
51415 sequence_id: feature.properties.sequence_id
51417 cache.forImageId[d.id] = d;
51428 cache.rtree.load(features);
51432 if (vectorTile.layers.hasOwnProperty('sequence')) {
51434 cache = _mlyCache.sequences;
51435 layer = vectorTile.layers.sequence;
51437 for (i = 0; i < layer.length; i++) {
51438 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51440 if (cache.lineString[feature.properties.id]) {
51441 cache.lineString[feature.properties.id].push(feature);
51443 cache.lineString[feature.properties.id] = [feature];
51448 if (vectorTile.layers.hasOwnProperty('point')) {
51450 cache = _mlyCache[which];
51451 layer = vectorTile.layers.point;
51453 for (i = 0; i < layer.length; i++) {
51454 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51455 loc = feature.geometry.coordinates;
51458 id: feature.properties.id,
51459 first_seen_at: feature.properties.first_seen_at,
51460 last_seen_at: feature.properties.last_seen_at,
51461 value: feature.properties.value
51473 cache.rtree.load(features);
51477 if (vectorTile.layers.hasOwnProperty('traffic_sign')) {
51479 cache = _mlyCache[which];
51480 layer = vectorTile.layers.traffic_sign;
51482 for (i = 0; i < layer.length; i++) {
51483 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51484 loc = feature.geometry.coordinates;
51487 id: feature.properties.id,
51488 first_seen_at: feature.properties.first_seen_at,
51489 last_seen_at: feature.properties.last_seen_at,
51490 value: feature.properties.value
51502 cache.rtree.load(features);
51505 } // Get data from the API
51508 function loadData(url) {
51509 return fetch(url).then(function (response) {
51510 if (!response.ok) {
51511 throw new Error(response.status + ' ' + response.statusText);
51514 return response.json();
51515 }).then(function (result) {
51520 return result.data || [];
51522 } // Partition viewport into higher zoom tiles
51525 function partitionViewport$2(projection) {
51526 var z = geoScaleToZoom(projection.scale());
51527 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
51529 var tiler = utilTiler().zoomExtent([z2, z2]);
51530 return tiler.getTiles(projection).map(function (tile) {
51531 return tile.extent;
51533 } // Return no more than `limit` results per partition.
51536 function searchLimited$2(limit, projection, rtree) {
51537 limit = limit || 5;
51538 return partitionViewport$2(projection).reduce(function (result, extent) {
51539 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
51542 return found.length ? result.concat(found) : result;
51546 var serviceMapillary = {
51547 // Initialize Mapillary
51548 init: function init() {
51553 this.event = utilRebind(this, dispatch$4, 'on');
51555 // Reset cache and state
51556 reset: function reset() {
51558 Object.values(_mlyCache.requests.inflight).forEach(function (request) {
51565 rtree: new RBush(),
51568 image_detections: {
51578 rtree: new RBush(),
51586 _mlyActiveImage = null;
51588 // Get visible images
51589 images: function images(projection) {
51591 return searchLimited$2(limit, projection, _mlyCache.images.rtree);
51593 // Get visible traffic signs
51594 signs: function signs(projection) {
51596 return searchLimited$2(limit, projection, _mlyCache.signs.rtree);
51598 // Get visible map (point) features
51599 mapFeatures: function mapFeatures(projection) {
51601 return searchLimited$2(limit, projection, _mlyCache.points.rtree);
51603 // Get cached image by id
51604 cachedImage: function cachedImage(imageId) {
51605 return _mlyCache.images.forImageId[imageId];
51607 // Get visible sequences
51608 sequences: function sequences(projection) {
51609 var viewport = projection.clipExtent();
51610 var min = [viewport[0][0], viewport[1][1]];
51611 var max = [viewport[1][0], viewport[0][1]];
51612 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
51613 var sequenceIds = {};
51614 var lineStrings = [];
51616 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
51617 if (d.data.sequence_id) {
51618 sequenceIds[d.data.sequence_id] = true;
51622 Object.keys(sequenceIds).forEach(function (sequenceId) {
51623 if (_mlyCache.sequences.lineString[sequenceId]) {
51624 lineStrings = lineStrings.concat(_mlyCache.sequences.lineString[sequenceId]);
51627 return lineStrings;
51629 // Load images in the visible area
51630 loadImages: function loadImages(projection) {
51631 loadTiles$2('images', tileUrl, 14, projection);
51633 // Load traffic signs in the visible area
51634 loadSigns: function loadSigns(projection) {
51635 loadTiles$2('signs', trafficSignTileUrl, 14, projection);
51637 // Load map (point) features in the visible area
51638 loadMapFeatures: function loadMapFeatures(projection) {
51639 loadTiles$2('points', mapFeatureTileUrl, 14, projection);
51641 // Return a promise that resolves when the image viewer (Mapillary JS) library has finished loading
51642 ensureViewerLoaded: function ensureViewerLoaded(context) {
51643 if (_loadViewerPromise$2) return _loadViewerPromise$2; // add mly-wrapper
51645 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
51646 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
51648 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
51649 var loadedCount = 0;
51651 function loaded() {
51652 loadedCount += 1; // wait until both files are loaded
51654 if (loadedCount === 2) resolve();
51657 var head = select('head'); // load mapillary-viewercss
51659 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 () {
51661 }); // load mapillary-viewerjs
51663 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 () {
51666 })["catch"](function () {
51667 _loadViewerPromise$2 = null;
51668 }).then(function () {
51669 that.initViewer(context);
51671 return _loadViewerPromise$2;
51673 // Load traffic sign image sprites
51674 loadSignResources: function loadSignResources(context) {
51675 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
51676 /* don't override colors */
51680 // Load map (point) feature image sprites
51681 loadObjectResources: function loadObjectResources(context) {
51682 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
51683 /* don't override colors */
51687 // Remove previous detections in image viewer
51688 resetTags: function resetTags() {
51689 if (_mlyViewer && !_mlyFallback) {
51690 _mlyViewer.getComponent('tag').removeAll();
51693 // Show map feature detections in image viewer
51694 showFeatureDetections: function showFeatureDetections(value) {
51695 _mlyShowFeatureDetections = value;
51697 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51701 // Show traffic sign detections in image viewer
51702 showSignDetections: function showSignDetections(value) {
51703 _mlyShowSignDetections = value;
51705 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51709 // Apply filter to image viewer
51710 filterViewer: function filterViewer(context) {
51711 var showsPano = context.photos().showsPanoramic();
51712 var showsFlat = context.photos().showsFlat();
51713 var fromDate = context.photos().fromDate();
51714 var toDate = context.photos().toDate();
51715 var filter = ['all'];
51716 if (!showsPano) filter.push(['!=', 'cameraType', 'spherical']);
51717 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
51720 filter.push(['>=', 'capturedAt', new Date(fromDate).getTime()]);
51724 filter.push(['>=', 'capturedAt', new Date(toDate).getTime()]);
51728 _mlyViewer.setFilter(filter);
51731 _mlyViewerFilter = filter;
51734 // Make the image viewer visible
51735 showViewer: function showViewer(context) {
51736 var wrap = context.container().select('.photoviewer').classed('hide', false);
51737 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
51739 if (isHidden && _mlyViewer) {
51740 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
51741 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
51743 _mlyViewer.resize();
51748 // Hide the image viewer and resets map markers
51749 hideViewer: function hideViewer(context) {
51750 _mlyActiveImage = null;
51752 if (!_mlyFallback && _mlyViewer) {
51753 _mlyViewer.getComponent('sequence').stop();
51756 var viewer = context.container().select('.photoviewer');
51757 if (!viewer.empty()) viewer.datum(null);
51758 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
51759 this.updateUrlImage(null);
51760 dispatch$4.call('imageChanged');
51761 dispatch$4.call('loadedMapFeatures');
51762 dispatch$4.call('loadedSigns');
51763 return this.setStyles(context, null);
51765 // Update the URL with current image id
51766 updateUrlImage: function updateUrlImage(imageId) {
51767 if (!window.mocha) {
51768 var hash = utilStringQs(window.location.hash);
51771 hash.photo = 'mapillary/' + imageId;
51776 window.location.replace('#' + utilQsString(hash, true));
51779 // Highlight the detection in the viewer that is related to the clicked map feature
51780 highlightDetection: function highlightDetection(detection) {
51782 _mlyHighlightedDetection = detection.id;
51787 // Initialize image viewer (Mapillar JS)
51788 initViewer: function initViewer(context) {
51790 if (!window.mapillary) return;
51792 accessToken: accessToken,
51798 container: 'ideditor-mly'
51799 }; // Disable components requiring WebGL support
51801 if (!mapillary.isSupported() && mapillary.isFallbackSupported()) {
51802 _mlyFallback = true;
51813 navigation: true // fallback
51818 _mlyViewer = new mapillary.Viewer(opts);
51820 _mlyViewer.on('image', imageChanged);
51822 _mlyViewer.on('bearing', bearingChanged);
51824 if (_mlyViewerFilter) {
51825 _mlyViewer.setFilter(_mlyViewerFilter);
51826 } // Register viewer resize handler
51829 context.ui().photoviewer.on('resize.mapillary', function () {
51830 if (_mlyViewer) _mlyViewer.resize();
51831 }); // imageChanged: called after the viewer has changed images and is ready.
51833 function imageChanged(node) {
51835 var image = node.image;
51836 that.setActiveImage(image);
51837 that.setStyles(context, null);
51838 var loc = [image.originalLngLat.lng, image.originalLngLat.lat];
51839 context.map().centerEase(loc);
51840 that.updateUrlImage(image.id);
51842 if (_mlyShowFeatureDetections || _mlyShowSignDetections) {
51843 that.updateDetections(image.id, "".concat(apiUrl, "/").concat(image.id, "/detections?access_token=").concat(accessToken, "&fields=id,image,geometry,value"));
51846 dispatch$4.call('imageChanged');
51847 } // bearingChanged: called when the bearing changes in the image viewer.
51850 function bearingChanged(e) {
51851 dispatch$4.call('bearingChanged', undefined, e);
51854 // Move to an image
51855 selectImage: function selectImage(context, imageId) {
51856 if (_mlyViewer && imageId) {
51857 _mlyViewer.moveTo(imageId)["catch"](function (e) {
51858 console.error('mly3', e); // eslint-disable-line no-console
51864 // Return the currently displayed image
51865 getActiveImage: function getActiveImage() {
51866 return _mlyActiveImage;
51868 // Return a list of detection objects for the given id
51869 getDetections: function getDetections(id) {
51870 return loadData("".concat(apiUrl, "/").concat(id, "/detections?access_token=").concat(accessToken, "&fields=id,value,image"));
51872 // Set the currently visible image
51873 setActiveImage: function setActiveImage(image) {
51875 _mlyActiveImage = {
51876 ca: image.originalCompassAngle,
51878 loc: [image.originalLngLat.lng, image.originalLngLat.lat],
51879 is_pano: image.cameraType === 'spherical',
51880 sequence_id: image.sequenceId
51883 _mlyActiveImage = null;
51886 // Update the currently highlighted sequence and selected bubble.
51887 setStyles: function setStyles(context, hovered) {
51888 var hoveredImageId = hovered && hovered.id;
51889 var hoveredSequenceId = hovered && hovered.sequence_id;
51890 var selectedSequenceId = _mlyActiveImage && _mlyActiveImage.sequence_id;
51891 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
51892 return d.sequence_id === selectedSequenceId || d.id === hoveredImageId;
51893 }).classed('hovered', function (d) {
51894 return d.id === hoveredImageId;
51896 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
51897 return d.properties.id === hoveredSequenceId;
51898 }).classed('currentView', function (d) {
51899 return d.properties.id === selectedSequenceId;
51903 // Get detections for the current image and shows them in the image viewer
51904 updateDetections: function updateDetections(imageId, url) {
51905 if (!_mlyViewer || _mlyFallback) return;
51906 if (!imageId) return;
51907 var cache = _mlyCache.image_detections;
51909 if (cache.forImageId[imageId]) {
51910 showDetections(_mlyCache.image_detections.forImageId[imageId]);
51912 loadData(url).then(function (detections) {
51913 detections.forEach(function (detection) {
51914 if (!cache.forImageId[imageId]) {
51915 cache.forImageId[imageId] = [];
51918 cache.forImageId[imageId].push({
51919 geometry: detection.geometry,
51922 value: detection.value
51925 showDetections(_mlyCache.image_detections.forImageId[imageId] || []);
51927 } // Create a tag for each detection and shows it in the image viewer
51930 function showDetections(detections) {
51931 var tagComponent = _mlyViewer.getComponent('tag');
51933 detections.forEach(function (data) {
51934 var tag = makeTag(data);
51937 tagComponent.add([tag]);
51940 } // Create a Mapillary JS tag object
51943 function makeTag(data) {
51944 var valueParts = data.value.split('--');
51945 if (!valueParts.length) return;
51948 var color = 0xffffff;
51950 if (_mlyHighlightedDetection === data.id) {
51952 text = valueParts[1];
51954 if (text === 'flat' || text === 'discrete' || text === 'sign') {
51955 text = valueParts[2];
51958 text = text.replace(/-/g, ' ');
51959 text = text.charAt(0).toUpperCase() + text.slice(1);
51960 _mlyHighlightedDetection = null;
51963 var decodedGeometry = window.atob(data.geometry);
51964 var uintArray = new Uint8Array(decodedGeometry.length);
51966 for (var i = 0; i < decodedGeometry.length; i++) {
51967 uintArray[i] = decodedGeometry.charCodeAt(i);
51970 var tile = new VectorTile(new pbf(uintArray.buffer));
51971 var layer = tile.layers['mpy-or'];
51972 var geometries = layer.feature(0).loadGeometry();
51973 var polygon = geometries.map(function (ring) {
51974 return ring.map(function (point) {
51975 return [point.x / layer.extent, point.y / layer.extent];
51978 tag = new mapillary.OutlineTag(data.id, new mapillary.PolygonGeometry(polygon[0]), {
51989 // Return the current cache
51990 cache: function cache() {
51995 function validationIssue(attrs) {
51996 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
51998 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
52000 this.severity = attrs.severity; // required - 'warning' or 'error'
52002 this.message = attrs.message; // required - function returning localized string
52004 this.reference = attrs.reference; // optional - function(selection) to render reference information
52006 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
52008 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
52010 this.data = attrs.data; // optional - object containing extra data for the fixes
52012 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
52014 this.hash = attrs.hash; // optional - string to further differentiate the issue
52016 this.id = generateID.apply(this); // generated - see below
52018 this.autoFix = null; // generated - if autofix exists, will be set below
52019 // A unique, deterministic string hash.
52020 // Issues with identical id values are considered identical.
52022 function generateID() {
52023 var parts = [this.type];
52026 // subclasses can pass in their own differentiator
52027 parts.push(this.hash);
52030 if (this.subtype) {
52031 parts.push(this.subtype);
52032 } // include the entities this issue is for
52033 // (sort them so the id is deterministic)
52036 if (this.entityIds) {
52037 var entityKeys = this.entityIds.slice().sort();
52038 parts.push.apply(parts, entityKeys);
52041 return parts.join(':');
52044 this.extent = function (resolver) {
52046 return geoExtent(this.loc);
52049 if (this.entityIds && this.entityIds.length) {
52050 return this.entityIds.reduce(function (extent, entityId) {
52051 return extent.extend(resolver.entity(entityId).extent(resolver));
52058 this.fixes = function (context) {
52059 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
52062 if (issue.severity === 'warning') {
52063 // allow ignoring any issue that's not an error
52064 fixes.push(new validationIssueFix({
52065 title: _t.html('issues.fix.ignore_issue.title'),
52066 icon: 'iD-icon-close',
52067 onClick: function onClick() {
52068 context.validator().ignoreIssue(this.issue.id);
52073 fixes.forEach(function (fix) {
52074 // the id doesn't matter as long as it's unique to this issue/fix
52075 fix.id = fix.title; // add a reference to the issue for use in actions
52079 if (fix.autoArgs) {
52080 issue.autoFix = fix;
52086 function validationIssueFix(attrs) {
52087 this.title = attrs.title; // Required
52089 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
52091 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
52093 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
52095 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
52097 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
52099 this.issue = null; // Generated link - added by validationIssue
52102 var buildRuleChecks = function buildRuleChecks() {
52104 equals: function equals(_equals) {
52105 return function (tags) {
52106 return Object.keys(_equals).every(function (k) {
52107 return _equals[k] === tags[k];
52111 notEquals: function notEquals(_notEquals) {
52112 return function (tags) {
52113 return Object.keys(_notEquals).some(function (k) {
52114 return _notEquals[k] !== tags[k];
52118 absence: function absence(_absence) {
52119 return function (tags) {
52120 return Object.keys(tags).indexOf(_absence) === -1;
52123 presence: function presence(_presence) {
52124 return function (tags) {
52125 return Object.keys(tags).indexOf(_presence) > -1;
52128 greaterThan: function greaterThan(_greaterThan) {
52129 var key = Object.keys(_greaterThan)[0];
52130 var value = _greaterThan[key];
52131 return function (tags) {
52132 return tags[key] > value;
52135 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
52136 var key = Object.keys(_greaterThanEqual)[0];
52137 var value = _greaterThanEqual[key];
52138 return function (tags) {
52139 return tags[key] >= value;
52142 lessThan: function lessThan(_lessThan) {
52143 var key = Object.keys(_lessThan)[0];
52144 var value = _lessThan[key];
52145 return function (tags) {
52146 return tags[key] < value;
52149 lessThanEqual: function lessThanEqual(_lessThanEqual) {
52150 var key = Object.keys(_lessThanEqual)[0];
52151 var value = _lessThanEqual[key];
52152 return function (tags) {
52153 return tags[key] <= value;
52156 positiveRegex: function positiveRegex(_positiveRegex) {
52157 var tagKey = Object.keys(_positiveRegex)[0];
52159 var expression = _positiveRegex[tagKey].join('|');
52161 var regex = new RegExp(expression);
52162 return function (tags) {
52163 return regex.test(tags[tagKey]);
52166 negativeRegex: function negativeRegex(_negativeRegex) {
52167 var tagKey = Object.keys(_negativeRegex)[0];
52169 var expression = _negativeRegex[tagKey].join('|');
52171 var regex = new RegExp(expression);
52172 return function (tags) {
52173 return !regex.test(tags[tagKey]);
52179 var buildLineKeys = function buildLineKeys() {
52195 var serviceMapRules = {
52196 init: function init() {
52197 this._ruleChecks = buildRuleChecks();
52198 this._validationRules = [];
52199 this._areaKeys = osmAreaKeys;
52200 this._lineKeys = buildLineKeys();
52202 // list of rules only relevant to tag checks...
52203 filterRuleChecks: function filterRuleChecks(selector) {
52204 var _ruleChecks = this._ruleChecks;
52205 return Object.keys(selector).reduce(function (rules, key) {
52206 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
52207 rules.push(_ruleChecks[key](selector[key]));
52213 // builds tagMap from mapcss-parse selector object...
52214 buildTagMap: function buildTagMap(selector) {
52215 var getRegexValues = function getRegexValues(regexes) {
52216 return regexes.map(function (regex) {
52217 return regex.replace(/\$|\^/g, '');
52221 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
52223 var isRegex = /regex/gi.test(key);
52224 var isEqual = /equals/gi.test(key);
52226 if (isRegex || isEqual) {
52227 Object.keys(selector[key]).forEach(function (selectorKey) {
52228 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
52230 if (expectedTags.hasOwnProperty(selectorKey)) {
52231 values = values.concat(expectedTags[selectorKey]);
52234 expectedTags[selectorKey] = values;
52236 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
52237 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
52238 values = [selector[key][tagKey]];
52240 if (expectedTags.hasOwnProperty(tagKey)) {
52241 values = values.concat(expectedTags[tagKey]);
52244 expectedTags[tagKey] = values;
52247 return expectedTags;
52251 // inspired by osmWay#isArea()
52252 inferGeometry: function inferGeometry(tagMap) {
52253 var _lineKeys = this._lineKeys;
52254 var _areaKeys = this._areaKeys;
52256 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
52257 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
52260 var keyValueImpliesLine = function keyValueImpliesLine(key) {
52261 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
52264 if (tagMap.hasOwnProperty('area')) {
52265 if (tagMap.area.indexOf('yes') > -1) {
52269 if (tagMap.area.indexOf('no') > -1) {
52274 for (var key in tagMap) {
52275 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
52279 if (key in _lineKeys && keyValueImpliesLine(key)) {
52286 // adds from mapcss-parse selector check...
52287 addRule: function addRule(selector) {
52289 // checks relevant to mapcss-selector
52290 checks: this.filterRuleChecks(selector),
52291 // true if all conditions for a tag error are true..
52292 matches: function matches(entity) {
52293 return this.checks.every(function (check) {
52294 return check(entity.tags);
52297 // borrowed from Way#isArea()
52298 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
52299 geometryMatches: function geometryMatches(entity, graph) {
52300 if (entity.type === 'node' || entity.type === 'relation') {
52301 return selector.geometry === entity.type;
52302 } else if (entity.type === 'way') {
52303 return this.inferredGeometry === entity.geometry(graph);
52306 // when geometries match and tag matches are present, return a warning...
52307 findIssues: function findIssues(entity, graph, issues) {
52308 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
52309 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
52310 var _message = selector[severity];
52311 issues.push(new validationIssue({
52313 severity: severity,
52314 message: function message() {
52317 entityIds: [entity.id]
52323 this._validationRules.push(rule);
52325 clearRules: function clearRules() {
52326 this._validationRules = [];
52328 // returns validationRules...
52329 validationRules: function validationRules() {
52330 return this._validationRules;
52332 // returns ruleChecks
52333 ruleChecks: function ruleChecks() {
52334 return this._ruleChecks;
52338 var apibase$2 = 'https://nominatim.openstreetmap.org/';
52339 var _inflight$2 = {};
52341 var _nominatimCache;
52343 var serviceNominatim = {
52344 init: function init() {
52346 _nominatimCache = new RBush();
52348 reset: function reset() {
52349 Object.values(_inflight$2).forEach(function (controller) {
52350 controller.abort();
52353 _nominatimCache = new RBush();
52355 countryCode: function countryCode(location, callback) {
52356 this.reverse(location, function (err, result) {
52358 return callback(err);
52359 } else if (result.address) {
52360 return callback(null, result.address.country_code);
52362 return callback('Unable to geocode', null);
52366 reverse: function reverse(loc, callback) {
52367 var cached = _nominatimCache.search({
52374 if (cached.length > 0) {
52375 if (callback) callback(null, cached[0].data);
52386 var url = apibase$2 + 'reverse?' + utilQsString(params);
52387 if (_inflight$2[url]) return;
52388 var controller = new AbortController();
52389 _inflight$2[url] = controller;
52391 signal: controller.signal
52392 }).then(function (result) {
52393 delete _inflight$2[url];
52395 if (result && result.error) {
52396 throw new Error(result.error);
52399 var extent = geoExtent(loc).padByMeters(200);
52401 _nominatimCache.insert(Object.assign(extent.bbox(), {
52405 if (callback) callback(null, result);
52406 })["catch"](function (err) {
52407 delete _inflight$2[url];
52408 if (err.name === 'AbortError') return;
52409 if (callback) callback(err.message);
52412 search: function search(val, callback) {
52413 var searchVal = encodeURIComponent(val);
52414 var url = apibase$2 + 'search/' + searchVal + '?limit=10&format=json';
52415 if (_inflight$2[url]) return;
52416 var controller = new AbortController();
52417 _inflight$2[url] = controller;
52419 signal: controller.signal
52420 }).then(function (result) {
52421 delete _inflight$2[url];
52423 if (result && result.error) {
52424 throw new Error(result.error);
52427 if (callback) callback(null, result);
52428 })["catch"](function (err) {
52429 delete _inflight$2[url];
52430 if (err.name === 'AbortError') return;
52431 if (callback) callback(err.message);
52436 // for punction see https://stackoverflow.com/a/21224179
52438 function simplify$1(str) {
52439 if (typeof str !== 'string') return '';
52440 return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i') // for BİM, İşbank - #5017
52441 .replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>«»~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2000-\u206f\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e7f\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
52444 var matchGroups$1 = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beer","shop/beverages","shop/wine"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],clinic:["amenity/clinic","amenity/doctors","healthcare/clinic","healthcare/dialysis"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/grocery","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],dentist:["amenity/dentist","amenity/doctors","healthcare/dentist"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fabric:["shop/fabric","shop/haberdashery","shop/sewing"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/pub","amenity/bar","amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/bathroom_furnishing","shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/hardware_store","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],hobby:["shop/electronics","shop/hobby","shop/books","shop/games","shop/collector","shop/toys","shop/model","shop/video_games","shop/anime"],hospital:["amenity/doctors","amenity/hospital","healthcare/hospital"],houseware:["shop/houseware","shop/interior_decoration"],lifeboat_station:["amenity/lifeboat_station","emergency/lifeboat_station","emergency/marine_rescue"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],office_supplies:["shop/office_supplies","shop/stationary","shop/stationery"],outdoor:["shop/outdoor","shop/sports"],pharmacy:["amenity/doctors","amenity/pharmacy","healthcare/pharmacy"],playground:["amenity/theme_park","leisure/amusement_arcade","leisure/playground"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],storage:["shop/storage_units","shop/storage_rental"],weight_loss:["amenity/doctors","amenity/weight_clinic","healthcare/counselling","leisure/fitness_centre","office/therapist","shop/beauty","shop/diet","shop/food","shop/health_food","shop/herbalist","shop/nutrition","shop/nutrition_supplements","shop/weight_loss"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
52445 var matchGroupsJSON = {
52446 matchGroups: matchGroups$1
52449 var genericWords = ["^(barn|bazaa?r|bench|bou?tique|building|casa|church)$","^(baseball|basketball|football|soccer|softball|tennis(halle)?)\\s?(field|court)?$","^(club|green|out|ware)\\s?house$","^(driveway|el árbol|fountain|golf|government|graveyard)$","^(hofladen|librairie|magazine?|maison)$","^(mobile home|skate)?\\s?park$","^(n\\s?\\/?\\s?a|name|no\\s?name|none|null|temporary|test|unknown)$","^(obuwie|pond|pool|sale|shops?|sklep|stores?)$","^\\?+$","^tattoo( studio)?$","^windmill$","^церковная( лавка)?$"];
52450 var genericWordsJSON = {
52451 genericWords: genericWords
52454 var trees$1 = {brands:{emoji:"🍔",mainTag:"brand:wikidata",sourceTags:["brand","name"],nameTags:{primary:"^(name|name:\\w+)$",alternate:"^(brand|brand:\\w+|operator|operator:\\w+|\\w+_name|\\w+_name:\\w+)$"}},flags:{emoji:"🚩",mainTag:"flag:wikidata",nameTags:{primary:"^(flag:name|flag:name:\\w+)$",alternate:"^(country|country:\\w+|flag|flag:\\w+|subject|subject:\\w+)$"}},operators:{emoji:"💼",mainTag:"operator:wikidata",sourceTags:["operator"],nameTags:{primary:"^(name|name:\\w+|operator|operator:\\w+)$",alternate:"^(brand|brand:\\w+|\\w+_name|\\w+_name:\\w+)$"}},transit:{emoji:"🚇",mainTag:"network:wikidata",sourceTags:["network"],nameTags:{primary:"^network$",alternate:"^(operator|operator:\\w+|network:\\w+|\\w+_name|\\w+_name:\\w+)$"}}};
52459 var matchGroups = matchGroupsJSON.matchGroups;
52460 var trees = treesJSON.trees;
52461 var Matcher = /*#__PURE__*/function () {
52464 // initialize the genericWords regexes
52465 function Matcher() {
52468 _classCallCheck$1(this, Matcher);
52470 // The `matchIndex` is a specialized structure that allows us to quickly answer
52471 // _"Given a [key/value tagpair, name, location], what canonical items (brands etc) can match it?"_
52473 // The index contains all valid combinations of k/v tagpairs and names
52477 // 'primary': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `name`, `name:xx`, etc.
52478 // 'alternate': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `alt_name`, `brand`, etc.
52479 // 'excludeNamed': Map (String 'pattern' -> RegExp),
52480 // 'excludeGeneric': Map (String 'pattern' -> RegExp)
52485 // 'amenity/bank': {
52487 // 'firstbank': Set ("firstbank-978cca", "firstbank-9794e6", "firstbank-f17495", …),
52491 // '1stbank': Set ("firstbank-f17495"),
52495 // 'shop/supermarket': {
52497 // 'coop': Set ("coop-76454b", "coop-ebf2d9", "coop-36e991", …),
52498 // 'coopfood': Set ("coopfood-a8278b", …),
52502 // 'coop': Set ("coopfood-a8278b", …),
52503 // 'federatedcooperatives': Set ("coop-76454b", …),
52504 // 'thecooperative': Set ("coopfood-a8278b", …),
52510 this.matchIndex = undefined; // The `genericWords` structure matches the contents of genericWords.json to instantiated RegExp objects
52511 // Map (String 'pattern' -> RegExp),
52513 this.genericWords = new Map();
52514 (genericWordsJSON.genericWords || []).forEach(function (s) {
52515 return _this.genericWords.set(s, new RegExp(s, 'i'));
52516 }); // The `itemLocation` structure maps itemIDs to locationSetIDs:
52518 // 'firstbank-f17495': '+[first_bank_western_us.geojson]',
52519 // 'firstbank-978cca': '+[first_bank_carolinas.geojson]',
52520 // 'coop-76454b': '+[Q16]',
52521 // 'coopfood-a8278b': '+[Q23666]',
52525 this.itemLocation = undefined; // The `locationSets` structure maps locationSetIDs to *resolved* locationSets:
52527 // '+[first_bank_western_us.geojson]': GeoJSON {…},
52528 // '+[first_bank_carolinas.geojson]': GeoJSON {…},
52529 // '+[Q16]': GeoJSON {…},
52530 // '+[Q23666]': GeoJSON {…},
52534 this.locationSets = undefined; // The `locationIndex` is an instance of which-polygon spatial index for the locationSets.
52536 this.locationIndex = undefined; // Array of match conflict pairs (currently unused)
52538 this.warnings = [];
52540 // `buildMatchIndex()`
52541 // Call this to prepare the matcher for use
52543 // `data` needs to be an Object indexed on a 'tree/key/value' path.
52544 // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52546 // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52547 // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] },
52553 _createClass$1(Matcher, [{
52554 key: "buildMatchIndex",
52555 value: function buildMatchIndex(data) {
52557 if (that.matchIndex) return; // it was built already
52559 that.matchIndex = new Map();
52560 Object.keys(data).forEach(function (tkv) {
52561 var category = data[tkv];
52562 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
52567 var thiskv = "".concat(k, "/").concat(v);
52568 var tree = trees[t];
52569 var branch = that.matchIndex.get(thiskv);
52573 primary: new Map(),
52574 alternate: new Map(),
52575 excludeGeneric: new Map(),
52576 excludeNamed: new Map()
52578 that.matchIndex.set(thiskv, branch);
52579 } // ADD EXCLUSIONS
52582 var properties = category.properties || {};
52583 var exclude = properties.exclude || {};
52584 (exclude.generic || []).forEach(function (s) {
52585 return branch.excludeGeneric.set(s, new RegExp(s, 'i'));
52587 (exclude.named || []).forEach(function (s) {
52588 return branch.excludeNamed.set(s, new RegExp(s, 'i'));
52590 var excludeRegexes = [].concat(_toConsumableArray(branch.excludeGeneric.values()), _toConsumableArray(branch.excludeNamed.values())); // ADD ITEMS
52592 var items = category.items;
52593 if (!Array.isArray(items) || !items.length) return; // Primary name patterns, match tags to take first
52594 // e.g. `name`, `name:ru`
52596 var primaryName = new RegExp(tree.nameTags.primary, 'i'); // Alternate name patterns, match tags to consider after primary
52597 // e.g. `alt_name`, `short_name`, `brand`, `brand:ru`, etc..
52599 var alternateName = new RegExp(tree.nameTags.alternate, 'i'); // There are a few exceptions to the name matching regexes.
52600 // Usually a tag suffix contains a language code like `name:en`, `name:ru`
52601 // but we want to exclude things like `operator:type`, `name:etymology`, etc..
52603 var notName = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52605 var skipGenericKV = skipGenericKVMatches(t, k, v); // We will collect the generic KV pairs anyway (for the purpose of filtering them out of matchTags)
52607 var genericKV = new Set(["".concat(k, "/yes"), "building/yes"]); // Collect alternate tagpairs for this kv category from matchGroups.
52608 // We might also pick up a few more generic KVs (like `shop/yes`)
52610 var matchGroupKV = new Set();
52611 Object.values(matchGroups).forEach(function (matchGroup) {
52612 var inGroup = matchGroup.some(function (otherkv) {
52613 return otherkv === thiskv;
52615 if (!inGroup) return;
52616 matchGroup.forEach(function (otherkv) {
52617 if (otherkv === thiskv) return; // skip self
52619 matchGroupKV.add(otherkv);
52620 var otherk = otherkv.split('/', 2)[0]; // we might pick up a `shop/yes`
52622 genericKV.add("".concat(otherk, "/yes"));
52624 }); // For each item, insert all [key, value, name] combinations into the match index
52626 items.forEach(function (item) {
52627 if (!item.id) return; // Automatically remove redundant `matchTags` - #3417
52628 // (i.e. This kv is already covered by matchGroups, so it doesn't need to be in `item.matchTags`)
52630 if (Array.isArray(item.matchTags) && item.matchTags.length) {
52631 item.matchTags = item.matchTags.filter(function (matchTag) {
52632 return !matchGroupKV.has(matchTag) && !genericKV.has(matchTag);
52634 if (!item.matchTags.length) delete item.matchTags;
52635 } // key/value tagpairs to insert into the match index..
52638 var kvTags = ["".concat(thiskv)].concat(item.matchTags || []);
52640 if (!skipGenericKV) {
52641 kvTags = kvTags.concat(Array.from(genericKV)); // #3454 - match some generic tags
52642 } // Index all the namelike tag values
52645 Object.keys(item.tags).forEach(function (osmkey) {
52646 if (notName.test(osmkey)) return; // osmkey is not a namelike tag, skip
52648 var osmvalue = item.tags[osmkey];
52649 if (!osmvalue || excludeRegexes.some(function (regex) {
52650 return regex.test(osmvalue);
52651 })) return; // osmvalue missing or excluded
52653 if (primaryName.test(osmkey)) {
52654 kvTags.forEach(function (kv) {
52655 return insertName('primary', kv, simplify$1(osmvalue), item.id);
52657 } else if (alternateName.test(osmkey)) {
52658 kvTags.forEach(function (kv) {
52659 return insertName('alternate', kv, simplify$1(osmvalue), item.id);
52662 }); // Index `matchNames` after indexing all other names..
52664 var keepMatchNames = new Set();
52665 (item.matchNames || []).forEach(function (matchName) {
52666 // If this matchname isn't already indexed, add it to the alternate index
52667 var nsimple = simplify$1(matchName);
52668 kvTags.forEach(function (kv) {
52669 var branch = that.matchIndex.get(kv);
52670 var primaryLeaf = branch && branch.primary.get(nsimple);
52671 var alternateLeaf = branch && branch.alternate.get(nsimple);
52672 var inPrimary = primaryLeaf && primaryLeaf.has(item.id);
52673 var inAlternate = alternateLeaf && alternateLeaf.has(item.id);
52675 if (!inPrimary && !inAlternate) {
52676 insertName('alternate', kv, nsimple, item.id);
52677 keepMatchNames.add(matchName);
52680 }); // Automatically remove redundant `matchNames` - #3417
52681 // (i.e. This name got indexed some other way, so it doesn't need to be in `item.matchNames`)
52683 if (keepMatchNames.size) {
52684 item.matchNames = Array.from(keepMatchNames);
52686 delete item.matchNames;
52690 // Insert this item into the matchIndex
52692 function insertName(which, kv, nsimple, itemID) {
52693 if (!nsimple) return;
52694 var branch = that.matchIndex.get(kv);
52698 primary: new Map(),
52699 alternate: new Map(),
52700 excludeGeneric: new Map(),
52701 excludeNamed: new Map()
52703 that.matchIndex.set(kv, branch);
52706 var leaf = branch[which].get(nsimple);
52710 branch[which].set(nsimple, leaf);
52713 leaf.add(itemID); // insert
52714 } // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52717 function skipGenericKVMatches(t, k, v) {
52718 return t === 'flags' || t === 'transit' || k === 'landuse' || v === 'atm' || v === 'bicycle_parking' || v === 'car_sharing' || v === 'caravan_site' || v === 'charging_station' || v === 'dog_park' || v === 'parking' || v === 'phone' || v === 'playground' || v === 'post_box' || v === 'public_bookcase' || v === 'recycling' || v === 'vending_machine';
52721 // `buildLocationIndex()`
52722 // Call this to prepare a which-polygon location index.
52723 // This *resolves* all the locationSets into GeoJSON, which takes some time.
52724 // You can skip this step if you don't care about matching within a location.
52726 // `data` needs to be an Object indexed on a 'tree/key/value' path.
52727 // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52729 // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52730 // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] },
52736 key: "buildLocationIndex",
52737 value: function buildLocationIndex(data, loco) {
52739 if (that.locationIndex) return; // it was built already
52741 that.itemLocation = new Map();
52742 that.locationSets = new Map();
52743 Object.keys(data).forEach(function (tkv) {
52744 var items = data[tkv].items;
52745 if (!Array.isArray(items) || !items.length) return;
52746 items.forEach(function (item) {
52747 if (that.itemLocation.has(item.id)) return; // we've seen item id already - shouldn't be possible?
52752 resolved = loco.resolveLocationSet(item.locationSet); // resolve a feature for this locationSet
52754 console.warn("buildLocationIndex: ".concat(err.message)); // couldn't resolve
52757 if (!resolved || !resolved.id) return;
52758 that.itemLocation.set(item.id, resolved.id); // link it to the item
52760 if (that.locationSets.has(resolved.id)) return; // we've seen this locationSet feature before..
52761 // First time seeing this locationSet feature, make a copy and add to locationSet cache..
52763 var feature = _cloneDeep(resolved.feature);
52765 feature.id = resolved.id; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
52767 feature.properties.id = resolved.id;
52769 if (!feature.geometry.coordinates.length || !feature.properties.area) {
52770 console.warn("buildLocationIndex: locationSet ".concat(resolved.id, " for ").concat(item.id, " resolves to an empty feature:"));
52771 console.warn(JSON.stringify(feature));
52775 that.locationSets.set(resolved.id, feature);
52778 that.locationIndex = whichPolygon_1({
52779 type: 'FeatureCollection',
52780 features: _toConsumableArray(that.locationSets.values())
52783 function _cloneDeep(obj) {
52784 return JSON.parse(JSON.stringify(obj));
52788 // Pass parts and return an Array of matches.
52792 // `loc` - optional - [lon,lat] location to search
52794 // 1. If the [k,v,n] tuple matches a canonical item…
52795 // Return an Array of match results.
52796 // Each result will include the area in km² that the item is valid.
52798 // Order of results:
52799 // Primary ordering will be on the "match" column:
52800 // "primary" - where the query matches the `name` tag, followed by
52801 // "alternate" - where the query matches an alternate name tag (e.g. short_name, brand, operator, etc)
52802 // Secondary ordering will be on the "area" column:
52803 // "area descending" if no location was provided, (worldwide before local)
52804 // "area ascending" if location was provided (local before worldwide)
52807 // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String },
52808 // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String },
52809 // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String },
52810 // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String },
52816 // 2. If the [k,v,n] tuple matches an exclude pattern…
52817 // Return an Array with a single exclude result, either
52819 // [ { match: 'excludeGeneric', pattern: String, kv: String } ] // "generic" e.g. "Food Court"
52821 // [ { match: 'excludeNamed', pattern: String, kv: String } ] // "named", e.g. "Kebabai"
52824 // "generic" - a generic word that is probably not really a name.
52825 // For these, iD should warn the user "Hey don't put 'food court' in the name tag".
52826 // "named" - a real name like "Kebabai" that is just common, but not a brand.
52827 // For these, iD should just let it be. We don't include these in NSI, but we don't want to nag users about it either.
52831 // 3. If the [k,v,n] tuple matches nothing of any kind, return `null`
52837 value: function match(k, v, n, loc) {
52840 if (!that.matchIndex) {
52841 throw new Error('match: matchIndex not built.');
52842 } // If we were supplied a location, and a that.locationIndex has been set up,
52843 // get the locationSets that are valid there so we can filter results.
52846 var matchLocations;
52848 if (Array.isArray(loc) && that.locationIndex) {
52849 // which-polygon query returns an array of GeoJSON properties, pass true to return all results
52850 matchLocations = that.locationIndex([loc[0], loc[1], loc[0], loc[1]], true);
52853 var nsimple = simplify$1(n);
52854 var seen = new Set();
52856 gatherResults('primary');
52857 gatherResults('alternate');
52858 if (results.length) return results;
52859 gatherResults('exclude');
52860 return results.length ? results : null;
52862 function gatherResults(which) {
52863 // First try an exact match on k/v
52864 var kv = "".concat(k, "/").concat(v);
52865 var didMatch = tryMatch(which, kv);
52866 if (didMatch) return; // If that didn't work, look in match groups for other pairs considered equivalent to k/v..
52868 for (var mg in matchGroups) {
52869 var matchGroup = matchGroups[mg];
52870 var inGroup = matchGroup.some(function (otherkv) {
52871 return otherkv === kv;
52873 if (!inGroup) continue;
52875 for (var i = 0; i < matchGroup.length; i++) {
52876 var otherkv = matchGroup[i];
52877 if (otherkv === kv) continue; // skip self
52879 didMatch = tryMatch(which, otherkv);
52880 if (didMatch) return;
52882 } // If finished 'exclude' pass and still haven't matched anything, try the global `genericWords.json` patterns
52885 if (which === 'exclude') {
52886 var regex = _toConsumableArray(that.genericWords.values()).find(function (regex) {
52887 return regex.test(n);
52892 match: 'excludeGeneric',
52893 pattern: String(regex)
52894 }); // note no `branch`, no `kv`
52901 function tryMatch(which, kv) {
52902 var branch = that.matchIndex.get(kv);
52903 if (!branch) return;
52905 if (which === 'exclude') {
52906 // Test name `n` against named and generic exclude patterns
52907 var regex = _toConsumableArray(branch.excludeNamed.values()).find(function (regex) {
52908 return regex.test(n);
52913 match: 'excludeNamed',
52914 pattern: String(regex),
52920 regex = _toConsumableArray(branch.excludeGeneric.values()).find(function (regex) {
52921 return regex.test(n);
52926 match: 'excludeGeneric',
52927 pattern: String(regex),
52936 var leaf = branch[which].get(nsimple);
52937 if (!leaf || !leaf.size) return; // If we get here, we matched something..
52938 // Prepare the results, calculate areas (if location index was set up)
52940 var hits = Array.from(leaf).map(function (itemID) {
52941 var area = Infinity;
52943 if (that.itemLocation && that.locationSets) {
52944 var location = that.locationSets.get(that.itemLocation.get(itemID));
52945 area = location && location.properties.area || Infinity;
52956 var sortFn = byAreaDescending; // Filter the match to include only results valid in the requested `loc`..
52958 if (matchLocations) {
52959 hits = hits.filter(isValidLocation);
52960 sortFn = byAreaAscending;
52963 if (!hits.length) return; // push results
52965 hits.sort(sortFn).forEach(function (hit) {
52966 if (seen.has(hit.itemID)) return;
52967 seen.add(hit.itemID);
52972 function isValidLocation(hit) {
52973 if (!that.itemLocation) return true;
52974 return matchLocations.find(function (props) {
52975 return props.id === that.itemLocation.get(hit.itemID);
52977 } // Sort smaller (more local) locations first.
52980 function byAreaAscending(hitA, hitB) {
52981 return hitA.area - hitB.area;
52982 } // Sort larger (more worldwide) locations first.
52985 function byAreaDescending(hitA, hitB) {
52986 return hitB.area - hitA.area;
52991 // Return any warnings discovered when buiding the index.
52992 // (currently this does nothing)
52996 key: "getWarnings",
52997 value: function getWarnings() {
52998 return this.warnings;
53006 * Checks if `value` is the
53007 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
53008 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
53014 * @param {*} value The value to check.
53015 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
53021 * _.isObject([1, 2, 3]);
53024 * _.isObject(_.noop);
53027 * _.isObject(null);
53030 function isObject$2(value) {
53031 var type = _typeof(value);
53033 return value != null && (type == 'object' || type == 'function');
53036 /** Detect free variable `global` from Node.js. */
53037 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
53039 /** Detect free variable `self`. */
53041 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
53042 /** Used as a reference to the global object. */
53044 var root = freeGlobal || freeSelf || Function('return this')();
53047 * Gets the timestamp of the number of milliseconds that have elapsed since
53048 * the Unix epoch (1 January 1970 00:00:00 UTC).
53054 * @returns {number} Returns the timestamp.
53057 * _.defer(function(stamp) {
53058 * console.log(_.now() - stamp);
53060 * // => Logs the number of milliseconds it took for the deferred invocation.
53063 var now = function now() {
53064 return root.Date.now();
53067 /** Used to match a single whitespace character. */
53068 var reWhitespace = /\s/;
53070 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
53071 * character of `string`.
53074 * @param {string} string The string to inspect.
53075 * @returns {number} Returns the index of the last non-whitespace character.
53078 function trimmedEndIndex(string) {
53079 var index = string.length;
53081 while (index-- && reWhitespace.test(string.charAt(index))) {}
53086 /** Used to match leading whitespace. */
53088 var reTrimStart = /^\s+/;
53090 * The base implementation of `_.trim`.
53093 * @param {string} string The string to trim.
53094 * @returns {string} Returns the trimmed string.
53097 function baseTrim(string) {
53098 return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
53101 /** Built-in value references. */
53103 var _Symbol = root.Symbol;
53105 /** Used for built-in method references. */
53107 var objectProto$1 = Object.prototype;
53108 /** Used to check objects for own properties. */
53110 var hasOwnProperty$2 = objectProto$1.hasOwnProperty;
53112 * Used to resolve the
53113 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53117 var nativeObjectToString$1 = objectProto$1.toString;
53118 /** Built-in value references. */
53120 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
53122 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
53125 * @param {*} value The value to query.
53126 * @returns {string} Returns the raw `toStringTag`.
53129 function getRawTag(value) {
53130 var isOwn = hasOwnProperty$2.call(value, symToStringTag$1),
53131 tag = value[symToStringTag$1];
53134 value[symToStringTag$1] = undefined;
53135 var unmasked = true;
53138 var result = nativeObjectToString$1.call(value);
53142 value[symToStringTag$1] = tag;
53144 delete value[symToStringTag$1];
53151 /** Used for built-in method references. */
53152 var objectProto = Object.prototype;
53154 * Used to resolve the
53155 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53159 var nativeObjectToString = objectProto.toString;
53161 * Converts `value` to a string using `Object.prototype.toString`.
53164 * @param {*} value The value to convert.
53165 * @returns {string} Returns the converted string.
53168 function objectToString(value) {
53169 return nativeObjectToString.call(value);
53172 /** `Object#toString` result references. */
53174 var nullTag = '[object Null]',
53175 undefinedTag = '[object Undefined]';
53176 /** Built-in value references. */
53178 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
53180 * The base implementation of `getTag` without fallbacks for buggy environments.
53183 * @param {*} value The value to query.
53184 * @returns {string} Returns the `toStringTag`.
53187 function baseGetTag(value) {
53188 if (value == null) {
53189 return value === undefined ? undefinedTag : nullTag;
53192 return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
53196 * Checks if `value` is object-like. A value is object-like if it's not `null`
53197 * and has a `typeof` result of "object".
53203 * @param {*} value The value to check.
53204 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
53207 * _.isObjectLike({});
53210 * _.isObjectLike([1, 2, 3]);
53213 * _.isObjectLike(_.noop);
53216 * _.isObjectLike(null);
53219 function isObjectLike(value) {
53220 return value != null && _typeof(value) == 'object';
53223 /** `Object#toString` result references. */
53225 var symbolTag = '[object Symbol]';
53227 * Checks if `value` is classified as a `Symbol` primitive or object.
53233 * @param {*} value The value to check.
53234 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
53237 * _.isSymbol(Symbol.iterator);
53240 * _.isSymbol('abc');
53244 function isSymbol(value) {
53245 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
53248 /** Used as references for various `Number` constants. */
53251 /** Used to detect bad signed hexadecimal string values. */
53253 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
53254 /** Used to detect binary string values. */
53256 var reIsBinary = /^0b[01]+$/i;
53257 /** Used to detect octal string values. */
53259 var reIsOctal = /^0o[0-7]+$/i;
53260 /** Built-in method references without a dependency on `root`. */
53262 var freeParseInt = parseInt;
53264 * Converts `value` to a number.
53270 * @param {*} value The value to process.
53271 * @returns {number} Returns the number.
53277 * _.toNumber(Number.MIN_VALUE);
53280 * _.toNumber(Infinity);
53283 * _.toNumber('3.2');
53287 function toNumber(value) {
53288 if (typeof value == 'number') {
53292 if (isSymbol(value)) {
53296 if (isObject$2(value)) {
53297 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
53298 value = isObject$2(other) ? other + '' : other;
53301 if (typeof value != 'string') {
53302 return value === 0 ? value : +value;
53305 value = baseTrim(value);
53306 var isBinary = reIsBinary.test(value);
53307 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
53310 /** Error message constants. */
53312 var FUNC_ERROR_TEXT$1 = 'Expected a function';
53313 /* Built-in method references for those with the same name as other `lodash` methods. */
53315 var nativeMax = Math.max,
53316 nativeMin = Math.min;
53318 * Creates a debounced function that delays invoking `func` until after `wait`
53319 * milliseconds have elapsed since the last time the debounced function was
53320 * invoked. The debounced function comes with a `cancel` method to cancel
53321 * delayed `func` invocations and a `flush` method to immediately invoke them.
53322 * Provide `options` to indicate whether `func` should be invoked on the
53323 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
53324 * with the last arguments provided to the debounced function. Subsequent
53325 * calls to the debounced function return the result of the last `func`
53328 * **Note:** If `leading` and `trailing` options are `true`, `func` is
53329 * invoked on the trailing edge of the timeout only if the debounced function
53330 * is invoked more than once during the `wait` timeout.
53332 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
53333 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
53335 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
53336 * for details over the differences between `_.debounce` and `_.throttle`.
53341 * @category Function
53342 * @param {Function} func The function to debounce.
53343 * @param {number} [wait=0] The number of milliseconds to delay.
53344 * @param {Object} [options={}] The options object.
53345 * @param {boolean} [options.leading=false]
53346 * Specify invoking on the leading edge of the timeout.
53347 * @param {number} [options.maxWait]
53348 * The maximum time `func` is allowed to be delayed before it's invoked.
53349 * @param {boolean} [options.trailing=true]
53350 * Specify invoking on the trailing edge of the timeout.
53351 * @returns {Function} Returns the new debounced function.
53354 * // Avoid costly calculations while the window size is in flux.
53355 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
53357 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
53358 * jQuery(element).on('click', _.debounce(sendMail, 300, {
53360 * 'trailing': false
53363 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
53364 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
53365 * var source = new EventSource('/stream');
53366 * jQuery(source).on('message', debounced);
53368 * // Cancel the trailing debounced invocation.
53369 * jQuery(window).on('popstate', debounced.cancel);
53372 function debounce(func, wait, options) {
53379 lastInvokeTime = 0,
53384 if (typeof func != 'function') {
53385 throw new TypeError(FUNC_ERROR_TEXT$1);
53388 wait = toNumber(wait) || 0;
53390 if (isObject$2(options)) {
53391 leading = !!options.leading;
53392 maxing = 'maxWait' in options;
53393 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
53394 trailing = 'trailing' in options ? !!options.trailing : trailing;
53397 function invokeFunc(time) {
53398 var args = lastArgs,
53399 thisArg = lastThis;
53400 lastArgs = lastThis = undefined;
53401 lastInvokeTime = time;
53402 result = func.apply(thisArg, args);
53406 function leadingEdge(time) {
53407 // Reset any `maxWait` timer.
53408 lastInvokeTime = time; // Start the timer for the trailing edge.
53410 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
53412 return leading ? invokeFunc(time) : result;
53415 function remainingWait(time) {
53416 var timeSinceLastCall = time - lastCallTime,
53417 timeSinceLastInvoke = time - lastInvokeTime,
53418 timeWaiting = wait - timeSinceLastCall;
53419 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
53422 function shouldInvoke(time) {
53423 var timeSinceLastCall = time - lastCallTime,
53424 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
53425 // trailing edge, the system time has gone backwards and we're treating
53426 // it as the trailing edge, or we've hit the `maxWait` limit.
53428 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
53431 function timerExpired() {
53434 if (shouldInvoke(time)) {
53435 return trailingEdge(time);
53436 } // Restart the timer.
53439 timerId = setTimeout(timerExpired, remainingWait(time));
53442 function trailingEdge(time) {
53443 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
53444 // debounced at least once.
53446 if (trailing && lastArgs) {
53447 return invokeFunc(time);
53450 lastArgs = lastThis = undefined;
53454 function cancel() {
53455 if (timerId !== undefined) {
53456 clearTimeout(timerId);
53459 lastInvokeTime = 0;
53460 lastArgs = lastCallTime = lastThis = timerId = undefined;
53464 return timerId === undefined ? result : trailingEdge(now());
53467 function debounced() {
53469 isInvoking = shouldInvoke(time);
53470 lastArgs = arguments;
53472 lastCallTime = time;
53475 if (timerId === undefined) {
53476 return leadingEdge(lastCallTime);
53480 // Handle invocations in a tight loop.
53481 clearTimeout(timerId);
53482 timerId = setTimeout(timerExpired, wait);
53483 return invokeFunc(lastCallTime);
53487 if (timerId === undefined) {
53488 timerId = setTimeout(timerExpired, wait);
53494 debounced.cancel = cancel;
53495 debounced.flush = flush;
53500 iD.coreDifference represents the difference between two graphs.
53501 It knows how to calculate the set of entities that were
53502 created, modified, or deleted, and also contains the logic
53503 for recursively extending a difference to the complete set
53504 of entities that will require a redraw, taking into account
53505 child and parent relationships.
53508 function coreDifference(base, head) {
53510 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
53514 function checkEntityID(id) {
53515 var h = head.entities[id];
53516 var b = base.entities[id];
53517 if (h === b) return;
53518 if (_changes[id]) return;
53525 _didChange.deletion = true;
53534 _didChange.addition = true;
53539 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
53544 _didChange.geometry = true;
53545 _didChange.properties = true;
53549 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
53554 _didChange.geometry = true;
53557 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
53562 _didChange.geometry = true;
53565 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
53570 _didChange.properties = true;
53576 // HOT CODE: there can be many thousands of downloaded entities, so looping
53577 // through them all can become a performance bottleneck. Optimize by
53578 // resolving duplicates and using a basic `for` loop
53579 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
53581 for (var i = 0; i < ids.length; i++) {
53582 checkEntityID(ids[i]);
53588 _diff.length = function length() {
53589 return Object.keys(_changes).length;
53592 _diff.changes = function changes() {
53596 _diff.didChange = _didChange; // pass true to include affected relation members
53598 _diff.extantIDs = function extantIDs(includeRelMembers) {
53599 var result = new Set();
53600 Object.keys(_changes).forEach(function (id) {
53601 if (_changes[id].head) {
53605 var h = _changes[id].head;
53606 var b = _changes[id].base;
53607 var entity = h || b;
53609 if (includeRelMembers && entity.type === 'relation') {
53610 var mh = h ? h.members.map(function (m) {
53613 var mb = b ? b.members.map(function (m) {
53616 utilArrayUnion(mh, mb).forEach(function (memberID) {
53617 if (head.hasEntity(memberID)) {
53618 result.add(memberID);
53623 return Array.from(result);
53626 _diff.modified = function modified() {
53628 Object.values(_changes).forEach(function (change) {
53629 if (change.base && change.head) {
53630 result.push(change.head);
53636 _diff.created = function created() {
53638 Object.values(_changes).forEach(function (change) {
53639 if (!change.base && change.head) {
53640 result.push(change.head);
53646 _diff.deleted = function deleted() {
53648 Object.values(_changes).forEach(function (change) {
53649 if (change.base && !change.head) {
53650 result.push(change.base);
53656 _diff.summary = function summary() {
53658 var keys = Object.keys(_changes);
53660 for (var i = 0; i < keys.length; i++) {
53661 var change = _changes[keys[i]];
53663 if (change.head && change.head.geometry(head) !== 'vertex') {
53664 addEntity(change.head, head, change.base ? 'modified' : 'created');
53665 } else if (change.base && change.base.geometry(base) !== 'vertex') {
53666 addEntity(change.base, base, 'deleted');
53667 } else if (change.base && change.head) {
53669 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
53670 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
53673 addParents(change.head);
53676 if (retagged || moved && change.head.hasInterestingTags()) {
53677 addEntity(change.head, head, 'modified');
53679 } else if (change.head && change.head.hasInterestingTags()) {
53681 addEntity(change.head, head, 'created');
53682 } else if (change.base && change.base.hasInterestingTags()) {
53684 addEntity(change.base, base, 'deleted');
53688 return Object.values(relevant);
53690 function addEntity(entity, graph, changeType) {
53691 relevant[entity.id] = {
53694 changeType: changeType
53698 function addParents(entity) {
53699 var parents = head.parentWays(entity);
53701 for (var j = parents.length - 1; j >= 0; j--) {
53702 var parent = parents[j];
53704 if (!(parent.id in relevant)) {
53705 addEntity(parent, head, 'modified');
53709 }; // returns complete set of entities that require a redraw
53710 // (optionally within given `extent`)
53713 _diff.complete = function complete(extent) {
53717 for (id in _changes) {
53718 change = _changes[id];
53719 var h = change.head;
53720 var b = change.base;
53721 var entity = h || b;
53724 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) {
53730 if (entity.type === 'way') {
53731 var nh = h ? h.nodes : [];
53732 var nb = b ? b.nodes : [];
53734 diff = utilArrayDifference(nh, nb);
53736 for (i = 0; i < diff.length; i++) {
53737 result[diff[i]] = head.hasEntity(diff[i]);
53740 diff = utilArrayDifference(nb, nh);
53742 for (i = 0; i < diff.length; i++) {
53743 result[diff[i]] = head.hasEntity(diff[i]);
53747 if (entity.type === 'relation' && entity.isMultipolygon()) {
53748 var mh = h ? h.members.map(function (m) {
53751 var mb = b ? b.members.map(function (m) {
53754 var ids = utilArrayUnion(mh, mb);
53756 for (i = 0; i < ids.length; i++) {
53757 var member = head.hasEntity(ids[i]);
53758 if (!member) continue; // not downloaded
53760 if (extent && !member.intersects(extent, head)) continue; // not visible
53762 result[ids[i]] = member;
53766 addParents(head.parentWays(entity), result);
53767 addParents(head.parentRelations(entity), result);
53772 function addParents(parents, result) {
53773 for (var i = 0; i < parents.length; i++) {
53774 var parent = parents[i];
53775 if (parent.id in result) continue;
53776 result[parent.id] = parent;
53777 addParents(head.parentRelations(parent), result);
53785 function coreTree(head) {
53786 // tree for entities
53787 var _rtree = new RBush();
53789 var _bboxes = {}; // maintain a separate tree for granular way segments
53791 var _segmentsRTree = new RBush();
53793 var _segmentsBBoxes = {};
53794 var _segmentsByWayId = {};
53797 function entityBBox(entity) {
53798 var bbox = entity.extent(head).bbox();
53799 bbox.id = entity.id;
53800 _bboxes[entity.id] = bbox;
53804 function segmentBBox(segment) {
53805 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
53807 if (!extent) return null;
53808 var bbox = extent.bbox();
53809 bbox.segment = segment;
53810 _segmentsBBoxes[segment.id] = bbox;
53814 function removeEntity(entity) {
53815 _rtree.remove(_bboxes[entity.id]);
53817 delete _bboxes[entity.id];
53819 if (_segmentsByWayId[entity.id]) {
53820 _segmentsByWayId[entity.id].forEach(function (segment) {
53821 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
53823 delete _segmentsBBoxes[segment.id];
53826 delete _segmentsByWayId[entity.id];
53830 function loadEntities(entities) {
53831 _rtree.load(entities.map(entityBBox));
53834 entities.forEach(function (entity) {
53835 if (entity.segments) {
53836 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
53838 _segmentsByWayId[entity.id] = entitySegments;
53839 segments = segments.concat(entitySegments);
53842 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
53845 function updateParents(entity, insertions, memo) {
53846 head.parentWays(entity).forEach(function (way) {
53847 if (_bboxes[way.id]) {
53849 insertions[way.id] = way;
53852 updateParents(way, insertions, memo);
53854 head.parentRelations(entity).forEach(function (relation) {
53855 if (memo[entity.id]) return;
53856 memo[entity.id] = true;
53858 if (_bboxes[relation.id]) {
53859 removeEntity(relation);
53860 insertions[relation.id] = relation;
53863 updateParents(relation, insertions, memo);
53867 tree.rebase = function (entities, force) {
53868 var insertions = {};
53870 for (var i = 0; i < entities.length; i++) {
53871 var entity = entities[i];
53872 if (!entity.visible) continue;
53874 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
53877 } else if (_bboxes[entity.id]) {
53878 removeEntity(entity);
53882 insertions[entity.id] = entity;
53883 updateParents(entity, insertions, {});
53886 loadEntities(Object.values(insertions));
53890 function updateToGraph(graph) {
53891 if (graph === head) return;
53892 var diff = coreDifference(head, graph);
53894 var changed = diff.didChange;
53895 if (!changed.addition && !changed.deletion && !changed.geometry) return;
53896 var insertions = {};
53898 if (changed.deletion) {
53899 diff.deleted().forEach(function (entity) {
53900 removeEntity(entity);
53904 if (changed.geometry) {
53905 diff.modified().forEach(function (entity) {
53906 removeEntity(entity);
53907 insertions[entity.id] = entity;
53908 updateParents(entity, insertions, {});
53912 if (changed.addition) {
53913 diff.created().forEach(function (entity) {
53914 insertions[entity.id] = entity;
53918 loadEntities(Object.values(insertions));
53919 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
53922 tree.intersects = function (extent, graph) {
53923 updateToGraph(graph);
53924 return _rtree.search(extent.bbox()).map(function (bbox) {
53925 return graph.entity(bbox.id);
53927 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
53930 tree.waySegments = function (extent, graph) {
53931 updateToGraph(graph);
53932 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
53933 return bbox.segment;
53940 function svgIcon(name, svgklass, useklass) {
53941 return function drawIcon(selection) {
53942 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);
53946 function uiModal(selection, blocking) {
53949 var keybinding = utilKeybinding('modal');
53950 var previous = selection.select('div.modal');
53951 var animate = previous.empty();
53952 previous.transition().duration(200).style('opacity', 0).remove();
53953 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
53955 shaded.close = function () {
53956 shaded.transition().duration(200).style('opacity', 0).remove();
53957 modal.transition().duration(200).style('top', '0px');
53958 select(document).call(keybinding.unbind);
53961 var modal = shaded.append('div').attr('class', 'modal fillL');
53962 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
53965 shaded.on('click.remove-modal', function (d3_event) {
53966 if (d3_event.target === _this) {
53970 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
53971 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
53972 select(document).call(keybinding);
53975 modal.append('div').attr('class', 'content');
53976 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
53979 shaded.transition().style('opacity', 1);
53981 shaded.style('opacity', 1);
53986 function moveFocusToFirst() {
53987 var node = modal // there are additional rules about what's focusable, but this suits our purposes
53988 .select('a, button, input:not(.keytrap), select, textarea').node();
53993 select(this).node().blur();
53997 function moveFocusToLast() {
53998 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
54000 if (nodes.length) {
54001 nodes[nodes.length - 1].focus();
54003 select(this).node().blur();
54008 function uiLoading(context) {
54009 var _modalSelection = select(null);
54012 var _blocking = false;
54014 var loading = function loading(selection) {
54015 _modalSelection = uiModal(selection, _blocking);
54017 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
54019 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
54020 loadertext.append('h3').html(_message);
54022 _modalSelection.select('button.close').attr('class', 'hide');
54027 loading.message = function (val) {
54028 if (!arguments.length) return _message;
54033 loading.blocking = function (val) {
54034 if (!arguments.length) return _blocking;
54039 loading.close = function () {
54040 _modalSelection.remove();
54043 loading.isShown = function () {
54044 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
54050 function coreHistory(context) {
54051 var dispatch = dispatch$8('reset', 'change', 'merge', 'restore', 'undone', 'redone');
54053 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
54056 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
54058 var duration = 150;
54059 var _imageryUsed = [];
54060 var _photoOverlaysUsed = [];
54061 var _checkpoints = {};
54069 var _tree; // internal _act, accepts list of actions and eased time
54072 function _act(actions, t) {
54073 actions = Array.prototype.slice.call(actions);
54076 if (typeof actions[actions.length - 1] !== 'function') {
54077 annotation = actions.pop();
54080 var graph = _stack[_index].graph;
54082 for (var i = 0; i < actions.length; i++) {
54083 graph = actions[i](graph, t);
54088 annotation: annotation,
54089 imageryUsed: _imageryUsed,
54090 photoOverlaysUsed: _photoOverlaysUsed,
54091 transform: context.projection.transform(),
54092 selectedIDs: context.selectedIDs()
54094 } // internal _perform with eased time
54097 function _perform(args, t) {
54098 var previous = _stack[_index].graph;
54099 _stack = _stack.slice(0, _index + 1);
54101 var actionResult = _act(args, t);
54103 _stack.push(actionResult);
54106 return change(previous);
54107 } // internal _replace with eased time
54110 function _replace(args, t) {
54111 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
54113 var actionResult = _act(args, t);
54115 _stack[_index] = actionResult;
54116 return change(previous);
54117 } // internal _overwrite with eased time
54120 function _overwrite(args, t) {
54121 var previous = _stack[_index].graph;
54129 _stack = _stack.slice(0, _index + 1);
54131 var actionResult = _act(args, t);
54133 _stack.push(actionResult);
54136 return change(previous);
54137 } // determine difference and dispatch a change event
54140 function change(previous) {
54141 var difference = coreDifference(previous, history.graph());
54143 if (!_pausedGraph) {
54144 dispatch.call('change', this, difference);
54148 } // iD uses namespaced keys so multiple installations do not conflict
54151 function getKey(n) {
54152 return 'iD_' + window.location.origin + '_' + n;
54156 graph: function graph() {
54157 return _stack[_index].graph;
54159 tree: function tree() {
54162 base: function base() {
54163 return _stack[0].graph;
54165 merge: function merge(entities
54168 var stack = _stack.map(function (state) {
54169 return state.graph;
54172 _stack[0].graph.rebase(entities, stack, false);
54174 _tree.rebase(entities, false);
54176 dispatch.call('merge', this, entities);
54178 perform: function perform() {
54179 // complete any transition already in progress
54180 select(document).interrupt('history.perform');
54181 var transitionable = false;
54182 var action0 = arguments[0];
54184 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
54185 transitionable = !!action0.transitionable;
54188 if (transitionable) {
54189 var origArguments = arguments;
54190 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
54191 return function (t) {
54192 if (t < 1) _overwrite([action0], t);
54194 }).on('start', function () {
54195 _perform([action0], 0);
54196 }).on('end interrupt', function () {
54197 _overwrite(origArguments, 1);
54200 return _perform(arguments);
54203 replace: function replace() {
54204 select(document).interrupt('history.perform');
54205 return _replace(arguments, 1);
54207 // Same as calling pop and then perform
54208 overwrite: function overwrite() {
54209 select(document).interrupt('history.perform');
54210 return _overwrite(arguments, 1);
54212 pop: function pop(n) {
54213 select(document).interrupt('history.perform');
54214 var previous = _stack[_index].graph;
54216 if (isNaN(+n) || +n < 0) {
54220 while (n-- > 0 && _index > 0) {
54226 return change(previous);
54228 // Back to the previous annotated state or _index = 0.
54229 undo: function undo() {
54230 select(document).interrupt('history.perform');
54231 var previousStack = _stack[_index];
54232 var previous = previousStack.graph;
54234 while (_index > 0) {
54236 if (_stack[_index].annotation) break;
54239 dispatch.call('undone', this, _stack[_index], previousStack);
54240 return change(previous);
54242 // Forward to the next annotated state.
54243 redo: function redo() {
54244 select(document).interrupt('history.perform');
54245 var previousStack = _stack[_index];
54246 var previous = previousStack.graph;
54247 var tryIndex = _index;
54249 while (tryIndex < _stack.length - 1) {
54252 if (_stack[tryIndex].annotation) {
54254 dispatch.call('redone', this, _stack[_index], previousStack);
54259 return change(previous);
54261 pauseChangeDispatch: function pauseChangeDispatch() {
54262 if (!_pausedGraph) {
54263 _pausedGraph = _stack[_index].graph;
54266 resumeChangeDispatch: function resumeChangeDispatch() {
54267 if (_pausedGraph) {
54268 var previous = _pausedGraph;
54269 _pausedGraph = null;
54270 return change(previous);
54273 undoAnnotation: function undoAnnotation() {
54277 if (_stack[i].annotation) return _stack[i].annotation;
54281 redoAnnotation: function redoAnnotation() {
54282 var i = _index + 1;
54284 while (i <= _stack.length - 1) {
54285 if (_stack[i].annotation) return _stack[i].annotation;
54289 // Returns the entities from the active graph with bounding boxes
54290 // overlapping the given `extent`.
54291 intersects: function intersects(extent) {
54292 return _tree.intersects(extent, _stack[_index].graph);
54294 difference: function difference() {
54295 var base = _stack[0].graph;
54296 var head = _stack[_index].graph;
54297 return coreDifference(base, head);
54299 changes: function changes(action) {
54300 var base = _stack[0].graph;
54301 var head = _stack[_index].graph;
54304 head = action(head);
54307 var difference = coreDifference(base, head);
54309 modified: difference.modified(),
54310 created: difference.created(),
54311 deleted: difference.deleted()
54314 hasChanges: function hasChanges() {
54315 return this.difference().length() > 0;
54317 imageryUsed: function imageryUsed(sources) {
54319 _imageryUsed = sources;
54324 _stack.slice(1, _index + 1).forEach(function (state) {
54325 state.imageryUsed.forEach(function (source) {
54326 if (source !== 'Custom') {
54332 return Array.from(s);
54335 photoOverlaysUsed: function photoOverlaysUsed(sources) {
54337 _photoOverlaysUsed = sources;
54342 _stack.slice(1, _index + 1).forEach(function (state) {
54343 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
54344 state.photoOverlaysUsed.forEach(function (photoOverlay) {
54345 s.add(photoOverlay);
54350 return Array.from(s);
54353 // save the current history state
54354 checkpoint: function checkpoint(key) {
54355 _checkpoints[key] = {
54361 // restore history state to a given checkpoint or reset completely
54362 reset: function reset(key) {
54363 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
54364 _stack = _checkpoints[key].stack;
54365 _index = _checkpoints[key].index;
54371 _tree = coreTree(_stack[0].graph);
54375 dispatch.call('reset');
54376 dispatch.call('change');
54379 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
54382 // 1. Start the walkthrough.
54383 // 2. Get to a "free editing" tutorial step
54384 // 3. Make your edits to the walkthrough map
54385 // 4. In your browser dev console run:
54386 // `id.history().toIntroGraph()`
54387 // 5. This outputs stringified JSON to the browser console
54388 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
54389 toIntroGraph: function toIntroGraph() {
54396 var graph = this.graph();
54397 var baseEntities = {}; // clone base entities..
54399 Object.values(graph.base().entities).forEach(function (entity) {
54400 var copy = copyIntroEntity(entity);
54401 baseEntities[copy.id] = copy;
54402 }); // replace base entities with head entities..
54404 Object.keys(graph.entities).forEach(function (id) {
54405 var entity = graph.entities[id];
54408 var copy = copyIntroEntity(entity);
54409 baseEntities[copy.id] = copy;
54411 delete baseEntities[id];
54413 }); // swap temporary for permanent ids..
54415 Object.values(baseEntities).forEach(function (entity) {
54416 if (Array.isArray(entity.nodes)) {
54417 entity.nodes = entity.nodes.map(function (node) {
54418 return permIDs[node] || node;
54422 if (Array.isArray(entity.members)) {
54423 entity.members = entity.members.map(function (member) {
54424 member.id = permIDs[member.id] || member.id;
54429 return JSON.stringify({
54430 dataIntroGraph: baseEntities
54433 function copyIntroEntity(source) {
54434 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
54436 if (copy.tags && !Object.keys(copy.tags)) {
54440 if (Array.isArray(copy.loc)) {
54441 copy.loc[0] = +copy.loc[0].toFixed(6);
54442 copy.loc[1] = +copy.loc[1].toFixed(6);
54445 var match = source.id.match(/([nrw])-\d*/); // temporary id
54447 if (match !== null) {
54448 var nrw = match[1];
54452 permID = nrw + ++nextID[nrw];
54453 } while (baseEntities.hasOwnProperty(permID));
54455 copy.id = permIDs[source.id] = permID;
54461 toJSON: function toJSON() {
54462 if (!this.hasChanges()) return;
54463 var allEntities = {};
54464 var baseEntities = {};
54465 var base = _stack[0];
54467 var s = _stack.map(function (i) {
54470 Object.keys(i.graph.entities).forEach(function (id) {
54471 var entity = i.graph.entities[id];
54474 var key = osmEntity.key(entity);
54475 allEntities[key] = entity;
54476 modified.push(key);
54479 } // make sure that the originals of changed or deleted entities get merged
54480 // into the base of the _stack after restoring the data from JSON.
54483 if (id in base.graph.entities) {
54484 baseEntities[id] = base.graph.entities[id];
54487 if (entity && entity.nodes) {
54488 // get originals of pre-existing child nodes
54489 entity.nodes.forEach(function (nodeID) {
54490 if (nodeID in base.graph.entities) {
54491 baseEntities[nodeID] = base.graph.entities[nodeID];
54494 } // get originals of parent entities too
54497 var baseParents = base.graph._parentWays[id];
54500 baseParents.forEach(function (parentID) {
54501 if (parentID in base.graph.entities) {
54502 baseEntities[parentID] = base.graph.entities[parentID];
54508 if (modified.length) x.modified = modified;
54509 if (deleted.length) x.deleted = deleted;
54510 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
54511 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
54512 if (i.annotation) x.annotation = i.annotation;
54513 if (i.transform) x.transform = i.transform;
54514 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
54518 return JSON.stringify({
54520 entities: Object.values(allEntities),
54521 baseEntities: Object.values(baseEntities),
54523 nextIDs: osmEntity.id.next,
54525 // note the time the changes were saved
54526 timestamp: new Date().getTime()
54529 fromJSON: function fromJSON(json, loadChildNodes) {
54530 var h = JSON.parse(json);
54531 var loadComplete = true;
54532 osmEntity.id.next = h.nextIDs;
54535 if (h.version === 2 || h.version === 3) {
54536 var allEntities = {};
54537 h.entities.forEach(function (entity) {
54538 allEntities[osmEntity.key(entity)] = osmEntity(entity);
54541 if (h.version === 3) {
54542 // This merges originals for changed entities into the base of
54543 // the _stack even if the current _stack doesn't have them (for
54544 // example when iD has been restarted in a different region)
54545 var baseEntities = h.baseEntities.map(function (d) {
54546 return osmEntity(d);
54549 var stack = _stack.map(function (state) {
54550 return state.graph;
54553 _stack[0].graph.rebase(baseEntities, stack, true);
54555 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
54556 // childnodes that would normally have been downloaded with it.. #2142
54559 if (loadChildNodes) {
54560 var osm = context.connection();
54561 var baseWays = baseEntities.filter(function (e) {
54562 return e.type === 'way';
54564 var nodeIDs = baseWays.reduce(function (acc, way) {
54565 return utilArrayUnion(acc, way.nodes);
54567 var missing = nodeIDs.filter(function (n) {
54568 return !_stack[0].graph.hasEntity(n);
54571 if (missing.length && osm) {
54572 loadComplete = false;
54573 context.map().redrawEnable(false);
54574 var loading = uiLoading(context).blocking(true);
54575 context.container().call(loading);
54577 var childNodesLoaded = function childNodesLoaded(err, result) {
54579 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
54580 var visibles = visibleGroups["true"] || []; // alive nodes
54582 var invisibles = visibleGroups["false"] || []; // deleted nodes
54584 if (visibles.length) {
54585 var visibleIDs = visibles.map(function (entity) {
54589 var stack = _stack.map(function (state) {
54590 return state.graph;
54593 missing = utilArrayDifference(missing, visibleIDs);
54595 _stack[0].graph.rebase(visibles, stack, true);
54597 _tree.rebase(visibles, true);
54598 } // fetch older versions of nodes that were deleted..
54601 invisibles.forEach(function (entity) {
54602 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
54606 if (err || !missing.length) {
54608 context.map().redrawEnable(true);
54609 dispatch.call('change');
54610 dispatch.call('restore', this);
54614 osm.loadMultiple(missing, childNodesLoaded);
54619 _stack = h.stack.map(function (d) {
54624 d.modified.forEach(function (key) {
54625 entity = allEntities[key];
54626 entities[entity.id] = entity;
54631 d.deleted.forEach(function (id) {
54632 entities[id] = undefined;
54637 graph: coreGraph(_stack[0].graph).load(entities),
54638 annotation: d.annotation,
54639 imageryUsed: d.imageryUsed,
54640 photoOverlaysUsed: d.photoOverlaysUsed,
54641 transform: d.transform,
54642 selectedIDs: d.selectedIDs
54646 // original version
54647 _stack = h.stack.map(function (d) {
54650 for (var i in d.entities) {
54651 var entity = d.entities[i];
54652 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
54655 d.graph = coreGraph(_stack[0].graph).load(entities);
54660 var transform = _stack[_index].transform;
54663 context.map().transformEase(transform, 0); // 0 = immediate, no easing
54666 if (loadComplete) {
54667 dispatch.call('change');
54668 dispatch.call('restore', this);
54673 lock: function lock() {
54674 return _lock.lock();
54676 unlock: function unlock() {
54679 save: function save() {
54680 if (_lock.locked() && // don't overwrite existing, unresolved changes
54681 !_hasUnresolvedRestorableChanges) {
54682 corePreferences(getKey('saved_history'), history.toJSON() || null);
54687 // delete the history version saved in localStorage
54688 clearSaved: function clearSaved() {
54689 context.debouncedSave.cancel();
54691 if (_lock.locked()) {
54692 _hasUnresolvedRestorableChanges = false;
54693 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
54695 corePreferences('comment', null);
54696 corePreferences('hashtags', null);
54697 corePreferences('source', null);
54702 savedHistoryJSON: function savedHistoryJSON() {
54703 return corePreferences(getKey('saved_history'));
54705 hasRestorableChanges: function hasRestorableChanges() {
54706 return _hasUnresolvedRestorableChanges;
54708 // load history from a version stored in localStorage
54709 restore: function restore() {
54710 if (_lock.locked()) {
54711 _hasUnresolvedRestorableChanges = false;
54712 var json = this.savedHistoryJSON();
54713 if (json) history.fromJSON(json, true);
54719 return utilRebind(history, dispatch, 'on');
54723 * Look for roads that can be connected to other roads with a short extension
54726 function validationAlmostJunction(context) {
54727 var type = 'almost_junction';
54728 var EXTEND_TH_METERS = 5;
54729 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
54731 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
54733 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
54735 function isHighway(entity) {
54736 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
54739 function isTaggedAsNotContinuing(node) {
54740 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
54743 var validation = function checkAlmostJunction(entity, graph) {
54744 if (!isHighway(entity)) return [];
54745 if (entity.isDegenerate()) return [];
54746 var tree = context.history().tree();
54747 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
54749 extendableNodeInfos.forEach(function (extendableNodeInfo) {
54750 issues.push(new validationIssue({
54752 subtype: 'highway-highway',
54753 severity: 'warning',
54754 message: function message(context) {
54755 var entity1 = context.hasEntity(this.entityIds[0]);
54757 if (this.entityIds[0] === this.entityIds[2]) {
54758 return entity1 ? _t.html('issues.almost_junction.self.message', {
54759 feature: utilDisplayLabel(entity1, context.graph())
54762 var entity2 = context.hasEntity(this.entityIds[2]);
54763 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
54764 feature: utilDisplayLabel(entity1, context.graph()),
54765 feature2: utilDisplayLabel(entity2, context.graph())
54769 reference: showReference,
54770 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
54771 loc: extendableNodeInfo.node.loc,
54772 hash: JSON.stringify(extendableNodeInfo.node.loc),
54774 midId: extendableNodeInfo.mid.id,
54775 edge: extendableNodeInfo.edge,
54776 cross_loc: extendableNodeInfo.cross_loc
54778 dynamicFixes: makeFixes
54783 function makeFixes(context) {
54784 var fixes = [new validationIssueFix({
54785 icon: 'iD-icon-abutment',
54786 title: _t.html('issues.fix.connect_features.title'),
54787 onClick: function onClick(context) {
54788 var annotation = _t('issues.fix.connect_almost_junction.annotation');
54790 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
54791 endNodeId = _this$issue$entityIds[1],
54792 crossWayId = _this$issue$entityIds[2];
54794 var midNode = context.entity(this.issue.data.midId);
54795 var endNode = context.entity(endNodeId);
54796 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
54798 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
54800 if (nearEndNodes.length > 0) {
54801 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
54804 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
54809 var targetEdge = this.issue.data.edge;
54810 var crossLoc = this.issue.data.cross_loc;
54811 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
54812 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
54814 if (closestNodeInfo.distance < WELD_TH_METERS) {
54815 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
54817 context.perform(actionAddMidpoint({
54820 }, endNode), annotation);
54824 var node = context.hasEntity(this.entityIds[1]);
54826 if (node && !node.hasInterestingTags()) {
54827 // node has no descriptive tags, suggest noexit fix
54828 fixes.push(new validationIssueFix({
54829 icon: 'maki-barrier',
54830 title: _t.html('issues.fix.tag_as_disconnected.title'),
54831 onClick: function onClick(context) {
54832 var nodeID = this.issue.entityIds[1];
54833 var tags = Object.assign({}, context.entity(nodeID).tags);
54834 tags.noexit = 'yes';
54835 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
54843 function showReference(selection) {
54844 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
54847 function isExtendableCandidate(node, way) {
54848 // can not accurately test vertices on tiles not downloaded from osm - #5938
54849 var osm = services.osm;
54851 if (osm && !osm.isDataLoaded(node.loc)) {
54855 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
54859 var occurrences = 0;
54861 for (var index in way.nodes) {
54862 if (way.nodes[index] === node.id) {
54865 if (occurrences > 1) {
54874 function findConnectableEndNodesByExtension(way) {
54876 if (way.isClosed()) return results;
54878 var indices = [0, way.nodes.length - 1];
54879 indices.forEach(function (nodeIndex) {
54880 var nodeID = way.nodes[nodeIndex];
54881 var node = graph.entity(nodeID);
54882 if (!isExtendableCandidate(node, way)) return;
54883 var connectionInfo = canConnectByExtend(way, nodeIndex);
54884 if (!connectionInfo) return;
54885 testNodes = graph.childNodes(way).slice(); // shallow copy
54887 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
54889 if (geoHasSelfIntersections(testNodes, nodeID)) return;
54890 results.push(connectionInfo);
54895 function findNearbyEndNodes(node, way) {
54896 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
54897 return graph.entity(d);
54898 }).filter(function (d) {
54899 // Node cannot be near to itself, but other endnode of same way could be
54900 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
54904 function findSmallJoinAngle(midNode, tipNode, endNodes) {
54905 // Both nodes could be close, so want to join whichever is closest to collinear
54907 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
54909 endNodes.forEach(function (endNode) {
54910 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
54911 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
54912 var diff = Math.max(a1, a2) - Math.min(a1, a2);
54914 if (diff < minAngle) {
54919 /* Threshold set by considering right angle triangle
54920 based on node joining threshold and extension distance */
54922 if (minAngle <= SIG_ANGLE_TH) return joinTo;
54926 function hasTag(tags, key) {
54927 return tags[key] !== undefined && tags[key] !== 'no';
54930 function canConnectWays(way, way2) {
54931 // allow self-connections
54932 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
54934 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
54935 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
54937 var layer1 = way.tags.layer || '0',
54938 layer2 = way2.tags.layer || '0';
54939 if (layer1 !== layer2) return false;
54940 var level1 = way.tags.level || '0',
54941 level2 = way2.tags.level || '0';
54942 if (level1 !== level2) return false;
54946 function canConnectByExtend(way, endNodeIdx) {
54947 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
54949 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
54951 var tipNode = graph.entity(tipNid);
54952 var midNode = graph.entity(midNid);
54953 var lon = tipNode.loc[0];
54954 var lat = tipNode.loc[1];
54955 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
54956 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
54957 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
54959 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
54960 var t = EXTEND_TH_METERS / edgeLen + 1.0;
54961 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
54963 var segmentInfos = tree.waySegments(queryExtent, graph);
54965 for (var i = 0; i < segmentInfos.length; i++) {
54966 var segmentInfo = segmentInfos[i];
54967 var way2 = graph.entity(segmentInfo.wayId);
54968 if (!isHighway(way2)) continue;
54969 if (!canConnectWays(way, way2)) continue;
54970 var nAid = segmentInfo.nodes[0],
54971 nBid = segmentInfo.nodes[1];
54972 if (nAid === tipNid || nBid === tipNid) continue;
54973 var nA = graph.entity(nAid),
54974 nB = graph.entity(nBid);
54975 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
54982 edge: [nA.id, nB.id],
54983 cross_loc: crossLoc
54992 validation.type = type;
54996 function validationCloseNodes(context) {
54997 var type = 'close_nodes';
54998 var pointThresholdMeters = 0.2;
55000 var validation = function validation(entity, graph) {
55001 if (entity.type === 'node') {
55002 return getIssuesForNode(entity);
55003 } else if (entity.type === 'way') {
55004 return getIssuesForWay(entity);
55009 function getIssuesForNode(node) {
55010 var parentWays = graph.parentWays(node);
55012 if (parentWays.length) {
55013 return getIssuesForVertex(node, parentWays);
55015 return getIssuesForDetachedPoint(node);
55019 function wayTypeFor(way) {
55020 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
55021 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
55022 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
55023 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
55024 var parentRelations = graph.parentRelations(way);
55026 for (var i in parentRelations) {
55027 var relation = parentRelations[i];
55028 if (relation.tags.type === 'boundary') return 'boundary';
55030 if (relation.isMultipolygon()) {
55031 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
55032 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
55039 function shouldCheckWay(way) {
55040 // don't flag issues where merging would create degenerate ways
55041 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
55042 var bbox = way.extent(graph).bbox();
55043 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
55045 if (hypotenuseMeters < 1.5) return false;
55049 function getIssuesForWay(way) {
55050 if (!shouldCheckWay(way)) return [];
55052 nodes = graph.childNodes(way);
55054 for (var i = 0; i < nodes.length - 1; i++) {
55055 var node1 = nodes[i];
55056 var node2 = nodes[i + 1];
55057 var issue = getWayIssueIfAny(node1, node2, way);
55058 if (issue) issues.push(issue);
55064 function getIssuesForVertex(node, parentWays) {
55067 function checkForCloseness(node1, node2, way) {
55068 var issue = getWayIssueIfAny(node1, node2, way);
55069 if (issue) issues.push(issue);
55072 for (var i = 0; i < parentWays.length; i++) {
55073 var parentWay = parentWays[i];
55074 if (!shouldCheckWay(parentWay)) continue;
55075 var lastIndex = parentWay.nodes.length - 1;
55077 for (var j = 0; j < parentWay.nodes.length; j++) {
55079 if (parentWay.nodes[j - 1] === node.id) {
55080 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
55084 if (j !== lastIndex) {
55085 if (parentWay.nodes[j + 1] === node.id) {
55086 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
55095 function thresholdMetersForWay(way) {
55096 if (!shouldCheckWay(way)) return 0;
55097 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
55099 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
55101 if (wayType === 'indoor') return 0.01;
55102 if (wayType === 'building') return 0.05;
55103 if (wayType === 'path') return 0.1;
55107 function getIssuesForDetachedPoint(node) {
55109 var lon = node.loc[0];
55110 var lat = node.loc[1];
55111 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
55112 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
55113 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
55114 var intersected = context.history().tree().intersects(queryExtent, graph);
55116 for (var j = 0; j < intersected.length; j++) {
55117 var nearby = intersected[j];
55118 if (nearby.id === node.id) continue;
55119 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
55121 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
55122 // allow very close points if tags indicate the z-axis might vary
55126 'addr:housenumber': true,
55129 var zAxisDifferentiates = false;
55131 for (var key in zAxisKeys) {
55132 var nodeValue = node.tags[key] || '0';
55133 var nearbyValue = nearby.tags[key] || '0';
55135 if (nodeValue !== nearbyValue) {
55136 zAxisDifferentiates = true;
55141 if (zAxisDifferentiates) continue;
55142 issues.push(new validationIssue({
55144 subtype: 'detached',
55145 severity: 'warning',
55146 message: function message(context) {
55147 var entity = context.hasEntity(this.entityIds[0]),
55148 entity2 = context.hasEntity(this.entityIds[1]);
55149 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
55150 feature: utilDisplayLabel(entity, context.graph()),
55151 feature2: utilDisplayLabel(entity2, context.graph())
55154 reference: showReference,
55155 entityIds: [node.id, nearby.id],
55156 dynamicFixes: function dynamicFixes() {
55157 return [new validationIssueFix({
55158 icon: 'iD-operation-disconnect',
55159 title: _t.html('issues.fix.move_points_apart.title')
55160 }), new validationIssueFix({
55161 icon: 'iD-icon-layers',
55162 title: _t.html('issues.fix.use_different_layers_or_levels.title')
55171 function showReference(selection) {
55172 var referenceText = _t('issues.close_nodes.detached.reference');
55173 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55177 function getWayIssueIfAny(node1, node2, way) {
55178 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
55182 if (node1.loc !== node2.loc) {
55183 var parentWays1 = graph.parentWays(node1);
55184 var parentWays2 = new Set(graph.parentWays(node2));
55185 var sharedWays = parentWays1.filter(function (parentWay) {
55186 return parentWays2.has(parentWay);
55188 var thresholds = sharedWays.map(function (parentWay) {
55189 return thresholdMetersForWay(parentWay);
55191 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
55192 var distance = geoSphericalDistance(node1.loc, node2.loc);
55193 if (distance > threshold) return null;
55196 return new validationIssue({
55198 subtype: 'vertices',
55199 severity: 'warning',
55200 message: function message(context) {
55201 var entity = context.hasEntity(this.entityIds[0]);
55202 return entity ? _t.html('issues.close_nodes.message', {
55203 way: utilDisplayLabel(entity, context.graph())
55206 reference: showReference,
55207 entityIds: [way.id, node1.id, node2.id],
55209 dynamicFixes: function dynamicFixes() {
55210 return [new validationIssueFix({
55211 icon: 'iD-icon-plus',
55212 title: _t.html('issues.fix.merge_points.title'),
55213 onClick: function onClick(context) {
55214 var entityIds = this.issue.entityIds;
55215 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
55216 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
55218 }), new validationIssueFix({
55219 icon: 'iD-operation-disconnect',
55220 title: _t.html('issues.fix.move_points_apart.title')
55225 function showReference(selection) {
55226 var referenceText = _t('issues.close_nodes.reference');
55227 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55232 validation.type = type;
55236 function validationCrossingWays(context) {
55237 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
55239 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
55240 if (getFeatureType(way, graph) === null) {
55241 // if the way doesn't match a feature type, check its parent relations
55242 var parentRels = graph.parentRelations(way);
55244 for (var i = 0; i < parentRels.length; i++) {
55245 var rel = parentRels[i];
55247 if (getFeatureType(rel, graph) !== null) {
55256 function hasTag(tags, key) {
55257 return tags[key] !== undefined && tags[key] !== 'no';
55260 function taggedAsIndoor(tags) {
55261 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
55264 function allowsBridge(featureType) {
55265 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55268 function allowsTunnel(featureType) {
55269 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55273 var ignoredBuildings = {
55280 function getFeatureType(entity, graph) {
55281 var geometry = entity.geometry(graph);
55282 if (geometry !== 'line' && geometry !== 'area') return null;
55283 var tags = entity.tags;
55284 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
55285 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
55287 if (geometry !== 'line') return null;
55288 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
55289 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
55293 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
55294 // assume 0 by default
55295 var level1 = tags1.level || '0';
55296 var level2 = tags2.level || '0';
55298 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
55299 // assume features don't interact if they're indoor on different levels
55301 } // assume 0 by default; don't use way.layer() since we account for structures here
55304 var layer1 = tags1.layer || '0';
55305 var layer2 = tags2.layer || '0';
55307 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
55308 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
55309 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
55311 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
55312 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
55314 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
55315 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
55316 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
55318 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
55319 } 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
55322 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
55323 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
55325 if (featureType1 === 'building' || featureType2 === 'building') {
55326 // for building crossings, different layers are enough
55327 if (layer1 !== layer2) return true;
55331 } // highway values for which we shouldn't recommend connecting to waterways
55334 var highwaysDisallowingFords = {
55336 motorway_link: true,
55340 primary_link: true,
55342 secondary_link: true
55344 var nonCrossingHighways = {
55348 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
55349 var featureType1 = getFeatureType(entity1, graph);
55350 var featureType2 = getFeatureType(entity2, graph);
55351 var geometry1 = entity1.geometry(graph);
55352 var geometry2 = entity2.geometry(graph);
55353 var bothLines = geometry1 === 'line' && geometry2 === 'line';
55355 if (featureType1 === featureType2) {
55356 if (featureType1 === 'highway') {
55357 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
55358 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
55360 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
55361 // one feature is a path but not both
55362 var roadFeature = entity1IsPath ? entity2 : entity1;
55364 if (nonCrossingHighways[roadFeature.tags.highway]) {
55365 // don't mark path connections with certain roads as crossings
55369 var pathFeature = entity1IsPath ? entity1 : entity2;
55371 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
55372 // if the path is a crossing, match the crossing type
55373 return bothLines ? {
55374 highway: 'crossing',
55375 crossing: pathFeature.tags.crossing
55377 } // don't add a `crossing` subtag to ambiguous crossings
55380 return bothLines ? {
55381 highway: 'crossing'
55388 if (featureType1 === 'waterway') return {};
55389 if (featureType1 === 'railway') return {};
55391 var featureTypes = [featureType1, featureType2];
55393 if (featureTypes.indexOf('highway') !== -1) {
55394 if (featureTypes.indexOf('railway') !== -1) {
55395 if (!bothLines) return {};
55396 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
55398 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
55399 // path-tram connections use this tag
55400 if (isTram) return {
55401 railway: 'tram_crossing'
55402 }; // other path-rail connections use this tag
55405 railway: 'crossing'
55408 // path-tram connections use this tag
55409 if (isTram) return {
55410 railway: 'tram_level_crossing'
55411 }; // other road-rail connections use this tag
55414 railway: 'level_crossing'
55419 if (featureTypes.indexOf('waterway') !== -1) {
55420 // do not allow fords on structures
55421 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
55422 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
55424 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
55425 // do not allow fords on major highways
55429 return bothLines ? {
55439 function findCrossingsByWay(way1, graph, tree) {
55440 var edgeCrossInfos = [];
55441 if (way1.type !== 'way') return edgeCrossInfos;
55442 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
55443 var way1FeatureType = getFeatureType(taggedFeature1, graph);
55444 if (way1FeatureType === null) return edgeCrossInfos;
55445 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
55449 var n1, n2, nA, nB, nAId, nBId;
55450 var segment1, segment2;
55452 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
55453 var way1Nodes = graph.childNodes(way1);
55454 var comparedWays = {};
55456 for (i = 0; i < way1Nodes.length - 1; i++) {
55458 n2 = way1Nodes[i + 1];
55459 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
55460 // of overlapping ways
55462 segmentInfos = tree.waySegments(extent, graph);
55464 for (j = 0; j < segmentInfos.length; j++) {
55465 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
55467 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
55469 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
55471 comparedWays[segment2Info.wayId] = true;
55472 way2 = graph.hasEntity(segment2Info.wayId);
55473 if (!way2) continue;
55474 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
55476 way2FeatureType = getFeatureType(taggedFeature2, graph);
55478 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
55480 } // create only one issue for building crossings
55483 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
55484 nAId = segment2Info.nodes[0];
55485 nBId = segment2Info.nodes[1];
55487 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
55488 // n1 or n2 is a connection node; skip
55492 nA = graph.hasEntity(nAId);
55494 nB = graph.hasEntity(nBId);
55496 segment1 = [n1.loc, n2.loc];
55497 segment2 = [nA.loc, nB.loc];
55498 var point = geoLineIntersection(segment1, segment2);
55501 edgeCrossInfos.push({
55504 featureType: way1FeatureType,
55505 edge: [n1.id, n2.id]
55508 featureType: way2FeatureType,
55509 edge: [nA.id, nB.id]
55515 checkedSingleCrossingWays[way2.id] = true;
55522 return edgeCrossInfos;
55525 function waysToCheck(entity, graph) {
55526 var featureType = getFeatureType(entity, graph);
55527 if (!featureType) return [];
55529 if (entity.type === 'way') {
55531 } else if (entity.type === 'relation') {
55532 return entity.members.reduce(function (array, member) {
55533 if (member.type === 'way' && ( // only look at geometry ways
55534 !member.role || member.role === 'outer' || member.role === 'inner')) {
55535 var entity = graph.hasEntity(member.id); // don't add duplicates
55537 if (entity && array.indexOf(entity) === -1) {
55538 array.push(entity);
55549 var validation = function checkCrossingWays(entity, graph) {
55550 var tree = context.history().tree();
55551 var ways = waysToCheck(entity, graph);
55552 var issues = []; // declare these here to reduce garbage collection
55554 var wayIndex, crossingIndex, crossings;
55556 for (wayIndex in ways) {
55557 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
55559 for (crossingIndex in crossings) {
55560 issues.push(createIssue(crossings[crossingIndex], graph));
55567 function createIssue(crossing, graph) {
55568 // use the entities with the tags that define the feature type
55569 crossing.wayInfos.sort(function (way1Info, way2Info) {
55570 var type1 = way1Info.featureType;
55571 var type2 = way2Info.featureType;
55573 if (type1 === type2) {
55574 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
55575 } else if (type1 === 'waterway') {
55577 } else if (type2 === 'waterway') {
55581 return type1 < type2;
55583 var entities = crossing.wayInfos.map(function (wayInfo) {
55584 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
55586 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
55587 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
55588 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
55589 var featureType1 = crossing.wayInfos[0].featureType;
55590 var featureType2 = crossing.wayInfos[1].featureType;
55591 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
55592 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
55593 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
55594 var subtype = [featureType1, featureType2].sort().join('-');
55595 var crossingTypeID = subtype;
55597 if (isCrossingIndoors) {
55598 crossingTypeID = 'indoor-indoor';
55599 } else if (isCrossingTunnels) {
55600 crossingTypeID = 'tunnel-tunnel';
55601 } else if (isCrossingBridges) {
55602 crossingTypeID = 'bridge-bridge';
55605 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
55606 crossingTypeID += '_connectable';
55609 return new validationIssue({
55612 severity: 'warning',
55613 message: function message(context) {
55614 var graph = context.graph();
55615 var entity1 = graph.hasEntity(this.entityIds[0]),
55616 entity2 = graph.hasEntity(this.entityIds[1]);
55617 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
55618 feature: utilDisplayLabel(entity1, graph),
55619 feature2: utilDisplayLabel(entity2, graph)
55622 reference: showReference,
55623 entityIds: entities.map(function (entity) {
55628 featureTypes: featureTypes,
55629 connectionTags: connectionTags
55631 // differentiate based on the loc since two ways can cross multiple times
55632 hash: crossing.crossPoint.toString() + // if the edges change then so does the fix
55633 edges.slice().sort(function (edge1, edge2) {
55634 // order to assure hash is deterministic
55635 return edge1[0] < edge2[0] ? -1 : 1;
55636 }).toString() + // ensure the correct connection tags are added in the fix
55637 JSON.stringify(connectionTags),
55638 loc: crossing.crossPoint,
55639 dynamicFixes: function dynamicFixes(context) {
55640 var mode = context.mode();
55641 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
55642 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
55643 var selectedFeatureType = this.data.featureTypes[selectedIndex];
55644 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
55647 if (connectionTags) {
55648 fixes.push(makeConnectWaysFix(this.data.connectionTags));
55651 if (isCrossingIndoors) {
55652 fixes.push(new validationIssueFix({
55653 icon: 'iD-icon-layers',
55654 title: _t.html('issues.fix.use_different_levels.title')
55656 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
55657 fixes.push(makeChangeLayerFix('higher'));
55658 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
55659 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
55660 // don't recommend adding bridges to waterways since they're uncommon
55661 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
55662 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
55663 } // don't recommend adding tunnels under waterways since they're uncommon
55666 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
55668 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
55669 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
55671 } // repositioning the features is always an option
55674 fixes.push(new validationIssueFix({
55675 icon: 'iD-operation-move',
55676 title: _t.html('issues.fix.reposition_features.title')
55682 function showReference(selection) {
55683 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
55687 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
55688 return new validationIssueFix({
55690 title: _t.html('issues.fix.' + fixTitleID + '.title'),
55691 onClick: function onClick(context) {
55692 var mode = context.mode();
55693 if (!mode || mode.id !== 'select') return;
55694 var selectedIDs = mode.selectedIDs();
55695 if (selectedIDs.length !== 1) return;
55696 var selectedWayID = selectedIDs[0];
55697 if (!context.hasEntity(selectedWayID)) return;
55698 var resultWayIDs = [selectedWayID];
55699 var edge, crossedEdge, crossedWayID;
55701 if (this.issue.entityIds[0] === selectedWayID) {
55702 edge = this.issue.data.edges[0];
55703 crossedEdge = this.issue.data.edges[1];
55704 crossedWayID = this.issue.entityIds[1];
55706 edge = this.issue.data.edges[1];
55707 crossedEdge = this.issue.data.edges[0];
55708 crossedWayID = this.issue.entityIds[0];
55711 var crossingLoc = this.issue.loc;
55712 var projection = context.projection;
55714 var action = function actionAddStructure(graph) {
55715 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55716 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
55718 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
55720 if (!structLengthMeters) {
55721 // if no explicit width is set, approximate the width based on the tags
55722 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
55725 if (structLengthMeters) {
55726 if (getFeatureType(crossedWay, graph) === 'railway') {
55727 // bridges over railways are generally much longer than the rail bed itself, compensate
55728 structLengthMeters *= 2;
55731 // should ideally never land here since all rail/water/road tags should have an implied width
55732 structLengthMeters = 8;
55735 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
55736 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
55737 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
55738 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
55740 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
55742 structLengthMeters += 4; // clamp the length to a reasonable range
55744 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
55746 function geomToProj(geoPoint) {
55747 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
55750 function projToGeom(projPoint) {
55751 var lat = geoMetersToLat(projPoint[1]);
55752 return [geoMetersToLon(projPoint[0], lat), lat];
55755 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
55756 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
55757 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
55758 var projectedCrossingLoc = geomToProj(crossingLoc);
55759 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
55761 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
55762 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
55763 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
55766 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
55767 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
55770 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
55771 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
55772 }; // avoid creating very short edges from splitting too close to another node
55775 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
55777 function determineEndpoint(edge, endNode, locGetter) {
55779 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
55780 // the maximum length of this side of the structure
55782 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
55784 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
55785 // the edge is long enough to insert a new node
55786 // the loc that would result in the full expected length
55787 var idealNodeLoc = locGetter(idealLengthMeters);
55788 newNode = osmNode();
55789 graph = actionAddMidpoint({
55792 }, newNode)(graph);
55795 endNode.parentIntersectionWays(graph).forEach(function (way) {
55796 way.nodes.forEach(function (nodeID) {
55797 if (nodeID === endNode.id) {
55798 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
55807 if (edgeCount >= 3) {
55808 // the end node is a junction, try to leave a segment
55809 // between it and the structure - #7202
55810 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
55812 if (insetLength > minEdgeLengthMeters) {
55813 var insetNodeLoc = locGetter(insetLength);
55814 newNode = osmNode();
55815 graph = actionAddMidpoint({
55818 }, newNode)(graph);
55821 } // if the edge is too short to subdivide as desired, then
55822 // just bound the structure at the existing end node
55825 if (!newNode) newNode = endNode;
55826 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
55829 graph = splitAction(graph);
55831 if (splitAction.getCreatedWayIDs().length) {
55832 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
55838 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
55839 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
55840 var structureWay = resultWayIDs.map(function (id) {
55841 return graph.entity(id);
55842 }).find(function (way) {
55843 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
55845 var tags = Object.assign({}, structureWay.tags); // copy tags
55847 if (bridgeOrTunnel === 'bridge') {
55848 tags.bridge = 'yes';
55851 var tunnelValue = 'yes';
55853 if (getFeatureType(structureWay, graph) === 'waterway') {
55854 // use `tunnel=culvert` for waterways by default
55855 tunnelValue = 'culvert';
55858 tags.tunnel = tunnelValue;
55860 } // apply the structure tags to the way
55863 graph = actionChangeTags(structureWay.id, tags)(graph);
55867 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
55868 context.enter(modeSelect(context, resultWayIDs));
55873 function makeConnectWaysFix(connectionTags) {
55874 var fixTitleID = 'connect_features';
55876 if (connectionTags.ford) {
55877 fixTitleID = 'connect_using_ford';
55880 return new validationIssueFix({
55881 icon: 'iD-icon-crossing',
55882 title: _t.html('issues.fix.' + fixTitleID + '.title'),
55883 onClick: function onClick(context) {
55884 var loc = this.issue.loc;
55885 var connectionTags = this.issue.data.connectionTags;
55886 var edges = this.issue.data.edges;
55887 context.perform(function actionConnectCrossingWays(graph) {
55888 // create the new node for the points
55889 var node = osmNode({
55891 tags: connectionTags
55893 graph = graph.replace(node);
55894 var nodesToMerge = [node.id];
55895 var mergeThresholdInMeters = 0.75;
55896 edges.forEach(function (edge) {
55897 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55898 var nearby = geoSphericalClosestNode(edgeNodes, loc); // if there is already a suitable node nearby, use that
55900 if (!nearby.node.hasInterestingTags() && nearby.distance < mergeThresholdInMeters) {
55901 nodesToMerge.push(nearby.node.id); // else add the new node to the way
55903 graph = actionAddMidpoint({
55910 if (nodesToMerge.length > 1) {
55911 // if we're using nearby nodes, merge them with the new node
55912 graph = actionMergeNodes(nodesToMerge, loc)(graph);
55916 }, _t('issues.fix.connect_crossing_features.annotation'));
55921 function makeChangeLayerFix(higherOrLower) {
55922 return new validationIssueFix({
55923 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
55924 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
55925 onClick: function onClick(context) {
55926 var mode = context.mode();
55927 if (!mode || mode.id !== 'select') return;
55928 var selectedIDs = mode.selectedIDs();
55929 if (selectedIDs.length !== 1) return;
55930 var selectedID = selectedIDs[0];
55931 if (!this.issue.entityIds.some(function (entityId) {
55932 return entityId === selectedID;
55934 var entity = context.hasEntity(selectedID);
55935 if (!entity) return;
55936 var tags = Object.assign({}, entity.tags); // shallow copy
55938 var layer = tags.layer && Number(tags.layer);
55940 if (layer && !isNaN(layer)) {
55941 if (higherOrLower === 'higher') {
55947 if (higherOrLower === 'higher') {
55954 tags.layer = layer.toString();
55955 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
55960 validation.type = type;
55964 function behaviorDrawWay(context, wayID, mode, startGraph) {
55965 var dispatch = dispatch$8('rejectedSelfIntersection');
55966 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
55978 var _pointerHasMoved = false; // The osmNode to be placed.
55979 // This is temporary and just follows the mouse cursor until an "add" event occurs.
55983 var _didResolveTempEdit = false;
55985 function createDrawNode(loc) {
55986 // don't make the draw node until we actually need it
55987 _drawNode = osmNode({
55990 context.pauseChangeDispatch();
55991 context.replace(function actionAddDrawNode(graph) {
55992 // add the draw node to the graph and insert it into the way
55993 var way = graph.entity(wayID);
55994 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
55996 context.resumeChangeDispatch();
55997 setActiveElements();
56000 function removeDrawNode() {
56001 context.pauseChangeDispatch();
56002 context.replace(function actionDeleteDrawNode(graph) {
56003 var way = graph.entity(wayID);
56004 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56006 _drawNode = undefined;
56007 context.resumeChangeDispatch();
56010 function keydown(d3_event) {
56011 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56012 if (context.surface().classed('nope')) {
56013 context.surface().classed('nope-suppressed', true);
56016 context.surface().classed('nope', false).classed('nope-disabled', true);
56020 function keyup(d3_event) {
56021 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56022 if (context.surface().classed('nope-suppressed')) {
56023 context.surface().classed('nope', true);
56026 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56030 function allowsVertex(d) {
56031 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56033 // - `mode/drag_node.js` `doMove()`
56034 // - `behavior/draw.js` `click()`
56035 // - `behavior/draw_way.js` `move()`
56038 function move(d3_event, datum) {
56039 var loc = context.map().mouseCoordinates();
56040 if (!_drawNode) createDrawNode(loc);
56041 context.surface().classed('nope-disabled', d3_event.altKey);
56042 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56043 var targetNodes = datum && datum.properties && datum.properties.nodes;
56046 // snap to node/vertex - a point target with `.loc`
56048 } else if (targetNodes) {
56049 // snap to way - a line target with `.nodes`
56050 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56057 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56058 _drawNode = context.entity(_drawNode.id);
56060 /* includeDrawNode */
56062 } // Check whether this edit causes the geometry to break.
56063 // If so, class the surface with a nope cursor.
56064 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56067 function checkGeometry(includeDrawNode) {
56068 var nopeDisabled = context.surface().classed('nope-disabled');
56069 var isInvalid = isInvalidGeometry(includeDrawNode);
56071 if (nopeDisabled) {
56072 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56074 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56078 function isInvalidGeometry(includeDrawNode) {
56079 var testNode = _drawNode; // we only need to test the single way we're drawing
56081 var parentWay = context.graph().entity(wayID);
56082 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56084 if (includeDrawNode) {
56085 if (parentWay.isClosed()) {
56086 // don't test the last segment for closed ways - #4655
56087 // (still test the first segment)
56091 // discount the draw node
56092 if (parentWay.isClosed()) {
56093 if (nodes.length < 3) return false;
56094 if (_drawNode) nodes.splice(-2, 1);
56095 testNode = nodes[nodes.length - 2];
56097 // there's nothing we need to test if we ignore the draw node on open ways
56102 return testNode && geoHasSelfIntersections(nodes, testNode.id);
56105 function undone() {
56106 // undoing removed the temp edit
56107 _didResolveTempEdit = true;
56108 context.pauseChangeDispatch();
56111 if (context.graph() === startGraph) {
56112 // We've undone back to the initial state before we started drawing.
56113 // Just exit the draw mode without undoing whatever we did before
56114 // we entered the draw mode.
56115 nextMode = modeSelect(context, [wayID]);
56117 // The `undo` only removed the temporary edit, so here we have to
56118 // manually undo to actually remove the last node we added. We can't
56119 // use the `undo` function since the initial "add" graph doesn't have
56120 // an annotation and so cannot be undone to.
56121 context.pop(1); // continue drawing
56124 } // clear the redo stack by adding and removing a blank edit
56127 context.perform(actionNoop());
56129 context.resumeChangeDispatch();
56130 context.enter(nextMode);
56133 function setActiveElements() {
56134 if (!_drawNode) return;
56135 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56138 function resetToStartGraph() {
56139 while (context.graph() !== startGraph) {
56144 var drawWay = function drawWay(surface) {
56145 _drawNode = undefined;
56146 _didResolveTempEdit = false;
56147 _origWay = context.entity(wayID);
56149 if (typeof _nodeIndex === 'number') {
56150 _headNodeID = _origWay.nodes[_nodeIndex];
56151 } else if (_origWay.isClosed()) {
56152 _headNodeID = _origWay.nodes[_origWay.nodes.length - 2];
56154 _headNodeID = _origWay.nodes[_origWay.nodes.length - 1];
56157 _wayGeometry = _origWay.geometry(context.graph());
56158 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56159 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56160 // We must make sure to replace or remove it later.
56162 context.pauseChangeDispatch();
56163 context.perform(actionNoop(), _annotation);
56164 context.resumeChangeDispatch();
56165 behavior.hover().initialNodeID(_headNodeID);
56166 behavior.on('move', function () {
56167 _pointerHasMoved = true;
56168 move.apply(this, arguments);
56169 }).on('down', function () {
56170 move.apply(this, arguments);
56171 }).on('downcancel', function () {
56172 if (_drawNode) removeDrawNode();
56173 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56174 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56175 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56176 setActiveElements();
56177 surface.call(behavior);
56178 context.history().on('undone.draw', undone);
56181 drawWay.off = function (surface) {
56182 if (!_didResolveTempEdit) {
56183 // Drawing was interrupted unexpectedly.
56184 // This can happen if the user changes modes,
56185 // clicks geolocate button, a hashchange event occurs, etc.
56186 context.pauseChangeDispatch();
56187 resetToStartGraph();
56188 context.resumeChangeDispatch();
56191 _drawNode = undefined;
56192 _nodeIndex = undefined;
56193 context.map().on('drawn.draw', null);
56194 surface.call(behavior.off).selectAll('.active').classed('active', false);
56195 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56196 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56197 context.history().on('undone.draw', null);
56200 function attemptAdd(d, loc, doAdd) {
56202 // move the node to the final loc in case move wasn't called
56203 // consistently (e.g. on touch devices)
56204 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56205 _drawNode = context.entity(_drawNode.id);
56207 createDrawNode(loc);
56211 /* includeDrawNode */
56214 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56215 if (!_pointerHasMoved) {
56216 // prevent the temporary draw node from appearing on touch devices
56220 dispatch.call('rejectedSelfIntersection', this);
56221 return; // can't click here
56224 context.pauseChangeDispatch();
56225 doAdd(); // we just replaced the temporary edit with the real one
56227 _didResolveTempEdit = true;
56228 context.resumeChangeDispatch();
56229 context.enter(mode);
56230 } // Accept the current position of the drawing node
56233 drawWay.add = function (loc, d) {
56234 attemptAdd(d, loc, function () {// don't need to do anything extra
56236 }; // Connect the way to an existing way
56239 drawWay.addWay = function (loc, edge, d) {
56240 attemptAdd(d, loc, function () {
56241 context.replace(actionAddMidpoint({
56244 }, _drawNode), _annotation);
56246 }; // Connect the way to an existing node
56249 drawWay.addNode = function (node, d) {
56250 // finish drawing if the mapper targets the prior node
56251 if (node.id === _headNodeID || // or the first node when drawing an area
56252 _origWay.isClosed() && node.id === _origWay.first()) {
56257 attemptAdd(d, node.loc, function () {
56258 context.replace(function actionReplaceDrawNode(graph) {
56259 // remove the temporary draw node and insert the existing node
56260 // at the same index
56261 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56262 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56265 }; // Finish the draw operation, removing the temporary edit.
56266 // If the way has enough nodes to be valid, it's selected.
56267 // Otherwise, delete everything and return to browse mode.
56270 drawWay.finish = function () {
56271 checkGeometry(false
56272 /* includeDrawNode */
56275 if (context.surface().classed('nope')) {
56276 dispatch.call('rejectedSelfIntersection', this);
56277 return; // can't click here
56280 context.pauseChangeDispatch(); // remove the temporary edit
56283 _didResolveTempEdit = true;
56284 context.resumeChangeDispatch();
56285 var way = context.hasEntity(wayID);
56287 if (!way || way.isDegenerate()) {
56292 window.setTimeout(function () {
56293 context.map().dblclickZoomEnable(true);
56295 var isNewFeature = !mode.isContinuing;
56296 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56297 }; // Cancel the draw operation, delete everything, and return to browse mode.
56300 drawWay.cancel = function () {
56301 context.pauseChangeDispatch();
56302 resetToStartGraph();
56303 context.resumeChangeDispatch();
56304 window.setTimeout(function () {
56305 context.map().dblclickZoomEnable(true);
56307 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56308 context.enter(modeBrowse(context));
56311 drawWay.nodeIndex = function (val) {
56312 if (!arguments.length) return _nodeIndex;
56317 drawWay.activeID = function () {
56318 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56323 return utilRebind(drawWay, dispatch, 'on');
56326 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56331 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56332 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56334 mode.wayID = wayID;
56335 mode.isContinuing = continuing;
56337 mode.enter = function () {
56338 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56339 context.install(behavior);
56342 mode.exit = function () {
56343 context.uninstall(behavior);
56346 mode.selectedIDs = function () {
56350 mode.activeID = function () {
56351 return behavior && behavior.activeID() || [];
56357 function validationDisconnectedWay() {
56358 var type = 'disconnected_way';
56360 function isTaggedAsHighway(entity) {
56361 return osmRoutableHighwayTagValues[entity.tags.highway];
56364 var validation = function checkDisconnectedWay(entity, graph) {
56365 var routingIslandWays = routingIslandForEntity(entity);
56366 if (!routingIslandWays) return [];
56367 return [new validationIssue({
56369 subtype: 'highway',
56370 severity: 'warning',
56371 message: function message(context) {
56372 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
56373 var label = entity && utilDisplayLabel(entity, context.graph());
56374 return _t.html('issues.disconnected_way.routable.message', {
56375 count: this.entityIds.length,
56379 reference: showReference,
56380 entityIds: Array.from(routingIslandWays).map(function (way) {
56383 dynamicFixes: makeFixes
56386 function makeFixes(context) {
56388 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
56390 if (singleEntity) {
56391 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
56392 var textDirection = _mainLocalizer.textDirection();
56393 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
56394 if (startFix) fixes.push(startFix);
56395 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
56396 if (endFix) fixes.push(endFix);
56399 if (!fixes.length) {
56400 fixes.push(new validationIssueFix({
56401 title: _t.html('issues.fix.connect_feature.title')
56405 fixes.push(new validationIssueFix({
56406 icon: 'iD-operation-delete',
56407 title: _t.html('issues.fix.delete_feature.title'),
56408 entityIds: [singleEntity.id],
56409 onClick: function onClick(context) {
56410 var id = this.issue.entityIds[0];
56411 var operation = operationDelete(context, [id]);
56413 if (!operation.disabled()) {
56419 fixes.push(new validationIssueFix({
56420 title: _t.html('issues.fix.connect_features.title')
56427 function showReference(selection) {
56428 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
56431 function routingIslandForEntity(entity) {
56432 var routingIsland = new Set(); // the interconnected routable features
56434 var waysToCheck = []; // the queue of remaining routable ways to traverse
56436 function queueParentWays(node) {
56437 graph.parentWays(node).forEach(function (parentWay) {
56438 if (!routingIsland.has(parentWay) && // only check each feature once
56439 isRoutableWay(parentWay, false)) {
56440 // only check routable features
56441 routingIsland.add(parentWay);
56442 waysToCheck.push(parentWay);
56447 if (entity.type === 'way' && isRoutableWay(entity, true)) {
56448 routingIsland.add(entity);
56449 waysToCheck.push(entity);
56450 } else if (entity.type === 'node' && isRoutableNode(entity)) {
56451 routingIsland.add(entity);
56452 queueParentWays(entity);
56454 // this feature isn't routable, cannot be a routing island
56458 while (waysToCheck.length) {
56459 var wayToCheck = waysToCheck.pop();
56460 var childNodes = graph.childNodes(wayToCheck);
56462 for (var i in childNodes) {
56463 var vertex = childNodes[i];
56465 if (isConnectedVertex(vertex)) {
56466 // found a link to the wider network, not a routing island
56470 if (isRoutableNode(vertex)) {
56471 routingIsland.add(vertex);
56474 queueParentWays(vertex);
56476 } // no network link found, this is a routing island, return its members
56479 return routingIsland;
56482 function isConnectedVertex(vertex) {
56483 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
56484 var osm = services.osm;
56485 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
56487 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
56488 if (vertex.tags.amenity === 'parking_entrance') return true;
56492 function isRoutableNode(node) {
56493 // treat elevators as distinct features in the highway network
56494 if (node.tags.highway === 'elevator') return true;
56498 function isRoutableWay(way, ignoreInnerWays) {
56499 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
56500 return graph.parentRelations(way).some(function (parentRelation) {
56501 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
56502 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
56507 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
56508 var vertex = graph.hasEntity(vertexID);
56509 if (!vertex || vertex.tags.noexit === 'yes') return null;
56510 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
56511 return new validationIssueFix({
56512 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56513 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
56514 entityIds: [vertexID],
56515 onClick: function onClick(context) {
56516 var wayId = this.issue.entityIds[0];
56517 var way = context.hasEntity(wayId);
56518 var vertexId = this.entityIds[0];
56519 var vertex = context.hasEntity(vertexId);
56520 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
56522 var map = context.map();
56524 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56525 map.zoomToEase(vertex);
56528 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
56534 validation.type = type;
56538 function validationFormatting() {
56539 var type = 'invalid_format';
56541 var validation = function validation(entity) {
56544 function isValidEmail(email) {
56545 // Emails in OSM are going to be official so they should be pretty simple
56546 // Using negated lists to better support all possible unicode characters (#6494)
56547 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
56549 return !email || valid_email.test(email);
56552 function isSchemePresent(url) {
56553 var valid_scheme = /^https?:\/\//i;
56554 return (!url || valid_scheme.test(url));
56559 function showReferenceEmail(selection) {
56560 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
56563 function showReferenceWebsite(selection) {
56564 selection.selectAll('.issue-reference')
56568 .attr('class', 'issue-reference')
56569 .html(t.html('issues.invalid_format.website.reference'));
56571 if (entity.tags.website) {
56572 // Multiple websites are possible
56573 // If ever we support ES6, arrow functions make this nicer
56574 var websites = entity.tags.website
56576 .map(function(s) { return s.trim(); })
56577 .filter(function(x) { return !isSchemePresent(x); });
56578 if (websites.length) {
56579 issues.push(new validationIssue({
56581 subtype: 'website',
56582 severity: 'warning',
56583 message: function(context) {
56584 var entity = context.hasEntity(this.entityIds[0]);
56585 return entity ? t.html('issues.invalid_format.website.message' + this.data,
56586 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
56588 reference: showReferenceWebsite,
56589 entityIds: [entity.id],
56590 hash: websites.join(),
56591 data: (websites.length > 1) ? '_multi' : ''
56598 if (entity.tags.email) {
56599 // Multiple emails are possible
56600 var emails = entity.tags.email.split(';').map(function (s) {
56602 }).filter(function (x) {
56603 return !isValidEmail(x);
56606 if (emails.length) {
56607 issues.push(new validationIssue({
56610 severity: 'warning',
56611 message: function message(context) {
56612 var entity = context.hasEntity(this.entityIds[0]);
56613 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
56614 feature: utilDisplayLabel(entity, context.graph()),
56615 email: emails.join(', ')
56618 reference: showReferenceEmail,
56619 entityIds: [entity.id],
56620 hash: emails.join(),
56621 data: emails.length > 1 ? '_multi' : ''
56629 validation.type = type;
56633 function validationHelpRequest(context) {
56634 var type = 'help_request';
56636 var validation = function checkFixmeTag(entity) {
56637 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
56639 if (entity.version === undefined) return [];
56641 if (entity.v !== undefined) {
56642 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
56644 if (!baseEntity || !baseEntity.tags.fixme) return [];
56647 return [new validationIssue({
56649 subtype: 'fixme_tag',
56650 severity: 'warning',
56651 message: function message(context) {
56652 var entity = context.hasEntity(this.entityIds[0]);
56653 return entity ? _t.html('issues.fixme_tag.message', {
56654 feature: utilDisplayLabel(entity, context.graph(), true
56659 dynamicFixes: function dynamicFixes() {
56660 return [new validationIssueFix({
56661 title: _t.html('issues.fix.address_the_concern.title')
56664 reference: showReference,
56665 entityIds: [entity.id]
56668 function showReference(selection) {
56669 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
56673 validation.type = type;
56677 function validationImpossibleOneway() {
56678 var type = 'impossible_oneway';
56680 var validation = function checkImpossibleOneway(entity, graph) {
56681 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
56682 if (entity.isClosed()) return [];
56683 if (!typeForWay(entity)) return [];
56684 if (!isOneway(entity)) return [];
56685 var firstIssues = issuesForNode(entity, entity.first());
56686 var lastIssues = issuesForNode(entity, entity.last());
56687 return firstIssues.concat(lastIssues);
56689 function typeForWay(way) {
56690 if (way.geometry(graph) !== 'line') return null;
56691 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
56692 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
56696 function isOneway(way) {
56697 if (way.tags.oneway === 'yes') return true;
56698 if (way.tags.oneway) return false;
56700 for (var key in way.tags) {
56701 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
56709 function nodeOccursMoreThanOnce(way, nodeID) {
56710 var occurrences = 0;
56712 for (var index in way.nodes) {
56713 if (way.nodes[index] === nodeID) {
56715 if (occurrences > 1) return true;
56722 function isConnectedViaOtherTypes(way, node) {
56723 var wayType = typeForWay(way);
56725 if (wayType === 'highway') {
56726 // entrances are considered connected
56727 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
56728 if (node.tags.amenity === 'parking_entrance') return true;
56729 } else if (wayType === 'waterway') {
56730 if (node.id === way.first()) {
56731 // multiple waterways may start at the same spring
56732 if (node.tags.natural === 'spring') return true;
56734 // multiple waterways may end at the same drain
56735 if (node.tags.manhole === 'drain') return true;
56739 return graph.parentWays(node).some(function (parentWay) {
56740 if (parentWay.id === way.id) return false;
56742 if (wayType === 'highway') {
56743 // allow connections to highway areas
56744 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
56746 if (parentWay.tags.route === 'ferry') return true;
56747 return graph.parentRelations(parentWay).some(function (parentRelation) {
56748 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
56750 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
56752 } else if (wayType === 'waterway') {
56753 // multiple waterways may start or end at a water body at the same node
56754 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
56761 function issuesForNode(way, nodeID) {
56762 var isFirst = nodeID === way.first();
56763 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
56765 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
56766 var osm = services.osm;
56767 if (!osm) return [];
56768 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
56770 if (!node || !osm.isDataLoaded(node.loc)) return [];
56771 if (isConnectedViaOtherTypes(way, node)) return [];
56772 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
56773 if (parentWay.id === way.id) return false;
56774 return typeForWay(parentWay) === wayType;
56775 }); // assume it's okay for waterways to start or end disconnected for now
56777 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
56778 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
56779 return isOneway(attachedWay);
56780 }); // ignore if the way is connected to some non-oneway features
56782 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
56784 if (attachedOneways.length) {
56785 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
56786 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
56787 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
56790 if (connectedEndpointsOkay) return [];
56793 var placement = isFirst ? 'start' : 'end',
56794 messageID = wayType + '.',
56795 referenceID = wayType + '.';
56797 if (wayType === 'waterway') {
56798 messageID += 'connected.' + placement;
56799 referenceID += 'connected';
56801 messageID += placement;
56802 referenceID += placement;
56805 return [new validationIssue({
56808 severity: 'warning',
56809 message: function message(context) {
56810 var entity = context.hasEntity(this.entityIds[0]);
56811 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
56812 feature: utilDisplayLabel(entity, context.graph())
56815 reference: getReference(referenceID),
56816 entityIds: [way.id, node.id],
56817 dynamicFixes: function dynamicFixes() {
56820 if (attachedOneways.length) {
56821 fixes.push(new validationIssueFix({
56822 icon: 'iD-operation-reverse',
56823 title: _t.html('issues.fix.reverse_feature.title'),
56824 entityIds: [way.id],
56825 onClick: function onClick(context) {
56826 var id = this.issue.entityIds[0];
56827 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
56834 if (node.tags.noexit !== 'yes') {
56835 var textDirection = _mainLocalizer.textDirection();
56836 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
56837 fixes.push(new validationIssueFix({
56838 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56839 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
56840 onClick: function onClick(context) {
56841 var entityID = this.issue.entityIds[0];
56842 var vertexID = this.issue.entityIds[1];
56843 var way = context.entity(entityID);
56844 var vertex = context.entity(vertexID);
56845 continueDrawing(way, vertex, context);
56855 function getReference(referenceID) {
56856 return function showReference(selection) {
56857 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
56863 function continueDrawing(way, vertex, context) {
56864 // make sure the vertex is actually visible and editable
56865 var map = context.map();
56867 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56868 map.zoomToEase(vertex);
56871 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
56874 validation.type = type;
56878 function validationIncompatibleSource() {
56879 var type = 'incompatible_source';
56880 var invalidSources = [{
56883 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
56886 var validation = function checkIncompatibleSource(entity) {
56887 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
56888 if (!entitySources) return [];
56890 invalidSources.forEach(function (invalidSource) {
56891 var hasInvalidSource = entitySources.some(function (source) {
56892 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
56893 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
56896 if (!hasInvalidSource) return;
56897 issues.push(new validationIssue({
56899 severity: 'warning',
56900 message: function message(context) {
56901 var entity = context.hasEntity(this.entityIds[0]);
56902 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
56903 feature: utilDisplayLabel(entity, context.graph(), true
56908 reference: getReference(invalidSource.id),
56909 entityIds: [entity.id],
56910 dynamicFixes: function dynamicFixes() {
56911 return [new validationIssueFix({
56912 title: _t.html('issues.fix.remove_proprietary_data.title')
56919 function getReference(id) {
56920 return function showReference(selection) {
56921 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
56926 validation.type = type;
56930 function validationMaprules() {
56931 var type = 'maprules';
56933 var validation = function checkMaprules(entity, graph) {
56934 if (!services.maprules) return [];
56935 var rules = services.maprules.validationRules();
56938 for (var i = 0; i < rules.length; i++) {
56939 var rule = rules[i];
56940 rule.findIssues(entity, graph, issues);
56946 validation.type = type;
56950 function validationMismatchedGeometry() {
56951 var type = 'mismatched_geometry';
56953 function tagSuggestingLineIsArea(entity) {
56954 if (entity.type !== 'way' || entity.isClosed()) return null;
56955 var tagSuggestingArea = entity.tagSuggestingArea();
56957 if (!tagSuggestingArea) {
56961 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
56962 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
56964 if (asLine && asArea && asLine === asArea) {
56965 // these tags also allow lines and making this an area wouldn't matter
56969 return tagSuggestingArea;
56972 function makeConnectEndpointsFixOnClick(way, graph) {
56973 // must have at least three nodes to close this automatically
56974 if (way.nodes.length < 3) return null;
56975 var nodes = graph.childNodes(way),
56977 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
56979 if (firstToLastDistanceMeters < 0.75) {
56980 testNodes = nodes.slice(); // shallow copy
56983 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
56985 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
56986 return function (context) {
56987 var way = context.entity(this.issue.entityIds[0]);
56988 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
56991 } // if the points were not merged, attempt to close the way
56994 testNodes = nodes.slice(); // shallow copy
56996 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
56998 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
56999 return function (context) {
57000 var wayId = this.issue.entityIds[0];
57001 var way = context.entity(wayId);
57002 var nodeId = way.nodes[0];
57003 var index = way.nodes.length;
57004 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
57009 function lineTaggedAsAreaIssue(entity) {
57010 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
57011 if (!tagSuggestingArea) return null;
57012 return new validationIssue({
57014 subtype: 'area_as_line',
57015 severity: 'warning',
57016 message: function message(context) {
57017 var entity = context.hasEntity(this.entityIds[0]);
57018 return entity ? _t.html('issues.tag_suggests_area.message', {
57019 feature: utilDisplayLabel(entity, 'area', true
57023 tags: tagSuggestingArea
57027 reference: showReference,
57028 entityIds: [entity.id],
57029 hash: JSON.stringify(tagSuggestingArea),
57030 dynamicFixes: function dynamicFixes(context) {
57032 var entity = context.entity(this.entityIds[0]);
57033 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
57034 fixes.push(new validationIssueFix({
57035 title: _t.html('issues.fix.connect_endpoints.title'),
57036 onClick: connectEndsOnClick
57038 fixes.push(new validationIssueFix({
57039 icon: 'iD-operation-delete',
57040 title: _t.html('issues.fix.remove_tag.title'),
57041 onClick: function onClick(context) {
57042 var entityId = this.issue.entityIds[0];
57043 var entity = context.entity(entityId);
57044 var tags = Object.assign({}, entity.tags); // shallow copy
57046 for (var key in tagSuggestingArea) {
57050 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
57057 function showReference(selection) {
57058 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
57062 function vertexPointIssue(entity, graph) {
57063 // we only care about nodes
57064 if (entity.type !== 'node') return null; // ignore tagless points
57066 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
57068 if (entity.isOnAddressLine(graph)) return null;
57069 var geometry = entity.geometry(graph);
57070 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
57072 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
57073 return new validationIssue({
57075 subtype: 'vertex_as_point',
57076 severity: 'warning',
57077 message: function message(context) {
57078 var entity = context.hasEntity(this.entityIds[0]);
57079 return entity ? _t.html('issues.vertex_as_point.message', {
57080 feature: utilDisplayLabel(entity, 'vertex', true
57085 reference: function showReference(selection) {
57086 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
57088 entityIds: [entity.id]
57090 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
57091 return new validationIssue({
57093 subtype: 'point_as_vertex',
57094 severity: 'warning',
57095 message: function message(context) {
57096 var entity = context.hasEntity(this.entityIds[0]);
57097 return entity ? _t.html('issues.point_as_vertex.message', {
57098 feature: utilDisplayLabel(entity, 'point', true
57103 reference: function showReference(selection) {
57104 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
57106 entityIds: [entity.id],
57107 dynamicFixes: extractPointDynamicFixes
57114 function otherMismatchIssue(entity, graph) {
57115 // ignore boring features
57116 if (!entity.hasInterestingTags()) return null;
57117 if (entity.type !== 'node' && entity.type !== 'way') return null; // address lines are special so just ignore them
57119 if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;
57120 var sourceGeom = entity.geometry(graph);
57121 var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];
57122 if (sourceGeom === 'area') targetGeoms.unshift('line');
57123 var targetGeom = targetGeoms.find(function (nodeGeom) {
57124 var asSource = _mainPresetIndex.matchTags(entity.tags, sourceGeom);
57125 var asTarget = _mainPresetIndex.matchTags(entity.tags, nodeGeom);
57126 if (!asSource || !asTarget || asSource === asTarget || // sometimes there are two presets with the same tags for different geometries
57127 fastDeepEqual(asSource.tags, asTarget.tags)) return false;
57128 if (asTarget.isFallback()) return false;
57129 var primaryKey = Object.keys(asTarget.tags)[0]; // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them
57131 if (primaryKey === 'building') return false;
57132 if (asTarget.tags[primaryKey] === '*') return false;
57133 return asSource.isFallback() || asSource.tags[primaryKey] === '*';
57135 if (!targetGeom) return null;
57136 var subtype = targetGeom + '_as_' + sourceGeom;
57137 if (targetGeom === 'vertex') targetGeom = 'point';
57138 if (sourceGeom === 'vertex') sourceGeom = 'point';
57139 var referenceId = targetGeom + '_as_' + sourceGeom;
57142 if (targetGeom === 'point') {
57143 dynamicFixes = extractPointDynamicFixes;
57144 } else if (sourceGeom === 'area' && targetGeom === 'line') {
57145 dynamicFixes = lineToAreaDynamicFixes;
57148 return new validationIssue({
57151 severity: 'warning',
57152 message: function message(context) {
57153 var entity = context.hasEntity(this.entityIds[0]);
57154 return entity ? _t.html('issues.' + referenceId + '.message', {
57155 feature: utilDisplayLabel(entity, targetGeom, true
57160 reference: function showReference(selection) {
57161 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.mismatched_geometry.reference'));
57163 entityIds: [entity.id],
57164 dynamicFixes: dynamicFixes
57168 function lineToAreaDynamicFixes(context) {
57169 var convertOnClick;
57170 var entityId = this.entityIds[0];
57171 var entity = context.entity(entityId);
57172 var tags = Object.assign({}, entity.tags); // shallow copy
57176 if (!osmTagSuggestingArea(tags)) {
57177 // if removing the area tag would make this a line, offer that as a quick fix
57178 convertOnClick = function convertOnClick(context) {
57179 var entityId = this.issue.entityIds[0];
57180 var entity = context.entity(entityId);
57181 var tags = Object.assign({}, entity.tags); // shallow copy
57187 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.convert_to_line.annotation'));
57191 return [new validationIssueFix({
57192 icon: 'iD-icon-line',
57193 title: _t.html('issues.fix.convert_to_line.title'),
57194 onClick: convertOnClick
57198 function extractPointDynamicFixes(context) {
57199 var entityId = this.entityIds[0];
57200 var extractOnClick = null;
57202 if (!context.hasHiddenConnections(entityId)) {
57203 extractOnClick = function extractOnClick(context) {
57204 var entityId = this.issue.entityIds[0];
57205 var action = actionExtract(entityId, context.projection);
57206 context.perform(action, _t('operations.extract.annotation', {
57208 })); // re-enter mode to trigger updates
57210 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
57214 return [new validationIssueFix({
57215 icon: 'iD-operation-extract',
57216 title: _t.html('issues.fix.extract_point.title'),
57217 onClick: extractOnClick
57221 function unclosedMultipolygonPartIssues(entity, graph) {
57222 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
57223 !entity.isComplete(graph)) return [];
57224 var sequences = osmJoinWays(entity.members, graph);
57227 for (var i in sequences) {
57228 var sequence = sequences[i];
57229 if (!sequence.nodes) continue;
57230 var firstNode = sequence.nodes[0];
57231 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
57233 if (firstNode === lastNode) continue;
57234 var issue = new validationIssue({
57236 subtype: 'unclosed_multipolygon_part',
57237 severity: 'warning',
57238 message: function message(context) {
57239 var entity = context.hasEntity(this.entityIds[0]);
57240 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
57241 feature: utilDisplayLabel(entity, context.graph(), true
57246 reference: showReference,
57247 loc: sequence.nodes[0].loc,
57248 entityIds: [entity.id],
57249 hash: sequence.map(function (way) {
57253 issues.push(issue);
57258 function showReference(selection) {
57259 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
57263 var validation = function checkMismatchedGeometry(entity, graph) {
57264 var vertexPoint = vertexPointIssue(entity, graph);
57265 if (vertexPoint) return [vertexPoint];
57266 var lineAsArea = lineTaggedAsAreaIssue(entity);
57267 if (lineAsArea) return [lineAsArea];
57268 var mismatch = otherMismatchIssue(entity, graph);
57269 if (mismatch) return [mismatch];
57270 return unclosedMultipolygonPartIssues(entity, graph);
57273 validation.type = type;
57277 function validationMissingRole() {
57278 var type = 'missing_role';
57280 var validation = function checkMissingRole(entity, graph) {
57283 if (entity.type === 'way') {
57284 graph.parentRelations(entity).forEach(function (relation) {
57285 if (!relation.isMultipolygon()) return;
57286 var member = relation.memberById(entity.id);
57288 if (member && isMissingRole(member)) {
57289 issues.push(makeIssue(entity, relation, member));
57292 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
57293 entity.indexedMembers().forEach(function (member) {
57294 var way = graph.hasEntity(member.id);
57296 if (way && isMissingRole(member)) {
57297 issues.push(makeIssue(way, entity, member));
57305 function isMissingRole(member) {
57306 return !member.role || !member.role.trim().length;
57309 function makeIssue(way, relation, member) {
57310 return new validationIssue({
57312 severity: 'warning',
57313 message: function message(context) {
57314 var member = context.hasEntity(this.entityIds[1]),
57315 relation = context.hasEntity(this.entityIds[0]);
57316 return member && relation ? _t.html('issues.missing_role.message', {
57317 member: utilDisplayLabel(member, context.graph()),
57318 relation: utilDisplayLabel(relation, context.graph())
57321 reference: showReference,
57322 entityIds: [relation.id, way.id],
57326 hash: member.index.toString(),
57327 dynamicFixes: function dynamicFixes() {
57328 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
57329 icon: 'iD-operation-delete',
57330 title: _t.html('issues.fix.remove_from_relation.title'),
57331 onClick: function onClick(context) {
57332 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
57340 function showReference(selection) {
57341 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
57345 function makeAddRoleFix(role) {
57346 return new validationIssueFix({
57347 title: _t.html('issues.fix.set_as_' + role + '.title'),
57348 onClick: function onClick(context) {
57349 var oldMember = this.issue.data.member;
57351 id: this.issue.entityIds[1],
57352 type: oldMember.type,
57355 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
57362 validation.type = type;
57366 function validationMissingTag(context) {
57367 var type = 'missing_tag';
57369 function hasDescriptiveTags(entity, graph) {
57370 var onlyAttributeKeys = ['description', 'name', 'note', 'start_date'];
57371 var entityDescriptiveKeys = Object.keys(entity.tags).filter(function (k) {
57372 if (k === 'area' || !osmIsInterestingTag(k)) return false;
57373 return !onlyAttributeKeys.some(function (attributeKey) {
57374 return k === attributeKey || k.indexOf(attributeKey + ':') === 0;
57378 if (entity.type === 'relation' && entityDescriptiveKeys.length === 1 && entity.tags.type === 'multipolygon') {
57379 // this relation's only interesting tag just says its a multipolygon,
57380 // which is not descriptive enough
57381 // It's okay for a simple multipolygon to have no descriptive tags
57382 // if its outer way has them (old model, see `outdated_tags.js`)
57383 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57386 return entityDescriptiveKeys.length > 0;
57389 function isUnknownRoad(entity) {
57390 return entity.type === 'way' && entity.tags.highway === 'road';
57393 function isUntypedRelation(entity) {
57394 return entity.type === 'relation' && !entity.tags.type;
57397 var validation = function checkMissingTag(entity, graph) {
57399 var osm = context.connection();
57400 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
57402 if (!isUnloadedNode && // allow untagged nodes that are part of ways
57403 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
57404 !entity.hasParentRelations(graph)) {
57405 if (Object.keys(entity.tags).length === 0) {
57407 } else if (!hasDescriptiveTags(entity, graph)) {
57408 subtype = 'descriptive';
57409 } else if (isUntypedRelation(entity)) {
57410 subtype = 'relation_type';
57412 } // flag an unknown road even if it's a member of a relation
57415 if (!subtype && isUnknownRoad(entity)) {
57416 subtype = 'highway_classification';
57419 if (!subtype) return [];
57420 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
57421 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
57423 var canDelete = entity.version === undefined || entity.v !== undefined;
57424 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
57425 return [new validationIssue({
57428 severity: severity,
57429 message: function message(context) {
57430 var entity = context.hasEntity(this.entityIds[0]);
57431 return entity ? _t.html('issues.' + messageID + '.message', {
57432 feature: utilDisplayLabel(entity, context.graph())
57435 reference: showReference,
57436 entityIds: [entity.id],
57437 dynamicFixes: function dynamicFixes(context) {
57439 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
57440 fixes.push(new validationIssueFix({
57441 icon: 'iD-icon-search',
57442 title: _t.html('issues.fix.' + selectFixType + '.title'),
57443 onClick: function onClick(context) {
57444 context.ui().sidebar.showPresetList();
57448 var id = this.entityIds[0];
57449 var operation = operationDelete(context, [id]);
57450 var disabledReasonID = operation.disabled();
57452 if (!disabledReasonID) {
57453 deleteOnClick = function deleteOnClick(context) {
57454 var id = this.issue.entityIds[0];
57455 var operation = operationDelete(context, [id]);
57457 if (!operation.disabled()) {
57463 fixes.push(new validationIssueFix({
57464 icon: 'iD-operation-delete',
57465 title: _t.html('issues.fix.delete_feature.title'),
57466 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
57467 onClick: deleteOnClick
57473 function showReference(selection) {
57474 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
57478 validation.type = type;
57482 function validationOutdatedTags() {
57483 var type = 'outdated_tags';
57484 var _waitingForDeprecated = true;
57486 var _dataDeprecated; // fetch deprecated tags
57489 _mainFileFetcher.get('deprecated').then(function (d) {
57490 return _dataDeprecated = d;
57491 })["catch"](function () {
57493 })["finally"](function () {
57494 return _waitingForDeprecated = false;
57497 function oldTagIssues(entity, graph) {
57498 var oldTags = Object.assign({}, entity.tags); // shallow copy
57500 var preset = _mainPresetIndex.match(entity, graph);
57501 var subtype = 'deprecated_tags';
57502 if (!preset) return [];
57503 if (!entity.hasInterestingTags()) return []; // Upgrade preset, if a replacement is available..
57505 if (preset.replacement) {
57506 var newPreset = _mainPresetIndex.item(preset.replacement);
57507 graph = actionChangePreset(entity.id, preset, newPreset, true
57508 /* skip field defaults */
57510 entity = graph.entity(entity.id);
57511 preset = newPreset;
57512 } // Upgrade deprecated tags..
57515 if (_dataDeprecated) {
57516 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
57518 if (deprecatedTags.length) {
57519 deprecatedTags.forEach(function (tag) {
57520 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
57522 entity = graph.entity(entity.id);
57524 } // Add missing addTags from the detected preset
57527 var newTags = Object.assign({}, entity.tags); // shallow copy
57529 if (preset.tags !== preset.addTags) {
57530 Object.keys(preset.addTags).forEach(function (k) {
57532 if (preset.addTags[k] === '*') {
57533 newTags[k] = 'yes';
57535 newTags[k] = preset.addTags[k];
57539 } // Attempt to match a canonical record in the name-suggestion-index.
57542 var nsi = services.nsi;
57543 var waitingForNsi = false;
57546 waitingForNsi = nsi.status() === 'loading';
57548 if (!waitingForNsi) {
57549 var loc = entity.extent(graph).center();
57550 var result = nsi.upgradeTags(newTags, loc);
57554 subtype = 'noncanonical_brand';
57560 issues.provisional = _waitingForDeprecated || waitingForNsi; // determine diff
57562 var tagDiff = utilTagDiff(oldTags, newTags);
57563 if (!tagDiff.length) return issues;
57564 var isOnlyAddingTags = tagDiff.every(function (d) {
57565 return d.type === '+';
57569 if (subtype === 'noncanonical_brand') {
57570 prefix = 'noncanonical_brand.';
57571 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
57572 subtype = 'incomplete_tags';
57573 prefix = 'incomplete.';
57574 } // don't allow autofixing brand tags
57577 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
57578 issues.push(new validationIssue({
57581 severity: 'warning',
57582 message: showMessage,
57583 reference: showReference,
57584 entityIds: [entity.id],
57585 hash: utilHashcode(JSON.stringify(tagDiff)),
57586 dynamicFixes: function dynamicFixes() {
57587 return [new validationIssueFix({
57588 autoArgs: autoArgs,
57589 title: _t.html('issues.fix.upgrade_tags.title'),
57590 onClick: function onClick(context) {
57591 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57598 function doUpgrade(graph) {
57599 var currEntity = graph.hasEntity(entity.id);
57600 if (!currEntity) return graph;
57601 var newTags = Object.assign({}, currEntity.tags); // shallow copy
57603 tagDiff.forEach(function (diff) {
57604 if (diff.type === '-') {
57605 delete newTags[diff.key];
57606 } else if (diff.type === '+') {
57607 newTags[diff.key] = diff.newVal;
57610 return actionChangeTags(currEntity.id, newTags)(graph);
57613 function showMessage(context) {
57614 var currEntity = context.hasEntity(entity.id);
57615 if (!currEntity) return '';
57616 var messageID = "issues.outdated_tags.".concat(prefix, "message");
57618 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
57619 messageID += '_incomplete';
57622 return _t.html(messageID, {
57623 feature: utilDisplayLabel(currEntity, context.graph(), true
57629 function showReference(selection) {
57630 var enter = selection.selectAll('.issue-reference').data([0]).enter();
57631 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
57632 enter.append('strong').html(_t.html('issues.suggested'));
57633 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) {
57634 var klass = d.type === '+' ? 'add' : 'remove';
57635 return "tagDiff-cell tagDiff-cell-".concat(klass);
57636 }).html(function (d) {
57642 function oldMultipolygonIssues(entity, graph) {
57643 var multipolygon, outerWay;
57645 if (entity.type === 'relation') {
57646 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57647 multipolygon = entity;
57648 } else if (entity.type === 'way') {
57649 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
57655 if (!multipolygon || !outerWay) return [];
57656 return [new validationIssue({
57658 subtype: 'old_multipolygon',
57659 severity: 'warning',
57660 message: showMessage,
57661 reference: showReference,
57662 entityIds: [outerWay.id, multipolygon.id],
57663 dynamicFixes: function dynamicFixes() {
57664 return [new validationIssueFix({
57665 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
57666 title: _t.html('issues.fix.move_tags.title'),
57667 onClick: function onClick(context) {
57668 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
57674 function doUpgrade(graph) {
57675 var currMultipolygon = graph.hasEntity(multipolygon.id);
57676 var currOuterWay = graph.hasEntity(outerWay.id);
57677 if (!currMultipolygon || !currOuterWay) return graph;
57678 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
57679 graph = graph.replace(currMultipolygon);
57680 return actionChangeTags(currOuterWay.id, {})(graph);
57683 function showMessage(context) {
57684 var currMultipolygon = context.hasEntity(multipolygon.id);
57685 if (!currMultipolygon) return '';
57686 return _t.html('issues.old_multipolygon.message', {
57687 multipolygon: utilDisplayLabel(currMultipolygon, context.graph(), true
57693 function showReference(selection) {
57694 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
57698 var validation = function checkOutdatedTags(entity, graph) {
57699 var issues = oldMultipolygonIssues(entity, graph);
57700 if (!issues.length) issues = oldTagIssues(entity, graph);
57704 validation.type = type;
57708 function validationPrivateData() {
57709 var type = 'private_data'; // assume that some buildings are private
57711 var privateBuildingValues = {
57717 semidetached_house: true,
57718 static_caravan: true
57719 }; // but they might be public if they have one of these other tags
57729 }; // these tags may contain personally identifying info
57731 var personalTags = {
57732 'contact:email': true,
57733 'contact:fax': true,
57734 'contact:phone': true,
57740 var validation = function checkPrivateData(entity) {
57741 var tags = entity.tags;
57742 if (!tags.building || !privateBuildingValues[tags.building]) return [];
57745 for (var k in tags) {
57746 if (publicKeys[k]) return []; // probably a public feature
57748 if (!personalTags[k]) {
57749 keepTags[k] = tags[k];
57753 var tagDiff = utilTagDiff(tags, keepTags);
57754 if (!tagDiff.length) return [];
57755 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
57756 return [new validationIssue({
57758 severity: 'warning',
57759 message: showMessage,
57760 reference: showReference,
57761 entityIds: [entity.id],
57762 dynamicFixes: function dynamicFixes() {
57763 return [new validationIssueFix({
57764 icon: 'iD-operation-delete',
57765 title: _t.html('issues.fix.' + fixID + '.title'),
57766 onClick: function onClick(context) {
57767 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57773 function doUpgrade(graph) {
57774 var currEntity = graph.hasEntity(entity.id);
57775 if (!currEntity) return graph;
57776 var newTags = Object.assign({}, currEntity.tags); // shallow copy
57778 tagDiff.forEach(function (diff) {
57779 if (diff.type === '-') {
57780 delete newTags[diff.key];
57781 } else if (diff.type === '+') {
57782 newTags[diff.key] = diff.newVal;
57785 return actionChangeTags(currEntity.id, newTags)(graph);
57788 function showMessage(context) {
57789 var currEntity = context.hasEntity(this.entityIds[0]);
57790 if (!currEntity) return '';
57791 return _t.html('issues.private_data.contact.message', {
57792 feature: utilDisplayLabel(currEntity, context.graph())
57796 function showReference(selection) {
57797 var enter = selection.selectAll('.issue-reference').data([0]).enter();
57798 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
57799 enter.append('strong').html(_t.html('issues.suggested'));
57800 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) {
57801 var klass = d.type === '+' ? 'add' : 'remove';
57802 return 'tagDiff-cell tagDiff-cell-' + klass;
57803 }).html(function (d) {
57809 validation.type = type;
57813 function validationSuspiciousName() {
57814 var type = 'suspicious_name';
57815 var keysToTestForGenericValues = ['aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'];
57816 var _waitingForNsi = false; // Attempt to match a generic record in the name-suggestion-index.
57818 function isGenericMatchInNsi(tags) {
57819 var nsi = services.nsi;
57822 _waitingForNsi = nsi.status() === 'loading';
57824 if (!_waitingForNsi) {
57825 return nsi.isGenericName(tags);
57830 } // Test if the name is just the key or tag value (e.g. "park")
57833 function nameMatchesRawTag(lowercaseName, tags) {
57834 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
57835 var key = keysToTestForGenericValues[i];
57836 var val = tags[key];
57839 val = val.toLowerCase();
57841 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
57850 function isGenericName(name, tags) {
57851 name = name.toLowerCase();
57852 return nameMatchesRawTag(name, tags) || isGenericMatchInNsi(tags);
57855 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
57856 return new validationIssue({
57858 subtype: 'generic_name',
57859 severity: 'warning',
57860 message: function message(context) {
57861 var entity = context.hasEntity(this.entityIds[0]);
57862 if (!entity) return '';
57863 var preset = _mainPresetIndex.match(entity, context.graph());
57864 var langName = langCode && _mainLocalizer.languageName(langCode);
57865 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
57866 feature: preset.name(),
57871 reference: showReference,
57872 entityIds: [entityId],
57873 hash: "".concat(nameKey, "=").concat(genericName),
57874 dynamicFixes: function dynamicFixes() {
57875 return [new validationIssueFix({
57876 icon: 'iD-operation-delete',
57877 title: _t.html('issues.fix.remove_the_name.title'),
57878 onClick: function onClick(context) {
57879 var entityId = this.issue.entityIds[0];
57880 var entity = context.entity(entityId);
57881 var tags = Object.assign({}, entity.tags); // shallow copy
57883 delete tags[nameKey];
57884 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
57890 function showReference(selection) {
57891 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57895 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
57896 return new validationIssue({
57898 subtype: 'not_name',
57899 severity: 'warning',
57900 message: function message(context) {
57901 var entity = context.hasEntity(this.entityIds[0]);
57902 if (!entity) return '';
57903 var preset = _mainPresetIndex.match(entity, context.graph());
57904 var langName = langCode && _mainLocalizer.languageName(langCode);
57905 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
57906 feature: preset.name(),
57907 name: incorrectName,
57911 reference: showReference,
57912 entityIds: [entityId],
57913 hash: "".concat(nameKey, "=").concat(incorrectName),
57914 dynamicFixes: function dynamicFixes() {
57915 return [new validationIssueFix({
57916 icon: 'iD-operation-delete',
57917 title: _t.html('issues.fix.remove_the_name.title'),
57918 onClick: function onClick(context) {
57919 var entityId = this.issue.entityIds[0];
57920 var entity = context.entity(entityId);
57921 var tags = Object.assign({}, entity.tags); // shallow copy
57923 delete tags[nameKey];
57924 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
57930 function showReference(selection) {
57931 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57935 var validation = function checkGenericName(entity) {
57936 var tags = entity.tags; // a generic name is allowed if it's a known brand or entity
57938 var hasWikidata = !!tags.wikidata || !!tags['brand:wikidata'] || !!tags['operator:wikidata'];
57939 if (hasWikidata) return [];
57941 var notNames = (tags['not:name'] || '').split(';');
57943 for (var key in tags) {
57944 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
57946 var langCode = m.length >= 2 ? m[1] : null;
57947 var value = tags[key];
57949 if (notNames.length) {
57950 for (var i in notNames) {
57951 var notName = notNames[i];
57953 if (notName && value === notName) {
57954 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
57960 if (isGenericName(value, tags)) {
57961 issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading
57963 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
57970 validation.type = type;
57974 function validationUnsquareWay(context) {
57975 var type = 'unsquare_way';
57976 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
57977 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
57979 var epsilon = 0.05;
57980 var nodeThreshold = 10;
57982 function isBuilding(entity, graph) {
57983 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
57984 return entity.tags.building && entity.tags.building !== 'no';
57987 var validation = function checkUnsquareWay(entity, graph) {
57988 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
57990 if (entity.tags.nonsquare === 'yes') return [];
57991 var isClosed = entity.isClosed();
57992 if (!isClosed) return []; // this building has bigger problems
57993 // don't flag ways with lots of nodes since they are likely detail-mapped
57995 var nodes = graph.childNodes(entity).slice(); // shallow copy
57997 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
57998 // ignore if not all nodes are fully downloaded
58000 var osm = services.osm;
58001 if (!osm || nodes.some(function (node) {
58002 return !osm.isDataLoaded(node.loc);
58003 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
58005 var hasConnectedSquarableWays = nodes.some(function (node) {
58006 return graph.parentWays(node).some(function (way) {
58007 if (way.id === entity.id) return false;
58008 if (isBuilding(way, graph)) return true;
58009 return graph.parentRelations(way).some(function (parentRelation) {
58010 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
58014 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
58016 var storedDegreeThreshold = corePreferences('validate-square-degrees');
58017 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
58018 var points = nodes.map(function (node) {
58019 return context.projection(node.loc);
58021 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
58022 var autoArgs; // don't allow autosquaring features linked to wikidata
58024 if (!entity.tags.wikidata) {
58025 // use same degree threshold as for detection
58026 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
58027 autoAction.transitionable = false; // when autofixing, do it instantly
58029 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
58034 return [new validationIssue({
58036 subtype: 'building',
58037 severity: 'warning',
58038 message: function message(context) {
58039 var entity = context.hasEntity(this.entityIds[0]);
58040 return entity ? _t.html('issues.unsquare_way.message', {
58041 feature: utilDisplayLabel(entity, context.graph())
58044 reference: showReference,
58045 entityIds: [entity.id],
58046 hash: degreeThreshold,
58047 dynamicFixes: function dynamicFixes() {
58048 return [new validationIssueFix({
58049 icon: 'iD-operation-orthogonalize',
58050 title: _t.html('issues.fix.square_feature.title'),
58051 autoArgs: autoArgs,
58052 onClick: function onClick(context, completionHandler) {
58053 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
58055 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
58057 })); // run after the squaring transition (currently 150ms)
58059 window.setTimeout(function () {
58060 completionHandler();
58065 new validationIssueFix({
58066 title: t.html('issues.fix.tag_as_unsquare.title'),
58067 onClick: function(context) {
58068 var entityId = this.issue.entityIds[0];
58069 var entity = context.entity(entityId);
58070 var tags = Object.assign({}, entity.tags); // shallow copy
58071 tags.nonsquare = 'yes';
58073 actionChangeTags(entityId, tags),
58074 t('issues.fix.tag_as_unsquare.annotation')
58083 function showReference(selection) {
58084 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
58088 validation.type = type;
58092 var Validations = /*#__PURE__*/Object.freeze({
58094 validationAlmostJunction: validationAlmostJunction,
58095 validationCloseNodes: validationCloseNodes,
58096 validationCrossingWays: validationCrossingWays,
58097 validationDisconnectedWay: validationDisconnectedWay,
58098 validationFormatting: validationFormatting,
58099 validationHelpRequest: validationHelpRequest,
58100 validationImpossibleOneway: validationImpossibleOneway,
58101 validationIncompatibleSource: validationIncompatibleSource,
58102 validationMaprules: validationMaprules,
58103 validationMismatchedGeometry: validationMismatchedGeometry,
58104 validationMissingRole: validationMissingRole,
58105 validationMissingTag: validationMissingTag,
58106 validationOutdatedTags: validationOutdatedTags,
58107 validationPrivateData: validationPrivateData,
58108 validationSuspiciousName: validationSuspiciousName,
58109 validationUnsquareWay: validationUnsquareWay
58112 function coreValidator(context) {
58115 var dispatch = dispatch$8('validated', 'focusedIssue');
58116 var validator = utilRebind({}, dispatch, 'on');
58118 var _disabledRules = {};
58120 var _ignoredIssueIDs = new Set();
58122 var _resolvedIssueIDs = new Set();
58124 var _baseCache = validationCache(); // issues before any user edits
58127 var _headCache = validationCache(); // issues after all user edits
58130 var _headGraph = null;
58131 var _headIsCurrent = false;
58133 var _deferredRIC = new Set(); // Set( RequestIdleCallback handles )
58136 var _deferredST = new Set(); // Set( SetTimeout handles )
58139 var _headPromise; // Promise fulfilled when validation is performed up to headGraph snapshot
58142 var RETRY = 5000; // wait 5sec before revalidating provisional entities
58143 // Allow validation severity to be overridden by url queryparams...
58144 // Each param should contain a urlencoded comma separated list of
58145 // `type/subtype` rules. `*` may be used as a wildcard..
58147 // `validationError=disconnected_way/*`
58148 // `validationError=disconnected_way/highway`
58149 // `validationError=crossing_ways/bridge*`
58150 // `validationError=crossing_ways/bridge*,crossing_ways/tunnel*`
58152 var _errorOverrides = parseHashParam(context.initialHashParams.validationError);
58154 var _warningOverrides = parseHashParam(context.initialHashParams.validationWarning);
58156 var _disableOverrides = parseHashParam(context.initialHashParams.validationDisable);
58158 function parseHashParam(param) {
58160 var rules = (param || '').split(',');
58161 rules.forEach(function (rule) {
58162 rule = rule.trim();
58163 var parts = rule.split('/', 2); // "type/subtype"
58165 var type = parts[0];
58166 var subtype = parts[1] || '*';
58167 if (!type || !subtype) return;
58169 type: makeRegExp(type),
58170 subtype: makeRegExp(subtype)
58176 function makeRegExp(str) {
58177 var escaped = str.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // escape all reserved chars except for the '*'
58178 .replace(/\*/g, '.*'); // treat a '*' like '.*'
58180 return new RegExp('^' + escaped + '$');
58182 // Initialize the validator, called once on iD startup
58186 validator.init = function () {
58187 Object.values(Validations).forEach(function (validation) {
58188 if (typeof validation !== 'function') return;
58189 var fn = validation(context);
58193 var disabledRules = corePreferences('validate-disabledRules');
58195 if (disabledRules) {
58196 disabledRules.split(',').forEach(function (k) {
58197 return _disabledRules[k] = true;
58200 }; // `reset()` (private)
58201 // Cancels deferred work and resets all caches
58204 // `resetIgnored` - `true` to clear the list of user-ignored issues
58208 function reset(resetIgnored) {
58209 // cancel deferred work
58210 _deferredRIC.forEach(window.cancelIdleCallback);
58212 _deferredRIC.clear();
58214 _deferredST.forEach(window.clearTimeout);
58216 _deferredST.clear(); // empty queues and resolve any pending promise
58219 _baseCache.queue = [];
58220 _headCache.queue = [];
58221 processQueue(_headCache);
58222 processQueue(_baseCache); // clear caches
58224 if (resetIgnored) _ignoredIssueIDs.clear();
58226 _resolvedIssueIDs.clear();
58228 _baseCache = validationCache();
58229 _headCache = validationCache();
58231 _headIsCurrent = false;
58233 // clear caches, called whenever iD resets after a save or switches sources
58234 // (clears out the _ignoredIssueIDs set also)
58238 validator.reset = function () {
58240 }; // `resetIgnoredIssues()`
58241 // clears out the _ignoredIssueIDs Set
58245 validator.resetIgnoredIssues = function () {
58246 _ignoredIssueIDs.clear();
58248 dispatch.call('validated'); // redraw UI
58249 }; // `revalidateUnsquare()`
58250 // Called whenever the user changes the unsquare threshold
58251 // It reruns just the "unsquare_way" validation on all buildings.
58255 validator.revalidateUnsquare = function () {
58256 revalidateUnsquare(_headCache, _headGraph);
58257 revalidateUnsquare(_baseCache, context.history().base());
58258 dispatch.call('validated');
58261 function revalidateUnsquare(cache, graph) {
58262 var checkUnsquareWay = _rules.unsquare_way;
58263 if (!graph || typeof checkUnsquareWay !== 'function') return; // uncache existing
58265 cache.uncacheIssuesOfType('unsquare_way');
58266 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), graph) // everywhere
58267 .filter(function (entity) {
58268 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
58269 }); // rerun for all buildings
58271 buildings.forEach(function (entity) {
58272 var detected = checkUnsquareWay(entity, graph);
58273 if (!detected.length) return;
58274 cache.cacheIssues(detected);
58277 // Gets all issues that match the given options
58278 // This is called by many other places
58281 // `options` Object like:
58283 // what: 'all', // 'all' or 'edited'
58284 // where: 'all', // 'all' or 'visible'
58285 // includeIgnored: false, // true, false, or 'only'
58286 // includeDisabledRules: false // true, false, or 'only'
58290 // An Array containing the issues
58294 validator.getIssues = function (options) {
58295 var opts = Object.assign({
58298 includeIgnored: false,
58299 includeDisabledRules: false
58301 var view = context.map().extent();
58303 var seen = new Set(); // collect head issues - caused by user edits
58305 var cache = _headCache;
58308 Object.values(cache.issuesByIssueID).forEach(function (issue) {
58309 if (!filter(issue, _headGraph, cache)) return;
58310 seen.add(issue.id);
58311 issues.push(issue);
58313 } // collect base issues - not caused by user edits
58316 if (opts.what === 'all') {
58317 cache = _baseCache;
58318 Object.values(cache.issuesByIssueID).forEach(function (issue) {
58319 if (!filter(issue, context.history().base(), cache)) return;
58320 seen.add(issue.id);
58321 issues.push(issue);
58327 function filter(issue, resolver, cache) {
58328 if (!issue) return false;
58329 if (seen.has(issue.id)) return false;
58330 if (_resolvedIssueIDs.has(issue.id)) return false;
58331 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
58332 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
58333 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs.has(issue.id)) return false;
58334 if (!opts.includeIgnored && _ignoredIssueIDs.has(issue.id)) return false; // Sanity check: This issue may be for an entity that not longer exists.
58335 // If we detect this, uncache and return false so it is not included..
58337 var entityIDs = issue.entityIds || [];
58339 for (var i = 0; i < entityIDs.length; i++) {
58340 var entityID = entityIDs[i];
58342 if (!resolver.hasEntity(entityID)) {
58343 cache.uncacheEntityID(entityID);
58348 if (opts.where === 'visible') {
58349 var extent = issue.extent(resolver);
58350 if (!view.intersects(extent)) return false;
58355 }; // `getResolvedIssues()`
58356 // Gets the issues that have been fixed by the user.
58357 // Resolved issues are tracked in the `_resolvedIssueIDs` Set
58360 // An Array containing the issues
58364 validator.getResolvedIssues = function () {
58365 var collected = new Set();
58366 Object.values(_baseCache.issuesByIssueID).forEach(function (issue) {
58367 if (_resolvedIssueIDs.has(issue.id)) collected.add(issue);
58369 Object.values(_headCache.issuesByIssueID).forEach(function (issue) {
58370 if (_resolvedIssueIDs.has(issue.id)) collected.add(issue);
58372 return Array.from(collected);
58373 }; // `focusIssue()`
58374 // Adjusts the map to focus on the given issue.
58375 // (requires the issue to have a reasonable extent defined)
58378 // `issue` - the issue to focus on
58382 validator.focusIssue = function (issue) {
58383 var extent = issue.extent(context.graph());
58384 if (!extent) return;
58385 var setZoom = Math.max(context.map().zoom(), 19);
58386 context.map().unobscuredCenterZoomEase(extent.center(), setZoom); // select the first entity
58388 if (issue.entityIds && issue.entityIds.length) {
58389 window.setTimeout(function () {
58390 var ids = issue.entityIds;
58391 context.enter(modeSelect(context, [ids[0]]));
58392 dispatch.call('focusedIssue', _this, issue);
58393 }, 250); // after ease
58395 }; // `getIssuesBySeverity()`
58396 // Gets the issues then groups them by error/warning
58397 // (This just calls getIssues, then puts issues in groups)
58400 // `options` - (see `getIssues`)
58402 // Object result like:
58404 // error: Array of errors,
58405 // warning: Array of warnings
58410 validator.getIssuesBySeverity = function (options) {
58411 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
58412 groups.error = groups.error || [];
58413 groups.warning = groups.warning || [];
58415 }; // `getEntityIssues()`
58416 // Gets the issues that the given entity IDs have in common, matching the given options
58417 // (This just calls getIssues, then filters for the given entity IDs)
58418 // The issues are sorted for relevance
58421 // `entityIDs` - Array or Set of entityIDs to get issues for
58422 // `options` - (see `getIssues`)
58424 // An Array containing the issues
58428 validator.getSharedEntityIssues = function (entityIDs, options) {
58429 // show some issue types in a particular order
58430 var orderedIssueTypes = [// flag missing data first
58431 'missing_tag', 'missing_role', // then flag identity issues
58432 'outdated_tags', 'mismatched_geometry', // flag geometry issues where fixing them might solve connectivity issues
58433 'crossing_ways', 'almost_junction', // then flag connectivity issues
58434 'disconnected_way', 'impossible_oneway'];
58435 var allIssues = validator.getIssues(options);
58436 var forEntityIDs = new Set(entityIDs);
58437 return allIssues.filter(function (issue) {
58438 return (issue.entityIds || []).some(function (entityID) {
58439 return forEntityIDs.has(entityID);
58441 }).sort(function (issue1, issue2) {
58442 if (issue1.type === issue2.type) {
58443 // issues of the same type, sort deterministically
58444 return issue1.id < issue2.id ? -1 : 1;
58447 var index1 = orderedIssueTypes.indexOf(issue1.type);
58448 var index2 = orderedIssueTypes.indexOf(issue2.type);
58450 if (index1 !== -1 && index2 !== -1) {
58451 // both issue types have explicit sort orders
58452 return index1 - index2;
58453 } else if (index1 === -1 && index2 === -1) {
58454 // neither issue type has an explicit sort order, sort by type
58455 return issue1.type < issue2.type ? -1 : 1;
58457 // order explicit types before everything else
58458 return index1 !== -1 ? -1 : 1;
58461 }; // `getEntityIssues()`
58462 // Get an array of detected issues for the given entityID.
58463 // (This just calls getSharedEntityIssues for a single entity)
58466 // `entityID` - the entity ID to get the issues for
58467 // `options` - (see `getIssues`)
58469 // An Array containing the issues
58473 validator.getEntityIssues = function (entityID, options) {
58474 return validator.getSharedEntityIssues([entityID], options);
58475 }; // `getRuleKeys()`
58478 // An Array containing the rule keys
58482 validator.getRuleKeys = function () {
58483 return Object.keys(_rules);
58484 }; // `isRuleEnabled()`
58487 // `key` - the rule to check (e.g. 'crossing_ways')
58493 validator.isRuleEnabled = function (key) {
58494 return !_disabledRules[key];
58495 }; // `toggleRule()`
58496 // Toggles a single validation rule,
58497 // then reruns the validation so that the user sees something happen in the UI
58500 // `key` - the rule to toggle (e.g. 'crossing_ways')
58504 validator.toggleRule = function (key) {
58505 if (_disabledRules[key]) {
58506 delete _disabledRules[key];
58508 _disabledRules[key] = true;
58511 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58512 validator.validate();
58513 }; // `disableRules()`
58514 // Disables given validation rules,
58515 // then reruns the validation so that the user sees something happen in the UI
58518 // `keys` - Array or Set containing rule keys to disable
58522 validator.disableRules = function (keys) {
58523 _disabledRules = {};
58524 keys.forEach(function (k) {
58525 return _disabledRules[k] = true;
58527 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58528 validator.validate();
58529 }; // `ignoreIssue()`
58530 // Don't show the given issue in lists
58533 // `issueID` - the issueID
58537 validator.ignoreIssue = function (issueID) {
58538 _ignoredIssueIDs.add(issueID);
58540 // Validates anything that has changed in the head graph since the last time it was run.
58541 // (head graph contains user's edits)
58544 // A Promise fulfilled when the validation has completed and then dispatches a `validated` event.
58545 // This may take time but happen in the background during browser idle time.
58549 validator.validate = function () {
58550 var currGraph = context.graph();
58552 var prevGraph = _headGraph || context.history().base();
58554 if (currGraph === prevGraph) {
58555 // _headGraph is current - we are caught up
58556 _headIsCurrent = true;
58557 dispatch.call('validated');
58558 return Promise.resolve();
58561 if (_headPromise) {
58562 // Validation already in process, but we aren't caught up to current
58563 _headIsCurrent = false; // We will need to catch up after the validation promise fulfills
58565 return _headPromise;
58568 _headGraph = currGraph; // take snapshot
58570 var difference = coreDifference(prevGraph, _headGraph); // Gather all entities related to this difference..
58571 // For created/modified, use the head graph
58573 var entityIDs = difference.extantIDs(true); // created/modified (true = w/relation members)
58575 entityIDs = entityIDsToValidate(entityIDs, _headGraph); // For modified/deleted, use the previous graph
58576 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
58578 var previousEntityIDs = difference.deleted().concat(difference.modified()).map(function (entity) {
58581 previousEntityIDs = entityIDsToValidate(previousEntityIDs, prevGraph);
58582 previousEntityIDs.forEach(entityIDs.add, entityIDs); // concat the sets
58584 if (!entityIDs.size) {
58585 dispatch.call('validated');
58586 return Promise.resolve();
58589 _headPromise = validateEntitiesAsync(entityIDs, _headGraph, _headCache).then(function () {
58590 return updateResolvedIssues(entityIDs);
58591 }).then(function () {
58592 return dispatch.call('validated');
58593 })["catch"](function () {
58595 }).then(function () {
58596 _headPromise = null;
58598 if (!_headIsCurrent) {
58599 validator.validate(); // run it again to catch up to current graph
58602 return _headPromise;
58603 }; // register event handlers:
58604 // WHEN TO RUN VALIDATION:
58605 // When history changes:
58608 context.history().on('restore.validator', validator.validate) // on restore saved history
58609 .on('undone.validator', validator.validate) // on undo
58610 .on('redone.validator', validator.validate) // on redo
58611 .on('reset.validator', function () {
58612 // on history reset - happens after save, or enter/exit walkthrough
58613 reset(false); // cached issues aren't valid any longer if the history has been reset
58615 validator.validate();
58616 }); // but not on 'change' (e.g. while drawing)
58617 // When user changes editing modes (to catch recent changes e.g. drawing)
58619 context.on('exit.validator', validator.validate); // When merging fetched data, validate base graph:
58621 context.history().on('merge.validator', function (entities) {
58622 if (!entities) return;
58623 var baseGraph = context.history().base();
58624 var entityIDs = entities.map(function (entity) {
58627 entityIDs = entityIDsToValidate(entityIDs, baseGraph);
58628 validateEntitiesAsync(entityIDs, baseGraph, _baseCache);
58629 }); // `validateEntity()` (private)
58630 // Runs all validation rules on a single entity.
58631 // Some things to note:
58632 // - Graph is passed in from whenever the validation was started. Validators shouldn't use
58633 // `context.graph()` because this all happens async, and the graph might have changed
58634 // (for example, nodes getting deleted before the validation can run)
58635 // - Validator functions may still be waiting on something and return a "provisional" result.
58636 // In this situation, we will schedule to revalidate the entity sometime later.
58639 // `entity` - The entity
58640 // `graph` - graph containing the entity
58643 // Object result like:
58645 // issues: Array of detected issues
58646 // provisional: `true` if provisional result, `false` if final result
58650 function validateEntity(entity, graph) {
58654 }; // runs validation and appends resulting issues
58656 function runValidation(key) {
58657 var fn = _rules[key];
58659 if (typeof fn !== 'function') {
58660 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
58665 var detected = fn(entity, graph).filter(applySeverityOverrides);
58667 if (detected.provisional) {
58668 // this validation should be run again later
58669 result.provisional = true;
58672 result.issues = result.issues.concat(detected);
58676 Object.keys(_rules).forEach(runValidation);
58678 } // If there are any override rules that match the issue type/subtype,
58679 // adjust severity (or disable it) and keep/discard as quickly as possible.
58682 function applySeverityOverrides(issue) {
58683 var type = issue.type;
58684 var subtype = issue.subtype || '';
58687 for (i = 0; i < _errorOverrides.length; i++) {
58688 if (_errorOverrides[i].type.test(type) && _errorOverrides[i].subtype.test(subtype)) {
58689 issue.severity = 'error';
58694 for (i = 0; i < _warningOverrides.length; i++) {
58695 if (_warningOverrides[i].type.test(type) && _warningOverrides[i].subtype.test(subtype)) {
58696 issue.severity = 'warning';
58701 for (i = 0; i < _disableOverrides.length; i++) {
58702 if (_disableOverrides[i].type.test(type) && _disableOverrides[i].subtype.test(subtype)) {
58708 } // `entityIDsToValidate()` (private)
58709 // Collects the complete list of entityIDs related to the input entityIDs.
58712 // `entityIDs` - Set or Array containing entityIDs.
58713 // `graph` - graph containing the entities
58716 // Set containing entityIDs
58720 function entityIDsToValidate(entityIDs, graph) {
58721 var seen = new Set();
58722 var collected = new Set();
58723 entityIDs.forEach(function (entityID) {
58724 // keep `seen` separate from `collected` because an `entityID`
58725 // could have been added to `collected` as a related entity through an earlier pass
58726 if (seen.has(entityID)) return;
58727 seen.add(entityID);
58728 var entity = graph.hasEntity(entityID);
58729 if (!entity) return;
58730 collected.add(entityID); // collect self
58732 var checkParentRels = [entity];
58734 if (entity.type === 'node') {
58735 graph.parentWays(entity).forEach(function (parentWay) {
58736 collected.add(parentWay.id); // collect parent ways
58738 checkParentRels.push(parentWay);
58740 } else if (entity.type === 'relation') {
58741 entity.members.forEach(function (member) {
58742 return collected.add(member.id);
58743 }); // collect members
58744 } else if (entity.type === 'way') {
58745 entity.nodes.forEach(function (nodeID) {
58746 collected.add(nodeID); // collect child nodes
58748 graph._parentWays[nodeID].forEach(function (wayID) {
58749 return collected.add(wayID);
58750 }); // collect connected ways
58755 checkParentRels.forEach(function (entity) {
58756 // collect parent relations
58757 if (entity.type !== 'relation') {
58758 // but not super-relations
58759 graph.parentRelations(entity).forEach(function (parentRelation) {
58760 return collected.add(parentRelation.id);
58766 } // `updateResolvedIssues()` (private)
58767 // Determine if any issues were resolved for the given entities.
58768 // This is called by `validate()` after validation of the head graph
58771 // `entityIDs` - Set containing entity IDs.
58775 function updateResolvedIssues(entityIDs) {
58776 entityIDs.forEach(function (entityID) {
58777 var headIssues = _headCache.issuesByEntityID[entityID];
58778 var baseIssues = _baseCache.issuesByEntityID[entityID];
58779 if (!baseIssues) return;
58780 baseIssues.forEach(function (issueID) {
58781 if (headIssues && headIssues.has(issueID)) {
58782 // issue still not resolved
58783 _resolvedIssueIDs["delete"](issueID); // (did undo, or possibly fixed and then re-caused the issue)
58786 _resolvedIssueIDs.add(issueID);
58790 } // `validateEntitiesAsync()` (private)
58791 // Schedule validation for many entities.
58794 // `entityIDs` - Set containing entity IDs.
58795 // `graph` - the graph to validate that contains those entities
58796 // `cache` - the cache to store results in (_headCache or _baseCache)
58799 // A Promise fulfilled when the validation has completed.
58800 // This may take time but happen in the background during browser idle time.
58804 function validateEntitiesAsync(entityIDs, graph, cache) {
58805 // Enqueue the work
58806 var jobs = Array.from(entityIDs).map(function (entityID) {
58807 if (cache.queuedEntityIDs.has(entityID)) return null; // queued already
58809 cache.queuedEntityIDs.add(entityID);
58810 return function () {
58811 // clear caches for existing issues related to this entity
58812 cache.uncacheEntityID(entityID);
58813 cache.queuedEntityIDs["delete"](entityID); // detect new issues and update caches
58815 var entity = graph.hasEntity(entityID); // Sanity check: don't validate deleted entities
58818 var result = validateEntity(entity, graph);
58820 if (result.provisional) {
58821 // provisional result
58822 cache.provisionalEntityIDs.add(entityID); // we'll need to revalidate this entity again later
58825 cache.cacheIssues(result.issues); // update cache
58828 }).filter(Boolean); // Perform the work in chunks.
58829 // Because this will happen during idle callbacks, we want to choose a chunk size
58830 // that won't make the browser stutter too badly.
58832 cache.queue = cache.queue.concat(utilArrayChunk(jobs, 100)); // Perform the work
58834 if (cache.queuePromise) return cache.queuePromise;
58835 cache.queuePromise = processQueue(cache).then(function () {
58836 return revalidateProvisionalEntities(cache);
58837 })["catch"](function () {
58839 })["finally"](function () {
58840 return cache.queuePromise = null;
58842 return cache.queuePromise;
58843 } // `revalidateProvisionalEntities()` (private)
58844 // Sometimes a validator will return a "provisional" result.
58845 // In this situation, we'll need to revalidate the entity later.
58846 // This function waits a delay, then places them back into the validation queue.
58849 // `cache` - The cache (_headCache or _baseCache)
58853 function revalidateProvisionalEntities(cache) {
58854 if (!cache.provisionalEntityIDs.size) return; // nothing to do
58856 var handle = window.setTimeout(function () {
58857 _deferredST["delete"](handle);
58859 if (!cache.provisionalEntityIDs.size) return; // nothing to do
58861 var graph = cache === _headCache ? _headGraph : context.history().base();
58862 validateEntitiesAsync(cache.provisionalEntityIDs, graph, cache);
58865 _deferredST.add(handle);
58866 } // `processQueue(queue)` (private)
58867 // Process the next chunk of deferred validation work
58870 // `cache` - The cache (_headCache or _baseCache)
58873 // A Promise fulfilled when the validation has completed.
58874 // This may take time but happen in the background during browser idle time.
58878 function processQueue(cache) {
58879 // const which = (cache === _headCache) ? 'head' : 'base';
58880 // console.log(`${which} queue length ${cache.queue.length}`);
58881 if (!cache.queue.length) return Promise.resolve(); // we're done
58883 var chunk = cache.queue.pop();
58884 return new Promise(function (resolvePromise) {
58885 var handle = window.requestIdleCallback(function () {
58886 _deferredRIC["delete"](handle); // const t0 = performance.now();
58889 chunk.forEach(function (job) {
58891 }); // const t1 = performance.now();
58892 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
58897 _deferredRIC.add(handle);
58898 }).then(function () {
58899 // dispatch an event sometimes to redraw various UI things
58900 if (cache.queue.length % 25 === 0) dispatch.call('validated');
58901 }).then(function () {
58902 return processQueue(cache);
58907 } // `validationCache()` (private)
58908 // Creates a cache to store validation state
58909 // We create 2 of these:
58910 // `_baseCache` for validation on the base graph (unedited)
58911 // `_headCache` for validation on the head graph (user edits applied)
58914 function validationCache() {
58917 queuePromise: null,
58918 queuedEntityIDs: new Set(),
58919 provisionalEntityIDs: new Set(),
58920 issuesByIssueID: {},
58921 // issue.id -> issue
58922 issuesByEntityID: {} // entity.id -> set(issue.id)
58926 cache.cacheIssues = function (issues) {
58927 issues.forEach(function (issue) {
58928 var entityIDs = issue.entityIds || [];
58929 entityIDs.forEach(function (entityID) {
58930 if (!cache.issuesByEntityID[entityID]) {
58931 cache.issuesByEntityID[entityID] = new Set();
58934 cache.issuesByEntityID[entityID].add(issue.id);
58936 cache.issuesByIssueID[issue.id] = issue;
58940 cache.uncacheIssue = function (issue) {
58941 // When multiple entities are involved (e.g. crossing_ways),
58942 // remove this issue from the other entity caches too..
58943 var entityIDs = issue.entityIds || [];
58944 entityIDs.forEach(function (entityID) {
58945 if (cache.issuesByEntityID[entityID]) {
58946 cache.issuesByEntityID[entityID]["delete"](issue.id);
58949 delete cache.issuesByIssueID[issue.id];
58952 cache.uncacheIssues = function (issues) {
58953 issues.forEach(cache.uncacheIssue);
58956 cache.uncacheIssuesOfType = function (type) {
58957 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
58958 return issue.type === type;
58960 cache.uncacheIssues(issuesOfType);
58961 }; // Remove a single entity and all its related issues from the caches
58964 cache.uncacheEntityID = function (entityID) {
58965 var issueIDs = cache.issuesByEntityID[entityID];
58968 issueIDs.forEach(function (issueID) {
58969 var issue = cache.issuesByIssueID[issueID];
58972 cache.uncacheIssue(issue);
58974 delete cache.issuesByIssueID[issueID];
58979 delete cache.issuesByEntityID[entityID];
58980 cache.provisionalEntityIDs["delete"](entityID);
58986 function coreUploader(context) {
58987 var dispatch = dispatch$8( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
58988 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
58989 'saveEnded', // dispatched after the result event has been dispatched
58990 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
58991 'progressChanged', // Each save results in one of these outcomes:
58992 'resultNoChanges', // upload wasn't attempted since there were no edits
58993 'resultErrors', // upload failed due to errors
58994 'resultConflicts', // upload failed due to data conflicts
58995 'resultSuccess' // upload completed without errors
58997 var _isSaving = false;
58998 var _conflicts = [];
59003 var _discardTags = {};
59004 _mainFileFetcher.get('discarded').then(function (d) {
59006 })["catch"](function () {
59009 var uploader = utilRebind({}, dispatch, 'on');
59011 uploader.isSaving = function () {
59015 uploader.save = function (changeset, tryAgain, checkConflicts) {
59016 // Guard against accidentally entering save code twice - #4641
59017 if (_isSaving && !tryAgain) {
59021 var osm = context.connection();
59022 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
59023 // This can happen if they were logged in from before, but the tokens are no longer valid.
59025 if (!osm.authenticated()) {
59026 osm.authenticate(function (err) {
59028 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
59036 dispatch.call('saveStarted', this);
59039 var history = context.history();
59041 _errors = []; // Store original changes, in case user wants to download them as an .osc file
59043 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
59044 // Any conflict resolutions will be done as `history.replace`
59045 // Remember to pop this later if needed
59048 history.perform(actionNoop());
59049 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
59052 if (!checkConflicts) {
59053 upload(changeset); // Do the full (slow) conflict check..
59055 performFullConflictCheck(changeset);
59059 function performFullConflictCheck(changeset) {
59060 var osm = context.connection();
59062 var history = context.history();
59063 var localGraph = context.graph();
59064 var remoteGraph = coreGraph(history.base(), true);
59065 var summary = history.difference().summary();
59068 for (var i = 0; i < summary.length; i++) {
59069 var item = summary[i];
59071 if (item.changeType === 'modified') {
59072 _toCheck.push(item.entity.id);
59076 var _toLoad = withChildNodes(_toCheck, localGraph);
59079 var _toLoadCount = 0;
59080 var _toLoadTotal = _toLoad.length;
59082 if (_toCheck.length) {
59083 dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59085 _toLoad.forEach(function (id) {
59086 _loaded[id] = false;
59089 osm.loadMultiple(_toLoad, loaded);
59096 function withChildNodes(ids, graph) {
59097 var s = new Set(ids);
59098 ids.forEach(function (id) {
59099 var entity = graph.entity(id);
59100 if (entity.type !== 'way') return;
59101 graph.childNodes(entity).forEach(function (child) {
59102 if (child.version !== undefined) {
59107 return Array.from(s);
59108 } // Reload modified entities into an alternate graph and check for conflicts..
59111 function loaded(err, result) {
59112 if (_errors.length) return;
59116 msg: err.message || err.responseText,
59117 details: [_t('save.status_code', {
59122 didResultInErrors();
59125 result.data.forEach(function (entity) {
59126 remoteGraph.replace(entity);
59127 _loaded[entity.id] = true;
59128 _toLoad = _toLoad.filter(function (val) {
59129 return val !== entity.id;
59131 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
59132 // need to also load children that aren't already being checked..
59136 if (entity.type === 'way') {
59137 for (i = 0; i < entity.nodes.length; i++) {
59138 id = entity.nodes[i];
59140 if (_loaded[id] === undefined) {
59141 _loaded[id] = false;
59145 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59146 for (i = 0; i < entity.members.length; i++) {
59147 id = entity.members[i].id;
59149 if (_loaded[id] === undefined) {
59150 _loaded[id] = false;
59156 _toLoadCount += result.data.length;
59157 _toLoadTotal += loadMore.length;
59158 dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59160 if (loadMore.length) {
59161 _toLoad.push.apply(_toLoad, loadMore);
59163 osm.loadMultiple(loadMore, loaded);
59166 if (!_toLoad.length) {
59173 function detectConflicts() {
59174 function choice(id, text, _action) {
59178 action: function action() {
59179 history.replace(_action);
59184 function formatUser(d) {
59185 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
59188 function entityName(entity) {
59189 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
59192 function sameVersions(local, remote) {
59193 if (local.version !== remote.version) return false;
59195 if (local.type === 'way') {
59196 var children = utilArrayUnion(local.nodes, remote.nodes);
59198 for (var i = 0; i < children.length; i++) {
59199 var a = localGraph.hasEntity(children[i]);
59200 var b = remoteGraph.hasEntity(children[i]);
59201 if (a && b && a.version !== b.version) return false;
59208 _toCheck.forEach(function (id) {
59209 var local = localGraph.entity(id);
59210 var remote = remoteGraph.entity(id);
59211 if (sameVersions(local, remote)) return;
59212 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
59213 history.replace(merge);
59214 var mergeConflicts = merge.conflicts();
59215 if (!mergeConflicts.length) return; // merged safely
59217 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
59218 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
59219 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
59220 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
59224 name: entityName(local),
59225 details: mergeConflicts,
59227 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
59233 function upload(changeset) {
59234 var osm = context.connection();
59238 msg: 'No OSM Service'
59242 if (_conflicts.length) {
59243 didResultInConflicts(changeset);
59244 } else if (_errors.length) {
59245 didResultInErrors();
59247 var history = context.history();
59248 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
59250 if (changes.modified.length || changes.created.length || changes.deleted.length) {
59251 dispatch.call('willAttemptUpload', this);
59252 osm.putChangeset(changeset, changes, uploadCallback);
59254 // changes were insignificant or reverted by user
59255 didResultInNoChanges();
59260 function uploadCallback(err, changeset) {
59262 if (err.status === 409) {
59264 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
59267 msg: err.message || err.responseText,
59268 details: [_t('save.status_code', {
59273 didResultInErrors();
59276 didResultInSuccess(changeset);
59280 function didResultInNoChanges() {
59281 dispatch.call('resultNoChanges', this);
59283 context.flush(); // reset iD
59286 function didResultInErrors() {
59287 context.history().pop();
59288 dispatch.call('resultErrors', this, _errors);
59292 function didResultInConflicts(changeset) {
59293 _conflicts.sort(function (a, b) {
59294 return b.id.localeCompare(a.id);
59297 dispatch.call('resultConflicts', this, changeset, _conflicts, _origChanges);
59301 function didResultInSuccess(changeset) {
59302 // delete the edit stack cached to local storage
59303 context.history().clearSaved();
59304 dispatch.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
59306 window.setTimeout(function () {
59308 context.flush(); // reset iD
59312 function endSave() {
59314 dispatch.call('saveEnded', this);
59317 uploader.cancelConflictResolution = function () {
59318 context.history().pop();
59321 uploader.processResolvedConflicts = function (changeset) {
59322 var history = context.history();
59324 for (var i = 0; i < _conflicts.length; i++) {
59325 if (_conflicts[i].chosen === 1) {
59326 // user chose "use theirs"
59327 var entity = context.hasEntity(_conflicts[i].id);
59329 if (entity && entity.type === 'way') {
59330 var children = utilArrayUniq(entity.nodes);
59332 for (var j = 0; j < children.length; j++) {
59333 history.replace(actionRevert(children[j]));
59337 history.replace(actionRevert(_conflicts[i].id));
59341 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
59344 uploader.reset = function () {};
59349 var abs = Math.abs;
59350 var exp = Math.exp;
59353 var FORCED = fails(function () {
59354 // eslint-disable-next-line es/no-math-sinh -- required for testing
59355 return Math.sinh(-2e-17) != -2e-17;
59358 // `Math.sinh` method
59359 // https://tc39.es/ecma262/#sec-math.sinh
59360 // V8 near Chromium 38 has a problem with very small numbers
59361 _export({ target: 'Math', stat: true, forced: FORCED }, {
59362 sinh: function sinh(x) {
59363 return abs(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
59367 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
59369 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
59370 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
59373 function localeDateString(s) {
59374 if (!s) return null;
59380 var d = new Date(s);
59381 if (isNaN(d.getTime())) return null;
59382 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
59385 function vintageRange(vintage) {
59388 if (vintage.start || vintage.end) {
59389 s = vintage.start || '?';
59391 if (vintage.start !== vintage.end) {
59392 s += ' - ' + (vintage.end || '?');
59399 function rendererBackgroundSource(data) {
59400 var source = Object.assign({}, data); // shallow copy
59402 var _offset = [0, 0];
59403 var _name = source.name;
59404 var _description = source.description;
59406 var _best = !!source.best;
59408 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
59410 source.tileSize = data.tileSize || 256;
59411 source.zoomExtent = data.zoomExtent || [0, 22];
59412 source.overzoom = data.overzoom !== false;
59414 source.offset = function (val) {
59415 if (!arguments.length) return _offset;
59420 source.nudge = function (val, zoomlevel) {
59421 _offset[0] += val[0] / Math.pow(2, zoomlevel);
59422 _offset[1] += val[1] / Math.pow(2, zoomlevel);
59426 source.name = function () {
59427 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59428 return _t('imagery.' + id_safe + '.name', {
59433 source.label = function () {
59434 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59435 return _t.html('imagery.' + id_safe + '.name', {
59440 source.description = function () {
59441 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59442 return _t.html('imagery.' + id_safe + '.description', {
59443 "default": _description
59447 source.best = function () {
59451 source.area = function () {
59452 if (!data.polygon) return Number.MAX_VALUE; // worldwide
59454 var area = d3_geoArea({
59455 type: 'MultiPolygon',
59456 coordinates: [data.polygon]
59458 return isNaN(area) ? 0 : area;
59461 source.imageryUsed = function () {
59462 return _name || source.id;
59465 source.template = function (val) {
59466 if (!arguments.length) return _template;
59468 if (source.id === 'custom' || source.id === 'Bing') {
59475 source.url = function (coord) {
59476 var result = _template;
59477 if (result === '') return result; // source 'none'
59478 // Guess a type based on the tokens present in the template
59479 // (This is for 'custom' source, where we don't know)
59481 if (!source.type) {
59482 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
59483 source.type = 'wms';
59484 source.projection = 'EPSG:3857'; // guess
59485 } else if (/\{(x|y)\}/.test(_template)) {
59486 source.type = 'tms';
59487 } else if (/\{u\}/.test(_template)) {
59488 source.type = 'bing';
59492 if (source.type === 'wms') {
59493 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
59494 //polyfill for IE11, PhantomJS
59495 var sinh = Math.sinh || function (x) {
59496 var y = Math.exp(x);
59497 return (y - 1 / y) / 2;
59500 var zoomSize = Math.pow(2, z);
59501 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
59502 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
59504 switch (source.projection) {
59507 x: lon * 180 / Math.PI,
59508 y: lat * 180 / Math.PI
59512 // EPSG:3857 and synonyms
59513 var mercCoords = mercatorRaw(lon, lat);
59515 x: 20037508.34 / Math.PI * mercCoords[0],
59516 y: 20037508.34 / Math.PI * mercCoords[1]
59521 var tileSize = source.tileSize;
59522 var projection = source.projection;
59523 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
59524 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
59525 result = result.replace(/\{(\w+)\}/g, function (token, key) {
59535 return projection.replace(/^EPSG:/, '');
59538 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
59539 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
59540 /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
59541 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
59543 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
59562 } else if (source.type === 'tms') {
59563 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
59564 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
59565 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
59566 } else if (source.type === 'bing') {
59567 result = result.replace('{u}', function () {
59570 for (var zoom = coord[2]; zoom > 0; zoom--) {
59572 var mask = 1 << zoom - 1;
59573 if ((coord[0] & mask) !== 0) b++;
59574 if ((coord[1] & mask) !== 0) b += 2;
59580 } // these apply to any type..
59583 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
59584 var subdomains = r.split(',');
59585 return subdomains[(coord[0] + coord[1]) % subdomains.length];
59590 source.validZoom = function (z) {
59591 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
59594 source.isLocatorOverlay = function () {
59595 return source.id === 'mapbox_locator_overlay';
59597 /* hides a source from the list, but leaves it available for use */
59600 source.isHidden = function () {
59601 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
59604 source.copyrightNotices = function () {};
59606 source.getMetadata = function (center, tileCoord, callback) {
59608 start: localeDateString(source.startDate),
59609 end: localeDateString(source.endDate)
59611 vintage.range = vintageRange(vintage);
59615 callback(null, metadata);
59621 rendererBackgroundSource.Bing = function (data, dispatch) {
59622 // https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata
59623 // https://docs.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles
59624 //fallback url template
59625 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=10555&n=z';
59626 var bing = rendererBackgroundSource(data); //var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
59628 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
59631 missing tile image strictness param (n=)
59632 • n=f -> (Fail) returns a 404
59633 • n=z -> (Empty) returns a 200 with 0 bytes (no content)
59634 • n=t -> (Transparent) returns a 200 with a transparent (png) tile
59637 var strictParam = 'n';
59638 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&uriScheme=https&key=' + key;
59641 var providers = [];
59642 d3_json(url).then(function (json) {
59643 var imageryResource = json.resourceSets[0].resources[0]; //retrieve and prepare up to date imagery template
59645 var template = imageryResource.imageUrl; //https://ecn.{subdomain}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=10339
59647 var subDomains = imageryResource.imageUrlSubdomains; //["t0, t1, t2, t3"]
59649 var subDomainNumbers = subDomains.map(function (subDomain) {
59650 return subDomain.substring(1);
59652 template = template.replace('{subdomain}', "t{switch:".concat(subDomainNumbers, "}")).replace('{quadkey}', '{u}');
59654 if (!new URLSearchParams(template).has(strictParam)) {
59655 template += "&".concat(strictParam, "=z");
59658 bing.template(template);
59659 providers = imageryResource.imageryProviders.map(function (provider) {
59661 attribution: provider.attribution,
59662 areas: provider.coverageAreas.map(function (area) {
59664 zoom: [area.zoomMin, area.zoomMax],
59665 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
59670 dispatch.call('change');
59671 })["catch"](function () {
59675 bing.copyrightNotices = function (zoom, extent) {
59676 zoom = Math.min(zoom, 21);
59677 return providers.filter(function (provider) {
59678 return provider.areas.some(function (area) {
59679 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
59681 }).map(function (provider) {
59682 return provider.attribution;
59686 bing.getMetadata = function (center, tileCoord, callback) {
59687 var tileID = tileCoord.slice(0, 3).join('/');
59688 var zoom = Math.min(tileCoord[2], 21);
59689 var centerPoint = center[1] + ',' + center[0]; // lat,lng
59691 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
59692 if (inflight[tileID]) return;
59694 if (!cache[tileID]) {
59695 cache[tileID] = {};
59698 if (cache[tileID] && cache[tileID].metadata) {
59699 return callback(null, cache[tileID].metadata);
59702 inflight[tileID] = true;
59703 d3_json(url).then(function (result) {
59704 delete inflight[tileID];
59707 throw new Error('Unknown Error');
59711 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
59712 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
59714 vintage.range = vintageRange(vintage);
59718 cache[tileID].metadata = metadata;
59719 if (callback) callback(null, metadata);
59720 })["catch"](function (err) {
59721 delete inflight[tileID];
59722 if (callback) callback(err.message);
59726 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
59730 rendererBackgroundSource.Esri = function (data) {
59731 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
59732 if (data.template.match(/blankTile/) === null) {
59733 data.template = data.template + '?blankTile=false';
59736 var esri = rendererBackgroundSource(data);
59740 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
59741 // https://developers.arcgis.com/documentation/tiled-elevation-service/
59744 esri.fetchTilemap = function (center) {
59745 // skip if we have already fetched a tilemap within 5km
59746 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
59747 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
59749 var z = 20; // first generate a random url using the template
59751 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
59753 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
59754 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
59756 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
59758 d3_json(tilemapUrl).then(function (tilemap) {
59760 throw new Error('Unknown Error');
59763 var hasTiles = true;
59765 for (var i = 0; i < tilemap.data.length; i++) {
59766 // 0 means an individual tile in the grid doesn't exist
59767 if (!tilemap.data[i]) {
59771 } // if any tiles are missing at level 20 we restrict maxZoom to 19
59774 esri.zoomExtent[1] = hasTiles ? 22 : 19;
59775 })["catch"](function () {
59780 esri.getMetadata = function (center, tileCoord, callback) {
59781 var tileID = tileCoord.slice(0, 3).join('/');
59782 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
59783 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
59785 var unknown = _t('info_panels.background.unknown');
59789 if (inflight[tileID]) return;
59792 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
59809 metadataLayer = 99;
59812 var url; // build up query using the layer appropriate to the current zoom
59814 if (esri.id === 'EsriWorldImagery') {
59815 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
59816 } else if (esri.id === 'EsriWorldImageryClarity') {
59817 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
59820 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
59822 if (!cache[tileID]) {
59823 cache[tileID] = {};
59826 if (cache[tileID] && cache[tileID].metadata) {
59827 return callback(null, cache[tileID].metadata);
59828 } // accurate metadata is only available >= 13
59831 if (metadataLayer === 99) {
59840 description: unknown,
59841 resolution: unknown,
59844 callback(null, metadata);
59846 inflight[tileID] = true;
59847 d3_json(url).then(function (result) {
59848 delete inflight[tileID];
59851 throw new Error('Unknown Error');
59852 } else if (result.features && result.features.length < 1) {
59853 throw new Error('No Results');
59854 } else if (result.error && result.error.message) {
59855 throw new Error(result.error.message);
59856 } // pass through the discrete capture date from metadata
59859 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
59861 start: captureDate,
59867 source: clean(result.features[0].attributes.NICE_NAME),
59868 description: clean(result.features[0].attributes.NICE_DESC),
59869 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
59870 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
59871 }; // append units - meters
59873 if (isFinite(metadata.resolution)) {
59874 metadata.resolution += ' m';
59877 if (isFinite(metadata.accuracy)) {
59878 metadata.accuracy += ' m';
59881 cache[tileID].metadata = metadata;
59882 if (callback) callback(null, metadata);
59883 })["catch"](function (err) {
59884 delete inflight[tileID];
59885 if (callback) callback(err.message);
59889 function clean(val) {
59890 return String(val).trim() || unknown;
59897 rendererBackgroundSource.None = function () {
59898 var source = rendererBackgroundSource({
59903 source.name = function () {
59904 return _t('background.none');
59907 source.label = function () {
59908 return _t.html('background.none');
59911 source.imageryUsed = function () {
59915 source.area = function () {
59916 return -1; // sources in background pane are sorted by area
59922 rendererBackgroundSource.Custom = function (template) {
59923 var source = rendererBackgroundSource({
59928 source.name = function () {
59929 return _t('background.custom');
59932 source.label = function () {
59933 return _t.html('background.custom');
59936 source.imageryUsed = function () {
59937 // sanitize personal connection tokens - #6801
59938 var cleaned = source.template(); // from query string parameters
59940 if (cleaned.indexOf('?') !== -1) {
59941 var parts = cleaned.split('?', 2);
59942 var qs = utilStringQs(parts[1]);
59943 ['access_token', 'connectId', 'token'].forEach(function (param) {
59945 qs[param] = '{apikey}';
59948 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
59949 } // from wms/wmts api path parameters
59952 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
59953 return 'Custom (' + cleaned + ' )';
59956 source.area = function () {
59957 return -2; // sources in background pane are sorted by area
59963 function rendererTileLayer(context) {
59964 var transformProp = utilPrefixCSSProperty('Transform');
59965 var tiler = utilTiler();
59966 var _tileSize = 256;
59978 function tileSizeAtZoom(d, z) {
59979 var EPSILON = 0.002; // close seams
59981 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
59984 function atZoom(t, distance) {
59985 var power = Math.pow(2, distance);
59986 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
59989 function lookUp(d) {
59990 for (var up = -1; up > -d[2]; up--) {
59991 var tile = atZoom(d, up);
59993 if (_cache[_source.url(tile)] !== false) {
59999 function uniqueBy(a, n) {
60003 for (var i = 0; i < a.length; i++) {
60004 if (seen[a[i][n]] === undefined) {
60006 seen[a[i][n]] = true;
60013 function addSource(d) {
60014 d.push(_source.url(d));
60016 } // Update tiles based on current state of `projection`.
60019 function background(selection) {
60020 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
60024 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
60026 pixelOffset = [0, 0];
60029 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
60030 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
60031 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
60033 } // Derive the tiles onscreen, remove those offscreen and position them.
60034 // Important that this part not depend on `_projection` because it's
60035 // rentered when tiles load/error (see #644).
60038 function render(selection) {
60039 if (!_source) return;
60041 var showDebug = context.getDebug('tile') && !_source.overlay;
60043 if (_source.validZoom(_zoom)) {
60044 tiler.skipNullIsland(!!_source.overlay);
60045 tiler().forEach(function (d) {
60047 if (d[3] === '') return;
60048 if (typeof d[3] !== 'string') return; // Workaround for #2295
60052 if (_cache[d[3]] === false && lookUp(d)) {
60053 requests.push(addSource(lookUp(d)));
60056 requests = uniqueBy(requests, 3).filter(function (r) {
60057 // don't re-request tiles which have failed in the past
60058 return _cache[r[3]] !== false;
60062 function load(d3_event, d) {
60063 _cache[d[3]] = true;
60064 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
60068 function error(d3_event, d) {
60069 _cache[d[3]] = false;
60070 select(this).on('error', null).on('load', null).remove();
60074 function imageTransform(d) {
60075 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60077 var scale = tileSizeAtZoom(d, _zoom);
60078 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
60081 function tileCenter(d) {
60082 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60084 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
60087 function debugTransform(d) {
60088 var coord = tileCenter(d);
60089 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
60090 } // Pick a representative tile near the center of the viewport
60091 // (This is useful for sampling the imagery vintage)
60094 var dims = tiler.size();
60095 var mapCenter = [dims[0] / 2, dims[1] / 2];
60096 var minDist = Math.max(dims[0], dims[1]);
60098 requests.forEach(function (d) {
60099 var c = tileCenter(d);
60100 var dist = geoVecLength(c, mapCenter);
60102 if (dist < minDist) {
60107 var image = selection.selectAll('img').data(requests, function (d) {
60110 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
60111 var tile = select(this);
60112 window.setTimeout(function () {
60113 if (tile.classed('tile-removing')) {
60118 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
60120 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
60121 return d === nearCenter;
60123 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
60126 debug.exit().remove();
60129 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
60130 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
60131 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
60132 debug = debug.merge(debugEnter);
60133 debug.style(transformProp, debugTransform);
60134 debug.selectAll('.tile-label-debug-coord').html(function (d) {
60135 return d[2] + ' / ' + d[0] + ' / ' + d[1];
60137 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
60138 var span = select(this);
60139 var center = context.projection.invert(tileCenter(d));
60141 _source.getMetadata(center, d, function (err, result) {
60142 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
60148 background.projection = function (val) {
60149 if (!arguments.length) return _projection;
60154 background.dimensions = function (val) {
60155 if (!arguments.length) return tiler.size();
60160 background.source = function (val) {
60161 if (!arguments.length) return _source;
60163 _tileSize = _source.tileSize;
60165 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
60172 var _imageryIndex = null;
60173 function rendererBackground(context) {
60174 var dispatch = dispatch$8('change');
60175 var detected = utilDetect();
60176 var baseLayer = rendererTileLayer(context).projection(context.projection);
60177 var _isValid = true;
60178 var _overlayLayers = [];
60179 var _brightness = 1;
60181 var _saturation = 1;
60182 var _sharpness = 1;
60184 function ensureImageryIndex() {
60185 return _mainFileFetcher.get('imagery').then(function (sources) {
60186 if (_imageryIndex) return _imageryIndex;
60190 }; // use which-polygon to support efficient index and querying for imagery
60192 var features = sources.map(function (source) {
60193 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
60194 // Add an extra array nest to each element in `source.polygon`
60195 // so the rings are not treated as a bunch of holes:
60196 // what we have: [ [[outer],[hole],[hole]] ]
60197 // what we want: [ [[outer]],[[outer]],[[outer]] ]
60199 var rings = source.polygon.map(function (ring) {
60208 type: 'MultiPolygon',
60212 _imageryIndex.features[source.id] = feature;
60214 }).filter(Boolean);
60215 _imageryIndex.query = whichPolygon_1({
60216 type: 'FeatureCollection',
60218 }); // Instantiate `rendererBackgroundSource` objects for each source
60220 _imageryIndex.backgrounds = sources.map(function (source) {
60221 if (source.type === 'bing') {
60222 return rendererBackgroundSource.Bing(source, dispatch);
60223 } else if (/^EsriWorldImagery/.test(source.id)) {
60224 return rendererBackgroundSource.Esri(source);
60226 return rendererBackgroundSource(source);
60230 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
60233 var template = corePreferences('background-custom-template') || '';
60234 var custom = rendererBackgroundSource.Custom(template);
60236 _imageryIndex.backgrounds.unshift(custom);
60238 return _imageryIndex;
60242 function background(selection) {
60243 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
60244 // check its tilemap to see how high the zoom can go
60246 if (context.map().zoom() > 18) {
60247 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
60248 var center = context.map().center();
60249 currSource.fetchTilemap(center);
60251 } // Is the imagery valid here? - #4827
60254 var sources = background.sources(context.map().extent());
60255 var wasValid = _isValid;
60256 _isValid = !!sources.filter(function (d) {
60257 return d === currSource;
60260 if (wasValid !== _isValid) {
60261 // change in valid status
60262 background.updateImagery();
60265 var baseFilter = '';
60267 if (detected.cssfilters) {
60268 if (_brightness !== 1) {
60269 baseFilter += " brightness(".concat(_brightness, ")");
60272 if (_contrast !== 1) {
60273 baseFilter += " contrast(".concat(_contrast, ")");
60276 if (_saturation !== 1) {
60277 baseFilter += " saturate(".concat(_saturation, ")");
60280 if (_sharpness < 1) {
60282 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
60283 baseFilter += " blur(".concat(blur, "px)");
60287 var base = selection.selectAll('.layer-background').data([0]);
60288 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
60290 if (detected.cssfilters) {
60291 base.style('filter', baseFilter || null);
60293 base.style('opacity', _brightness);
60296 var imagery = base.selectAll('.layer-imagery').data([0]);
60297 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
60298 var maskFilter = '';
60299 var mixBlendMode = '';
60301 if (detected.cssfilters && _sharpness > 1) {
60302 // apply unsharp mask
60303 mixBlendMode = 'overlay';
60304 maskFilter = 'saturate(0) blur(3px) invert(1)';
60305 var contrast = _sharpness - 1;
60306 maskFilter += " contrast(".concat(contrast, ")");
60307 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
60308 maskFilter += " brightness(".concat(brightness, ")");
60311 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
60312 mask.exit().remove();
60313 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);
60314 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
60315 return d.source().name();
60317 overlays.exit().remove();
60318 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
60319 return select(nodes[i]).call(layer);
60323 background.updateImagery = function () {
60324 var currSource = baseLayer.source();
60325 if (context.inIntro() || !currSource) return;
60327 var o = _overlayLayers.filter(function (d) {
60328 return !d.source().isLocatorOverlay() && !d.source().isHidden();
60329 }).map(function (d) {
60330 return d.source().id;
60333 var meters = geoOffsetToMeters(currSource.offset());
60334 var EPSILON = 0.01;
60335 var x = +meters[0].toFixed(2);
60336 var y = +meters[1].toFixed(2);
60337 var hash = utilStringQs(window.location.hash);
60338 var id = currSource.id;
60340 if (id === 'custom') {
60341 id = "custom:".concat(currSource.template());
60345 hash.background = id;
60347 delete hash.background;
60353 delete hash.overlays;
60356 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
60357 hash.offset = "".concat(x, ",").concat(y);
60359 delete hash.offset;
60362 if (!window.mocha) {
60363 window.location.replace('#' + utilQsString(hash, true));
60366 var imageryUsed = [];
60367 var photoOverlaysUsed = [];
60368 var currUsed = currSource.imageryUsed();
60370 if (currUsed && _isValid) {
60371 imageryUsed.push(currUsed);
60374 _overlayLayers.filter(function (d) {
60375 return !d.source().isLocatorOverlay() && !d.source().isHidden();
60376 }).forEach(function (d) {
60377 return imageryUsed.push(d.source().imageryUsed());
60380 var dataLayer = context.layers().layer('data');
60382 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
60383 imageryUsed.push(dataLayer.getSrc());
60386 var photoOverlayLayers = {
60387 streetside: 'Bing Streetside',
60388 mapillary: 'Mapillary Images',
60389 'mapillary-map-features': 'Mapillary Map Features',
60390 'mapillary-signs': 'Mapillary Signs',
60391 openstreetcam: 'OpenStreetCam Images'
60394 for (var layerID in photoOverlayLayers) {
60395 var layer = context.layers().layer(layerID);
60397 if (layer && layer.enabled()) {
60398 photoOverlaysUsed.push(layerID);
60399 imageryUsed.push(photoOverlayLayers[layerID]);
60403 context.history().imageryUsed(imageryUsed);
60404 context.history().photoOverlaysUsed(photoOverlaysUsed);
60407 var _checkedBlocklists;
60409 background.sources = function (extent, zoom, includeCurrent) {
60410 if (!_imageryIndex) return []; // called before init()?
60413 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
60414 return visible[d.id] = true;
60416 var currSource = baseLayer.source();
60417 var osm = context.connection();
60418 var blocklists = osm && osm.imageryBlocklists();
60420 if (blocklists && blocklists !== _checkedBlocklists) {
60421 _imageryIndex.backgrounds.forEach(function (source) {
60422 source.isBlocked = blocklists.some(function (blocklist) {
60423 return blocklist.test(source.template());
60427 _checkedBlocklists = blocklists;
60430 return _imageryIndex.backgrounds.filter(function (source) {
60431 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
60433 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
60435 if (!source.polygon) return true; // always include imagery with worldwide coverage
60437 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
60439 return visible[source.id]; // include imagery visible in given extent
60443 background.dimensions = function (val) {
60445 baseLayer.dimensions(val);
60447 _overlayLayers.forEach(function (layer) {
60448 return layer.dimensions(val);
60452 background.baseLayerSource = function (d) {
60453 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
60455 var osm = context.connection();
60456 if (!osm) return background;
60457 var blocklists = osm.imageryBlocklists();
60458 var template = d.template();
60463 for (var i = 0; i < blocklists.length; i++) {
60464 regex = blocklists[i];
60465 fail = regex.test(template);
60468 } // ensure at least one test was run.
60472 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
60473 fail = regex.test(template);
60476 baseLayer.source(!fail ? d : background.findSource('none'));
60477 dispatch.call('change');
60478 background.updateImagery();
60482 background.findSource = function (id) {
60483 if (!id || !_imageryIndex) return null; // called before init()?
60485 return _imageryIndex.backgrounds.find(function (d) {
60486 return d.id && d.id === id;
60490 background.bing = function () {
60491 background.baseLayerSource(background.findSource('Bing'));
60494 background.showsLayer = function (d) {
60495 var currSource = baseLayer.source();
60496 if (!d || !currSource) return false;
60497 return d.id === currSource.id || _overlayLayers.some(function (layer) {
60498 return d.id === layer.source().id;
60502 background.overlayLayerSources = function () {
60503 return _overlayLayers.map(function (layer) {
60504 return layer.source();
60508 background.toggleOverlayLayer = function (d) {
60511 for (var i = 0; i < _overlayLayers.length; i++) {
60512 layer = _overlayLayers[i];
60514 if (layer.source() === d) {
60515 _overlayLayers.splice(i, 1);
60517 dispatch.call('change');
60518 background.updateImagery();
60523 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
60525 _overlayLayers.push(layer);
60527 dispatch.call('change');
60528 background.updateImagery();
60531 background.nudge = function (d, zoom) {
60532 var currSource = baseLayer.source();
60535 currSource.nudge(d, zoom);
60536 dispatch.call('change');
60537 background.updateImagery();
60543 background.offset = function (d) {
60544 var currSource = baseLayer.source();
60546 if (!arguments.length) {
60547 return currSource && currSource.offset() || [0, 0];
60551 currSource.offset(d);
60552 dispatch.call('change');
60553 background.updateImagery();
60559 background.brightness = function (d) {
60560 if (!arguments.length) return _brightness;
60562 if (context.mode()) dispatch.call('change');
60566 background.contrast = function (d) {
60567 if (!arguments.length) return _contrast;
60569 if (context.mode()) dispatch.call('change');
60573 background.saturation = function (d) {
60574 if (!arguments.length) return _saturation;
60576 if (context.mode()) dispatch.call('change');
60580 background.sharpness = function (d) {
60581 if (!arguments.length) return _sharpness;
60583 if (context.mode()) dispatch.call('change');
60589 background.ensureLoaded = function () {
60590 if (_loadPromise) return _loadPromise;
60592 function parseMapParams(qmap) {
60593 if (!qmap) return false;
60594 var params = qmap.split('/').map(Number);
60595 if (params.length < 3 || params.some(isNaN)) return false;
60596 return geoExtent([params[2], params[1]]); // lon,lat
60599 var hash = utilStringQs(window.location.hash);
60600 var requested = hash.background || hash.layer;
60601 var extent = parseMapParams(hash.map);
60602 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
60603 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
60606 if (!requested && extent) {
60607 best = background.sources(extent).find(function (s) {
60610 } // Decide which background layer to display
60613 if (requested && requested.indexOf('custom:') === 0) {
60614 var template = requested.replace(/^custom:/, '');
60615 var custom = background.findSource('custom');
60616 background.baseLayerSource(custom.template(template));
60617 corePreferences('background-custom-template', template);
60619 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
60622 var locator = imageryIndex.backgrounds.find(function (d) {
60623 return d.overlay && d["default"];
60627 background.toggleOverlayLayer(locator);
60630 var overlays = (hash.overlays || '').split(',');
60631 overlays.forEach(function (overlay) {
60632 overlay = background.findSource(overlay);
60635 background.toggleOverlayLayer(overlay);
60640 var gpx = context.layers().layer('data');
60643 gpx.url(hash.gpx, '.gpx');
60648 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
60649 return !isNaN(n) && n;
60652 if (offset.length === 2) {
60653 background.offset(geoMetersToOffset(offset));
60656 })["catch"](function () {
60661 return utilRebind(background, dispatch, 'on');
60664 function rendererFeatures(context) {
60665 var dispatch = dispatch$8('change', 'redraw');
60666 var features = utilRebind({}, dispatch, 'on');
60668 var _deferred = new Set();
60670 var traffic_roads = {
60672 'motorway_link': true,
60674 'trunk_link': true,
60676 'primary_link': true,
60678 'secondary_link': true,
60680 'tertiary_link': true,
60681 'residential': true,
60682 'unclassified': true,
60683 'living_street': true
60685 var service_roads = {
60698 var past_futures = {
60700 'construction': true,
60702 'dismantled': true,
60705 'demolished': true,
60706 'obliterated': true
60708 var _cullFactor = 1;
60714 var _forceVisible = {};
60716 function update() {
60717 if (!window.mocha) {
60718 var hash = utilStringQs(window.location.hash);
60719 var disabled = features.disabled();
60721 if (disabled.length) {
60722 hash.disable_features = disabled.join(',');
60724 delete hash.disable_features;
60727 window.location.replace('#' + utilQsString(hash, true));
60728 corePreferences('disabled-features', disabled.join(','));
60731 _hidden = features.hidden();
60732 dispatch.call('change');
60733 dispatch.call('redraw');
60736 function defineRule(k, filter, max) {
60737 var isEnabled = true;
60743 enabled: isEnabled,
60744 // whether the user wants it enabled..
60746 currentMax: max || Infinity,
60747 defaultMax: max || Infinity,
60748 enable: function enable() {
60749 this.enabled = true;
60750 this.currentMax = this.defaultMax;
60752 disable: function disable() {
60753 this.enabled = false;
60754 this.currentMax = 0;
60756 hidden: function hidden() {
60757 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
60759 autoHidden: function autoHidden() {
60760 return this.hidden() && this.currentMax > 0;
60765 defineRule('points', function isPoint(tags, geometry) {
60766 return geometry === 'point';
60768 defineRule('traffic_roads', function isTrafficRoad(tags) {
60769 return traffic_roads[tags.highway];
60771 defineRule('service_roads', function isServiceRoad(tags) {
60772 return service_roads[tags.highway];
60774 defineRule('paths', function isPath(tags) {
60775 return paths[tags.highway];
60777 defineRule('buildings', function isBuilding(tags) {
60778 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
60780 defineRule('building_parts', function isBuildingPart(tags) {
60781 return tags['building:part'];
60783 defineRule('indoor', function isIndoor(tags) {
60784 return tags.indoor;
60786 defineRule('landuse', function isLanduse(tags, geometry) {
60787 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
60789 defineRule('boundaries', function isBoundary(tags) {
60790 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);
60792 defineRule('water', function isWater(tags) {
60793 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';
60795 defineRule('rail', function isRail(tags) {
60796 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
60798 defineRule('pistes', function isPiste(tags) {
60799 return tags['piste:type'];
60801 defineRule('aerialways', function isPiste(tags) {
60802 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
60804 defineRule('power', function isPower(tags) {
60805 return !!tags.power;
60806 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
60808 defineRule('past_future', function isPastFuture(tags) {
60809 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
60813 var strings = Object.keys(tags);
60815 for (var i = 0; i < strings.length; i++) {
60816 var s = strings[i];
60818 if (past_futures[s] || past_futures[tags[s]]) {
60824 }); // Lines or areas that don't match another feature filter.
60825 // IMPORTANT: The 'others' feature must be the last one defined,
60826 // so that code in getMatches can skip this test if `hasMatch = true`
60828 defineRule('others', function isOther(tags, geometry) {
60829 return geometry === 'line' || geometry === 'area';
60832 features.features = function () {
60836 features.keys = function () {
60840 features.enabled = function (k) {
60841 if (!arguments.length) {
60842 return _keys.filter(function (k) {
60843 return _rules[k].enabled;
60847 return _rules[k] && _rules[k].enabled;
60850 features.disabled = function (k) {
60851 if (!arguments.length) {
60852 return _keys.filter(function (k) {
60853 return !_rules[k].enabled;
60857 return _rules[k] && !_rules[k].enabled;
60860 features.hidden = function (k) {
60861 if (!arguments.length) {
60862 return _keys.filter(function (k) {
60863 return _rules[k].hidden();
60867 return _rules[k] && _rules[k].hidden();
60870 features.autoHidden = function (k) {
60871 if (!arguments.length) {
60872 return _keys.filter(function (k) {
60873 return _rules[k].autoHidden();
60877 return _rules[k] && _rules[k].autoHidden();
60880 features.enable = function (k) {
60881 if (_rules[k] && !_rules[k].enabled) {
60882 _rules[k].enable();
60888 features.enableAll = function () {
60889 var didEnable = false;
60891 for (var k in _rules) {
60892 if (!_rules[k].enabled) {
60895 _rules[k].enable();
60899 if (didEnable) update();
60902 features.disable = function (k) {
60903 if (_rules[k] && _rules[k].enabled) {
60904 _rules[k].disable();
60910 features.disableAll = function () {
60911 var didDisable = false;
60913 for (var k in _rules) {
60914 if (_rules[k].enabled) {
60917 _rules[k].disable();
60921 if (didDisable) update();
60924 features.toggle = function (k) {
60927 return f.enabled ? f.disable() : f.enable();
60934 features.resetStats = function () {
60935 for (var i = 0; i < _keys.length; i++) {
60936 _rules[_keys[i]].count = 0;
60939 dispatch.call('change');
60942 features.gatherStats = function (d, resolver, dimensions) {
60943 var needsRedraw = false;
60944 var types = utilArrayGroupBy(d, 'type');
60945 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
60946 var currHidden, geometry, matches, i, j;
60948 for (i = 0; i < _keys.length; i++) {
60949 _rules[_keys[i]].count = 0;
60950 } // adjust the threshold for point/building culling based on viewport size..
60951 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
60954 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
60956 for (i = 0; i < entities.length; i++) {
60957 geometry = entities[i].geometry(resolver);
60958 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
60960 for (j = 0; j < matches.length; j++) {
60961 _rules[matches[j]].count++;
60965 currHidden = features.hidden();
60967 if (currHidden !== _hidden) {
60968 _hidden = currHidden;
60969 needsRedraw = true;
60970 dispatch.call('change');
60973 return needsRedraw;
60976 features.stats = function () {
60977 for (var i = 0; i < _keys.length; i++) {
60978 _stats[_keys[i]] = _rules[_keys[i]].count;
60984 features.clear = function (d) {
60985 for (var i = 0; i < d.length; i++) {
60986 features.clearEntity(d[i]);
60990 features.clearEntity = function (entity) {
60991 delete _cache[osmEntity.key(entity)];
60994 features.reset = function () {
60995 Array.from(_deferred).forEach(function (handle) {
60996 window.cancelIdleCallback(handle);
60998 _deferred["delete"](handle);
61001 }; // only certain relations are worth checking
61004 function relationShouldBeChecked(relation) {
61005 // multipolygon features have `area` geometry and aren't checked here
61006 return relation.tags.type === 'boundary';
61009 features.getMatches = function (entity, resolver, geometry) {
61010 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
61011 var ent = osmEntity.key(entity);
61013 if (!_cache[ent]) {
61017 if (!_cache[ent].matches) {
61019 var hasMatch = false;
61021 for (var i = 0; i < _keys.length; i++) {
61022 if (_keys[i] === 'others') {
61023 if (hasMatch) continue; // If an entity...
61024 // 1. is a way that hasn't matched other 'interesting' feature rules,
61026 if (entity.type === 'way') {
61027 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
61029 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
61030 parents.length > 0 && parents.every(function (parent) {
61031 return parent.tags.type === 'boundary';
61033 // ...then match whatever feature rules the parent relation has matched.
61034 // see #2548, #2887
61037 // For this to work, getMatches must be called on relations before ways.
61039 var pkey = osmEntity.key(parents[0]);
61041 if (_cache[pkey] && _cache[pkey].matches) {
61042 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
61050 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
61051 matches[_keys[i]] = hasMatch = true;
61055 _cache[ent].matches = matches;
61058 return _cache[ent].matches;
61061 features.getParents = function (entity, resolver, geometry) {
61062 if (geometry === 'point') return [];
61063 var ent = osmEntity.key(entity);
61065 if (!_cache[ent]) {
61069 if (!_cache[ent].parents) {
61072 if (geometry === 'vertex') {
61073 parents = resolver.parentWays(entity);
61075 // 'line', 'area', 'relation'
61076 parents = resolver.parentRelations(entity);
61079 _cache[ent].parents = parents;
61082 return _cache[ent].parents;
61085 features.isHiddenPreset = function (preset, geometry) {
61086 if (!_hidden.length) return false;
61087 if (!preset.tags) return false;
61088 var test = preset.setTags({}, geometry);
61090 for (var key in _rules) {
61091 if (_rules[key].filter(test, geometry)) {
61092 if (_hidden.indexOf(key) !== -1) {
61103 features.isHiddenFeature = function (entity, resolver, geometry) {
61104 if (!_hidden.length) return false;
61105 if (!entity.version) return false;
61106 if (_forceVisible[entity.id]) return false;
61107 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
61108 return matches.length && matches.every(function (k) {
61109 return features.hidden(k);
61113 features.isHiddenChild = function (entity, resolver, geometry) {
61114 if (!_hidden.length) return false;
61115 if (!entity.version || geometry === 'point') return false;
61116 if (_forceVisible[entity.id]) return false;
61117 var parents = features.getParents(entity, resolver, geometry);
61118 if (!parents.length) return false;
61120 for (var i = 0; i < parents.length; i++) {
61121 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
61129 features.hasHiddenConnections = function (entity, resolver) {
61130 if (!_hidden.length) return false;
61131 var childNodes, connections;
61133 if (entity.type === 'midpoint') {
61134 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
61137 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
61138 connections = features.getParents(entity, resolver, entity.geometry(resolver));
61139 } // gather ways connected to child nodes..
61142 connections = childNodes.reduce(function (result, e) {
61143 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
61145 return connections.some(function (e) {
61146 return features.isHidden(e, resolver, e.geometry(resolver));
61150 features.isHidden = function (entity, resolver, geometry) {
61151 if (!_hidden.length) return false;
61152 if (!entity.version) return false;
61153 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
61154 return fn(entity, resolver, geometry);
61157 features.filter = function (d, resolver) {
61158 if (!_hidden.length) return d;
61161 for (var i = 0; i < d.length; i++) {
61164 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
61165 result.push(entity);
61172 features.forceVisible = function (entityIDs) {
61173 if (!arguments.length) return Object.keys(_forceVisible);
61174 _forceVisible = {};
61176 for (var i = 0; i < entityIDs.length; i++) {
61177 _forceVisible[entityIDs[i]] = true;
61178 var entity = context.hasEntity(entityIDs[i]);
61180 if (entity && entity.type === 'relation') {
61181 // also show relation members (one level deep)
61182 for (var j in entity.members) {
61183 _forceVisible[entity.members[j].id] = true;
61191 features.init = function () {
61192 var storage = corePreferences('disabled-features');
61195 var storageDisabled = storage.replace(/;/g, ',').split(',');
61196 storageDisabled.forEach(features.disable);
61199 var hash = utilStringQs(window.location.hash);
61201 if (hash.disable_features) {
61202 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
61203 hashDisabled.forEach(features.disable);
61205 }; // warm up the feature matching cache upon merging fetched data
61208 context.history().on('merge.features', function (newEntities) {
61209 if (!newEntities) return;
61210 var handle = window.requestIdleCallback(function () {
61211 var graph = context.graph();
61212 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
61214 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
61216 for (var i = 0; i < entities.length; i++) {
61217 var geometry = entities[i].geometry(graph);
61218 features.getMatches(entities[i], graph, geometry);
61222 _deferred.add(handle);
61227 /** Error message constants. */
61229 var FUNC_ERROR_TEXT = 'Expected a function';
61231 * Creates a throttled function that only invokes `func` at most once per
61232 * every `wait` milliseconds. The throttled function comes with a `cancel`
61233 * method to cancel delayed `func` invocations and a `flush` method to
61234 * immediately invoke them. Provide `options` to indicate whether `func`
61235 * should be invoked on the leading and/or trailing edge of the `wait`
61236 * timeout. The `func` is invoked with the last arguments provided to the
61237 * throttled function. Subsequent calls to the throttled function return the
61238 * result of the last `func` invocation.
61240 * **Note:** If `leading` and `trailing` options are `true`, `func` is
61241 * invoked on the trailing edge of the timeout only if the throttled function
61242 * is invoked more than once during the `wait` timeout.
61244 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
61245 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
61247 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
61248 * for details over the differences between `_.throttle` and `_.debounce`.
61253 * @category Function
61254 * @param {Function} func The function to throttle.
61255 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
61256 * @param {Object} [options={}] The options object.
61257 * @param {boolean} [options.leading=true]
61258 * Specify invoking on the leading edge of the timeout.
61259 * @param {boolean} [options.trailing=true]
61260 * Specify invoking on the trailing edge of the timeout.
61261 * @returns {Function} Returns the new throttled function.
61264 * // Avoid excessively updating the position while scrolling.
61265 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
61267 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
61268 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
61269 * jQuery(element).on('click', throttled);
61271 * // Cancel the trailing throttled invocation.
61272 * jQuery(window).on('popstate', throttled.cancel);
61275 function throttle(func, wait, options) {
61276 var leading = true,
61279 if (typeof func != 'function') {
61280 throw new TypeError(FUNC_ERROR_TEXT);
61283 if (isObject$2(options)) {
61284 leading = 'leading' in options ? !!options.leading : leading;
61285 trailing = 'trailing' in options ? !!options.trailing : trailing;
61288 return debounce(func, wait, {
61289 'leading': leading,
61291 'trailing': trailing
61296 // - the activeID - nope
61297 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
61298 // - 2 away from the activeID - nope (would create a self intersecting segment)
61299 // - all others on a linear way - yes
61300 // - all others on a closed way - nope (would create a self intersecting polygon)
61303 // 0 = active vertex - no touch/connect
61304 // 1 = passive vertex - yes touch/connect
61305 // 2 = adjacent vertex - yes but pay attention segmenting a line here
61308 function svgPassiveVertex(node, graph, activeID) {
61309 if (!activeID) return 1;
61310 if (activeID === node.id) return 0;
61311 var parents = graph.parentWays(node);
61312 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
61314 for (i = 0; i < parents.length; i++) {
61315 nodes = parents[i].nodes;
61316 isClosed = parents[i].isClosed();
61318 for (j = 0; j < nodes.length; j++) {
61319 // find this vertex, look nearby
61320 if (nodes[j] === node.id) {
61327 // wraparound if needed
61328 max = nodes.length - 1;
61329 if (ix1 < 0) ix1 = max + ix1;
61330 if (ix2 < 0) ix2 = max + ix2;
61331 if (ix3 > max) ix3 = ix3 - max;
61332 if (ix4 > max) ix4 = ix4 - max;
61335 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
61336 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
61337 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
61338 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
61339 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
61346 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
61347 return function (entity) {
61351 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
61352 var coordinates = graph.childNodes(entity).map(function (n) {
61357 if (shouldReverse(entity)) {
61358 coordinates.reverse();
61362 type: 'LineString',
61363 coordinates: coordinates
61364 }, projection.stream(clip({
61365 lineStart: function lineStart() {},
61366 lineEnd: function lineEnd() {
61369 point: function point(x, y) {
61373 var span = geoVecLength(a, b) - offset;
61376 var heading = geoVecAngle(a, b);
61377 var dx = dt * Math.cos(heading);
61378 var dy = dt * Math.sin(heading);
61379 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
61381 var coord = [a, p];
61383 for (span -= dt; span >= 0; span -= dt) {
61384 p = geoVecAdd(p, [dx, dy]);
61388 coord.push(b); // generate svg paths
61393 for (j = 0; j < coord.length; j++) {
61394 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61403 if (bothDirections(entity)) {
61406 for (j = coord.length - 1; j >= 0; j--) {
61407 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61427 function svgPath(projection, graph, isArea) {
61428 // Explanation of magic numbers:
61429 // "padding" here allows space for strokes to extend beyond the viewport,
61430 // so that the stroke isn't drawn along the edge of the viewport when
61431 // the shape is clipped.
61433 // When drawing lines, pad viewport by 5px.
61434 // When drawing areas, pad viewport by 65px in each direction to allow
61435 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
61437 var padding = isArea ? 65 : 5;
61438 var viewport = projection.clipExtent();
61439 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
61440 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
61441 var project = projection.stream;
61442 var path = d3_geoPath().projection({
61443 stream: function stream(output) {
61444 return project(clip(output));
61448 var svgpath = function svgpath(entity) {
61449 if (entity.id in cache) {
61450 return cache[entity.id];
61452 return cache[entity.id] = path(entity.asGeoJSON(graph));
61456 svgpath.geojson = function (d) {
61457 if (d.__featurehash__ !== undefined) {
61458 if (d.__featurehash__ in cache) {
61459 return cache[d.__featurehash__];
61461 return cache[d.__featurehash__] = path(d);
61470 function svgPointTransform(projection) {
61471 var svgpoint = function svgpoint(entity) {
61472 // http://jsperf.com/short-array-join
61473 var pt = projection(entity.loc);
61474 return 'translate(' + pt[0] + ',' + pt[1] + ')';
61477 svgpoint.geojson = function (d) {
61478 return svgpoint(d.properties.entity);
61483 function svgRelationMemberTags(graph) {
61484 return function (entity) {
61485 var tags = entity.tags;
61486 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
61487 graph.parentRelations(entity).forEach(function (relation) {
61488 var type = relation.tags.type;
61490 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
61491 tags = Object.assign({}, relation.tags, tags);
61497 function svgSegmentWay(way, graph, activeID) {
61498 // When there is no activeID, we can memoize this expensive computation
61499 if (activeID === undefined) {
61500 return graph["transient"](way, 'waySegments', getWaySegments);
61502 return getWaySegments();
61505 function getWaySegments() {
61506 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
61515 for (var i = 0; i < way.nodes.length; i++) {
61516 node = graph.entity(way.nodes[i]);
61517 type = svgPassiveVertex(node, graph, activeID);
61523 if (start.type !== undefined) {
61524 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
61525 // one adjacent vertex
61526 pushActive(start, end, i);
61527 } else if (start.type === 0 && end.type === 0) {
61528 // both active vertices
61529 pushActive(start, end, i);
61531 pushPassive(start, end, i);
61540 function pushActive(start, end, index) {
61541 features.active.push({
61543 id: way.id + '-' + index + '-nope',
61548 nodes: [start.node, end.node],
61552 type: 'LineString',
61553 coordinates: [start.node.loc, end.node.loc]
61558 function pushPassive(start, end, index) {
61559 features.passive.push({
61561 id: way.id + '-' + index,
61565 nodes: [start.node, end.node],
61569 type: 'LineString',
61570 coordinates: [start.node.loc, end.node.loc]
61577 function svgTagClasses() {
61578 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'];
61579 var statuses = [// nonexistent, might be built
61580 'proposed', 'planned', // under maintentance or between groundbreaking and opening
61581 'construction', // existent but not functional
61582 'disused', // dilapidated to nonexistent
61583 'abandoned', // nonexistent, still may appear in imagery
61584 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
61586 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
61588 var _tags = function _tags(entity) {
61589 return entity.tags;
61592 var tagClasses = function tagClasses(selection) {
61593 selection.each(function tagClassesEach(entity) {
61594 var value = this.className;
61596 if (value.baseVal !== undefined) {
61597 value = value.baseVal;
61600 var t = _tags(entity);
61602 var computed = tagClasses.getClassesString(t, value);
61604 if (computed !== value) {
61605 select(this).attr('class', computed);
61610 tagClasses.getClassesString = function (t, value) {
61611 var primary, status;
61612 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
61614 var overrideGeometry;
61616 if (/\bstroke\b/.test(value)) {
61617 if (!!t.barrier && t.barrier !== 'no') {
61618 overrideGeometry = 'line';
61620 } // preserve base classes (nothing with `tag-`)
61623 var classes = value.trim().split(/\s+/).filter(function (klass) {
61624 return klass.length && !/^tag-/.test(klass);
61625 }).map(function (klass) {
61626 // special overrides for some perimeter strokes
61627 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
61628 }); // pick at most one primary classification tag..
61630 for (i = 0; i < primaries.length; i++) {
61633 if (!v || v === 'no') continue;
61635 if (k === 'piste:type') {
61636 // avoid a ':' in the class name
61638 } else if (k === 'building:part') {
61639 // avoid a ':' in the class name
61640 k = 'building_part';
61645 if (statuses.indexOf(v) !== -1) {
61646 // e.g. `railway=abandoned`
61648 classes.push('tag-' + k);
61650 classes.push('tag-' + k);
61651 classes.push('tag-' + k + '-' + v);
61658 for (i = 0; i < statuses.length; i++) {
61659 for (j = 0; j < primaries.length; j++) {
61660 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
61663 if (!v || v === 'no') continue;
61664 status = statuses[i];
61668 } // add at most one status tag, only if relates to primary tag..
61672 for (i = 0; i < statuses.length; i++) {
61675 if (!v || v === 'no') continue;
61678 // e.g. `railway=rail + abandoned=yes`
61680 } else if (primary && primary === v) {
61681 // e.g. `railway=rail + abandoned=railway`
61683 } else if (!primary && primaries.indexOf(v) !== -1) {
61684 // e.g. `abandoned=railway`
61687 classes.push('tag-' + v);
61688 } // else ignore e.g. `highway=path + abandoned=railway`
61696 classes.push('tag-status');
61697 classes.push('tag-status-' + status);
61698 } // add any secondary tags
61701 for (i = 0; i < secondaries.length; i++) {
61702 k = secondaries[i];
61704 if (!v || v === 'no' || k === primary) continue;
61705 classes.push('tag-' + k);
61706 classes.push('tag-' + k + '-' + v);
61707 } // For highways, look for surface tagging..
61710 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
61711 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
61716 if (k in osmPavedTags) {
61717 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
61720 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
61721 surface = 'semipaved';
61725 classes.push('tag-' + surface);
61726 } // If this is a wikidata-tagged item, add a class for that..
61729 var qid = t.wikidata || t['flag:wikidata'] || t['brand:wikidata'] || t['network:wikidata'] || t['operator:wikidata'];
61732 classes.push('tag-wikidata');
61735 return classes.join(' ').trim();
61738 tagClasses.tags = function (val) {
61739 if (!arguments.length) return _tags;
61747 // Patterns only work in Firefox when set directly on element.
61748 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
61750 // tag - pattern name
61752 // tag - value - pattern name
61754 // tag - value - rules (optional tag-values, pattern name)
61755 // (matches earlier rules first, so fallback should be last entry)
61757 grave_yard: 'cemetery',
61758 fountain: 'water_standing'
61762 religion: 'christian',
61763 pattern: 'cemetery_christian'
61765 religion: 'buddhist',
61766 pattern: 'cemetery_buddhist'
61768 religion: 'muslim',
61769 pattern: 'cemetery_muslim'
61771 religion: 'jewish',
61772 pattern: 'cemetery_jewish'
61774 pattern: 'cemetery'
61776 construction: 'construction',
61777 farmland: 'farmland',
61778 farmyard: 'farmyard',
61780 leaf_type: 'broadleaved',
61781 pattern: 'forest_broadleaved'
61783 leaf_type: 'needleleaved',
61784 pattern: 'forest_needleleaved'
61786 leaf_type: 'leafless',
61787 pattern: 'forest_leafless'
61790 } // same as 'leaf_type:mixed'
61792 grave_yard: 'cemetery',
61795 pattern: 'golf_green'
61799 landfill: 'landfill',
61801 military: 'construction',
61802 orchard: 'orchard',
61804 vineyard: 'vineyard'
61808 grassland: 'grass',
61815 water: 'reservoir',
61816 pattern: 'water_standing'
61822 pattern: 'wetland_marsh'
61825 pattern: 'wetland_swamp'
61828 pattern: 'wetland_bog'
61830 wetland: 'reedbed',
61831 pattern: 'wetland_reedbed'
61836 leaf_type: 'broadleaved',
61837 pattern: 'forest_broadleaved'
61839 leaf_type: 'needleleaved',
61840 pattern: 'forest_needleleaved'
61842 leaf_type: 'leafless',
61843 pattern: 'forest_leafless'
61846 } // same as 'leaf_type:mixed'
61864 function svgTagPattern(tags) {
61865 // Skip pattern filling if this is a building (buildings don't get patterns applied)
61866 if (tags.building && tags.building !== 'no') {
61870 for (var tag in patterns) {
61871 var entityValue = tags[tag];
61872 if (!entityValue) continue;
61874 if (typeof patterns[tag] === 'string') {
61875 // extra short syntax (just tag) - pattern name
61876 return 'pattern-' + patterns[tag];
61878 var values = patterns[tag];
61880 for (var value in values) {
61881 if (entityValue !== value) continue;
61882 var rules = values[value];
61884 if (typeof rules === 'string') {
61885 // short syntax - pattern name
61886 return 'pattern-' + rules;
61887 } // long syntax - rule array
61890 for (var ruleKey in rules) {
61891 var rule = rules[ruleKey];
61894 for (var criterion in rule) {
61895 if (criterion !== 'pattern') {
61896 // reserved for pattern name
61897 // The only rule is a required tag-value pair
61898 var v = tags[criterion];
61900 if (!v || v !== rule[criterion]) {
61908 return 'pattern-' + rule.pattern;
61918 function svgAreas(projection, context) {
61919 function getPatternStyle(tags) {
61920 var imageID = svgTagPattern(tags);
61923 return 'url("#ideditor-' + imageID + '")';
61929 function drawTargets(selection, graph, entities, filter) {
61930 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
61931 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
61932 var getPath = svgPath(projection).geojson;
61933 var activeID = context.activeID();
61934 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
61940 entities.forEach(function (way) {
61941 var features = svgSegmentWay(way, graph, activeID);
61942 data.targets.push.apply(data.targets, features.passive);
61943 data.nopes.push.apply(data.nopes, features.active);
61944 }); // Targets allow hover and vertex snapping
61946 var targetData = data.targets.filter(getPath);
61947 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
61948 return filter(d.properties.entity);
61949 }).data(targetData, function key(d) {
61953 targets.exit().remove();
61955 var segmentWasEdited = function segmentWasEdited(d) {
61956 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
61958 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
61962 return d.properties.nodes.some(function (n) {
61963 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
61968 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
61969 return 'way area target target-allowed ' + targetClass + d.id;
61970 }).classed('segment-edited', segmentWasEdited); // NOPE
61972 var nopeData = data.nopes.filter(getPath);
61973 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
61974 return filter(d.properties.entity);
61975 }).data(nopeData, function key(d) {
61979 nopes.exit().remove(); // enter/update
61981 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
61982 return 'way area target target-nope ' + nopeClass + d.id;
61983 }).classed('segment-edited', segmentWasEdited);
61986 function drawAreas(selection, graph, entities, filter) {
61987 var path = svgPath(projection, graph, true);
61990 var base = context.history().base();
61992 for (var i = 0; i < entities.length; i++) {
61993 var entity = entities[i];
61994 if (entity.geometry(graph) !== 'area') continue;
61995 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
61997 if (multipolygon) {
61998 areas[multipolygon.id] = {
61999 entity: multipolygon.mergeTags(entity.tags),
62000 area: Math.abs(entity.area(graph))
62002 } else if (!areas[entity.id]) {
62003 areas[entity.id] = {
62005 area: Math.abs(entity.area(graph))
62010 var fills = Object.values(areas).filter(function hasPath(a) {
62011 return path(a.entity);
62013 fills.sort(function areaSort(a, b) {
62014 return b.area - a.area;
62016 fills = fills.map(function (a) {
62019 var strokes = fills.filter(function (area) {
62020 return area.type === 'way';
62028 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
62029 clipPaths.exit().remove();
62030 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
62031 return 'ideditor-' + entity.id + '-clippath';
62033 clipPathsEnter.append('path');
62034 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
62035 var drawLayer = selection.selectAll('.layer-osm.areas');
62036 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
62038 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
62039 areagroup = areagroup.enter().append('g').attr('class', function (d) {
62040 return 'areagroup area-' + d;
62041 }).merge(areagroup);
62042 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
62043 return data[layer];
62045 paths.exit().remove();
62046 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
62047 var bisect = d3_bisector(function (node) {
62048 return -node.__data__.area(graph);
62051 function sortedByArea(entity) {
62052 if (this._parent.__data__ === 'fill') {
62053 return fillpaths[bisect(fillpaths, -entity.area(graph))];
62057 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
62058 var layer = this.parentNode.__data__;
62059 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
62061 if (layer === 'fill') {
62062 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
62063 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
62065 }).classed('added', function (d) {
62066 return !base.entities[d.id];
62067 }).classed('geometry-edited', function (d) {
62068 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
62069 }).classed('retagged', function (d) {
62070 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
62071 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
62073 touchLayer.call(drawTargets, graph, data.stroke, filter);
62079 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
62080 if (!opts) opts = {};
62081 if (typeof opts === 'function') opts = {
62084 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
62086 var cmp = opts.cmp && function (f) {
62087 return function (node) {
62088 return function (a, b) {
62097 return f(aobj, bobj);
62103 return function stringify(node) {
62104 if (node && node.toJSON && typeof node.toJSON === 'function') {
62105 node = node.toJSON();
62108 if (node === undefined) return;
62109 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
62110 if (_typeof(node) !== 'object') return JSON.stringify(node);
62113 if (Array.isArray(node)) {
62116 for (i = 0; i < node.length; i++) {
62118 out += stringify(node[i]) || 'null';
62124 if (node === null) return 'null';
62126 if (seen.indexOf(node) !== -1) {
62127 if (cycles) return JSON.stringify('__cycle__');
62128 throw new TypeError('Converting circular structure to JSON');
62131 var seenIndex = seen.push(node) - 1;
62132 var keys = Object.keys(node).sort(cmp && cmp(node));
62135 for (i = 0; i < keys.length; i++) {
62137 var value = stringify(node[key]);
62138 if (!value) continue;
62139 if (out) out += ',';
62140 out += JSON.stringify(key) + ':' + value;
62143 seen.splice(seenIndex, 1);
62144 return '{' + out + '}';
62148 //[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]
62149 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
62150 //[5] Name ::= NameStartChar (NameChar)*
62151 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
62153 var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
62154 var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
62155 //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(',')
62156 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
62157 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
62159 var S_TAG = 0; //tag name offerring
62161 var S_ATTR = 1; //attr name offerring
62163 var S_ATTR_SPACE = 2; //attr name end and space offer
62165 var S_EQ = 3; //=space?
62167 var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
62169 var S_ATTR_END = 5; //attr value end and no space(quot end)
62171 var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
62173 var S_TAG_CLOSE = 7; //closed el<el />
62175 function XMLReader() {}
62177 XMLReader.prototype = {
62178 parse: function parse(source, defaultNSMap, entityMap) {
62179 var domBuilder = this.domBuilder;
62180 domBuilder.startDocument();
62182 _copy(defaultNSMap, defaultNSMap = {});
62184 _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
62186 domBuilder.endDocument();
62190 function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
62191 function fixedFromCharCode(code) {
62192 // String.prototype.fromCharCode does not supports
62193 // > 2 bytes unicode chars directly
62194 if (code > 0xffff) {
62196 var surrogate1 = 0xd800 + (code >> 10),
62197 surrogate2 = 0xdc00 + (code & 0x3ff);
62198 return String.fromCharCode(surrogate1, surrogate2);
62200 return String.fromCharCode(code);
62204 function entityReplacer(a) {
62205 var k = a.slice(1, -1);
62207 if (k in entityMap) {
62208 return entityMap[k];
62209 } else if (k.charAt(0) === '#') {
62210 return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
62212 errorHandler.error('entity not found:' + a);
62217 function appendText(end) {
62220 var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
62221 locator && position(start);
62222 domBuilder.characters(xt, 0, end - start);
62227 function position(p, m) {
62228 while (p >= lineEnd && (m = linePattern.exec(source))) {
62229 lineStart = m.index;
62230 lineEnd = lineStart + m[0].length;
62231 locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
62234 locator.columnNumber = p - lineStart + 1;
62239 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
62240 var locator = domBuilder.locator;
62241 var parseStack = [{
62242 currentNSMap: defaultNSMapCopy
62249 var tagStart = source.indexOf('<', start);
62251 if (tagStart < 0) {
62252 if (!source.substr(start).match(/^\s*$/)) {
62253 var doc = domBuilder.doc;
62254 var text = doc.createTextNode(source.substr(start));
62255 doc.appendChild(text);
62256 domBuilder.currentElement = text;
62262 if (tagStart > start) {
62263 appendText(tagStart);
62266 switch (source.charAt(tagStart + 1)) {
62268 var end = source.indexOf('>', tagStart + 3);
62269 var tagName = source.substring(tagStart + 2, end);
62270 var config = parseStack.pop();
62273 tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
62275 errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
62276 end = tagStart + 1 + tagName.length;
62277 } else if (tagName.match(/\s</)) {
62278 tagName = tagName.replace(/[\s<].*/, '');
62279 errorHandler.error("end tag name: " + tagName + ' maybe not complete');
62280 end = tagStart + 1 + tagName.length;
62281 } //console.error(parseStack.length,parseStack)
62282 //console.error(config);
62285 var localNSMap = config.localNSMap;
62286 var endMatch = config.tagName == tagName;
62287 var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
62289 if (endIgnoreCaseMach) {
62290 domBuilder.endElement(config.uri, config.localName, tagName);
62293 for (var prefix in localNSMap) {
62294 domBuilder.endPrefixMapping(prefix);
62299 errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
62302 parseStack.push(config);
62311 locator && position(tagStart);
62312 end = parseInstruction(source, tagStart, domBuilder);
62316 // <!doctype,<![CDATA,<!--
62317 locator && position(tagStart);
62318 end = parseDCC(source, tagStart, domBuilder, errorHandler);
62322 locator && position(tagStart);
62323 var el = new ElementAttributes();
62324 var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
62326 var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
62327 var len = el.length;
62329 if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
62332 if (!entityMap.nbsp) {
62333 errorHandler.warning('unclosed xml attribute');
62337 if (locator && len) {
62338 var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
62340 for (var i = 0; i < len; i++) {
62342 position(a.offset);
62343 a.locator = copyLocator(locator, {});
62344 } //}catch(e){console.error('@@@@@'+e)}
62347 domBuilder.locator = locator2;
62349 if (appendElement(el, domBuilder, currentNSMap)) {
62350 parseStack.push(el);
62353 domBuilder.locator = locator;
62355 if (appendElement(el, domBuilder, currentNSMap)) {
62356 parseStack.push(el);
62360 if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
62361 end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
62368 errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
62370 end = -1; //throw e;
62376 //TODO: 这里有可能sax回退,有位置错误风险
62377 appendText(Math.max(tagStart, start) + 1);
62382 function copyLocator(f, t) {
62383 t.lineNumber = f.lineNumber;
62384 t.columnNumber = f.columnNumber;
62388 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
62389 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
62393 function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
62397 var s = S_TAG; //status
62400 var c = source.charAt(p);
62404 if (s === S_ATTR) {
62406 attrName = source.slice(start, p);
62408 } else if (s === S_ATTR_SPACE) {
62411 //fatalError: equal must after attrName or space after attrName
62412 throw new Error('attribute equal must after attrName');
62419 if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
62422 if (s === S_ATTR) {
62423 errorHandler.warning('attribute value must after "="');
62424 attrName = source.slice(start, p);
62428 p = source.indexOf(c, start);
62431 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
62432 el.add(attrName, value, start - 1);
62435 //fatalError: no end quot match
62436 throw new Error('attribute value no end \'' + c + '\' match');
62438 } else if (s == S_ATTR_NOQUOT_VALUE) {
62439 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
62441 el.add(attrName, value, start); //console.dir(el)
62443 errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
62447 //fatalError: no equal before
62448 throw new Error('attribute value must after "="');
62456 el.setTagName(source.slice(start, p));
62464 case S_ATTR_NOQUOT_VALUE:
62471 throw new Error("attribute invalid close char('/')");
62478 //throw new Error('unexpected end of input')
62479 errorHandler.error('unexpected end of input');
62482 el.setTagName(source.slice(start, p));
62490 el.setTagName(source.slice(start, p));
62498 case S_ATTR_NOQUOT_VALUE: //Compatible state
62501 value = source.slice(start, p);
62503 if (value.slice(-1) === '/') {
62505 value = value.slice(0, -1);
62509 if (s === S_ATTR_SPACE) {
62513 if (s == S_ATTR_NOQUOT_VALUE) {
62514 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
62515 el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
62517 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
62518 errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
62521 el.add(value, value, start);
62527 throw new Error('attribute value missed!!');
62528 } // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
62533 /*xml space '\x20' | #x9 | #xD | #xA; */
62543 el.setTagName(source.slice(start, p)); //tagName
62549 attrName = source.slice(start, p);
62553 case S_ATTR_NOQUOT_VALUE:
62554 var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
62555 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
62556 el.add(attrName, value, start);
62561 //case S_TAG_SPACE:
62563 //case S_ATTR_SPACE:
62565 //case S_TAG_CLOSE:
62570 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
62571 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
62573 //case S_TAG:void();break;
62574 //case S_ATTR:void();break;
62575 //case S_ATTR_NOQUOT_VALUE:void();break;
62579 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
62580 errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
62583 el.add(attrName, attrName, start);
62589 errorHandler.warning('attribute space is required"' + attrName + '"!!');
62597 s = S_ATTR_NOQUOT_VALUE;
62602 throw new Error("elements closed character '/' and '>' must be connected to");
62606 } //end outer switch
62607 //console.log('p++',p)
62614 * @return true if has new namespace define
62618 function appendElement(el, domBuilder, currentNSMap) {
62619 var tagName = el.tagName;
62620 var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
62626 var qName = a.qName;
62627 var value = a.value;
62628 var nsp = qName.indexOf(':');
62631 var prefix = a.prefix = qName.slice(0, nsp);
62632 var localName = qName.slice(nsp + 1);
62633 var nsPrefix = prefix === 'xmlns' && localName;
62637 nsPrefix = qName === 'xmlns' && '';
62638 } //can not set prefix,because prefix !== ''
62641 a.localName = localName; //prefix == null for no ns prefix attribute
62643 if (nsPrefix !== false) {
62645 if (localNSMap == null) {
62646 localNSMap = {}; //console.log(currentNSMap,0)
62648 _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
62652 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
62653 a.uri = 'http://www.w3.org/2000/xmlns/';
62654 domBuilder.startPrefixMapping(nsPrefix, value);
62662 var prefix = a.prefix;
62665 //no prefix attribute has no namespace
62666 if (prefix === 'xml') {
62667 a.uri = 'http://www.w3.org/XML/1998/namespace';
62670 if (prefix !== 'xmlns') {
62671 a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
62676 var nsp = tagName.indexOf(':');
62679 prefix = el.prefix = tagName.slice(0, nsp);
62680 localName = el.localName = tagName.slice(nsp + 1);
62682 prefix = null; //important!!
62684 localName = el.localName = tagName;
62685 } //no prefix element has default namespace
62688 var ns = el.uri = currentNSMap[prefix || ''];
62689 domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
62690 //localNSMap = null
62693 domBuilder.endElement(ns, localName, tagName);
62696 for (prefix in localNSMap) {
62697 domBuilder.endPrefixMapping(prefix);
62701 el.currentNSMap = currentNSMap;
62702 el.localNSMap = localNSMap; //parseStack.push(el);
62708 function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
62709 if (/^(?:script|textarea)$/i.test(tagName)) {
62710 var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
62711 var text = source.substring(elStartEnd + 1, elEndStart);
62713 if (/[&<]/.test(text)) {
62714 if (/^script$/i.test(tagName)) {
62715 //if(!/\]\]>/.test(text)){
62716 //lexHandler.startCDATA();
62717 domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
62719 return elEndStart; //}
62720 } //}else{//text area
62723 text = text.replace(/&#?\w+;/g, entityReplacer);
62724 domBuilder.characters(text, 0, text.length);
62725 return elEndStart; //}
62729 return elStartEnd + 1;
62732 function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
62733 //if(tagName in closeMap){
62734 var pos = closeMap[tagName];
62737 //console.log(tagName)
62738 pos = source.lastIndexOf('</' + tagName + '>');
62740 if (pos < elStartEnd) {
62742 pos = source.lastIndexOf('</' + tagName);
62745 closeMap[tagName] = pos;
62748 return pos < elStartEnd; //}
62751 function _copy(source, target) {
62752 for (var n in source) {
62753 target[n] = source[n];
62757 function parseDCC(source, start, domBuilder, errorHandler) {
62758 //sure start with '<!'
62759 var next = source.charAt(start + 2);
62763 if (source.charAt(start + 3) === '-') {
62764 var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
62767 domBuilder.comment(source, start + 4, end - start - 4);
62770 errorHandler.error("Unclosed comment");
62779 if (source.substr(start + 3, 6) == 'CDATA[') {
62780 var end = source.indexOf(']]>', start + 9);
62781 domBuilder.startCDATA();
62782 domBuilder.characters(source, start + 9, end - start - 9);
62783 domBuilder.endCDATA();
62786 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
62789 var matchs = split(source, start);
62790 var len = matchs.length;
62792 if (len > 1 && /!doctype/i.test(matchs[0][0])) {
62793 var name = matchs[1][0];
62794 var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
62795 var sysid = len > 4 && matchs[4][0];
62796 var lastMatch = matchs[len - 1];
62797 domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
62798 domBuilder.endDTD();
62799 return lastMatch.index + lastMatch[0].length;
62807 function parseInstruction(source, start, domBuilder) {
62808 var end = source.indexOf('?>', start);
62811 var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
62815 domBuilder.processingInstruction(match[1], match[2]);
62830 function ElementAttributes(source) {}
62832 ElementAttributes.prototype = {
62833 setTagName: function setTagName(tagName) {
62834 if (!tagNamePattern.test(tagName)) {
62835 throw new Error('invalid tagName:' + tagName);
62838 this.tagName = tagName;
62840 add: function add(qName, value, offset) {
62841 if (!tagNamePattern.test(qName)) {
62842 throw new Error('invalid attribute:' + qName);
62845 this[this.length++] = {
62852 getLocalName: function getLocalName(i) {
62853 return this[i].localName;
62855 getLocator: function getLocator(i) {
62856 return this[i].locator;
62858 getQName: function getQName(i) {
62859 return this[i].qName;
62861 getURI: function getURI(i) {
62862 return this[i].uri;
62864 getValue: function getValue(i) {
62865 return this[i].value;
62866 } // ,getIndex:function(uri, localName)){
62873 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
62874 // getType:function(uri,localName){}
62875 // getType:function(i){},
62879 function _set_proto_(thiz, parent) {
62880 thiz.__proto__ = parent;
62884 if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
62885 _set_proto_ = function _set_proto_(thiz, parent) {
62887 p.prototype = parent;
62890 for (parent in thiz) {
62891 p[parent] = thiz[parent];
62898 function split(source, start) {
62901 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
62902 reg.lastIndex = start;
62903 reg.exec(source); //skip <
62905 while (match = reg.exec(source)) {
62907 if (match[1]) return buf;
62911 var XMLReader_1 = XMLReader;
62913 XMLReader: XMLReader_1
62918 * Object DOMException
62919 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
62920 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
62922 function copy(src, dest) {
62923 for (var p in src) {
62928 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
62929 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
62933 function _extends(Class, Super) {
62934 var pt = Class.prototype;
62936 if (Object.create) {
62937 var ppt = Object.create(Super.prototype);
62938 pt.__proto__ = ppt;
62941 if (!(pt instanceof Super)) {
62942 var t = function t() {};
62943 t.prototype = Super.prototype;
62946 Class.prototype = pt = t;
62949 if (pt.constructor != Class) {
62950 if (typeof Class != 'function') {
62951 console.error("unknow Class:" + Class);
62954 pt.constructor = Class;
62958 var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
62961 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
62962 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
62963 var TEXT_NODE = NodeType.TEXT_NODE = 3;
62964 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
62965 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
62966 var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
62967 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
62968 var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
62969 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
62970 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
62971 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
62972 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
62974 var ExceptionCode = {};
62975 var ExceptionMessage = {};
62976 ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
62977 ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
62978 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
62979 ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
62980 ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
62981 ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
62982 ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
62983 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
62984 ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
62985 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
62987 ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
62988 ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
62989 ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
62990 ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
62991 ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
62993 function DOMException$1(code, message) {
62994 if (message instanceof Error) {
62995 var error = message;
62998 Error.call(this, ExceptionMessage[code]);
62999 this.message = ExceptionMessage[code];
63000 if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException$1);
63004 if (message) this.message = this.message + ": " + message;
63007 DOMException$1.prototype = Error.prototype;
63008 copy(ExceptionCode, DOMException$1);
63010 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
63011 * 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.
63012 * The items in the NodeList are accessible via an integral index, starting from 0.
63015 function NodeList() {}
63016 NodeList.prototype = {
63018 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
63024 * 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.
63026 * @param index unsigned long
63027 * Index into the collection.
63029 * The node at the indexth position in the NodeList, or null if that is not a valid index.
63031 item: function item(index) {
63032 return this[index] || null;
63034 toString: function toString(isHTML, nodeFilter) {
63035 for (var buf = [], i = 0; i < this.length; i++) {
63036 serializeToString(this[i], buf, isHTML, nodeFilter);
63039 return buf.join('');
63043 function LiveNodeList(node, refresh) {
63045 this._refresh = refresh;
63047 _updateLiveList(this);
63050 function _updateLiveList(list) {
63051 var inc = list._node._inc || list._node.ownerDocument._inc;
63053 if (list._inc != inc) {
63054 var ls = list._refresh(list._node); //console.log(ls.length)
63057 __set__(list, 'length', ls.length);
63064 LiveNodeList.prototype.item = function (i) {
63065 _updateLiveList(this);
63070 _extends(LiveNodeList, NodeList);
63073 * 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.
63074 * NamedNodeMap objects in the DOM are live.
63075 * used for attributes or DocumentType entities
63079 function NamedNodeMap() {}
63081 function _findNodeIndex(list, node) {
63082 var i = list.length;
63085 if (list[i] === node) {
63091 function _addNamedNode(el, list, newAttr, oldAttr) {
63093 list[_findNodeIndex(list, oldAttr)] = newAttr;
63095 list[list.length++] = newAttr;
63099 newAttr.ownerElement = el;
63100 var doc = el.ownerDocument;
63103 oldAttr && _onRemoveAttribute(doc, el, oldAttr);
63105 _onAddAttribute(doc, el, newAttr);
63110 function _removeNamedNode(el, list, attr) {
63111 //console.log('remove attr:'+attr)
63112 var i = _findNodeIndex(list, attr);
63115 var lastIndex = list.length - 1;
63117 while (i < lastIndex) {
63118 list[i] = list[++i];
63121 list.length = lastIndex;
63124 var doc = el.ownerDocument;
63127 _onRemoveAttribute(doc, el, attr);
63129 attr.ownerElement = null;
63133 throw DOMException$1(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
63137 NamedNodeMap.prototype = {
63139 item: NodeList.prototype.item,
63140 getNamedItem: function getNamedItem(key) {
63141 // if(key.indexOf(':')>0 || key == 'xmlns'){
63145 var i = this.length;
63148 var attr = this[i]; //console.log(attr.nodeName,key)
63150 if (attr.nodeName == key) {
63155 setNamedItem: function setNamedItem(attr) {
63156 var el = attr.ownerElement;
63158 if (el && el != this._ownerElement) {
63159 throw new DOMException$1(INUSE_ATTRIBUTE_ERR);
63162 var oldAttr = this.getNamedItem(attr.nodeName);
63164 _addNamedNode(this._ownerElement, this, attr, oldAttr);
63170 setNamedItemNS: function setNamedItemNS(attr) {
63171 // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
63172 var el = attr.ownerElement,
63175 if (el && el != this._ownerElement) {
63176 throw new DOMException$1(INUSE_ATTRIBUTE_ERR);
63179 oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
63181 _addNamedNode(this._ownerElement, this, attr, oldAttr);
63187 removeNamedItem: function removeNamedItem(key) {
63188 var attr = this.getNamedItem(key);
63190 _removeNamedNode(this._ownerElement, this, attr);
63194 // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
63196 removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
63197 var attr = this.getNamedItemNS(namespaceURI, localName);
63199 _removeNamedNode(this._ownerElement, this, attr);
63203 getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
63204 var i = this.length;
63207 var node = this[i];
63209 if (node.localName == localName && node.namespaceURI == namespaceURI) {
63218 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
63221 function DOMImplementation(
63224 this._features = {};
63227 for (var feature in features) {
63228 this._features = features[feature];
63232 DOMImplementation.prototype = {
63233 hasFeature: function hasFeature(
63238 var versions = this._features[feature.toLowerCase()];
63240 if (versions && (!version || version in versions)) {
63246 // Introduced in DOM Level 2:
63247 createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
63248 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
63249 var doc = new Document();
63250 doc.implementation = this;
63251 doc.childNodes = new NodeList();
63252 doc.doctype = doctype;
63255 doc.appendChild(doctype);
63258 if (qualifiedName) {
63259 var root = doc.createElementNS(namespaceURI, qualifiedName);
63260 doc.appendChild(root);
63265 // Introduced in DOM Level 2:
63266 createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
63267 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
63268 var node = new DocumentType();
63269 node.name = qualifiedName;
63270 node.nodeName = qualifiedName;
63271 node.publicId = publicId;
63272 node.systemId = systemId; // Introduced in DOM Level 2:
63273 //readonly attribute DOMString internalSubset;
63275 // readonly attribute NamedNodeMap entities;
63276 // readonly attribute NamedNodeMap notations;
63282 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
63289 previousSibling: null,
63294 ownerDocument: null,
63296 namespaceURI: null,
63299 // Modified in DOM Level 2:
63300 insertBefore: function insertBefore(newChild, refChild) {
63302 return _insertBefore(this, newChild, refChild);
63304 replaceChild: function replaceChild(newChild, oldChild) {
63306 this.insertBefore(newChild, oldChild);
63309 this.removeChild(oldChild);
63312 removeChild: function removeChild(oldChild) {
63313 return _removeChild(this, oldChild);
63315 appendChild: function appendChild(newChild) {
63316 return this.insertBefore(newChild, null);
63318 hasChildNodes: function hasChildNodes() {
63319 return this.firstChild != null;
63321 cloneNode: function cloneNode(deep) {
63322 return _cloneNode(this.ownerDocument || this, this, deep);
63324 // Modified in DOM Level 2:
63325 normalize: function normalize() {
63326 var child = this.firstChild;
63329 var next = child.nextSibling;
63331 if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
63332 this.removeChild(next);
63333 child.appendData(next.data);
63340 // Introduced in DOM Level 2:
63341 isSupported: function isSupported(feature, version) {
63342 return this.ownerDocument.implementation.hasFeature(feature, version);
63344 // Introduced in DOM Level 2:
63345 hasAttributes: function hasAttributes() {
63346 return this.attributes.length > 0;
63348 lookupPrefix: function lookupPrefix(namespaceURI) {
63352 var map = el._nsMap; //console.dir(map)
63355 for (var n in map) {
63356 if (map[n] == namespaceURI) {
63362 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
63367 // Introduced in DOM Level 3:
63368 lookupNamespaceURI: function lookupNamespaceURI(prefix) {
63372 var map = el._nsMap; //console.dir(map)
63375 if (prefix in map) {
63376 return map[prefix];
63380 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
63385 // Introduced in DOM Level 3:
63386 isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
63387 var prefix = this.lookupPrefix(namespaceURI);
63388 return prefix == null;
63392 function _xmlEncoder(c) {
63393 return c == '<' && '<' || c == '>' && '>' || c == '&' && '&' || c == '"' && '"' || '&#' + c.charCodeAt() + ';';
63396 copy(NodeType, Node);
63397 copy(NodeType, Node.prototype);
63399 * @param callback return true for continue,false for break
63400 * @return boolean true: break visit;
63403 function _visitNode(node, callback) {
63404 if (callback(node)) {
63408 if (node = node.firstChild) {
63410 if (_visitNode(node, callback)) {
63413 } while (node = node.nextSibling);
63417 function Document() {}
63419 function _onAddAttribute(doc, el, newAttr) {
63421 var ns = newAttr.namespaceURI;
63423 if (ns == 'http://www.w3.org/2000/xmlns/') {
63425 el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
63429 function _onRemoveAttribute(doc, el, newAttr, remove) {
63431 var ns = newAttr.namespaceURI;
63433 if (ns == 'http://www.w3.org/2000/xmlns/') {
63435 delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
63439 function _onUpdateChild(doc, el, newChild) {
63440 if (doc && doc._inc) {
63441 doc._inc++; //update childNodes
63443 var cs = el.childNodes;
63446 cs[cs.length++] = newChild;
63449 var child = el.firstChild;
63454 child = child.nextSibling;
63465 * writeable properties:
63466 * nodeValue,Attr:value,CharacterData:data
63471 function _removeChild(parentNode, child) {
63472 var previous = child.previousSibling;
63473 var next = child.nextSibling;
63476 previous.nextSibling = next;
63478 parentNode.firstChild = next;
63482 next.previousSibling = previous;
63484 parentNode.lastChild = previous;
63487 _onUpdateChild(parentNode.ownerDocument, parentNode);
63492 * preformance key(refChild == null)
63496 function _insertBefore(parentNode, newChild, nextChild) {
63497 var cp = newChild.parentNode;
63500 cp.removeChild(newChild); //remove and update
63503 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
63504 var newFirst = newChild.firstChild;
63506 if (newFirst == null) {
63510 var newLast = newChild.lastChild;
63512 newFirst = newLast = newChild;
63515 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
63516 newFirst.previousSibling = pre;
63517 newLast.nextSibling = nextChild;
63520 pre.nextSibling = newFirst;
63522 parentNode.firstChild = newFirst;
63525 if (nextChild == null) {
63526 parentNode.lastChild = newLast;
63528 nextChild.previousSibling = newLast;
63532 newFirst.parentNode = parentNode;
63533 } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
63535 _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
63538 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
63539 newChild.firstChild = newChild.lastChild = null;
63545 function _appendSingleChild(parentNode, newChild) {
63546 var cp = newChild.parentNode;
63549 var pre = parentNode.lastChild;
63550 cp.removeChild(newChild); //remove and update
63552 var pre = parentNode.lastChild;
63555 var pre = parentNode.lastChild;
63556 newChild.parentNode = parentNode;
63557 newChild.previousSibling = pre;
63558 newChild.nextSibling = null;
63561 pre.nextSibling = newChild;
63563 parentNode.firstChild = newChild;
63566 parentNode.lastChild = newChild;
63568 _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
63570 return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
63573 Document.prototype = {
63574 //implementation : null,
63575 nodeName: '#document',
63576 nodeType: DOCUMENT_NODE,
63578 documentElement: null,
63580 insertBefore: function insertBefore(newChild, refChild) {
63582 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
63583 var child = newChild.firstChild;
63586 var next = child.nextSibling;
63587 this.insertBefore(child, refChild);
63594 if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
63595 this.documentElement = newChild;
63598 return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
63600 removeChild: function removeChild(oldChild) {
63601 if (this.documentElement == oldChild) {
63602 this.documentElement = null;
63605 return _removeChild(this, oldChild);
63607 // Introduced in DOM Level 2:
63608 importNode: function importNode(importedNode, deep) {
63609 return _importNode(this, importedNode, deep);
63611 // Introduced in DOM Level 2:
63612 getElementById: function getElementById(id) {
63615 _visitNode(this.documentElement, function (node) {
63616 if (node.nodeType == ELEMENT_NODE) {
63617 if (node.getAttribute('id') == id) {
63626 //document factory method:
63627 createElement: function createElement(tagName) {
63628 var node = new Element();
63629 node.ownerDocument = this;
63630 node.nodeName = tagName;
63631 node.tagName = tagName;
63632 node.childNodes = new NodeList();
63633 var attrs = node.attributes = new NamedNodeMap();
63634 attrs._ownerElement = node;
63637 createDocumentFragment: function createDocumentFragment() {
63638 var node = new DocumentFragment();
63639 node.ownerDocument = this;
63640 node.childNodes = new NodeList();
63643 createTextNode: function createTextNode(data) {
63644 var node = new Text();
63645 node.ownerDocument = this;
63646 node.appendData(data);
63649 createComment: function createComment(data) {
63650 var node = new Comment();
63651 node.ownerDocument = this;
63652 node.appendData(data);
63655 createCDATASection: function createCDATASection(data) {
63656 var node = new CDATASection();
63657 node.ownerDocument = this;
63658 node.appendData(data);
63661 createProcessingInstruction: function createProcessingInstruction(target, data) {
63662 var node = new ProcessingInstruction();
63663 node.ownerDocument = this;
63664 node.tagName = node.target = target;
63665 node.nodeValue = node.data = data;
63668 createAttribute: function createAttribute(name) {
63669 var node = new Attr();
63670 node.ownerDocument = this;
63672 node.nodeName = name;
63673 node.localName = name;
63674 node.specified = true;
63677 createEntityReference: function createEntityReference(name) {
63678 var node = new EntityReference();
63679 node.ownerDocument = this;
63680 node.nodeName = name;
63683 // Introduced in DOM Level 2:
63684 createElementNS: function createElementNS(namespaceURI, qualifiedName) {
63685 var node = new Element();
63686 var pl = qualifiedName.split(':');
63687 var attrs = node.attributes = new NamedNodeMap();
63688 node.childNodes = new NodeList();
63689 node.ownerDocument = this;
63690 node.nodeName = qualifiedName;
63691 node.tagName = qualifiedName;
63692 node.namespaceURI = namespaceURI;
63694 if (pl.length == 2) {
63695 node.prefix = pl[0];
63696 node.localName = pl[1];
63698 //el.prefix = null;
63699 node.localName = qualifiedName;
63702 attrs._ownerElement = node;
63705 // Introduced in DOM Level 2:
63706 createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
63707 var node = new Attr();
63708 var pl = qualifiedName.split(':');
63709 node.ownerDocument = this;
63710 node.nodeName = qualifiedName;
63711 node.name = qualifiedName;
63712 node.namespaceURI = namespaceURI;
63713 node.specified = true;
63715 if (pl.length == 2) {
63716 node.prefix = pl[0];
63717 node.localName = pl[1];
63719 //el.prefix = null;
63720 node.localName = qualifiedName;
63727 _extends(Document, Node);
63729 function Element() {
63732 Element.prototype = {
63733 nodeType: ELEMENT_NODE,
63734 hasAttribute: function hasAttribute(name) {
63735 return this.getAttributeNode(name) != null;
63737 getAttribute: function getAttribute(name) {
63738 var attr = this.getAttributeNode(name);
63739 return attr && attr.value || '';
63741 getAttributeNode: function getAttributeNode(name) {
63742 return this.attributes.getNamedItem(name);
63744 setAttribute: function setAttribute(name, value) {
63745 var attr = this.ownerDocument.createAttribute(name);
63746 attr.value = attr.nodeValue = "" + value;
63747 this.setAttributeNode(attr);
63749 removeAttribute: function removeAttribute(name) {
63750 var attr = this.getAttributeNode(name);
63751 attr && this.removeAttributeNode(attr);
63753 //four real opeartion method
63754 appendChild: function appendChild(newChild) {
63755 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
63756 return this.insertBefore(newChild, null);
63758 return _appendSingleChild(this, newChild);
63761 setAttributeNode: function setAttributeNode(newAttr) {
63762 return this.attributes.setNamedItem(newAttr);
63764 setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
63765 return this.attributes.setNamedItemNS(newAttr);
63767 removeAttributeNode: function removeAttributeNode(oldAttr) {
63768 //console.log(this == oldAttr.ownerElement)
63769 return this.attributes.removeNamedItem(oldAttr.nodeName);
63771 //get real attribute name,and remove it by removeAttributeNode
63772 removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
63773 var old = this.getAttributeNodeNS(namespaceURI, localName);
63774 old && this.removeAttributeNode(old);
63776 hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
63777 return this.getAttributeNodeNS(namespaceURI, localName) != null;
63779 getAttributeNS: function getAttributeNS(namespaceURI, localName) {
63780 var attr = this.getAttributeNodeNS(namespaceURI, localName);
63781 return attr && attr.value || '';
63783 setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
63784 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
63785 attr.value = attr.nodeValue = "" + value;
63786 this.setAttributeNode(attr);
63788 getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
63789 return this.attributes.getNamedItemNS(namespaceURI, localName);
63791 getElementsByTagName: function getElementsByTagName(tagName) {
63792 return new LiveNodeList(this, function (base) {
63795 _visitNode(base, function (node) {
63796 if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
63804 getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
63805 return new LiveNodeList(this, function (base) {
63808 _visitNode(base, function (node) {
63809 if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
63818 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
63819 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
63821 _extends(Element, Node);
63824 Attr.prototype.nodeType = ATTRIBUTE_NODE;
63826 _extends(Attr, Node);
63828 function CharacterData() {}
63829 CharacterData.prototype = {
63831 substringData: function substringData(offset, count) {
63832 return this.data.substring(offset, offset + count);
63834 appendData: function appendData(text) {
63835 text = this.data + text;
63836 this.nodeValue = this.data = text;
63837 this.length = text.length;
63839 insertData: function insertData(offset, text) {
63840 this.replaceData(offset, 0, text);
63842 appendChild: function appendChild(newChild) {
63843 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
63845 deleteData: function deleteData(offset, count) {
63846 this.replaceData(offset, count, "");
63848 replaceData: function replaceData(offset, count, text) {
63849 var start = this.data.substring(0, offset);
63850 var end = this.data.substring(offset + count);
63851 text = start + text + end;
63852 this.nodeValue = this.data = text;
63853 this.length = text.length;
63857 _extends(CharacterData, Node);
63862 nodeType: TEXT_NODE,
63863 splitText: function splitText(offset) {
63864 var text = this.data;
63865 var newText = text.substring(offset);
63866 text = text.substring(0, offset);
63867 this.data = this.nodeValue = text;
63868 this.length = text.length;
63869 var newNode = this.ownerDocument.createTextNode(newText);
63871 if (this.parentNode) {
63872 this.parentNode.insertBefore(newNode, this.nextSibling);
63879 _extends(Text, CharacterData);
63881 function Comment() {}
63882 Comment.prototype = {
63883 nodeName: "#comment",
63884 nodeType: COMMENT_NODE
63887 _extends(Comment, CharacterData);
63889 function CDATASection() {}
63890 CDATASection.prototype = {
63891 nodeName: "#cdata-section",
63892 nodeType: CDATA_SECTION_NODE
63895 _extends(CDATASection, CharacterData);
63897 function DocumentType() {}
63898 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
63900 _extends(DocumentType, Node);
63902 function Notation() {}
63903 Notation.prototype.nodeType = NOTATION_NODE;
63905 _extends(Notation, Node);
63907 function Entity() {}
63908 Entity.prototype.nodeType = ENTITY_NODE;
63910 _extends(Entity, Node);
63912 function EntityReference() {}
63913 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
63915 _extends(EntityReference, Node);
63917 function DocumentFragment() {}
63918 DocumentFragment.prototype.nodeName = "#document-fragment";
63919 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
63921 _extends(DocumentFragment, Node);
63923 function ProcessingInstruction() {}
63925 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
63927 _extends(ProcessingInstruction, Node);
63929 function XMLSerializer$1() {}
63931 XMLSerializer$1.prototype.serializeToString = function (node, isHtml, nodeFilter) {
63932 return nodeSerializeToString.call(node, isHtml, nodeFilter);
63935 Node.prototype.toString = nodeSerializeToString;
63937 function nodeSerializeToString(isHtml, nodeFilter) {
63939 var refNode = this.nodeType == 9 ? this.documentElement : this;
63940 var prefix = refNode.prefix;
63941 var uri = refNode.namespaceURI;
63943 if (uri && prefix == null) {
63944 //console.log(prefix)
63945 var prefix = refNode.lookupPrefix(uri);
63947 if (prefix == null) {
63949 var visibleNamespaces = [{
63952 } //{namespace:uri,prefix:''}
63957 serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
63959 return buf.join('');
63962 function needNamespaceDefine(node, isHTML, visibleNamespaces) {
63963 var prefix = node.prefix || '';
63964 var uri = node.namespaceURI;
63966 if (!prefix && !uri) {
63970 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
63974 var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
63977 var ns = visibleNamespaces[i]; // get namespace prefix
63978 //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
63980 if (ns.prefix == prefix) {
63981 return ns.namespace != uri;
63983 } //console.log(isHTML,uri,prefix=='')
63984 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
63987 //node.flag = '11111'
63988 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
63994 function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
63996 node = nodeFilter(node);
63999 if (typeof node == 'string') {
64005 } //buf.sort.apply(attrs, attributeSorter);
64009 switch (node.nodeType) {
64011 if (!visibleNamespaces) visibleNamespaces = [];
64012 visibleNamespaces.length;
64013 var attrs = node.attributes;
64014 var len = attrs.length;
64015 var child = node.firstChild;
64016 var nodeName = node.tagName;
64017 isHTML = htmlns === node.namespaceURI || isHTML;
64018 buf.push('<', nodeName);
64020 for (var i = 0; i < len; i++) {
64021 // add namespaces for attributes
64022 var attr = attrs.item(i);
64024 if (attr.prefix == 'xmlns') {
64025 visibleNamespaces.push({
64026 prefix: attr.localName,
64027 namespace: attr.value
64029 } else if (attr.nodeName == 'xmlns') {
64030 visibleNamespaces.push({
64032 namespace: attr.value
64037 for (var i = 0; i < len; i++) {
64038 var attr = attrs.item(i);
64040 if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
64041 var prefix = attr.prefix || '';
64042 var uri = attr.namespaceURI;
64043 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
64044 buf.push(ns, '="', uri, '"');
64045 visibleNamespaces.push({
64051 serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
64052 } // add namespace for current node
64055 if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
64056 var prefix = node.prefix || '';
64057 var uri = node.namespaceURI;
64058 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
64059 buf.push(ns, '="', uri, '"');
64060 visibleNamespaces.push({
64066 if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
64067 buf.push('>'); //if is cdata child node
64069 if (isHTML && /^script$/i.test(nodeName)) {
64072 buf.push(child.data);
64074 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
64077 child = child.nextSibling;
64081 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
64082 child = child.nextSibling;
64086 buf.push('</', nodeName, '>');
64089 } // remove added visible namespaces
64090 //visibleNamespaces.length = startVisibleNamespaces;
64095 case DOCUMENT_NODE:
64096 case DOCUMENT_FRAGMENT_NODE:
64097 var child = node.firstChild;
64100 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
64101 child = child.nextSibling;
64106 case ATTRIBUTE_NODE:
64107 return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
64110 return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
64112 case CDATA_SECTION_NODE:
64113 return buf.push('<![CDATA[', node.data, ']]>');
64116 return buf.push("<!--", node.data, "-->");
64118 case DOCUMENT_TYPE_NODE:
64119 var pubid = node.publicId;
64120 var sysid = node.systemId;
64121 buf.push('<!DOCTYPE ', node.name);
64124 buf.push(' PUBLIC "', pubid);
64126 if (sysid && sysid != '.') {
64127 buf.push('" "', sysid);
64131 } else if (sysid && sysid != '.') {
64132 buf.push(' SYSTEM "', sysid, '">');
64134 var sub = node.internalSubset;
64137 buf.push(" [", sub, "]");
64145 case PROCESSING_INSTRUCTION_NODE:
64146 return buf.push("<?", node.target, " ", node.data, "?>");
64148 case ENTITY_REFERENCE_NODE:
64149 return buf.push('&', node.nodeName, ';');
64150 //case ENTITY_NODE:
64151 //case NOTATION_NODE:
64154 buf.push('??', node.nodeName);
64158 function _importNode(doc, node, deep) {
64161 switch (node.nodeType) {
64163 node2 = node.cloneNode(false);
64164 node2.ownerDocument = doc;
64165 //var attrs = node2.attributes;
64166 //var len = attrs.length;
64167 //for(var i=0;i<len;i++){
64168 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
64171 case DOCUMENT_FRAGMENT_NODE:
64174 case ATTRIBUTE_NODE:
64177 //case ENTITY_REFERENCE_NODE:
64178 //case PROCESSING_INSTRUCTION_NODE:
64179 ////case TEXT_NODE:
64180 //case CDATA_SECTION_NODE:
64181 //case COMMENT_NODE:
64184 //case DOCUMENT_NODE:
64185 //case DOCUMENT_TYPE_NODE:
64186 //cannot be imported.
64187 //case ENTITY_NODE:
64188 //case NOTATION_NODE:
64189 //can not hit in level3
64194 node2 = node.cloneNode(false); //false
64197 node2.ownerDocument = doc;
64198 node2.parentNode = null;
64201 var child = node.firstChild;
64204 node2.appendChild(_importNode(doc, child, deep));
64205 child = child.nextSibling;
64211 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
64212 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
64215 function _cloneNode(doc, node, deep) {
64216 var node2 = new node.constructor();
64218 for (var n in node) {
64221 if (_typeof(v) != 'object') {
64222 if (v != node2[n]) {
64228 if (node.childNodes) {
64229 node2.childNodes = new NodeList();
64232 node2.ownerDocument = doc;
64234 switch (node2.nodeType) {
64236 var attrs = node.attributes;
64237 var attrs2 = node2.attributes = new NamedNodeMap();
64238 var len = attrs.length;
64239 attrs2._ownerElement = node2;
64241 for (var i = 0; i < len; i++) {
64242 node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
64247 case ATTRIBUTE_NODE:
64252 var child = node.firstChild;
64255 node2.appendChild(_cloneNode(doc, child, deep));
64256 child = child.nextSibling;
64263 function __set__(object, key, value) {
64264 object[key] = value;
64269 if (Object.defineProperty) {
64270 var getTextContent = function getTextContent(node) {
64271 switch (node.nodeType) {
64273 case DOCUMENT_FRAGMENT_NODE:
64275 node = node.firstChild;
64278 if (node.nodeType !== 7 && node.nodeType !== 8) {
64279 buf.push(getTextContent(node));
64282 node = node.nextSibling;
64285 return buf.join('');
64288 return node.nodeValue;
64292 Object.defineProperty(LiveNodeList.prototype, 'length', {
64293 get: function get() {
64294 _updateLiveList(this);
64296 return this.$$length;
64299 Object.defineProperty(Node.prototype, 'textContent', {
64300 get: function get() {
64301 return getTextContent(this);
64303 set: function set(data) {
64304 switch (this.nodeType) {
64306 case DOCUMENT_FRAGMENT_NODE:
64307 while (this.firstChild) {
64308 this.removeChild(this.firstChild);
64311 if (data || String(data)) {
64312 this.appendChild(this.ownerDocument.createTextNode(data));
64321 this.nodeValue = data;
64326 __set__ = function __set__(object, key, value) {
64327 //console.log(value)
64328 object['$$' + key] = value;
64332 } //if(typeof require == 'function'){
64335 var DOMImplementation_1 = DOMImplementation;
64336 var XMLSerializer_1 = XMLSerializer$1; //}
64339 DOMImplementation: DOMImplementation_1,
64340 XMLSerializer: XMLSerializer_1
64343 var domParser = createCommonjsModule(function (module, exports) {
64344 function DOMParser(options) {
64345 this.options = options || {
64350 DOMParser.prototype.parseFromString = function (source, mimeType) {
64351 var options = this.options;
64352 var sax = new XMLReader();
64353 var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
64355 var errorHandler = options.errorHandler;
64356 var locator = options.locator;
64357 var defaultNSMap = options.xmlns || {};
64367 domBuilder.setDocumentLocator(locator);
64370 sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
64371 sax.domBuilder = options.domBuilder || domBuilder;
64373 if (/\/x?html?$/.test(mimeType)) {
64374 entityMap.nbsp = '\xa0';
64375 entityMap.copy = '\xa9';
64376 defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
64379 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
64382 sax.parse(source, defaultNSMap, entityMap);
64384 sax.errorHandler.error("invalid doc source");
64387 return domBuilder.doc;
64390 function buildErrorHandler(errorImpl, domBuilder, locator) {
64392 if (domBuilder instanceof DOMHandler) {
64396 errorImpl = domBuilder;
64399 var errorHandler = {};
64400 var isCallback = errorImpl instanceof Function;
64401 locator = locator || {};
64403 function build(key) {
64404 var fn = errorImpl[key];
64406 if (!fn && isCallback) {
64407 fn = errorImpl.length == 2 ? function (msg) {
64408 errorImpl(key, msg);
64412 errorHandler[key] = fn && function (msg) {
64413 fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
64414 } || function () {};
64419 build('fatalError');
64420 return errorHandler;
64421 } //console.log('#\n\n\n\n\n\n\n####')
64424 * +ContentHandler+ErrorHandler
64425 * +LexicalHandler+EntityResolver2
64426 * -DeclHandler-DTDHandler
64428 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
64429 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
64430 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
64434 function DOMHandler() {
64435 this.cdata = false;
64438 function position(locator, node) {
64439 node.lineNumber = locator.lineNumber;
64440 node.columnNumber = locator.columnNumber;
64443 * @see org.xml.sax.ContentHandler#startDocument
64444 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
64448 DOMHandler.prototype = {
64449 startDocument: function startDocument() {
64450 this.doc = new DOMImplementation().createDocument(null, null, null);
64452 if (this.locator) {
64453 this.doc.documentURI = this.locator.systemId;
64456 startElement: function startElement(namespaceURI, localName, qName, attrs) {
64457 var doc = this.doc;
64458 var el = doc.createElementNS(namespaceURI, qName || localName);
64459 var len = attrs.length;
64460 appendElement(this, el);
64461 this.currentElement = el;
64462 this.locator && position(this.locator, el);
64464 for (var i = 0; i < len; i++) {
64465 var namespaceURI = attrs.getURI(i);
64466 var value = attrs.getValue(i);
64467 var qName = attrs.getQName(i);
64468 var attr = doc.createAttributeNS(namespaceURI, qName);
64469 this.locator && position(attrs.getLocator(i), attr);
64470 attr.value = attr.nodeValue = value;
64471 el.setAttributeNode(attr);
64474 endElement: function endElement(namespaceURI, localName, qName) {
64475 var current = this.currentElement;
64477 this.currentElement = current.parentNode;
64479 startPrefixMapping: function startPrefixMapping(prefix, uri) {},
64480 endPrefixMapping: function endPrefixMapping(prefix) {},
64481 processingInstruction: function processingInstruction(target, data) {
64482 var ins = this.doc.createProcessingInstruction(target, data);
64483 this.locator && position(this.locator, ins);
64484 appendElement(this, ins);
64486 ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
64487 characters: function characters(chars, start, length) {
64488 chars = _toString.apply(this, arguments); //console.log(chars)
64492 var charNode = this.doc.createCDATASection(chars);
64494 var charNode = this.doc.createTextNode(chars);
64497 if (this.currentElement) {
64498 this.currentElement.appendChild(charNode);
64499 } else if (/^\s*$/.test(chars)) {
64500 this.doc.appendChild(charNode); //process xml
64503 this.locator && position(this.locator, charNode);
64506 skippedEntity: function skippedEntity(name) {},
64507 endDocument: function endDocument() {
64508 this.doc.normalize();
64510 setDocumentLocator: function setDocumentLocator(locator) {
64511 if (this.locator = locator) {
64512 // && !('lineNumber' in locator)){
64513 locator.lineNumber = 0;
64517 comment: function comment(chars, start, length) {
64518 chars = _toString.apply(this, arguments);
64519 var comm = this.doc.createComment(chars);
64520 this.locator && position(this.locator, comm);
64521 appendElement(this, comm);
64523 startCDATA: function startCDATA() {
64524 //used in characters() methods
64527 endCDATA: function endCDATA() {
64528 this.cdata = false;
64530 startDTD: function startDTD(name, publicId, systemId) {
64531 var impl = this.doc.implementation;
64533 if (impl && impl.createDocumentType) {
64534 var dt = impl.createDocumentType(name, publicId, systemId);
64535 this.locator && position(this.locator, dt);
64536 appendElement(this, dt);
64541 * @see org.xml.sax.ErrorHandler
64542 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
64544 warning: function warning(error) {
64545 console.warn('[xmldom warning]\t' + error, _locator(this.locator));
64547 error: function error(_error) {
64548 console.error('[xmldom error]\t' + _error, _locator(this.locator));
64550 fatalError: function fatalError(error) {
64551 console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
64556 function _locator(l) {
64558 return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
64562 function _toString(chars, start, length) {
64563 if (typeof chars == 'string') {
64564 return chars.substr(start, length);
64566 //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
64567 if (chars.length >= start + length || start) {
64568 return new java.lang.String(chars, start, length) + '';
64575 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
64576 * used method of org.xml.sax.ext.LexicalHandler:
64577 * #comment(chars, start, length)
64580 * #startDTD(name, publicId, systemId)
64583 * IGNORED method of org.xml.sax.ext.LexicalHandler:
64585 * #startEntity(name)
64589 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
64590 * IGNORED method of org.xml.sax.ext.DeclHandler
64591 * #attributeDecl(eName, aName, type, mode, value)
64592 * #elementDecl(name, model)
64593 * #externalEntityDecl(name, publicId, systemId)
64594 * #internalEntityDecl(name, value)
64595 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
64596 * IGNORED method of org.xml.sax.EntityResolver2
64597 * #resolveEntity(String name,String publicId,String baseURI,String systemId)
64598 * #resolveEntity(publicId, systemId)
64599 * #getExternalSubset(name, baseURI)
64600 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
64601 * IGNORED method of org.xml.sax.DTDHandler
64602 * #notationDecl(name, publicId, systemId) {};
64603 * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
64607 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
64608 DOMHandler.prototype[key] = function () {
64612 /* 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 */
64614 function appendElement(hander, node) {
64615 if (!hander.currentElement) {
64616 hander.doc.appendChild(node);
64618 hander.currentElement.appendChild(node);
64620 } //appendChild and setAttributeNS are preformance key
64621 //if(typeof require == 'function'){
64624 var XMLReader = sax.XMLReader;
64625 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
64626 exports.XMLSerializer = dom.XMLSerializer;
64627 exports.DOMParser = DOMParser; //}
64630 var togeojson = createCommonjsModule(function (module, exports) {
64631 var toGeoJSON = function () {
64633 var removeSpace = /\s*/g,
64634 trimSpace = /^\s*|\s*$/g,
64635 splitSpace = /\s+/; // generate a short, numeric hash of a string
64637 function okhash(x) {
64638 if (!x || !x.length) return 0;
64640 for (var i = 0, h = 0; i < x.length; i++) {
64641 h = (h << 5) - h + x.charCodeAt(i) | 0;
64645 } // all Y children of X
64648 function get(x, y) {
64649 return x.getElementsByTagName(y);
64652 function attr(x, y) {
64653 return x.getAttribute(y);
64656 function attrf(x, y) {
64657 return parseFloat(attr(x, y));
64658 } // one Y child of X, if any, otherwise null
64661 function get1(x, y) {
64663 return n.length ? n[0] : null;
64664 } // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
64667 function norm(el) {
64668 if (el.normalize) {
64673 } // cast array x into numbers
64676 function numarray(x) {
64677 for (var j = 0, o = []; j < x.length; j++) {
64678 o[j] = parseFloat(x[j]);
64682 } // get the content of a text node, if any
64685 function nodeVal(x) {
64690 return x && x.textContent || '';
64691 } // get the contents of multiple text nodes, if present
64694 function getMulti(x, ys) {
64699 for (k = 0; k < ys.length; k++) {
64700 n = get1(x, ys[k]);
64701 if (n) o[ys[k]] = nodeVal(n);
64705 } // add properties of Y to X, overwriting if present in both
64708 function extend(x, y) {
64712 } // get one coordinate from a coordinate array, if any
64715 function coord1(v) {
64716 return numarray(v.replace(removeSpace, '').split(','));
64717 } // get all coordinates from a coordinate array as [[],[]]
64720 function coord(v) {
64721 var coords = v.replace(trimSpace, '').split(splitSpace),
64724 for (var i = 0; i < coords.length; i++) {
64725 o.push(coord1(coords[i]));
64731 function coordPair(x) {
64732 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
64733 ele = get1(x, 'ele'),
64734 // handle namespaced attribute in browser
64735 heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
64736 time = get1(x, 'time'),
64740 e = parseFloat(nodeVal(ele));
64749 time: time ? nodeVal(time) : null,
64750 heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
64752 } // create a new feature collection parent object
64757 type: 'FeatureCollection',
64764 if (typeof XMLSerializer !== 'undefined') {
64765 /* istanbul ignore next */
64766 serializer = new XMLSerializer(); // only require xmldom in a node environment
64767 } else if ((typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && !process.browser) {
64768 serializer = new domParser.XMLSerializer();
64771 function xml2str(str) {
64772 // IE9 will create a new XMLSerializer but it'll crash immediately.
64773 // This line is ignored because we don't run coverage tests in IE9
64775 /* istanbul ignore next */
64776 if (str.xml !== undefined) return str.xml;
64777 return serializer.serializeToString(str);
64781 kml: function kml(doc) {
64783 // styleindex keeps track of hashed styles in order to match features
64786 // stylemapindex keeps track of style maps to expose in properties
64787 styleMapIndex = {},
64788 // atomic geospatial types supported by KML - MultiGeometry is
64789 // handled separately
64790 geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
64791 // all root placemarks in the file
64792 placemarks = get(doc, 'Placemark'),
64793 styles = get(doc, 'Style'),
64794 styleMaps = get(doc, 'StyleMap');
64796 for (var k = 0; k < styles.length; k++) {
64797 var hash = okhash(xml2str(styles[k])).toString(16);
64798 styleIndex['#' + attr(styles[k], 'id')] = hash;
64799 styleByHash[hash] = styles[k];
64802 for (var l = 0; l < styleMaps.length; l++) {
64803 styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
64804 var pairs = get(styleMaps[l], 'Pair');
64807 for (var m = 0; m < pairs.length; m++) {
64808 pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
64811 styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
64814 for (var j = 0; j < placemarks.length; j++) {
64815 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
64818 function kmlColor(v) {
64819 var color, opacity;
64822 if (v.substr(0, 1) === '#') {
64826 if (v.length === 6 || v.length === 3) {
64830 if (v.length === 8) {
64831 opacity = parseInt(v.substr(0, 2), 16) / 255;
64832 color = '#' + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
64835 return [color, isNaN(opacity) ? undefined : opacity];
64838 function gxCoord(v) {
64839 return numarray(v.split(' '));
64842 function gxCoords(root) {
64843 var elems = get(root, 'coord'),
64846 if (elems.length === 0) elems = get(root, 'gx:coord');
64848 for (var i = 0; i < elems.length; i++) {
64849 coords.push(gxCoord(nodeVal(elems[i])));
64852 var timeElems = get(root, 'when');
64854 for (var j = 0; j < timeElems.length; j++) {
64855 times.push(nodeVal(timeElems[j]));
64864 function getGeometry(root) {
64873 if (get1(root, 'MultiGeometry')) {
64874 return getGeometry(get1(root, 'MultiGeometry'));
64877 if (get1(root, 'MultiTrack')) {
64878 return getGeometry(get1(root, 'MultiTrack'));
64881 if (get1(root, 'gx:MultiTrack')) {
64882 return getGeometry(get1(root, 'gx:MultiTrack'));
64885 for (i = 0; i < geotypes.length; i++) {
64886 geomNodes = get(root, geotypes[i]);
64889 for (j = 0; j < geomNodes.length; j++) {
64890 geomNode = geomNodes[j];
64892 if (geotypes[i] === 'Point') {
64895 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
64897 } else if (geotypes[i] === 'LineString') {
64899 type: 'LineString',
64900 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
64902 } else if (geotypes[i] === 'Polygon') {
64903 var rings = get(geomNode, 'LinearRing'),
64906 for (k = 0; k < rings.length; k++) {
64907 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
64912 coordinates: coords
64914 } else if (geotypes[i] === 'Track' || geotypes[i] === 'gx:Track') {
64915 var track = gxCoords(geomNode);
64917 type: 'LineString',
64918 coordinates: track.coords
64920 if (track.times.length) coordTimes.push(track.times);
64928 coordTimes: coordTimes
64932 function getPlacemark(root) {
64933 var geomsAndTimes = getGeometry(root),
64936 name = nodeVal(get1(root, 'name')),
64937 address = nodeVal(get1(root, 'address')),
64938 styleUrl = nodeVal(get1(root, 'styleUrl')),
64939 description = nodeVal(get1(root, 'description')),
64940 timeSpan = get1(root, 'TimeSpan'),
64941 timeStamp = get1(root, 'TimeStamp'),
64942 extendedData = get1(root, 'ExtendedData'),
64943 lineStyle = get1(root, 'LineStyle'),
64944 polyStyle = get1(root, 'PolyStyle'),
64945 visibility = get1(root, 'visibility');
64946 if (!geomsAndTimes.geoms.length) return [];
64947 if (name) properties.name = name;
64948 if (address) properties.address = address;
64951 if (styleUrl[0] !== '#') {
64952 styleUrl = '#' + styleUrl;
64955 properties.styleUrl = styleUrl;
64957 if (styleIndex[styleUrl]) {
64958 properties.styleHash = styleIndex[styleUrl];
64961 if (styleMapIndex[styleUrl]) {
64962 properties.styleMapHash = styleMapIndex[styleUrl];
64963 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
64964 } // Try to populate the lineStyle or polyStyle since we got the style hash
64967 var style = styleByHash[properties.styleHash];
64970 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
64971 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
64975 if (description) properties.description = description;
64978 var begin = nodeVal(get1(timeSpan, 'begin'));
64979 var end = nodeVal(get1(timeSpan, 'end'));
64980 properties.timespan = {
64987 properties.timestamp = nodeVal(get1(timeStamp, 'when'));
64991 var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
64992 color = linestyles[0],
64993 opacity = linestyles[1],
64994 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
64995 if (color) properties.stroke = color;
64996 if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
64997 if (!isNaN(width)) properties['stroke-width'] = width;
65001 var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
65002 pcolor = polystyles[0],
65003 popacity = polystyles[1],
65004 fill = nodeVal(get1(polyStyle, 'fill')),
65005 outline = nodeVal(get1(polyStyle, 'outline'));
65006 if (pcolor) properties.fill = pcolor;
65007 if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
65008 if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
65009 if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
65012 if (extendedData) {
65013 var datas = get(extendedData, 'Data'),
65014 simpleDatas = get(extendedData, 'SimpleData');
65016 for (i = 0; i < datas.length; i++) {
65017 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
65020 for (i = 0; i < simpleDatas.length; i++) {
65021 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
65026 properties.visibility = nodeVal(visibility);
65029 if (geomsAndTimes.coordTimes.length) {
65030 properties.coordTimes = geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
65035 geometry: geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
65036 type: 'GeometryCollection',
65037 geometries: geomsAndTimes.geoms
65039 properties: properties
65041 if (attr(root, 'id')) feature.id = attr(root, 'id');
65047 gpx: function gpx(doc) {
65049 tracks = get(doc, 'trk'),
65050 routes = get(doc, 'rte'),
65051 waypoints = get(doc, 'wpt'),
65052 // a feature collection
65056 for (i = 0; i < tracks.length; i++) {
65057 feature = getTrack(tracks[i]);
65058 if (feature) gj.features.push(feature);
65061 for (i = 0; i < routes.length; i++) {
65062 feature = getRoute(routes[i]);
65063 if (feature) gj.features.push(feature);
65066 for (i = 0; i < waypoints.length; i++) {
65067 gj.features.push(getPoint(waypoints[i]));
65070 function getPoints(node, pointname) {
65071 var pts = get(node, pointname),
65076 if (l < 2) return {}; // Invalid line in GeoJSON
65078 for (var i = 0; i < l; i++) {
65079 var c = coordPair(pts[i]);
65080 line.push(c.coordinates);
65081 if (c.time) times.push(c.time);
65082 if (c.heartRate) heartRates.push(c.heartRate);
65088 heartRates: heartRates
65092 function getTrack(node) {
65093 var segments = get(node, 'trkseg'),
65099 for (var i = 0; i < segments.length; i++) {
65100 line = getPoints(segments[i], 'trkpt');
65103 if (line.line) track.push(line.line);
65104 if (line.times && line.times.length) times.push(line.times);
65105 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
65109 if (track.length === 0) return;
65110 var properties = getProperties(node);
65111 extend(properties, getLineStyle(get1(node, 'extensions')));
65112 if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
65113 if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
65116 properties: properties,
65118 type: track.length === 1 ? 'LineString' : 'MultiLineString',
65119 coordinates: track.length === 1 ? track[0] : track
65124 function getRoute(node) {
65125 var line = getPoints(node, 'rtept');
65126 if (!line.line) return;
65127 var prop = getProperties(node);
65128 extend(prop, getLineStyle(get1(node, 'extensions')));
65133 type: 'LineString',
65134 coordinates: line.line
65140 function getPoint(node) {
65141 var prop = getProperties(node);
65142 extend(prop, getMulti(node, ['sym']));
65148 coordinates: coordPair(node).coordinates
65153 function getLineStyle(extensions) {
65157 var lineStyle = get1(extensions, 'line');
65160 var color = nodeVal(get1(lineStyle, 'color')),
65161 opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
65162 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
65163 if (color) style.stroke = color;
65164 if (!isNaN(opacity)) style['stroke-opacity'] = opacity; // GPX width is in mm, convert to px with 96 px per inch
65166 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
65173 function getProperties(node) {
65174 var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
65175 links = get(node, 'link');
65176 if (links.length) prop.links = [];
65178 for (var i = 0, link; i < links.length; i++) {
65180 href: attr(links[i], 'href')
65182 extend(link, getMulti(links[i], ['text', 'type']));
65183 prop.links.push(link);
65195 module.exports = toGeoJSON;
65198 var _initialized = false;
65199 var _enabled = false;
65203 function svgData(projection, context, dispatch) {
65204 var throttledRedraw = throttle(function () {
65205 dispatch.call('change');
65208 var _showLabels = true;
65209 var detected = utilDetect();
65210 var layer = select(null);
65221 if (_initialized) return; // run once
65226 function over(d3_event) {
65227 d3_event.stopPropagation();
65228 d3_event.preventDefault();
65229 d3_event.dataTransfer.dropEffect = 'copy';
65232 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
65233 d3_event.stopPropagation();
65234 d3_event.preventDefault();
65235 if (!detected.filedrop) return;
65236 drawData.fileList(d3_event.dataTransfer.files);
65237 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
65238 _initialized = true;
65241 function getService() {
65242 if (services.vectorTile && !_vtService) {
65243 _vtService = services.vectorTile;
65245 _vtService.event.on('loadedData', throttledRedraw);
65246 } else if (!services.vectorTile && _vtService) {
65253 function showLayer() {
65255 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
65256 dispatch.call('change');
65260 function hideLayer() {
65261 throttledRedraw.cancel();
65262 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
65265 function layerOn() {
65266 layer.style('display', 'block');
65269 function layerOff() {
65270 layer.selectAll('.viewfield-group').remove();
65271 layer.style('display', 'none');
65272 } // ensure that all geojson features in a collection have IDs
65275 function ensureIDs(gj) {
65276 if (!gj) return null;
65278 if (gj.type === 'FeatureCollection') {
65279 for (var i = 0; i < gj.features.length; i++) {
65280 ensureFeatureID(gj.features[i]);
65283 ensureFeatureID(gj);
65287 } // ensure that each single Feature object has a unique ID
65290 function ensureFeatureID(feature) {
65291 if (!feature) return;
65292 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
65294 } // Prefer an array of Features instead of a FeatureCollection
65297 function getFeatures(gj) {
65298 if (!gj) return [];
65300 if (gj.type === 'FeatureCollection') {
65301 return gj.features;
65307 function featureKey(d) {
65308 return d.__featurehash__;
65311 function isPolygon(d) {
65312 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
65315 function clipPathID(d) {
65316 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
65319 function featureClasses(d) {
65320 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
65323 function drawData(selection) {
65324 var vtService = getService();
65325 var getPath = svgPath(projection).geojson;
65326 var getAreaPath = svgPath(projection, null, true).geojson;
65327 var hasData = drawData.hasData();
65328 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
65329 layer.exit().remove();
65330 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
65331 var surface = context.surface();
65332 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
65335 var geoData, polygonData;
65337 if (_template && vtService) {
65338 // fetch data from vector tile service
65339 var sourceID = _template;
65340 vtService.loadTiles(sourceID, _template, projection);
65341 geoData = vtService.data(sourceID, projection);
65343 geoData = getFeatures(_geojson);
65346 geoData = geoData.filter(getPath);
65347 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
65349 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
65350 clipPaths.exit().remove();
65351 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
65352 clipPathsEnter.append('path');
65353 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
65355 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
65356 datagroups = datagroups.enter().append('g').attr('class', function (d) {
65357 return 'datagroup datagroup-' + d;
65358 }).merge(datagroups); // Draw paths
65365 var paths = datagroups.selectAll('path').data(function (layer) {
65366 return pathData[layer];
65367 }, featureKey); // exit
65369 paths.exit().remove(); // enter/update
65371 paths = paths.enter().append('path').attr('class', function (d) {
65372 var datagroup = this.parentNode.__data__;
65373 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
65374 }).attr('clip-path', function (d) {
65375 var datagroup = this.parentNode.__data__;
65376 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
65377 }).merge(paths).attr('d', function (d) {
65378 var datagroup = this.parentNode.__data__;
65379 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
65382 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
65384 function drawLabels(selection, textClass, data) {
65385 var labelPath = d3_geoPath(projection);
65386 var labelData = data.filter(function (d) {
65387 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
65389 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
65391 labels.exit().remove(); // enter/update
65393 labels = labels.enter().append('text').attr('class', function (d) {
65394 return textClass + ' ' + featureClasses(d);
65395 }).merge(labels).text(function (d) {
65396 return d.properties.desc || d.properties.name;
65397 }).attr('x', function (d) {
65398 var centroid = labelPath.centroid(d);
65399 return centroid[0] + 11;
65400 }).attr('y', function (d) {
65401 var centroid = labelPath.centroid(d);
65402 return centroid[1];
65407 function getExtension(fileName) {
65408 if (!fileName) return;
65409 var re = /\.(gpx|kml|(geo)?json)$/i;
65410 var match = fileName.toLowerCase().match(re);
65411 return match && match.length && match[0];
65414 function xmlToDom(textdata) {
65415 return new DOMParser().parseFromString(textdata, 'text/xml');
65418 drawData.setFile = function (extension, data) {
65425 switch (extension) {
65427 gj = togeojson.gpx(xmlToDom(data));
65431 gj = togeojson.kml(xmlToDom(data));
65436 gj = JSON.parse(data);
65442 if (Object.keys(gj).length) {
65443 _geojson = ensureIDs(gj);
65444 _src = extension + ' data file';
65448 dispatch.call('change');
65452 drawData.showLabels = function (val) {
65453 if (!arguments.length) return _showLabels;
65458 drawData.enabled = function (val) {
65459 if (!arguments.length) return _enabled;
65468 dispatch.call('change');
65472 drawData.hasData = function () {
65473 var gj = _geojson || {};
65474 return !!(_template || Object.keys(gj).length);
65477 drawData.template = function (val, src) {
65478 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
65480 var osm = context.connection();
65483 var blocklists = osm.imageryBlocklists();
65488 for (var i = 0; i < blocklists.length; i++) {
65489 regex = blocklists[i];
65490 fail = regex.test(val);
65493 } // ensure at least one test was run.
65497 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
65498 fail = regex.test(val);
65504 _geojson = null; // strip off the querystring/hash from the template,
65505 // it often includes the access token
65507 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
65508 dispatch.call('change');
65512 drawData.geojson = function (gj, src) {
65513 if (!arguments.length) return _geojson;
65520 if (Object.keys(gj).length) {
65521 _geojson = ensureIDs(gj);
65522 _src = src || 'unknown.geojson';
65525 dispatch.call('change');
65529 drawData.fileList = function (fileList) {
65530 if (!arguments.length) return _fileList;
65532 _fileList = fileList;
65535 if (!fileList || !fileList.length) return this;
65536 var f = fileList[0];
65537 var extension = getExtension(f.name);
65538 var reader = new FileReader();
65540 reader.onload = function () {
65541 return function (e) {
65542 drawData.setFile(extension, e.target.result);
65546 reader.readAsText(f);
65550 drawData.url = function (url, defaultExtension) {
65554 _src = null; // strip off any querystring/hash from the url before checking extension
65556 var testUrl = url.split(/[?#]/)[0];
65557 var extension = getExtension(testUrl) || defaultExtension;
65561 d3_text(url).then(function (data) {
65562 drawData.setFile(extension, data);
65563 })["catch"](function () {
65567 drawData.template(url);
65573 drawData.getSrc = function () {
65577 drawData.fitZoom = function () {
65578 var features = getFeatures(_geojson);
65579 if (!features.length) return;
65580 var map = context.map();
65581 var viewport = map.trimmedExtent().polygon();
65582 var coords = features.reduce(function (coords, feature) {
65583 var geom = feature.geometry;
65584 if (!geom) return coords;
65585 var c = geom.coordinates;
65586 /* eslint-disable no-fallthrough */
65588 switch (geom.type) {
65596 case 'MultiPolygon':
65597 c = utilArrayFlatten(c);
65600 case 'MultiLineString':
65601 c = utilArrayFlatten(c);
65604 /* eslint-enable no-fallthrough */
65607 return utilArrayUnion(coords, c);
65610 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
65611 var extent = geoExtent(d3_geoBounds({
65612 type: 'LineString',
65613 coordinates: coords
65615 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
65625 function svgDebug(projection, context) {
65626 function drawDebug(selection) {
65627 var showTile = context.getDebug('tile');
65628 var showCollision = context.getDebug('collision');
65629 var showImagery = context.getDebug('imagery');
65630 var showTouchTargets = context.getDebug('target');
65631 var showDownloaded = context.getDebug('downloaded');
65632 var debugData = [];
65641 if (showCollision) {
65655 if (showTouchTargets) {
65658 label: 'touchTargets'
65662 if (showDownloaded) {
65665 label: 'downloaded'
65669 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
65670 legend.exit().remove();
65671 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
65672 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
65675 legendItems.exit().remove();
65676 legendItems.enter().append('span').attr('class', function (d) {
65677 return "debug-legend-item ".concat(d["class"]);
65678 }).text(function (d) {
65681 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
65682 layer.exit().remove();
65683 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
65685 var extent = context.map().extent();
65686 _mainFileFetcher.get('imagery').then(function (d) {
65687 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
65688 var features = hits.map(function (d) {
65689 return d.features[d.id];
65691 var imagery = layer.selectAll('path.debug-imagery').data(features);
65692 imagery.exit().remove();
65693 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
65694 })["catch"](function () {
65698 var osm = context.connection();
65699 var dataDownloaded = [];
65701 if (osm && showDownloaded) {
65702 var rtree = osm.caches('get').tile.rtree;
65703 dataDownloaded = rtree.all().map(function (bbox) {
65711 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
65717 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
65718 downloaded.exit().remove();
65719 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
65721 layer.selectAll('path').attr('d', svgPath(projection).geojson);
65722 } // This looks strange because `enabled` methods on other layers are
65723 // chainable getter/setters, and this one is just a getter.
65726 drawDebug.enabled = function () {
65727 if (!arguments.length) {
65728 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
65738 A standalone SVG element that contains only a `defs` sub-element. To be
65739 used once globally, since defs IDs must be unique within a document.
65742 function svgDefs(context) {
65743 var _defsSelection = select(null);
65745 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
65747 function drawDefs(selection) {
65748 _defsSelection = selection.append('defs'); // add markers
65750 _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
65751 // (they can't inherit it from the line they're attached to),
65752 // so we need to manually define markers for each color of tag
65753 // (also, it's slightly nicer if we can control the
65754 // positioning for different tags)
65757 function addSidedMarker(name, color, offset) {
65758 _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);
65761 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
65762 // the water side, so let's color them blue (with a gap) to
65763 // give a stronger indication
65765 addSidedMarker('coastline', '#77dede', 1);
65766 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
65767 // from the line visually suits that
65769 addSidedMarker('barrier', '#ddd', 1);
65770 addSidedMarker('man_made', '#fff', 0);
65772 _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');
65774 _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
65777 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
65778 ['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) {
65779 return 'ideditor-pattern-' + d[0];
65780 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
65782 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
65783 return 'pattern-color-' + d[0];
65785 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
65786 return context.imagePath('pattern/' + d[1] + '.png');
65787 }); // add clip paths
65789 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
65790 return 'ideditor-clip-square-' + d;
65791 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
65793 }).attr('height', function (d) {
65795 }); // add symbol spritesheets
65798 addSprites(_spritesheetIds, true);
65801 function addSprites(ids, overrideColors) {
65802 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
65804 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
65806 spritesheets.enter().append('g').attr('class', function (d) {
65807 return 'spritesheet spritesheet-' + d;
65808 }).each(function (d) {
65809 var url = context.imagePath(d + '.svg');
65810 var node = select(this).node();
65811 svg(url).then(function (svg) {
65812 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
65814 if (overrideColors && d !== 'iD-sprite') {
65815 // allow icon colors to be overridden..
65816 select(node).selectAll('path').attr('fill', 'currentColor');
65818 })["catch"](function () {
65822 spritesheets.exit().remove();
65825 drawDefs.addSprites = addSprites;
65829 var _layerEnabled$2 = false;
65833 function svgKeepRight(projection, context, dispatch) {
65834 var throttledRedraw = throttle(function () {
65835 return dispatch.call('change');
65839 var touchLayer = select(null);
65840 var drawLayer = select(null);
65841 var layerVisible = false;
65843 function markerPath(selection, klass) {
65844 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');
65845 } // Loosely-coupled keepRight service for fetching issues.
65848 function getService() {
65849 if (services.keepRight && !_qaService$2) {
65850 _qaService$2 = services.keepRight;
65852 _qaService$2.on('loaded', throttledRedraw);
65853 } else if (!services.keepRight && _qaService$2) {
65854 _qaService$2 = null;
65857 return _qaService$2;
65858 } // Show the markers
65861 function editOn() {
65862 if (!layerVisible) {
65863 layerVisible = true;
65864 drawLayer.style('display', 'block');
65866 } // Immediately remove the markers and their touch targets
65869 function editOff() {
65870 if (layerVisible) {
65871 layerVisible = false;
65872 drawLayer.style('display', 'none');
65873 drawLayer.selectAll('.qaItem.keepRight').remove();
65874 touchLayer.selectAll('.qaItem.keepRight').remove();
65876 } // Enable the layer. This shows the markers and transitions them to visible.
65879 function layerOn() {
65881 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
65882 return dispatch.call('change');
65884 } // Disable the layer. This transitions the layer invisible and then hides the markers.
65887 function layerOff() {
65888 throttledRedraw.cancel();
65889 drawLayer.interrupt();
65890 touchLayer.selectAll('.qaItem.keepRight').remove();
65891 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
65893 dispatch.call('change');
65895 } // Update the issue markers
65898 function updateMarkers() {
65899 if (!layerVisible || !_layerEnabled$2) return;
65900 var service = getService();
65901 var selectedID = context.selectedErrorID();
65902 var data = service ? service.getItems(projection) : [];
65903 var getTransform = svgPointTransform(projection); // Draw markers..
65905 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
65909 markers.exit().remove(); // enter
65911 var markersEnter = markers.enter().append('g').attr('class', function (d) {
65912 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
65914 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
65915 markersEnter.append('path').call(markerPath, 'shadow');
65916 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
65918 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
65919 return d.id === selectedID;
65920 }).attr('transform', getTransform); // Draw targets..
65922 if (touchLayer.empty()) return;
65923 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
65924 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
65928 targets.exit().remove(); // enter/update
65930 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
65931 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
65932 }).attr('transform', getTransform);
65934 function sortY(a, b) {
65935 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];
65937 } // Draw the keepRight layer and schedule loading issues and updating markers.
65940 function drawKeepRight(selection) {
65941 var service = getService();
65942 var surface = context.surface();
65944 if (surface && !surface.empty()) {
65945 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
65948 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
65949 drawLayer.exit().remove();
65950 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
65952 if (_layerEnabled$2) {
65953 if (service && ~~context.map().zoom() >= minZoom) {
65955 service.loadIssues(projection);
65961 } // Toggles the layer on and off
65964 drawKeepRight.enabled = function (val) {
65965 if (!arguments.length) return _layerEnabled$2;
65966 _layerEnabled$2 = val;
65968 if (_layerEnabled$2) {
65973 if (context.selectedErrorID()) {
65974 context.enter(modeBrowse(context));
65978 dispatch.call('change');
65982 drawKeepRight.supported = function () {
65983 return !!getService();
65986 return drawKeepRight;
65989 function svgGeolocate(projection) {
65990 var layer = select(null);
65995 if (svgGeolocate.initialized) return; // run once
65997 svgGeolocate.enabled = false;
65998 svgGeolocate.initialized = true;
66001 function showLayer() {
66002 layer.style('display', 'block');
66005 function hideLayer() {
66006 layer.transition().duration(250).style('opacity', 0);
66009 function layerOn() {
66010 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
66013 function layerOff() {
66014 layer.style('display', 'none');
66017 function transform(d) {
66018 return svgPointTransform(projection)(d);
66021 function accuracy(accuracy, loc) {
66022 // converts accuracy to pixels...
66023 var degreesRadius = geoMetersToLat(accuracy),
66024 tangentLoc = [loc[0], loc[1] + degreesRadius],
66025 projectedTangent = projection(tangentLoc),
66026 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
66028 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
66031 function update() {
66032 var geolocation = {
66033 loc: [_position.coords.longitude, _position.coords.latitude]
66035 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
66036 groups.exit().remove();
66037 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
66038 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');
66039 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');
66040 groups.merge(pointsEnter).attr('transform', transform);
66041 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
66044 function drawLocation(selection) {
66045 var enabled = svgGeolocate.enabled;
66046 layer = selection.selectAll('.layer-geolocate').data([0]);
66047 layer.exit().remove();
66048 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
66049 layerEnter.append('g').attr('class', 'geolocations');
66050 layer = layerEnter.merge(layer);
66059 drawLocation.enabled = function (position, enabled) {
66060 if (!arguments.length) return svgGeolocate.enabled;
66061 _position = position;
66062 svgGeolocate.enabled = enabled;
66064 if (svgGeolocate.enabled) {
66075 return drawLocation;
66078 function svgLabels(projection, context) {
66079 var path = d3_geoPath(projection);
66080 var detected = utilDetect();
66081 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
66083 var _rdrawn = new RBush();
66085 var _rskipped = new RBush();
66087 var _textWidthCache = {};
66088 var _entitybboxes = {}; // Listed from highest to lowest priority
66090 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]];
66092 function shouldSkipIcon(preset) {
66093 var noIcons = ['building', 'landuse', 'natural'];
66094 return noIcons.some(function (s) {
66095 return preset.id.indexOf(s) >= 0;
66099 function get(array, prop) {
66100 return function (d, i) {
66101 return array[i][prop];
66105 function textWidth(text, size, elem) {
66106 var c = _textWidthCache[size];
66107 if (!c) c = _textWidthCache[size] = {};
66112 c[text] = elem.getComputedTextLength();
66115 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
66117 if (str === null) {
66118 return size / 3 * 2 * text.length;
66120 return size / 3 * (2 * text.length + str.length);
66125 function drawLinePaths(selection, entities, filter, classes, labels) {
66126 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
66128 paths.exit().remove(); // enter/update
66130 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
66131 return 'ideditor-labelpath-' + d.id;
66132 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
66135 function drawLineLabels(selection, entities, filter, classes, labels) {
66136 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
66138 texts.exit().remove(); // enter
66140 texts.enter().append('text').attr('class', function (d, i) {
66141 return classes + ' ' + labels[i].classes + ' ' + d.id;
66142 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
66144 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
66145 return '#ideditor-labelpath-' + d.id;
66146 }).text(utilDisplayNameForPath);
66149 function drawPointLabels(selection, entities, filter, classes, labels) {
66150 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
66152 texts.exit().remove(); // enter/update
66154 texts.enter().append('text').attr('class', function (d, i) {
66155 return classes + ' ' + labels[i].classes + ' ' + d.id;
66156 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
66157 textWidth(utilDisplayName(d), labels[i].height, this);
66161 function drawAreaLabels(selection, entities, filter, classes, labels) {
66162 entities = entities.filter(hasText);
66163 labels = labels.filter(hasText);
66164 drawPointLabels(selection, entities, filter, classes, labels);
66166 function hasText(d, i) {
66167 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
66171 function drawAreaIcons(selection, entities, filter, classes, labels) {
66172 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
66174 icons.exit().remove(); // enter/update
66176 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) {
66177 var preset = _mainPresetIndex.match(d, context.graph());
66178 var picon = preset && preset.icon;
66183 var isMaki = /^maki-/.test(picon);
66184 return '#' + picon + (isMaki ? '-15' : '');
66189 function drawCollisionBoxes(selection, rtree, which) {
66190 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
66193 if (context.getDebug('collision')) {
66194 gj = rtree.all().map(function (d) {
66197 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
66202 var boxes = selection.selectAll('.' + which).data(gj); // exit
66204 boxes.exit().remove(); // enter/update
66206 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
66209 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
66210 var wireframe = context.surface().classed('fill-wireframe');
66211 var zoom = geoScaleToZoom(projection.scale());
66212 var labelable = [];
66213 var renderNodeAs = {};
66214 var i, j, k, entity, geometry;
66216 for (i = 0; i < labelStack.length; i++) {
66217 labelable.push([]);
66225 _entitybboxes = {};
66227 for (i = 0; i < entities.length; i++) {
66228 entity = entities[i];
66229 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
66231 for (j = 0; j < toRemove.length; j++) {
66232 _rdrawn.remove(toRemove[j]);
66234 _rskipped.remove(toRemove[j]);
66237 } // Loop through all the entities to do some preprocessing
66240 for (i = 0; i < entities.length; i++) {
66241 entity = entities[i];
66242 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
66244 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
66245 var hasDirections = entity.directions(graph, projection).length;
66248 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
66249 renderNodeAs[entity.id] = 'point';
66250 markerPadding = 20; // extra y for marker height
66252 renderNodeAs[entity.id] = 'vertex';
66256 var coord = projection(entity.loc);
66257 var nodePadding = 10;
66259 minX: coord[0] - nodePadding,
66260 minY: coord[1] - nodePadding - markerPadding,
66261 maxX: coord[0] + nodePadding,
66262 maxY: coord[1] + nodePadding
66264 doInsert(bbox, entity.id + 'P');
66265 } // From here on, treat vertices like points
66268 if (geometry === 'vertex') {
66269 geometry = 'point';
66270 } // Determine which entities are label-able
66273 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
66274 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
66275 if (!icon && !utilDisplayName(entity)) continue;
66277 for (k = 0; k < labelStack.length; k++) {
66278 var matchGeom = labelStack[k][0];
66279 var matchKey = labelStack[k][1];
66280 var matchVal = labelStack[k][2];
66281 var hasVal = entity.tags[matchKey];
66283 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
66284 labelable[k].push(entity);
66299 }; // Try and find a valid label for labellable entities
66301 for (k = 0; k < labelable.length; k++) {
66302 var fontSize = labelStack[k][3];
66304 for (i = 0; i < labelable[k].length; i++) {
66305 entity = labelable[k][i];
66306 geometry = entity.geometry(graph);
66307 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
66308 var name = getName(entity);
66309 var width = name && textWidth(name, fontSize);
66312 if (geometry === 'point' || geometry === 'vertex') {
66313 // no point or vertex labels in wireframe mode
66314 // no vertex labels at low zooms (vertices have no icons)
66315 if (wireframe) continue;
66316 var renderAs = renderNodeAs[entity.id];
66317 if (renderAs === 'vertex' && zoom < 17) continue;
66318 p = getPointLabel(entity, width, fontSize, renderAs);
66319 } else if (geometry === 'line') {
66320 p = getLineLabel(entity, width, fontSize);
66321 } else if (geometry === 'area') {
66322 p = getAreaLabel(entity, width, fontSize);
66326 if (geometry === 'vertex') {
66327 geometry = 'point';
66328 } // treat vertex like point
66331 p.classes = geometry + ' tag-' + labelStack[k][1];
66332 positions[geometry].push(p);
66333 labelled[geometry].push(entity);
66338 function isInterestingVertex(entity) {
66339 var selectedIDs = context.selectedIDs();
66340 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
66341 return selectedIDs.indexOf(parent.id) !== -1;
66345 function getPointLabel(entity, width, height, geometry) {
66346 var y = geometry === 'point' ? -12 : 0;
66347 var pointOffsets = {
66348 ltr: [15, y, 'start'],
66349 rtl: [-15, y, 'end']
66351 var textDirection = _mainLocalizer.textDirection();
66352 var coord = projection(entity.loc);
66353 var textPadding = 2;
66354 var offset = pointOffsets[textDirection];
66358 x: coord[0] + offset[0],
66359 y: coord[1] + offset[1],
66360 textAnchor: offset[2]
66361 }; // insert a collision box for the text label..
66365 if (textDirection === 'rtl') {
66367 minX: p.x - width - textPadding,
66368 minY: p.y - height / 2 - textPadding,
66369 maxX: p.x + textPadding,
66370 maxY: p.y + height / 2 + textPadding
66374 minX: p.x - textPadding,
66375 minY: p.y - height / 2 - textPadding,
66376 maxX: p.x + width + textPadding,
66377 maxY: p.y + height / 2 + textPadding
66381 if (tryInsert([bbox], entity.id, true)) {
66386 function getLineLabel(entity, width, height) {
66387 var viewport = geoExtent(context.projection.clipExtent()).polygon();
66388 var points = graph.childNodes(entity).map(function (node) {
66389 return projection(node.loc);
66391 var length = geoPathLength(points);
66392 if (length < width + 20) return; // % along the line to attempt to place the label
66394 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
66397 for (var i = 0; i < lineOffsets.length; i++) {
66398 var offset = lineOffsets[i];
66399 var middle = offset / 100 * length;
66400 var start = middle - width / 2;
66401 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
66403 var sub = subpath(points, start, start + width);
66405 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
66409 var isReverse = reverse(sub);
66412 sub = sub.reverse();
66416 var boxsize = (height + 2) / 2;
66418 for (var j = 0; j < sub.length - 1; j++) {
66420 var b = sub[j + 1]; // split up the text into small collision boxes
66422 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
66424 for (var box = 0; box < num; box++) {
66425 var p = geoVecInterp(a, b, box / num);
66426 var x0 = p[0] - boxsize - padding;
66427 var y0 = p[1] - boxsize - padding;
66428 var x1 = p[0] + boxsize + padding;
66429 var y1 = p[1] + boxsize + padding;
66431 minX: Math.min(x0, x1),
66432 minY: Math.min(y0, y1),
66433 maxX: Math.max(x0, x1),
66434 maxY: Math.max(y0, y1)
66439 if (tryInsert(bboxes, entity.id, false)) {
66442 'font-size': height + 2,
66443 lineString: lineString(sub),
66444 startOffset: offset + '%'
66449 function reverse(p) {
66450 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
66451 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
66454 function lineString(points) {
66455 return 'M' + points.join('L');
66458 function subpath(points, from, to) {
66460 var start, end, i0, i1;
66462 for (var i = 0; i < points.length - 1; i++) {
66464 var b = points[i + 1];
66465 var current = geoVecLength(a, b);
66468 if (!start && sofar + current >= from) {
66469 portion = (from - sofar) / current;
66470 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
66474 if (!end && sofar + current >= to) {
66475 portion = (to - sofar) / current;
66476 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
66483 var result = points.slice(i0, i1);
66484 result.unshift(start);
66490 function getAreaLabel(entity, width, height) {
66491 var centroid = path.centroid(entity.asGeoJSON(graph));
66492 var extent = entity.extent(graph);
66493 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
66494 if (isNaN(centroid[0]) || areaWidth < 20) return;
66495 var preset = _mainPresetIndex.match(entity, context.graph());
66496 var picon = preset && preset.icon;
66502 // icon and label..
66504 addLabel(iconSize + padding);
66514 function addIcon() {
66515 var iconX = centroid[0] - iconSize / 2;
66516 var iconY = centroid[1] - iconSize / 2;
66520 maxX: iconX + iconSize,
66521 maxY: iconY + iconSize
66524 if (tryInsert([bbox], entity.id + 'I', true)) {
66525 p.transform = 'translate(' + iconX + ',' + iconY + ')';
66532 function addLabel(yOffset) {
66533 if (width && areaWidth >= width + 20) {
66534 var labelX = centroid[0];
66535 var labelY = centroid[1] + yOffset;
66537 minX: labelX - width / 2 - padding,
66538 minY: labelY - height / 2 - padding,
66539 maxX: labelX + width / 2 + padding,
66540 maxY: labelY + height / 2 + padding
66543 if (tryInsert([bbox], entity.id, true)) {
66546 p.textAnchor = 'middle';
66554 } // force insert a singular bounding box
66555 // singular box only, no array, id better be unique
66558 function doInsert(bbox, id) {
66560 var oldbox = _entitybboxes[id];
66563 _rdrawn.remove(oldbox);
66566 _entitybboxes[id] = bbox;
66568 _rdrawn.insert(bbox);
66571 function tryInsert(bboxes, id, saveSkipped) {
66572 var skipped = false;
66574 for (var i = 0; i < bboxes.length; i++) {
66575 var bbox = bboxes[i];
66576 bbox.id = id; // Check that label is visible
66578 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
66583 if (_rdrawn.collides(bbox)) {
66589 _entitybboxes[id] = bboxes;
66593 _rskipped.load(bboxes);
66596 _rdrawn.load(bboxes);
66602 var layer = selection.selectAll('.layer-osm.labels');
66603 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
66604 return 'labels-group ' + d;
66606 var halo = layer.selectAll('.labels-group.halo');
66607 var label = layer.selectAll('.labels-group.label');
66608 var debug = layer.selectAll('.labels-group.debug'); // points
66610 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
66611 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
66613 drawLinePaths(layer, labelled.line, filter, '', positions.line);
66614 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
66615 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
66617 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
66618 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
66619 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
66620 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
66622 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
66623 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
66624 layer.call(filterLabels);
66627 function filterLabels(selection) {
66628 var drawLayer = selection.selectAll('.layer-osm.labels');
66629 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
66630 layers.selectAll('.nolabel').classed('nolabel', false);
66631 var mouse = context.map().mouse();
66632 var graph = context.graph();
66633 var selectedIDs = context.selectedIDs();
66635 var pad, bbox; // hide labels near the mouse
66640 minX: mouse[0] - pad,
66641 minY: mouse[1] - pad,
66642 maxX: mouse[0] + pad,
66643 maxY: mouse[1] + pad
66646 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
66650 ids.push.apply(ids, nearMouse);
66651 } // hide labels on selected nodes (they look weird when dragging / haloed)
66654 for (var i = 0; i < selectedIDs.length; i++) {
66655 var entity = graph.hasEntity(selectedIDs[i]);
66657 if (entity && entity.type === 'node') {
66658 ids.push(selectedIDs[i]);
66662 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
66664 var debug = selection.selectAll('.labels-group.debug');
66667 if (context.getDebug('collision')) {
66670 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
66674 var box = debug.selectAll('.debug-mouse').data(gj); // exit
66676 box.exit().remove(); // enter/update
66678 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
66681 var throttleFilterLabels = throttle(filterLabels, 100);
66683 drawLabels.observe = function (selection) {
66684 var listener = function listener() {
66685 throttleFilterLabels(selection);
66688 selection.on('mousemove.hidelabels', listener);
66689 context.on('enter.hidelabels', listener);
66692 drawLabels.off = function (selection) {
66693 throttleFilterLabels.cancel();
66694 selection.on('mousemove.hidelabels', null);
66695 context.on('enter.hidelabels', null);
66701 var _layerEnabled$1 = false;
66705 function svgImproveOSM(projection, context, dispatch) {
66706 var throttledRedraw = throttle(function () {
66707 return dispatch.call('change');
66711 var touchLayer = select(null);
66712 var drawLayer = select(null);
66713 var layerVisible = false;
66715 function markerPath(selection, klass) {
66716 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');
66717 } // Loosely-coupled improveOSM service for fetching issues
66720 function getService() {
66721 if (services.improveOSM && !_qaService$1) {
66722 _qaService$1 = services.improveOSM;
66724 _qaService$1.on('loaded', throttledRedraw);
66725 } else if (!services.improveOSM && _qaService$1) {
66726 _qaService$1 = null;
66729 return _qaService$1;
66730 } // Show the markers
66733 function editOn() {
66734 if (!layerVisible) {
66735 layerVisible = true;
66736 drawLayer.style('display', 'block');
66738 } // Immediately remove the markers and their touch targets
66741 function editOff() {
66742 if (layerVisible) {
66743 layerVisible = false;
66744 drawLayer.style('display', 'none');
66745 drawLayer.selectAll('.qaItem.improveOSM').remove();
66746 touchLayer.selectAll('.qaItem.improveOSM').remove();
66748 } // Enable the layer. This shows the markers and transitions them to visible.
66751 function layerOn() {
66753 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
66754 return dispatch.call('change');
66756 } // Disable the layer. This transitions the layer invisible and then hides the markers.
66759 function layerOff() {
66760 throttledRedraw.cancel();
66761 drawLayer.interrupt();
66762 touchLayer.selectAll('.qaItem.improveOSM').remove();
66763 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
66765 dispatch.call('change');
66767 } // Update the issue markers
66770 function updateMarkers() {
66771 if (!layerVisible || !_layerEnabled$1) return;
66772 var service = getService();
66773 var selectedID = context.selectedErrorID();
66774 var data = service ? service.getItems(projection) : [];
66775 var getTransform = svgPointTransform(projection); // Draw markers..
66777 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
66781 markers.exit().remove(); // enter
66783 var markersEnter = markers.enter().append('g').attr('class', function (d) {
66784 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
66786 markersEnter.append('polygon').call(markerPath, 'shadow');
66787 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
66788 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
66789 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
66790 var picon = d.icon;
66795 var isMaki = /^maki-/.test(picon);
66796 return "#".concat(picon).concat(isMaki ? '-11' : '');
66800 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
66801 return d.id === selectedID;
66802 }).attr('transform', getTransform); // Draw targets..
66804 if (touchLayer.empty()) return;
66805 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66806 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
66810 targets.exit().remove(); // enter/update
66812 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
66813 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
66814 }).attr('transform', getTransform);
66816 function sortY(a, b) {
66817 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
66819 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
66822 function drawImproveOSM(selection) {
66823 var service = getService();
66824 var surface = context.surface();
66826 if (surface && !surface.empty()) {
66827 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
66830 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
66831 drawLayer.exit().remove();
66832 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
66834 if (_layerEnabled$1) {
66835 if (service && ~~context.map().zoom() >= minZoom) {
66837 service.loadIssues(projection);
66843 } // Toggles the layer on and off
66846 drawImproveOSM.enabled = function (val) {
66847 if (!arguments.length) return _layerEnabled$1;
66848 _layerEnabled$1 = val;
66850 if (_layerEnabled$1) {
66855 if (context.selectedErrorID()) {
66856 context.enter(modeBrowse(context));
66860 dispatch.call('change');
66864 drawImproveOSM.supported = function () {
66865 return !!getService();
66868 return drawImproveOSM;
66871 var _layerEnabled = false;
66875 function svgOsmose(projection, context, dispatch) {
66876 var throttledRedraw = throttle(function () {
66877 return dispatch.call('change');
66881 var touchLayer = select(null);
66882 var drawLayer = select(null);
66883 var layerVisible = false;
66885 function markerPath(selection, klass) {
66886 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');
66887 } // Loosely-coupled osmose service for fetching issues
66890 function getService() {
66891 if (services.osmose && !_qaService) {
66892 _qaService = services.osmose;
66894 _qaService.on('loaded', throttledRedraw);
66895 } else if (!services.osmose && _qaService) {
66900 } // Show the markers
66903 function editOn() {
66904 if (!layerVisible) {
66905 layerVisible = true;
66906 drawLayer.style('display', 'block');
66908 } // Immediately remove the markers and their touch targets
66911 function editOff() {
66912 if (layerVisible) {
66913 layerVisible = false;
66914 drawLayer.style('display', 'none');
66915 drawLayer.selectAll('.qaItem.osmose').remove();
66916 touchLayer.selectAll('.qaItem.osmose').remove();
66918 } // Enable the layer. This shows the markers and transitions them to visible.
66921 function layerOn() {
66923 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
66924 return dispatch.call('change');
66926 } // Disable the layer. This transitions the layer invisible and then hides the markers.
66929 function layerOff() {
66930 throttledRedraw.cancel();
66931 drawLayer.interrupt();
66932 touchLayer.selectAll('.qaItem.osmose').remove();
66933 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
66935 dispatch.call('change');
66937 } // Update the issue markers
66940 function updateMarkers() {
66941 if (!layerVisible || !_layerEnabled) return;
66942 var service = getService();
66943 var selectedID = context.selectedErrorID();
66944 var data = service ? service.getItems(projection) : [];
66945 var getTransform = svgPointTransform(projection); // Draw markers..
66947 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
66951 markers.exit().remove(); // enter
66953 var markersEnter = markers.enter().append('g').attr('class', function (d) {
66954 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
66956 markersEnter.append('polygon').call(markerPath, 'shadow');
66957 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
66958 markersEnter.append('polygon').attr('fill', function (d) {
66959 return service.getColor(d.item);
66960 }).call(markerPath, 'qaItem-fill');
66961 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
66962 var picon = d.icon;
66967 var isMaki = /^maki-/.test(picon);
66968 return "#".concat(picon).concat(isMaki ? '-11' : '');
66972 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
66973 return d.id === selectedID;
66974 }).attr('transform', getTransform); // Draw targets..
66976 if (touchLayer.empty()) return;
66977 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
66978 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
66982 targets.exit().remove(); // enter/update
66984 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
66985 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
66986 }).attr('transform', getTransform);
66988 function sortY(a, b) {
66989 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
66991 } // Draw the Osmose layer and schedule loading issues and updating markers.
66994 function drawOsmose(selection) {
66995 var service = getService();
66996 var surface = context.surface();
66998 if (surface && !surface.empty()) {
66999 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
67002 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
67003 drawLayer.exit().remove();
67004 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
67006 if (_layerEnabled) {
67007 if (service && ~~context.map().zoom() >= minZoom) {
67009 service.loadIssues(projection);
67015 } // Toggles the layer on and off
67018 drawOsmose.enabled = function (val) {
67019 if (!arguments.length) return _layerEnabled;
67020 _layerEnabled = val;
67022 if (_layerEnabled) {
67023 // Strings supplied by Osmose fetched before showing layer for first time
67024 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
67025 // Also, If layer is toggled quickly multiple requests are sent
67026 getService().loadStrings().then(layerOn)["catch"](function (err) {
67027 console.log(err); // eslint-disable-line no-console
67032 if (context.selectedErrorID()) {
67033 context.enter(modeBrowse(context));
67037 dispatch.call('change');
67041 drawOsmose.supported = function () {
67042 return !!getService();
67048 function svgStreetside(projection, context, dispatch) {
67049 var throttledRedraw = throttle(function () {
67050 dispatch.call('change');
67054 var minMarkerZoom = 16;
67055 var minViewfieldZoom = 18;
67056 var layer = select(null);
67057 var _viewerYaw = 0;
67058 var _selectedSequence = null;
67067 if (svgStreetside.initialized) return; // run once
67069 svgStreetside.enabled = false;
67070 svgStreetside.initialized = true;
67077 function getService() {
67078 if (services.streetside && !_streetside) {
67079 _streetside = services.streetside;
67081 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
67082 } else if (!services.streetside && _streetside) {
67083 _streetside = null;
67086 return _streetside;
67093 function showLayer() {
67094 var service = getService();
67095 if (!service) return;
67097 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
67098 dispatch.call('change');
67106 function hideLayer() {
67107 throttledRedraw.cancel();
67108 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
67115 function editOn() {
67116 layer.style('display', 'block');
67123 function editOff() {
67124 layer.selectAll('.viewfield-group').remove();
67125 layer.style('display', 'none');
67128 * click() Handles 'bubble' point click event.
67132 function click(d3_event, d) {
67133 var service = getService();
67134 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
67136 if (d.sequenceKey !== _selectedSequence) {
67137 _viewerYaw = 0; // reset
67140 _selectedSequence = d.sequenceKey;
67141 service.ensureViewerLoaded(context).then(function () {
67142 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
67144 context.map().centerEase(d.loc);
67151 function mouseover(d3_event, d) {
67152 var service = getService();
67153 if (service) service.setStyles(context, d);
67160 function mouseout() {
67161 var service = getService();
67162 if (service) service.setStyles(context, null);
67169 function transform(d) {
67170 var t = svgPointTransform(projection)(d);
67171 var rot = d.ca + _viewerYaw;
67174 t += ' rotate(' + Math.floor(rot) + ',0,0)';
67180 function viewerChanged() {
67181 var service = getService();
67182 if (!service) return;
67183 var viewer = service.viewer();
67184 if (!viewer) return; // update viewfield rotation
67186 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
67187 // e.g. during drags or easing.
67189 if (context.map().isTransformed()) return;
67190 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
67193 function filterBubbles(bubbles) {
67194 var fromDate = context.photos().fromDate();
67195 var toDate = context.photos().toDate();
67196 var usernames = context.photos().usernames();
67199 var fromTimestamp = new Date(fromDate).getTime();
67200 bubbles = bubbles.filter(function (bubble) {
67201 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
67206 var toTimestamp = new Date(toDate).getTime();
67207 bubbles = bubbles.filter(function (bubble) {
67208 return new Date(bubble.captured_at).getTime() <= toTimestamp;
67213 bubbles = bubbles.filter(function (bubble) {
67214 return usernames.indexOf(bubble.captured_by) !== -1;
67221 function filterSequences(sequences) {
67222 var fromDate = context.photos().fromDate();
67223 var toDate = context.photos().toDate();
67224 var usernames = context.photos().usernames();
67227 var fromTimestamp = new Date(fromDate).getTime();
67228 sequences = sequences.filter(function (sequences) {
67229 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
67234 var toTimestamp = new Date(toDate).getTime();
67235 sequences = sequences.filter(function (sequences) {
67236 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
67241 sequences = sequences.filter(function (sequences) {
67242 return usernames.indexOf(sequences.properties.captured_by) !== -1;
67253 function update() {
67254 var viewer = context.container().select('.photoviewer');
67255 var selected = viewer.empty() ? undefined : viewer.datum();
67256 var z = ~~context.map().zoom();
67257 var showMarkers = z >= minMarkerZoom;
67258 var showViewfields = z >= minViewfieldZoom;
67259 var service = getService();
67260 var sequences = [];
67263 if (context.photos().showsPanoramic()) {
67264 sequences = service ? service.sequences(projection) : [];
67265 bubbles = service && showMarkers ? service.bubbles(projection) : [];
67266 sequences = filterSequences(sequences);
67267 bubbles = filterBubbles(bubbles);
67270 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
67271 return d.properties.key;
67274 traces.exit().remove(); // enter/update
67276 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
67277 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
67278 // force reenter once bubbles are attached to a sequence
67279 return d.key + (d.sequenceKey ? 'v1' : 'v0');
67282 groups.exit().remove(); // enter
67284 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
67285 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
67287 var markers = groups.merge(groupsEnter).sort(function (a, b) {
67288 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
67289 }).attr('transform', transform).select('.viewfield-scale');
67290 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
67291 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
67292 viewfields.exit().remove(); // viewfields may or may not be drawn...
67293 // but if they are, draw below the circles
67295 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
67297 function viewfieldPath() {
67298 var d = this.parentNode.__data__;
67301 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
67303 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
67309 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
67310 * 'svgStreetside()' is called from index.js
67314 function drawImages(selection) {
67315 var enabled = svgStreetside.enabled;
67316 var service = getService();
67317 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
67318 layer.exit().remove();
67319 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
67320 layerEnter.append('g').attr('class', 'sequences');
67321 layerEnter.append('g').attr('class', 'markers');
67322 layer = layerEnter.merge(layer);
67325 if (service && ~~context.map().zoom() >= minZoom) {
67328 service.loadBubbles(projection);
67335 * drawImages.enabled().
67339 drawImages.enabled = function (_) {
67340 if (!arguments.length) return svgStreetside.enabled;
67341 svgStreetside.enabled = _;
67343 if (svgStreetside.enabled) {
67345 context.photos().on('change.streetside', update);
67348 context.photos().on('change.streetside', null);
67351 dispatch.call('change');
67355 * drawImages.supported().
67359 drawImages.supported = function () {
67360 return !!getService();
67367 function svgMapillaryImages(projection, context, dispatch) {
67368 var throttledRedraw = throttle(function () {
67369 dispatch.call('change');
67373 var minMarkerZoom = 16;
67374 var minViewfieldZoom = 18;
67375 var layer = select(null);
67380 if (svgMapillaryImages.initialized) return; // run once
67382 svgMapillaryImages.enabled = false;
67383 svgMapillaryImages.initialized = true;
67386 function getService() {
67387 if (services.mapillary && !_mapillary) {
67388 _mapillary = services.mapillary;
67390 _mapillary.event.on('loadedImages', throttledRedraw);
67391 } else if (!services.mapillary && _mapillary) {
67398 function showLayer() {
67399 var service = getService();
67400 if (!service) return;
67402 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
67403 dispatch.call('change');
67407 function hideLayer() {
67408 throttledRedraw.cancel();
67409 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
67412 function editOn() {
67413 layer.style('display', 'block');
67416 function editOff() {
67417 layer.selectAll('.viewfield-group').remove();
67418 layer.style('display', 'none');
67421 function click(d3_event, image) {
67422 var service = getService();
67423 if (!service) return;
67424 service.ensureViewerLoaded(context).then(function () {
67425 service.selectImage(context, image.id).showViewer(context);
67427 context.map().centerEase(image.loc);
67430 function mouseover(d3_event, image) {
67431 var service = getService();
67432 if (service) service.setStyles(context, image);
67435 function mouseout() {
67436 var service = getService();
67437 if (service) service.setStyles(context, null);
67440 function transform(d) {
67441 var t = svgPointTransform(projection)(d);
67444 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
67450 function filterImages(images) {
67451 var showsPano = context.photos().showsPanoramic();
67452 var showsFlat = context.photos().showsFlat();
67453 var fromDate = context.photos().fromDate();
67454 var toDate = context.photos().toDate();
67456 if (!showsPano || !showsFlat) {
67457 images = images.filter(function (image) {
67458 if (image.is_pano) return showsPano;
67464 images = images.filter(function (image) {
67465 return new Date(image.captured_at).getTime() >= new Date(fromDate).getTime();
67470 images = images.filter(function (image) {
67471 return new Date(image.captured_at).getTime() <= new Date(toDate).getTime();
67478 function filterSequences(sequences) {
67479 var showsPano = context.photos().showsPanoramic();
67480 var showsFlat = context.photos().showsFlat();
67481 var fromDate = context.photos().fromDate();
67482 var toDate = context.photos().toDate();
67484 if (!showsPano || !showsFlat) {
67485 sequences = sequences.filter(function (sequence) {
67486 if (sequence.properties.hasOwnProperty('is_pano')) {
67487 if (sequence.properties.is_pano) return showsPano;
67496 sequences = sequences.filter(function (sequence) {
67497 return new Date(sequence.properties.captured_at).getTime() >= new Date(fromDate).getTime().toString();
67502 sequences = sequences.filter(function (sequence) {
67503 return new Date(sequence.properties.captured_at).getTime() <= new Date(toDate).getTime().toString();
67510 function update() {
67511 var z = ~~context.map().zoom();
67512 var showMarkers = z >= minMarkerZoom;
67513 var showViewfields = z >= minViewfieldZoom;
67514 var service = getService();
67515 var sequences = service ? service.sequences(projection) : [];
67516 var images = service && showMarkers ? service.images(projection) : [];
67517 images = filterImages(images);
67518 sequences = filterSequences(sequences);
67519 service.filterViewer(context);
67520 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
67521 return d.properties.id;
67524 traces.exit().remove(); // enter/update
67526 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
67527 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
67531 groups.exit().remove(); // enter
67533 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
67534 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
67536 var markers = groups.merge(groupsEnter).sort(function (a, b) {
67537 return b.loc[1] - a.loc[1]; // sort Y
67538 }).attr('transform', transform).select('.viewfield-scale');
67539 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
67540 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
67541 viewfields.exit().remove();
67542 viewfields.enter() // viewfields may or may not be drawn...
67543 .insert('path', 'circle') // but if they are, draw below the circles
67544 .attr('class', 'viewfield').classed('pano', function () {
67545 return this.parentNode.__data__.is_pano;
67546 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
67548 function viewfieldPath() {
67549 if (this.parentNode.__data__.is_pano) {
67550 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
67552 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
67557 function drawImages(selection) {
67558 var enabled = svgMapillaryImages.enabled;
67559 var service = getService();
67560 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
67561 layer.exit().remove();
67562 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
67563 layerEnter.append('g').attr('class', 'sequences');
67564 layerEnter.append('g').attr('class', 'markers');
67565 layer = layerEnter.merge(layer);
67568 if (service && ~~context.map().zoom() >= minZoom) {
67571 service.loadImages(projection);
67578 drawImages.enabled = function (_) {
67579 if (!arguments.length) return svgMapillaryImages.enabled;
67580 svgMapillaryImages.enabled = _;
67582 if (svgMapillaryImages.enabled) {
67584 context.photos().on('change.mapillary_images', update);
67587 context.photos().on('change.mapillary_images', null);
67590 dispatch.call('change');
67594 drawImages.supported = function () {
67595 return !!getService();
67602 function svgMapillaryPosition(projection, context) {
67603 var throttledRedraw = throttle(function () {
67608 var minViewfieldZoom = 18;
67609 var layer = select(null);
67613 var viewerCompassAngle;
67616 if (svgMapillaryPosition.initialized) return; // run once
67618 svgMapillaryPosition.initialized = true;
67621 function getService() {
67622 if (services.mapillary && !_mapillary) {
67623 _mapillary = services.mapillary;
67625 _mapillary.event.on('imageChanged', throttledRedraw);
67627 _mapillary.event.on('bearingChanged', function (e) {
67628 viewerCompassAngle = e.bearing;
67629 if (context.map().isTransformed()) return;
67630 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
67632 }).attr('transform', transform);
67634 } else if (!services.mapillary && _mapillary) {
67641 function editOn() {
67642 layer.style('display', 'block');
67645 function editOff() {
67646 layer.selectAll('.viewfield-group').remove();
67647 layer.style('display', 'none');
67650 function transform(d) {
67651 var t = svgPointTransform(projection)(d);
67653 if (d.is_pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
67654 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
67656 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
67662 function update() {
67663 var z = ~~context.map().zoom();
67664 var showViewfields = z >= minViewfieldZoom;
67665 var service = getService();
67666 var image = service && service.getActiveImage();
67667 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(image ? [image] : [], function (d) {
67671 groups.exit().remove(); // enter
67673 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
67674 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
67676 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
67677 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
67678 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
67679 viewfields.exit().remove();
67680 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z');
67683 function drawImages(selection) {
67684 var service = getService();
67685 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
67686 layer.exit().remove();
67687 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
67688 layerEnter.append('g').attr('class', 'markers');
67689 layer = layerEnter.merge(layer);
67691 if (service && ~~context.map().zoom() >= minZoom) {
67699 drawImages.enabled = function () {
67704 drawImages.supported = function () {
67705 return !!getService();
67712 function svgMapillarySigns(projection, context, dispatch) {
67713 var throttledRedraw = throttle(function () {
67714 dispatch.call('change');
67718 var layer = select(null);
67723 if (svgMapillarySigns.initialized) return; // run once
67725 svgMapillarySigns.enabled = false;
67726 svgMapillarySigns.initialized = true;
67729 function getService() {
67730 if (services.mapillary && !_mapillary) {
67731 _mapillary = services.mapillary;
67733 _mapillary.event.on('loadedSigns', throttledRedraw);
67734 } else if (!services.mapillary && _mapillary) {
67741 function showLayer() {
67742 var service = getService();
67743 if (!service) return;
67744 service.loadSignResources(context);
67748 function hideLayer() {
67749 throttledRedraw.cancel();
67753 function editOn() {
67754 layer.style('display', 'block');
67757 function editOff() {
67758 layer.selectAll('.icon-sign').remove();
67759 layer.style('display', 'none');
67762 function click(d3_event, d) {
67763 var service = getService();
67764 if (!service) return;
67765 context.map().centerEase(d.loc);
67766 var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
67767 service.getDetections(d.id).then(function (detections) {
67768 if (detections.length) {
67769 var imageId = detections[0].image.id;
67771 if (imageId === selectedImageId) {
67772 service.highlightDetection(detections[0]).selectImage(context, imageId);
67774 service.ensureViewerLoaded(context).then(function () {
67775 service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
67782 function filterData(detectedFeatures) {
67783 var fromDate = context.photos().fromDate();
67784 var toDate = context.photos().toDate();
67787 var fromTimestamp = new Date(fromDate).getTime();
67788 detectedFeatures = detectedFeatures.filter(function (feature) {
67789 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
67794 var toTimestamp = new Date(toDate).getTime();
67795 detectedFeatures = detectedFeatures.filter(function (feature) {
67796 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
67800 return detectedFeatures;
67803 function update() {
67804 var service = getService();
67805 var data = service ? service.signs(projection) : [];
67806 data = filterData(data);
67807 var transform = svgPointTransform(projection);
67808 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
67812 signs.exit().remove(); // enter
67814 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
67815 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
67816 return '#' + d.value;
67818 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
67820 signs.merge(enter).attr('transform', transform);
67823 function drawSigns(selection) {
67824 var enabled = svgMapillarySigns.enabled;
67825 var service = getService();
67826 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
67827 layer.exit().remove();
67828 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
67831 if (service && ~~context.map().zoom() >= minZoom) {
67834 service.loadSigns(projection);
67835 service.showSignDetections(true);
67839 } else if (service) {
67840 service.showSignDetections(false);
67844 drawSigns.enabled = function (_) {
67845 if (!arguments.length) return svgMapillarySigns.enabled;
67846 svgMapillarySigns.enabled = _;
67848 if (svgMapillarySigns.enabled) {
67850 context.photos().on('change.mapillary_signs', update);
67853 context.photos().on('change.mapillary_signs', null);
67856 dispatch.call('change');
67860 drawSigns.supported = function () {
67861 return !!getService();
67868 function svgMapillaryMapFeatures(projection, context, dispatch) {
67869 var throttledRedraw = throttle(function () {
67870 dispatch.call('change');
67874 var layer = select(null);
67879 if (svgMapillaryMapFeatures.initialized) return; // run once
67881 svgMapillaryMapFeatures.enabled = false;
67882 svgMapillaryMapFeatures.initialized = true;
67885 function getService() {
67886 if (services.mapillary && !_mapillary) {
67887 _mapillary = services.mapillary;
67889 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
67890 } else if (!services.mapillary && _mapillary) {
67897 function showLayer() {
67898 var service = getService();
67899 if (!service) return;
67900 service.loadObjectResources(context);
67904 function hideLayer() {
67905 throttledRedraw.cancel();
67909 function editOn() {
67910 layer.style('display', 'block');
67913 function editOff() {
67914 layer.selectAll('.icon-map-feature').remove();
67915 layer.style('display', 'none');
67918 function click(d3_event, d) {
67919 var service = getService();
67920 if (!service) return;
67921 context.map().centerEase(d.loc);
67922 var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
67923 service.getDetections(d.id).then(function (detections) {
67924 if (detections.length) {
67925 var imageId = detections[0].image.id;
67927 if (imageId === selectedImageId) {
67928 service.highlightDetection(detections[0]).selectImage(context, imageId);
67930 service.ensureViewerLoaded(context).then(function () {
67931 service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
67938 function filterData(detectedFeatures) {
67939 var fromDate = context.photos().fromDate();
67940 var toDate = context.photos().toDate();
67943 detectedFeatures = detectedFeatures.filter(function (feature) {
67944 return new Date(feature.last_seen_at).getTime() >= new Date(fromDate).getTime();
67949 detectedFeatures = detectedFeatures.filter(function (feature) {
67950 return new Date(feature.first_seen_at).getTime() <= new Date(toDate).getTime();
67954 return detectedFeatures;
67957 function update() {
67958 var service = getService();
67959 var data = service ? service.mapFeatures(projection) : [];
67960 data = filterData(data);
67961 var transform = svgPointTransform(projection);
67962 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
67966 mapFeatures.exit().remove(); // enter
67968 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
67969 enter.append('title').text(function (d) {
67970 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
67971 return _t('mapillary_map_features.' + id);
67973 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
67974 if (d.value === 'object--billboard') {
67975 // no billboard icon right now, so use the advertisement icon
67976 return '#object--sign--advertisement';
67979 return '#' + d.value;
67981 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
67983 mapFeatures.merge(enter).attr('transform', transform);
67986 function drawMapFeatures(selection) {
67987 var enabled = svgMapillaryMapFeatures.enabled;
67988 var service = getService();
67989 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
67990 layer.exit().remove();
67991 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
67994 if (service && ~~context.map().zoom() >= minZoom) {
67997 service.loadMapFeatures(projection);
67998 service.showFeatureDetections(true);
68002 } else if (service) {
68003 service.showFeatureDetections(false);
68007 drawMapFeatures.enabled = function (_) {
68008 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
68009 svgMapillaryMapFeatures.enabled = _;
68011 if (svgMapillaryMapFeatures.enabled) {
68013 context.photos().on('change.mapillary_map_features', update);
68016 context.photos().on('change.mapillary_map_features', null);
68019 dispatch.call('change');
68023 drawMapFeatures.supported = function () {
68024 return !!getService();
68028 return drawMapFeatures;
68031 function svgOpenstreetcamImages(projection, context, dispatch) {
68032 var throttledRedraw = throttle(function () {
68033 dispatch.call('change');
68037 var minMarkerZoom = 16;
68038 var minViewfieldZoom = 18;
68039 var layer = select(null);
68041 var _openstreetcam;
68044 if (svgOpenstreetcamImages.initialized) return; // run once
68046 svgOpenstreetcamImages.enabled = false;
68047 svgOpenstreetcamImages.initialized = true;
68050 function getService() {
68051 if (services.openstreetcam && !_openstreetcam) {
68052 _openstreetcam = services.openstreetcam;
68054 _openstreetcam.event.on('loadedImages', throttledRedraw);
68055 } else if (!services.openstreetcam && _openstreetcam) {
68056 _openstreetcam = null;
68059 return _openstreetcam;
68062 function showLayer() {
68063 var service = getService();
68064 if (!service) return;
68066 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
68067 dispatch.call('change');
68071 function hideLayer() {
68072 throttledRedraw.cancel();
68073 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
68076 function editOn() {
68077 layer.style('display', 'block');
68080 function editOff() {
68081 layer.selectAll('.viewfield-group').remove();
68082 layer.style('display', 'none');
68085 function click(d3_event, d) {
68086 var service = getService();
68087 if (!service) return;
68088 service.ensureViewerLoaded(context).then(function () {
68089 service.selectImage(context, d.key).showViewer(context);
68091 context.map().centerEase(d.loc);
68094 function mouseover(d3_event, d) {
68095 var service = getService();
68096 if (service) service.setStyles(context, d);
68099 function mouseout() {
68100 var service = getService();
68101 if (service) service.setStyles(context, null);
68104 function transform(d) {
68105 var t = svgPointTransform(projection)(d);
68108 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
68114 function filterImages(images) {
68115 var fromDate = context.photos().fromDate();
68116 var toDate = context.photos().toDate();
68117 var usernames = context.photos().usernames();
68120 var fromTimestamp = new Date(fromDate).getTime();
68121 images = images.filter(function (item) {
68122 return new Date(item.captured_at).getTime() >= fromTimestamp;
68127 var toTimestamp = new Date(toDate).getTime();
68128 images = images.filter(function (item) {
68129 return new Date(item.captured_at).getTime() <= toTimestamp;
68134 images = images.filter(function (item) {
68135 return usernames.indexOf(item.captured_by) !== -1;
68142 function filterSequences(sequences) {
68143 var fromDate = context.photos().fromDate();
68144 var toDate = context.photos().toDate();
68145 var usernames = context.photos().usernames();
68148 var fromTimestamp = new Date(fromDate).getTime();
68149 sequences = sequences.filter(function (image) {
68150 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
68155 var toTimestamp = new Date(toDate).getTime();
68156 sequences = sequences.filter(function (image) {
68157 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
68162 sequences = sequences.filter(function (image) {
68163 return usernames.indexOf(image.properties.captured_by) !== -1;
68170 function update() {
68171 var viewer = context.container().select('.photoviewer');
68172 var selected = viewer.empty() ? undefined : viewer.datum();
68173 var z = ~~context.map().zoom();
68174 var showMarkers = z >= minMarkerZoom;
68175 var showViewfields = z >= minViewfieldZoom;
68176 var service = getService();
68177 var sequences = [];
68180 if (context.photos().showsFlat()) {
68181 sequences = service ? service.sequences(projection) : [];
68182 images = service && showMarkers ? service.images(projection) : [];
68183 sequences = filterSequences(sequences);
68184 images = filterImages(images);
68187 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
68188 return d.properties.key;
68191 traces.exit().remove(); // enter/update
68193 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
68194 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
68198 groups.exit().remove(); // enter
68200 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
68201 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
68203 var markers = groups.merge(groupsEnter).sort(function (a, b) {
68204 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
68205 }).attr('transform', transform).select('.viewfield-scale');
68206 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
68207 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
68208 viewfields.exit().remove();
68209 viewfields.enter() // viewfields may or may not be drawn...
68210 .insert('path', 'circle') // but if they are, draw below the circles
68211 .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');
68214 function drawImages(selection) {
68215 var enabled = svgOpenstreetcamImages.enabled,
68216 service = getService();
68217 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
68218 layer.exit().remove();
68219 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
68220 layerEnter.append('g').attr('class', 'sequences');
68221 layerEnter.append('g').attr('class', 'markers');
68222 layer = layerEnter.merge(layer);
68225 if (service && ~~context.map().zoom() >= minZoom) {
68228 service.loadImages(projection);
68235 drawImages.enabled = function (_) {
68236 if (!arguments.length) return svgOpenstreetcamImages.enabled;
68237 svgOpenstreetcamImages.enabled = _;
68239 if (svgOpenstreetcamImages.enabled) {
68241 context.photos().on('change.openstreetcam_images', update);
68244 context.photos().on('change.openstreetcam_images', null);
68247 dispatch.call('change');
68251 drawImages.supported = function () {
68252 return !!getService();
68259 function svgOsm(projection, context, dispatch) {
68260 var enabled = true;
68262 function drawOsm(selection) {
68263 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
68264 return 'layer-osm ' + d;
68266 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
68267 return 'points-group ' + d;
68271 function showLayer() {
68272 var layer = context.surface().selectAll('.data-layer.osm');
68274 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
68275 dispatch.call('change');
68279 function hideLayer() {
68280 var layer = context.surface().selectAll('.data-layer.osm');
68282 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
68283 layer.classed('disabled', true);
68284 dispatch.call('change');
68288 drawOsm.enabled = function (val) {
68289 if (!arguments.length) return enabled;
68298 dispatch.call('change');
68305 var _notesEnabled = false;
68309 function svgNotes(projection, context, dispatch) {
68311 dispatch = dispatch$8('change');
68314 var throttledRedraw = throttle(function () {
68315 dispatch.call('change');
68319 var touchLayer = select(null);
68320 var drawLayer = select(null);
68321 var _notesVisible = false;
68323 function markerPath(selection, klass) {
68324 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');
68325 } // Loosely-coupled osm service for fetching notes.
68328 function getService() {
68329 if (services.osm && !_osmService) {
68330 _osmService = services.osm;
68332 _osmService.on('loadedNotes', throttledRedraw);
68333 } else if (!services.osm && _osmService) {
68334 _osmService = null;
68337 return _osmService;
68338 } // Show the notes
68341 function editOn() {
68342 if (!_notesVisible) {
68343 _notesVisible = true;
68344 drawLayer.style('display', 'block');
68346 } // Immediately remove the notes and their touch targets
68349 function editOff() {
68350 if (_notesVisible) {
68351 _notesVisible = false;
68352 drawLayer.style('display', 'none');
68353 drawLayer.selectAll('.note').remove();
68354 touchLayer.selectAll('.note').remove();
68356 } // Enable the layer. This shows the notes and transitions them to visible.
68359 function layerOn() {
68361 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
68362 dispatch.call('change');
68364 } // Disable the layer. This transitions the layer invisible and then hides the notes.
68367 function layerOff() {
68368 throttledRedraw.cancel();
68369 drawLayer.interrupt();
68370 touchLayer.selectAll('.note').remove();
68371 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
68373 dispatch.call('change');
68375 } // Update the note markers
68378 function updateMarkers() {
68379 if (!_notesVisible || !_notesEnabled) return;
68380 var service = getService();
68381 var selectedID = context.selectedNoteID();
68382 var data = service ? service.notes(projection) : [];
68383 var getTransform = svgPointTransform(projection); // Draw markers..
68385 var notes = drawLayer.selectAll('.note').data(data, function (d) {
68386 return d.status + d.id;
68389 notes.exit().remove(); // enter
68391 var notesEnter = notes.enter().append('g').attr('class', function (d) {
68392 return 'note note-' + d.id + ' ' + d.status;
68393 }).classed('new', function (d) {
68396 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
68397 notesEnter.append('path').call(markerPath, 'shadow');
68398 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');
68399 notesEnter.selectAll('.icon-annotation').data(function (d) {
68401 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
68402 if (d.id < 0) return '#iD-icon-plus';
68403 if (d.status === 'open') return '#iD-icon-close';
68404 return '#iD-icon-apply';
68407 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
68408 var mode = context.mode();
68409 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
68411 return !isMoving && d.id === selectedID;
68412 }).attr('transform', getTransform); // Draw targets..
68414 if (touchLayer.empty()) return;
68415 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68416 var targets = touchLayer.selectAll('.note').data(data, function (d) {
68420 targets.exit().remove(); // enter/update
68422 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
68423 var newClass = d.id < 0 ? 'new' : '';
68424 return 'note target note-' + d.id + ' ' + fillClass + newClass;
68425 }).attr('transform', getTransform);
68427 function sortY(a, b) {
68428 if (a.id === selectedID) return 1;
68429 if (b.id === selectedID) return -1;
68430 return b.loc[1] - a.loc[1];
68432 } // Draw the notes layer and schedule loading notes and updating markers.
68435 function drawNotes(selection) {
68436 var service = getService();
68437 var surface = context.surface();
68439 if (surface && !surface.empty()) {
68440 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68443 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
68444 drawLayer.exit().remove();
68445 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
68447 if (_notesEnabled) {
68448 if (service && ~~context.map().zoom() >= minZoom) {
68450 service.loadNotes(projection);
68456 } // Toggles the layer on and off
68459 drawNotes.enabled = function (val) {
68460 if (!arguments.length) return _notesEnabled;
68461 _notesEnabled = val;
68463 if (_notesEnabled) {
68468 if (context.selectedNoteID()) {
68469 context.enter(modeBrowse(context));
68473 dispatch.call('change');
68480 function svgTouch() {
68481 function drawTouch(selection) {
68482 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
68483 return 'layer-touch ' + d;
68490 function refresh(selection, node) {
68491 var cr = node.getBoundingClientRect();
68492 var prop = [cr.width, cr.height];
68493 selection.property('__dimensions__', prop);
68497 function utilGetDimensions(selection, force) {
68498 if (!selection || selection.empty()) {
68502 var node = selection.node(),
68503 cached = selection.property('__dimensions__');
68504 return !cached || force ? refresh(selection, node) : cached;
68506 function utilSetDimensions(selection, dimensions) {
68507 if (!selection || selection.empty()) {
68511 var node = selection.node();
68513 if (dimensions === null) {
68514 refresh(selection, node);
68518 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
68521 function svgLayers(projection, context) {
68522 var dispatch = dispatch$8('change');
68523 var svg = select(null);
68526 layer: svgOsm(projection, context, dispatch)
68529 layer: svgNotes(projection, context, dispatch)
68532 layer: svgData(projection, context, dispatch)
68535 layer: svgKeepRight(projection, context, dispatch)
68538 layer: svgImproveOSM(projection, context, dispatch)
68541 layer: svgOsmose(projection, context, dispatch)
68544 layer: svgStreetside(projection, context, dispatch)
68547 layer: svgMapillaryImages(projection, context, dispatch)
68549 id: 'mapillary-position',
68550 layer: svgMapillaryPosition(projection, context)
68552 id: 'mapillary-map-features',
68553 layer: svgMapillaryMapFeatures(projection, context, dispatch)
68555 id: 'mapillary-signs',
68556 layer: svgMapillarySigns(projection, context, dispatch)
68558 id: 'openstreetcam',
68559 layer: svgOpenstreetcamImages(projection, context, dispatch)
68562 layer: svgDebug(projection, context)
68565 layer: svgGeolocate(projection)
68571 function drawLayers(selection) {
68572 svg = selection.selectAll('.surface').data([0]);
68573 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
68574 var defs = svg.selectAll('.surface-defs').data([0]);
68575 defs.enter().append('defs').attr('class', 'surface-defs');
68576 var groups = svg.selectAll('.data-layer').data(_layers);
68577 groups.exit().remove();
68578 groups.enter().append('g').attr('class', function (d) {
68579 return 'data-layer ' + d.id;
68580 }).merge(groups).each(function (d) {
68581 select(this).call(d.layer);
68585 drawLayers.all = function () {
68589 drawLayers.layer = function (id) {
68590 var obj = _layers.find(function (o) {
68591 return o.id === id;
68594 return obj && obj.layer;
68597 drawLayers.only = function (what) {
68598 var arr = [].concat(what);
68600 var all = _layers.map(function (layer) {
68604 return drawLayers.remove(utilArrayDifference(all, arr));
68607 drawLayers.remove = function (what) {
68608 var arr = [].concat(what);
68609 arr.forEach(function (id) {
68610 _layers = _layers.filter(function (o) {
68611 return o.id !== id;
68614 dispatch.call('change');
68618 drawLayers.add = function (what) {
68619 var arr = [].concat(what);
68620 arr.forEach(function (obj) {
68621 if ('id' in obj && 'layer' in obj) {
68625 dispatch.call('change');
68629 drawLayers.dimensions = function (val) {
68630 if (!arguments.length) return utilGetDimensions(svg);
68631 utilSetDimensions(svg, val);
68635 return utilRebind(drawLayers, dispatch, 'on');
68638 function svgLines(projection, context) {
68639 var detected = utilDetect();
68640 var highway_stack = {
68655 function drawTargets(selection, graph, entities, filter) {
68656 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68657 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
68658 var getPath = svgPath(projection).geojson;
68659 var activeID = context.activeID();
68660 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
68666 entities.forEach(function (way) {
68667 var features = svgSegmentWay(way, graph, activeID);
68668 data.targets.push.apply(data.targets, features.passive);
68669 data.nopes.push.apply(data.nopes, features.active);
68670 }); // Targets allow hover and vertex snapping
68672 var targetData = data.targets.filter(getPath);
68673 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
68674 return filter(d.properties.entity);
68675 }).data(targetData, function key(d) {
68679 targets.exit().remove();
68681 var segmentWasEdited = function segmentWasEdited(d) {
68682 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
68684 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
68688 return d.properties.nodes.some(function (n) {
68689 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
68694 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
68695 return 'way line target target-allowed ' + targetClass + d.id;
68696 }).classed('segment-edited', segmentWasEdited); // NOPE
68698 var nopeData = data.nopes.filter(getPath);
68699 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
68700 return filter(d.properties.entity);
68701 }).data(nopeData, function key(d) {
68705 nopes.exit().remove(); // enter/update
68707 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
68708 return 'way line target target-nope ' + nopeClass + d.id;
68709 }).classed('segment-edited', segmentWasEdited);
68712 function drawLines(selection, graph, entities, filter) {
68713 var base = context.history().base();
68715 function waystack(a, b) {
68716 var selected = context.selectedIDs();
68717 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
68718 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
68720 if (a.tags.highway) {
68721 scoreA -= highway_stack[a.tags.highway];
68724 if (b.tags.highway) {
68725 scoreB -= highway_stack[b.tags.highway];
68728 return scoreA - scoreB;
68731 function drawLineGroup(selection, klass, isSelected) {
68732 // Note: Don't add `.selected` class in draw modes
68733 var mode = context.mode();
68734 var isDrawing = mode && /^draw/.test(mode.id);
68735 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
68736 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
68737 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
68738 // works because osmEntity.key is defined to include the entity v attribute.
68740 lines.enter().append('path').attr('class', function (d) {
68741 var prefix = 'way line'; // if this line isn't styled by its own tags
68743 if (!d.hasInterestingTags()) {
68744 var parentRelations = graph.parentRelations(d);
68745 var parentMultipolygons = parentRelations.filter(function (relation) {
68746 return relation.isMultipolygon();
68747 }); // and if it's a member of at least one multipolygon relation
68749 if (parentMultipolygons.length > 0 && // and only multipolygon relations
68750 parentRelations.length === parentMultipolygons.length) {
68751 // then fudge the classes to style this as an area edge
68752 prefix = 'relation area';
68756 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
68757 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
68758 }).classed('added', function (d) {
68759 return !base.entities[d.id];
68760 }).classed('geometry-edited', function (d) {
68761 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
68762 }).classed('retagged', function (d) {
68763 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
68764 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
68768 function getPathData(isSelected) {
68769 return function () {
68770 var layer = this.parentNode.__data__;
68771 var data = pathdata[layer] || [];
68772 return data.filter(function (d) {
68774 return context.selectedIDs().indexOf(d.id) !== -1;
68776 return context.selectedIDs().indexOf(d.id) === -1;
68782 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
68783 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
68784 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
68785 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
68786 return groupdata[this.parentNode.__data__] || [];
68787 }, function key(d) {
68788 return [d.id, d.index];
68790 markers.exit().remove();
68791 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
68796 markers.each(function () {
68797 this.parentNode.insertBefore(this, this);
68802 var getPath = svgPath(projection, graph);
68804 var onewaydata = {};
68805 var sideddata = {};
68806 var oldMultiPolygonOuters = {};
68808 for (var i = 0; i < entities.length; i++) {
68809 var entity = entities[i];
68810 var outer = osmOldMultipolygonOuterMember(entity, graph);
68813 ways.push(entity.mergeTags(outer.tags));
68814 oldMultiPolygonOuters[outer.id] = true;
68815 } else if (entity.geometry(graph) === 'line') {
68820 ways = ways.filter(getPath);
68821 var pathdata = utilArrayGroupBy(ways, function (way) {
68822 return way.layer();
68824 Object.keys(pathdata).forEach(function (k) {
68825 var v = pathdata[k];
68826 var onewayArr = v.filter(function (d) {
68827 return d.isOneWay();
68829 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
68830 return entity.tags.oneway === '-1';
68831 }, function bothDirections(entity) {
68832 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
68834 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
68835 var sidedArr = v.filter(function (d) {
68836 return d.isSided();
68838 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
68840 }, function bothDirections() {
68843 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
68845 var covered = selection.selectAll('.layer-osm.covered'); // under areas
68847 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
68849 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
68851 [covered, uncovered].forEach(function (selection) {
68852 var range = selection === covered ? range$1(-10, 0) : range$1(0, 11);
68853 var layergroup = selection.selectAll('g.layergroup').data(range);
68854 layergroup = layergroup.enter().append('g').attr('class', function (d) {
68855 return 'layergroup layer' + String(d);
68856 }).merge(layergroup);
68857 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
68858 return 'linegroup line-' + d;
68860 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
68861 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
68862 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
68863 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
68864 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
68865 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
68866 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
68867 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
68868 var category = graph.entity(d.id).sidednessIdentifier();
68869 return 'url(#ideditor-sided-marker-' + category + ')';
68871 }); // Draw touch targets..
68873 touchLayer.call(drawTargets, graph, ways, filter);
68879 function svgMidpoints(projection, context) {
68880 var targetRadius = 8;
68882 function drawTargets(selection, graph, entities, filter) {
68883 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68884 var getTransform = svgPointTransform(projection).geojson;
68885 var data = entities.map(function (midpoint) {
68895 coordinates: midpoint.loc
68899 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
68900 return filter(d.properties.entity);
68901 }).data(data, function key(d) {
68905 targets.exit().remove(); // enter/update
68907 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
68908 return 'node midpoint target ' + fillClass + d.id;
68909 }).attr('transform', getTransform);
68912 function drawMidpoints(selection, graph, entities, filter, extent) {
68913 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
68914 var touchLayer = selection.selectAll('.layer-touch.points');
68915 var mode = context.mode();
68917 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
68918 drawLayer.selectAll('.midpoint').remove();
68919 touchLayer.selectAll('.midpoint.target').remove();
68923 var poly = extent.polygon();
68924 var midpoints = {};
68926 for (var i = 0; i < entities.length; i++) {
68927 var entity = entities[i];
68928 if (entity.type !== 'way') continue;
68929 if (!filter(entity)) continue;
68930 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
68931 var nodes = graph.childNodes(entity);
68933 for (var j = 0; j < nodes.length - 1; j++) {
68935 var b = nodes[j + 1];
68936 var id = [a.id, b.id].sort().join('-');
68938 if (midpoints[id]) {
68939 midpoints[id].parents.push(entity);
68940 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
68941 var point = geoVecInterp(a.loc, b.loc, 0.5);
68944 if (extent.intersects(point)) {
68947 for (var k = 0; k < 4; k++) {
68948 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
68950 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
68962 edge: [a.id, b.id],
68970 function midpointFilter(d) {
68971 if (midpoints[d.id]) return true;
68973 for (var i = 0; i < d.parents.length; i++) {
68974 if (filter(d.parents[i])) {
68982 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
68985 groups.exit().remove();
68986 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
68987 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
68988 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
68989 groups = groups.merge(enter).attr('transform', function (d) {
68990 var translate = svgPointTransform(projection);
68991 var a = graph.entity(d.edge[0]);
68992 var b = graph.entity(d.edge[1]);
68993 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
68994 return translate(d) + ' rotate(' + angle + ')';
68995 }).call(svgTagClasses().tags(function (d) {
68996 return d.parents[0].tags;
68997 })); // Propagate data bindings.
68999 groups.select('polygon.shadow');
69000 groups.select('polygon.fill'); // Draw touch targets..
69002 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
69005 return drawMidpoints;
69008 function svgPoints(projection, context) {
69009 function markerPath(selection, klass) {
69010 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');
69013 function sortY(a, b) {
69014 return b.loc[1] - a.loc[1];
69015 } // Avoid exit/enter if we're just moving stuff around.
69016 // The node will get a new version but we only need to run the update selection.
69019 function fastEntityKey(d) {
69020 var mode = context.mode();
69021 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
69022 return isMoving ? d.id : osmEntity.key(d);
69025 function drawTargets(selection, graph, entities, filter) {
69026 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69027 var getTransform = svgPointTransform(projection).geojson;
69028 var activeID = context.activeID();
69030 entities.forEach(function (node) {
69031 if (activeID === node.id) return; // draw no target on the activeID
69040 geometry: node.asGeoJSON()
69043 var targets = selection.selectAll('.point.target').filter(function (d) {
69044 return filter(d.properties.entity);
69045 }).data(data, function key(d) {
69049 targets.exit().remove(); // enter/update
69051 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
69052 return 'node point target ' + fillClass + d.id;
69053 }).attr('transform', getTransform);
69056 function drawPoints(selection, graph, entities, filter) {
69057 var wireframe = context.surface().classed('fill-wireframe');
69058 var zoom = geoScaleToZoom(projection.scale());
69059 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
69061 function renderAsPoint(entity) {
69062 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
69063 } // All points will render as vertices in wireframe mode too..
69066 var points = wireframe ? [] : entities.filter(renderAsPoint);
69067 points.sort(sortY);
69068 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
69069 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
69071 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
69072 groups.exit().remove();
69073 var enter = groups.enter().append('g').attr('class', function (d) {
69074 return 'node point ' + d.id;
69076 enter.append('path').call(markerPath, 'shadow');
69077 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
69078 enter.append('path').call(markerPath, 'stroke');
69079 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
69080 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
69081 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
69082 }).classed('moved', function (d) {
69083 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
69084 }).classed('retagged', function (d) {
69085 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
69086 }).call(svgTagClasses());
69087 groups.select('.shadow'); // propagate bound data
69089 groups.select('.stroke'); // propagate bound data
69091 groups.select('.icon') // propagate bound data
69092 .attr('xlink:href', function (entity) {
69093 var preset = _mainPresetIndex.match(entity, graph);
69094 var picon = preset && preset.icon;
69099 var isMaki = /^maki-/.test(picon);
69100 return '#' + picon + (isMaki ? '-11' : '');
69102 }); // Draw touch targets..
69104 touchLayer.call(drawTargets, graph, points, filter);
69110 function svgTurns(projection, context) {
69111 function icon(turn) {
69112 var u = turn.u ? '-u' : '';
69113 if (turn.no) return '#iD-turn-no' + u;
69114 if (turn.only) return '#iD-turn-only' + u;
69115 return '#iD-turn-yes' + u;
69118 function drawTurns(selection, graph, turns) {
69119 function turnTransform(d) {
69121 var toWay = graph.entity(d.to.way);
69122 var toPoints = graph.childNodes(toWay).map(function (n) {
69124 }).map(projection);
69125 var toLength = geoPathLength(toPoints);
69126 var mid = toLength / 2; // midpoint of destination way
69128 var toNode = graph.entity(d.to.node);
69129 var toVertex = graph.entity(d.to.vertex);
69130 var a = geoAngle(toVertex, toNode, projection);
69131 var o = projection(toVertex.loc);
69132 var r = d.u ? 0 // u-turn: no radius
69133 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
69134 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
69136 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
69139 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
69140 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
69142 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
69146 groups.exit().remove(); // enter
69148 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
69149 return 'turn ' + d.key;
69151 var turnsEnter = groupsEnter.filter(function (d) {
69154 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
69155 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
69156 var uEnter = groupsEnter.filter(function (d) {
69159 uEnter.append('circle').attr('r', '16');
69160 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
69162 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
69163 return d.direct === false ? '0.7' : null;
69164 }).attr('transform', turnTransform);
69165 groups.select('use').attr('xlink:href', icon);
69166 groups.select('rect'); // propagate bound data
69168 groups.select('circle'); // propagate bound data
69169 // Draw touch targets..
69171 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69172 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
69176 groups.exit().remove(); // enter
69178 groupsEnter = groups.enter().append('g').attr('class', function (d) {
69179 return 'turn ' + d.key;
69181 turnsEnter = groupsEnter.filter(function (d) {
69184 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
69185 uEnter = groupsEnter.filter(function (d) {
69188 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
69190 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
69191 groups.select('rect'); // propagate bound data
69193 groups.select('circle'); // propagate bound data
69201 function svgVertices(projection, context) {
69203 // z16-, z17, z18+, w/icon
69204 shadow: [6, 7.5, 7.5, 12],
69205 stroke: [2.5, 3.5, 3.5, 8],
69206 fill: [1, 1.5, 1.5, 1.5]
69209 var _currHoverTarget;
69211 var _currPersistent = {};
69212 var _currHover = {};
69213 var _prevHover = {};
69214 var _currSelected = {};
69215 var _prevSelected = {};
69218 function sortY(a, b) {
69219 return b.loc[1] - a.loc[1];
69220 } // Avoid exit/enter if we're just moving stuff around.
69221 // The node will get a new version but we only need to run the update selection.
69224 function fastEntityKey(d) {
69225 var mode = context.mode();
69226 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
69227 return isMoving ? d.id : osmEntity.key(d);
69230 function draw(selection, graph, vertices, sets, filter) {
69237 var directions = {};
69238 var wireframe = context.surface().classed('fill-wireframe');
69239 var zoom = geoScaleToZoom(projection.scale());
69240 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
69241 var activeID = context.activeID();
69242 var base = context.history().base();
69244 function getIcon(d) {
69245 // always check latest entity, as fastEntityKey avoids enter/exit now
69246 var entity = graph.entity(d.id);
69247 if (entity.id in icons) return icons[entity.id];
69248 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
69249 return icons[entity.id];
69250 } // memoize directions results, return false for empty arrays (for use in filter)
69253 function getDirections(entity) {
69254 if (entity.id in directions) return directions[entity.id];
69255 var angles = entity.directions(graph, projection);
69256 directions[entity.id] = angles.length ? angles : false;
69260 function updateAttributes(selection) {
69261 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
69262 var rads = radiuses[klass];
69263 selection.selectAll('.' + klass).each(function (entity) {
69264 var i = z && getIcon(entity);
69265 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
69267 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
69271 if (klass === 'shadow') {
69272 // remember this value, so we don't need to
69273 _radii[entity.id] = r; // recompute it when we draw the touch targets
69276 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
69281 vertices.sort(sortY);
69282 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
69284 groups.exit().remove(); // enter
69286 var enter = groups.enter().append('g').attr('class', function (d) {
69287 return 'node vertex ' + d.id;
69289 enter.append('circle').attr('class', 'shadow');
69290 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
69292 enter.filter(function (d) {
69293 return d.hasInterestingTags();
69294 }).append('circle').attr('class', 'fill'); // update
69296 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
69297 return d.id in sets.selected;
69298 }).classed('shared', function (d) {
69299 return graph.isShared(d);
69300 }).classed('endpoint', function (d) {
69301 return d.isEndpoint(graph);
69302 }).classed('added', function (d) {
69303 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
69304 }).classed('moved', function (d) {
69305 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
69306 }).classed('retagged', function (d) {
69307 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
69308 }).call(updateAttributes); // Vertices with icons get a `use`.
69310 var iconUse = groups.selectAll('.icon').data(function data(d) {
69311 return zoom >= 17 && getIcon(d) ? [d] : [];
69312 }, fastEntityKey); // exit
69314 iconUse.exit().remove(); // enter
69316 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) {
69317 var picon = getIcon(d);
69318 var isMaki = /^maki-/.test(picon);
69319 return '#' + picon + (isMaki ? '-11' : '');
69320 }); // Vertices with directions get viewfields
69322 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
69323 return zoom >= 18 && getDirections(d) ? [d] : [];
69324 }, fastEntityKey); // exit
69326 dgroups.exit().remove(); // enter/update
69328 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
69329 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
69330 return osmEntity.key(d);
69333 viewfields.exit().remove(); // enter/update
69335 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) {
69336 return 'rotate(' + d + ')';
69340 function drawTargets(selection, graph, entities, filter) {
69341 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69342 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
69343 var getTransform = svgPointTransform(projection).geojson;
69344 var activeID = context.activeID();
69349 entities.forEach(function (node) {
69350 if (activeID === node.id) return; // draw no target on the activeID
69352 var vertexType = svgPassiveVertex(node, graph, activeID);
69354 if (vertexType !== 0) {
69355 // passive or adjacent - allow to connect
69356 data.targets.push({
69363 geometry: node.asGeoJSON()
69368 id: node.id + '-nope',
69374 geometry: node.asGeoJSON()
69377 }); // Targets allow hover and vertex snapping
69379 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
69380 return filter(d.properties.entity);
69381 }).data(data.targets, function key(d) {
69385 targets.exit().remove(); // enter/update
69387 targets.enter().append('circle').attr('r', function (d) {
69388 return _radii[d.id] || radiuses.shadow[3];
69389 }).merge(targets).attr('class', function (d) {
69390 return 'node vertex target target-allowed ' + targetClass + d.id;
69391 }).attr('transform', getTransform); // NOPE
69393 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
69394 return filter(d.properties.entity);
69395 }).data(data.nopes, function key(d) {
69399 nopes.exit().remove(); // enter/update
69401 nopes.enter().append('circle').attr('r', function (d) {
69402 return _radii[d.properties.entity.id] || radiuses.shadow[3];
69403 }).merge(nopes).attr('class', function (d) {
69404 return 'node vertex target target-nope ' + nopeClass + d.id;
69405 }).attr('transform', getTransform);
69406 } // Points can also render as vertices:
69407 // 1. in wireframe mode or
69408 // 2. at higher zooms if they have a direction
69411 function renderAsVertex(entity, graph, wireframe, zoom) {
69412 var geometry = entity.geometry(graph);
69413 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
69416 function isEditedNode(node, base, head) {
69417 var baseNode = base.entities[node.id];
69418 var headNode = head.entities[node.id];
69419 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
69422 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
69426 function addChildVertices(entity) {
69427 // avoid redundant work and infinite recursion of circular relations
69428 if (seenIds[entity.id]) return;
69429 seenIds[entity.id] = true;
69430 var geometry = entity.geometry(graph);
69432 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
69435 if (entity.type === 'way') {
69436 for (i = 0; i < entity.nodes.length; i++) {
69437 var child = graph.hasEntity(entity.nodes[i]);
69440 addChildVertices(child);
69443 } else if (entity.type === 'relation') {
69444 for (i = 0; i < entity.members.length; i++) {
69445 var member = graph.hasEntity(entity.members[i].id);
69448 addChildVertices(member);
69451 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
69452 results[entity.id] = entity;
69457 ids.forEach(function (id) {
69458 var entity = graph.hasEntity(id);
69459 if (!entity) return;
69461 if (entity.type === 'node') {
69462 if (renderAsVertex(entity, graph, wireframe, zoom)) {
69463 results[entity.id] = entity;
69464 graph.parentWays(entity).forEach(function (entity) {
69465 addChildVertices(entity);
69470 addChildVertices(entity);
69476 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
69477 var wireframe = context.surface().classed('fill-wireframe');
69478 var visualDiff = context.surface().classed('highlight-edited');
69479 var zoom = geoScaleToZoom(projection.scale());
69480 var mode = context.mode();
69481 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
69482 var base = context.history().base();
69483 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
69484 var touchLayer = selection.selectAll('.layer-touch.points');
69487 _currPersistent = {};
69489 } // Collect important vertices from the `entities` list..
69490 // (during a partial redraw, it will not contain everything)
69493 for (var i = 0; i < entities.length; i++) {
69494 var entity = entities[i];
69495 var geometry = entity.geometry(graph);
69496 var keep = false; // a point that looks like a vertex..
69498 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
69499 _currPersistent[entity.id] = entity;
69500 keep = true; // a vertex of some importance..
69501 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
69502 _currPersistent[entity.id] = entity;
69504 } // whatever this is, it's not a persistent vertex..
69507 if (!keep && !fullRedraw) {
69508 delete _currPersistent[entity.id];
69510 } // 3 sets of vertices to consider:
69514 persistent: _currPersistent,
69515 // persistent = important vertices (render always)
69516 selected: _currSelected,
69517 // selected + siblings of selected (render always)
69518 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
69521 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
69522 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
69523 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
69525 var filterRendered = function filterRendered(d) {
69526 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
69529 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
69530 // When drawing, render all targets (not just those affected by a partial redraw)
69532 var filterTouch = function filterTouch(d) {
69533 return isMoving ? true : filterRendered(d);
69536 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
69538 function currentVisible(which) {
69539 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
69540 .filter(function (entity) {
69541 return entity && entity.intersects(extent, graph);
69544 } // partial redraw - only update the selected items..
69547 drawVertices.drawSelected = function (selection, graph, extent) {
69548 var wireframe = context.surface().classed('fill-wireframe');
69549 var zoom = geoScaleToZoom(projection.scale());
69550 _prevSelected = _currSelected || {};
69552 if (context.map().isInWideSelection()) {
69553 _currSelected = {};
69554 context.selectedIDs().forEach(function (id) {
69555 var entity = graph.hasEntity(id);
69556 if (!entity) return;
69558 if (entity.type === 'node') {
69559 if (renderAsVertex(entity, graph, wireframe, zoom)) {
69560 _currSelected[entity.id] = entity;
69565 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
69566 } // note that drawVertices will add `_currSelected` automatically if needed..
69569 var filter = function filter(d) {
69570 return d.id in _prevSelected;
69573 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
69574 }; // partial redraw - only update the hovered items..
69577 drawVertices.drawHover = function (selection, graph, target, extent) {
69578 if (target === _currHoverTarget) return; // continue only if something changed
69580 var wireframe = context.surface().classed('fill-wireframe');
69581 var zoom = geoScaleToZoom(projection.scale());
69582 _prevHover = _currHover || {};
69583 _currHoverTarget = target;
69584 var entity = target && target.properties && target.properties.entity;
69587 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
69590 } // note that drawVertices will add `_currHover` automatically if needed..
69593 var filter = function filter(d) {
69594 return d.id in _prevHover;
69597 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
69600 return drawVertices;
69603 function utilBindOnce(target, type, listener, capture) {
69604 var typeOnce = type + '.once';
69607 target.on(typeOnce, null);
69608 listener.apply(this, arguments);
69611 target.on(typeOnce, one, capture);
69615 function defaultFilter(d3_event) {
69616 return !d3_event.ctrlKey && !d3_event.button;
69619 function defaultExtent() {
69622 if (e instanceof SVGElement) {
69623 e = e.ownerSVGElement || e;
69625 if (e.hasAttribute('viewBox')) {
69626 e = e.viewBox.baseVal;
69627 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
69630 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
69633 return [[0, 0], [e.clientWidth, e.clientHeight]];
69636 function defaultWheelDelta(d3_event) {
69637 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
69640 function defaultConstrain(transform, extent, translateExtent) {
69641 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
69642 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
69643 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
69644 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
69645 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));
69648 function utilZoomPan() {
69649 var filter = defaultFilter,
69650 extent = defaultExtent,
69651 constrain = defaultConstrain,
69652 wheelDelta = defaultWheelDelta,
69653 scaleExtent = [0, Infinity],
69654 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
69655 interpolate = interpolateZoom,
69656 dispatch = dispatch$8('start', 'zoom', 'end'),
69658 _transform = identity$2,
69661 function zoom(selection) {
69662 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
69663 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
69666 zoom.transform = function (collection, transform, point) {
69667 var selection = collection.selection ? collection.selection() : collection;
69669 if (collection !== selection) {
69670 schedule(collection, transform, point);
69672 selection.interrupt().each(function () {
69673 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
69678 zoom.scaleBy = function (selection, k, p) {
69679 zoom.scaleTo(selection, function () {
69680 var k0 = _transform.k,
69681 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
69686 zoom.scaleTo = function (selection, k, p) {
69687 zoom.transform(selection, function () {
69688 var e = extent.apply(this, arguments),
69690 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
69691 p1 = t0.invert(p0),
69692 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
69693 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
69697 zoom.translateBy = function (selection, x, y) {
69698 zoom.transform(selection, function () {
69699 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);
69703 zoom.translateTo = function (selection, x, y, p) {
69704 zoom.transform(selection, function () {
69705 var e = extent.apply(this, arguments),
69707 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
69708 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);
69712 function scale(transform, k) {
69713 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
69714 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
69717 function translate(transform, p0, p1) {
69718 var x = p0[0] - p1[0] * transform.k,
69719 y = p0[1] - p1[1] * transform.k;
69720 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
69723 function centroid(extent) {
69724 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
69727 function schedule(transition, transform, point) {
69728 transition.on('start.zoom', function () {
69729 gesture(this, arguments).start(null);
69730 }).on('interrupt.zoom end.zoom', function () {
69731 gesture(this, arguments).end(null);
69732 }).tween('zoom', function () {
69735 g = gesture(that, args),
69736 e = extent.apply(that, args),
69737 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
69738 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
69740 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
69741 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
69742 return function (t) {
69744 // Avoid rounding error on end.
69749 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
69752 g.zoom(null, null, t);
69757 function gesture(that, args, clean) {
69758 return !clean && _activeGesture || new Gesture(that, args);
69761 function Gesture(that, args) {
69765 this.extent = extent.apply(that, args);
69768 Gesture.prototype = {
69769 start: function start(d3_event) {
69770 if (++this.active === 1) {
69771 _activeGesture = this;
69772 dispatch.call('start', this, d3_event);
69777 zoom: function zoom(d3_event, key, transform) {
69778 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
69779 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
69780 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
69781 _transform = transform;
69782 dispatch.call('zoom', this, d3_event, key, transform);
69785 end: function end(d3_event) {
69786 if (--this.active === 0) {
69787 _activeGesture = null;
69788 dispatch.call('end', this, d3_event);
69795 function wheeled(d3_event) {
69796 if (!filter.apply(this, arguments)) return;
69797 var g = gesture(this, arguments),
69799 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
69800 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
69801 // If there were recent wheel events, reset the wheel idle timeout.
69804 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
69805 g.mouse[1] = t.invert(g.mouse[0] = p);
69808 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
69810 g.mouse = [p, t.invert(p)];
69815 d3_event.preventDefault();
69816 d3_event.stopImmediatePropagation();
69817 g.wheel = setTimeout(wheelidled, _wheelDelay);
69818 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
69820 function wheelidled() {
69826 var _downPointerIDs = new Set();
69828 var _pointerLocGetter;
69830 function pointerdown(d3_event) {
69831 _downPointerIDs.add(d3_event.pointerId);
69833 if (!filter.apply(this, arguments)) return;
69834 var g = gesture(this, arguments, _downPointerIDs.size === 1);
69836 d3_event.stopImmediatePropagation();
69837 _pointerLocGetter = utilFastMouse(this);
69839 var loc = _pointerLocGetter(d3_event);
69841 var p = [loc, _transform.invert(loc), d3_event.pointerId];
69846 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
69856 function pointermove(d3_event) {
69857 if (!_downPointerIDs.has(d3_event.pointerId)) return;
69858 if (!_activeGesture || !_pointerLocGetter) return;
69859 var g = gesture(this, arguments);
69860 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
69861 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
69863 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
69864 // The pointer went up without ending the gesture somehow, e.g.
69865 // a down mouse was moved off the map and released. End it here.
69866 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
69867 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
69872 d3_event.preventDefault();
69873 d3_event.stopImmediatePropagation();
69875 var loc = _pointerLocGetter(d3_event);
69878 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
69882 var p0 = g.pointer0[0],
69883 l0 = g.pointer0[1],
69884 p1 = g.pointer1[0],
69885 l1 = g.pointer1[1],
69886 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
69887 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
69888 t = scale(t, Math.sqrt(dp / dl));
69889 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
69890 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
69891 } else if (g.pointer0) {
69898 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
69901 function pointerup(d3_event) {
69902 if (!_downPointerIDs.has(d3_event.pointerId)) return;
69904 _downPointerIDs["delete"](d3_event.pointerId);
69906 if (!_activeGesture) return;
69907 var g = gesture(this, arguments);
69908 d3_event.stopImmediatePropagation();
69909 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;
69911 if (g.pointer1 && !g.pointer0) {
69912 g.pointer0 = g.pointer1;
69917 g.pointer0[1] = _transform.invert(g.pointer0[0]);
69923 zoom.wheelDelta = function (_) {
69924 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
69927 zoom.filter = function (_) {
69928 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
69931 zoom.extent = function (_) {
69932 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
69935 zoom.scaleExtent = function (_) {
69936 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
69939 zoom.translateExtent = function (_) {
69940 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]]];
69943 zoom.constrain = function (_) {
69944 return arguments.length ? (constrain = _, zoom) : constrain;
69947 zoom.interpolate = function (_) {
69948 return arguments.length ? (interpolate = _, zoom) : interpolate;
69951 zoom._transform = function (_) {
69952 return arguments.length ? (_transform = _, zoom) : _transform;
69955 return utilRebind(zoom, dispatch, 'on');
69958 // if pointer events are supported. Falls back to default `dblclick` event.
69960 function utilDoubleUp() {
69961 var dispatch = dispatch$8('doubleUp');
69962 var _maxTimespan = 500; // milliseconds
69964 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
69966 var _pointer; // object representing the pointer that could trigger double up
69969 function pointerIsValidFor(loc) {
69970 // second pointerup must occur within a small timeframe after the first pointerdown
69971 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
69972 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
69975 function pointerdown(d3_event) {
69976 // ignore right-click
69977 if (d3_event.ctrlKey || d3_event.button === 2) return;
69978 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
69979 // events on touch devices
69981 if (_pointer && !pointerIsValidFor(loc)) {
69982 // if this pointer is no longer valid, clear it so another can be started
69983 _pointer = undefined;
69989 startTime: new Date().getTime(),
69991 pointerId: d3_event.pointerId
69995 _pointer.pointerId = d3_event.pointerId;
69999 function pointerup(d3_event) {
70000 // ignore right-click
70001 if (d3_event.ctrlKey || d3_event.button === 2) return;
70002 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
70003 _pointer.upCount += 1;
70005 if (_pointer.upCount === 2) {
70007 var loc = [d3_event.clientX, d3_event.clientY];
70009 if (pointerIsValidFor(loc)) {
70010 var locInThis = utilFastMouse(this)(d3_event);
70011 dispatch.call('doubleUp', this, d3_event, locInThis);
70012 } // clear the pointer info in any case
70015 _pointer = undefined;
70019 function doubleUp(selection) {
70020 if ('PointerEvent' in window) {
70021 // dblclick isn't well supported on touch devices so manually use
70022 // pointer events if they're available
70023 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
70025 // fallback to dblclick
70026 selection.on('dblclick.doubleUp', function (d3_event) {
70027 dispatch.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
70032 doubleUp.off = function (selection) {
70033 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
70036 return utilRebind(doubleUp, dispatch, 'on');
70039 var TILESIZE = 256;
70042 var kMin = geoZoomToScale(minZoom, TILESIZE);
70043 var kMax = geoZoomToScale(maxZoom, TILESIZE);
70045 function clamp$1(num, min, max) {
70046 return Math.max(min, Math.min(num, max));
70049 function rendererMap(context) {
70050 var dispatch = dispatch$8('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
70051 var projection = context.projection;
70052 var curtainProjection = context.curtainProjection;
70061 var _selection = select(null);
70063 var supersurface = select(null);
70064 var wrapper = select(null);
70065 var surface = select(null);
70066 var _dimensions = [1, 1];
70067 var _dblClickZoomEnabled = true;
70068 var _redrawEnabled = true;
70070 var _gestureTransformStart;
70072 var _transformStart = projection.transform();
70074 var _transformLast;
70076 var _isTransformed = false;
70079 var _getMouseCoords;
70081 var _lastPointerEvent;
70083 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
70086 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
70088 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
70091 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
70093 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate$1).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
70094 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
70095 }).on('end.map', function () {
70096 _pointerDown = false;
70099 var _doubleUpHandler = utilDoubleUp();
70101 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
70102 // var pendingRedrawCall;
70103 // function scheduleRedraw() {
70104 // // Only schedule the redraw if one has not already been set.
70105 // if (isRedrawScheduled) return;
70106 // isRedrawScheduled = true;
70107 // var that = this;
70108 // var args = arguments;
70109 // pendingRedrawCall = window.requestIdleCallback(function () {
70110 // // Reset the boolean so future redraws can be set.
70111 // isRedrawScheduled = false;
70112 // redraw.apply(that, args);
70113 // }, { timeout: 1400 });
70117 function cancelPendingRedraw() {
70118 scheduleRedraw.cancel(); // isRedrawScheduled = false;
70119 // window.cancelIdleCallback(pendingRedrawCall);
70122 function map(selection) {
70123 _selection = selection;
70124 context.on('change.map', immediateRedraw);
70125 var osm = context.connection();
70128 osm.on('change.map', immediateRedraw);
70131 function didUndoOrRedo(targetTransform) {
70132 var mode = context.mode().id;
70133 if (mode !== 'browse' && mode !== 'select') return;
70135 if (targetTransform) {
70136 map.transformEase(targetTransform);
70140 context.history().on('merge.map', function () {
70142 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
70143 didUndoOrRedo(fromStack.transform);
70144 }).on('redone.map', function (stack) {
70145 didUndoOrRedo(stack.transform);
70147 context.background().on('change.map', immediateRedraw);
70148 context.features().on('redraw.map', immediateRedraw);
70149 drawLayers.on('change.map', function () {
70150 context.background().updateImagery();
70153 selection.on('wheel.map mousewheel.map', function (d3_event) {
70154 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
70155 d3_event.preventDefault();
70156 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
70158 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
70159 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
70161 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
70162 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
70163 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
70164 _lastPointerEvent = d3_event;
70166 if (d3_event.button === 2) {
70167 d3_event.stopPropagation();
70169 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
70170 _lastPointerEvent = d3_event;
70172 if (resetTransform()) {
70175 }).on(_pointerPrefix + 'move.map', function (d3_event) {
70176 _lastPointerEvent = d3_event;
70177 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
70178 if (map.editableDataEnabled() && !_isTransformed) {
70179 var hover = d3_event.target.__data__;
70180 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
70181 dispatch.call('drawn', this, {
70185 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
70186 if (map.editableDataEnabled() && !_isTransformed) {
70187 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
70188 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
70189 dispatch.call('drawn', this, {
70194 var detected = utilDetect(); // only WebKit supports gesture events
70196 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
70197 // but we only need to do this on desktop Safari anyway. – #7694
70198 !detected.isMobileWebKit) {
70199 // Desktop Safari sends gesture events for multitouch trackpad pinches.
70200 // We can listen for these and translate them into map zooms.
70201 surface.on('gesturestart.surface', function (d3_event) {
70202 d3_event.preventDefault();
70203 _gestureTransformStart = projection.transform();
70204 }).on('gesturechange.surface', gestureChange);
70205 } // must call after surface init
70210 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
70211 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
70213 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
70214 !select(d3_event.target).classed('fill')) return;
70215 var zoomOut = d3_event.shiftKey;
70216 var t = projection.transform();
70217 var p1 = t.invert(p0);
70218 t = t.scale(zoomOut ? 0.5 : 2);
70219 t.x = p0[0] - p1[0] * t.k;
70220 t.y = p0[1] - p1[1] * t.k;
70221 map.transformEase(t);
70224 context.on('enter.map', function () {
70225 if (!map.editableDataEnabled(true
70226 /* skip zoom check */
70227 )) return; // redraw immediately any objects affected by a change in selectedIDs.
70229 var graph = context.graph();
70230 var selectedAndParents = {};
70231 context.selectedIDs().forEach(function (id) {
70232 var entity = graph.hasEntity(id);
70235 selectedAndParents[entity.id] = entity;
70237 if (entity.type === 'node') {
70238 graph.parentWays(entity).forEach(function (parent) {
70239 selectedAndParents[parent.id] = parent;
70244 var data = Object.values(selectedAndParents);
70246 var filter = function filter(d) {
70247 return d.id in selectedAndParents;
70250 data = context.features().filter(data, graph);
70251 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
70252 dispatch.call('drawn', this, {
70254 }); // redraw everything else later
70258 map.dimensions(utilGetDimensions(selection));
70261 function zoomEventFilter(d3_event) {
70262 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
70263 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
70264 // This can happen if a previous `mousedown` occurred without a `mouseup`.
70265 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
70266 // so that d3-zoom won't stop propagation of new `mousedown` events.
70267 if (d3_event.type === 'mousedown') {
70268 var hasOrphan = false;
70269 var listeners = window.__on;
70271 for (var i = 0; i < listeners.length; i++) {
70272 var listener = listeners[i];
70274 if (listener.name === 'zoom' && listener.type === 'mouseup') {
70281 var event = window.CustomEvent;
70284 event = new event('mouseup');
70286 event = window.document.createEvent('Event');
70287 event.initEvent('mouseup', false, false);
70288 } // Event needs to be dispatched with an event.view property.
70291 event.view = window;
70292 window.dispatchEvent(event);
70296 return d3_event.button !== 2; // ignore right clicks
70299 function pxCenter() {
70300 return [_dimensions[0] / 2, _dimensions[1] / 2];
70303 function drawEditable(difference, extent) {
70304 var mode = context.mode();
70305 var graph = context.graph();
70306 var features = context.features();
70307 var all = context.history().intersects(map.extent());
70308 var fullRedraw = false;
70312 var applyFeatureLayerFilters = true;
70314 if (map.isInWideSelection()) {
70316 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
70317 var entity = context.hasEntity(id);
70318 if (entity) data.push(entity);
70321 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
70323 applyFeatureLayerFilters = false;
70324 } else if (difference) {
70325 var complete = difference.complete(map.extent());
70326 data = Object.values(complete).filter(Boolean);
70327 set = new Set(Object.keys(complete));
70329 filter = function filter(d) {
70330 return set.has(d.id);
70333 features.clear(data);
70335 // force a full redraw if gatherStats detects that a feature
70336 // should be auto-hidden (e.g. points or buildings)..
70337 if (features.gatherStats(all, graph, _dimensions)) {
70338 extent = undefined;
70342 data = context.history().intersects(map.extent().intersection(extent));
70343 set = new Set(data.map(function (entity) {
70347 filter = function filter(d) {
70348 return set.has(d.id);
70353 filter = utilFunctor(true);
70357 if (applyFeatureLayerFilters) {
70358 data = features.filter(data, graph);
70360 context.features().resetStats();
70363 if (mode && mode.id === 'select') {
70364 // update selected vertices - the user might have just double-clicked a way,
70365 // creating a new vertex, triggering a partial redraw without a mode change
70366 surface.call(drawVertices.drawSelected, graph, map.extent());
70369 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);
70370 dispatch.call('drawn', this, {
70375 map.init = function () {
70376 drawLayers = svgLayers(projection, context);
70377 drawPoints = svgPoints(projection, context);
70378 drawVertices = svgVertices(projection, context);
70379 drawLines = svgLines(projection, context);
70380 drawAreas = svgAreas(projection, context);
70381 drawMidpoints = svgMidpoints(projection, context);
70382 drawLabels = svgLabels(projection, context);
70385 function editOff() {
70386 context.features().resetStats();
70387 surface.selectAll('.layer-osm *').remove();
70388 surface.selectAll('.layer-touch:not(.markers) *').remove();
70392 'select-note': true,
70393 'select-data': true,
70394 'select-error': true
70396 var mode = context.mode();
70398 if (mode && !allowed[mode.id]) {
70399 context.enter(modeBrowse(context));
70402 dispatch.call('drawn', this, {
70407 function gestureChange(d3_event) {
70408 // Remap Safari gesture events to wheel events - #5492
70409 // We want these disabled most places, but enabled for zoom/unzoom on map surface
70410 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
70412 e.preventDefault();
70415 // dummy values to ignore in zoomPan
70417 // dummy values to ignore in zoomPan
70418 clientX: e.clientX,
70419 clientY: e.clientY,
70420 screenX: e.screenX,
70421 screenY: e.screenY,
70425 var e2 = new WheelEvent('wheel', props);
70426 e2._scale = e.scale; // preserve the original scale
70428 e2._rotation = e.rotation; // preserve the original rotation
70430 _selection.node().dispatchEvent(e2);
70433 function zoomPan(event, key, transform) {
70434 var source = event && event.sourceEvent || event;
70435 var eventTransform = transform || event && event.transform;
70436 var x = eventTransform.x;
70437 var y = eventTransform.y;
70438 var k = eventTransform.k; // Special handling of 'wheel' events:
70439 // They might be triggered by the user scrolling the mouse wheel,
70440 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
70442 if (source && source.type === 'wheel') {
70443 // assume that the gesture is already handled by pointer events
70444 if (_pointerDown) return;
70445 var detected = utilDetect();
70446 var dX = source.deltaX;
70447 var dY = source.deltaY;
70451 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
70452 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
70453 // We are essentially redoing the calculations that occur here:
70454 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
70455 // See this for more info:
70456 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
70458 if (source.deltaMode === 1
70461 // Convert from lines to pixels, more if the user is scrolling fast.
70462 // (I made up the exp function to roughly match Firefox to what Chrome does)
70463 // These numbers should be floats, because integers are treated as pan gesture below.
70464 var lines = Math.abs(source.deltaY);
70465 var sign = source.deltaY > 0 ? 1 : -1;
70466 dY = sign * clamp$1(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
70467 350.000244140625 // max
70468 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
70469 // There doesn't seem to be any scroll acceleration.
70470 // This multiplier increases the speed a little bit - #5512
70472 if (detected.os !== 'mac') {
70474 } // recalculate x2,y2,k2
70477 t0 = _isTransformed ? _transformLast : _transformStart;
70478 p0 = _getMouseCoords(source);
70479 p1 = t0.invert(p0);
70480 k2 = t0.k * Math.pow(2, -dY / 500);
70481 k2 = clamp$1(k2, kMin, kMax);
70482 x2 = p0[0] - p1[0] * k2;
70483 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
70484 // These are fake `wheel` events we made from Safari `gesturechange` events..
70485 } else if (source._scale) {
70486 // recalculate x2,y2,k2
70487 t0 = _gestureTransformStart;
70488 p0 = _getMouseCoords(source);
70489 p1 = t0.invert(p0);
70490 k2 = t0.k * source._scale;
70491 k2 = clamp$1(k2, kMin, kMax);
70492 x2 = p0[0] - p1[0] * k2;
70493 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
70494 // Pinch zooming via the `wheel` event will always have:
70495 // - `ctrlKey = true`
70496 // - `deltaY` is not round integer pixels (ignore `deltaX`)
70497 } else if (source.ctrlKey && !isInteger(dY)) {
70498 dY *= 6; // slightly scale up whatever the browser gave us
70499 // recalculate x2,y2,k2
70501 t0 = _isTransformed ? _transformLast : _transformStart;
70502 p0 = _getMouseCoords(source);
70503 p1 = t0.invert(p0);
70504 k2 = t0.k * Math.pow(2, -dY / 500);
70505 k2 = clamp$1(k2, kMin, kMax);
70506 x2 = p0[0] - p1[0] * k2;
70507 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
70508 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
70509 // recalculate x2,y2,k2
70510 t0 = _isTransformed ? _transformLast : _transformStart;
70511 p0 = _getMouseCoords(source);
70512 p1 = t0.invert(p0);
70513 k2 = t0.k * Math.pow(2, -dY / 500);
70514 k2 = clamp$1(k2, kMin, kMax);
70515 x2 = p0[0] - p1[0] * k2;
70516 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers) - #5492, #5512
70517 // Panning via the `wheel` event will always have:
70518 // - `ctrlKey = false`
70519 // - `deltaX`,`deltaY` are round integer pixels
70520 } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
70521 p1 = projection.translate();
70524 k2 = projection.scale();
70525 k2 = clamp$1(k2, kMin, kMax);
70526 } // something changed - replace the event transform
70529 if (x2 !== x || y2 !== y || k2 !== k) {
70533 eventTransform = identity$2.translate(x2, y2).scale(k2);
70535 if (_zoomerPanner._transform) {
70536 // utilZoomPan interface
70537 _zoomerPanner._transform(eventTransform);
70539 // d3_zoom interface
70540 _selection.node().__zoom = eventTransform;
70545 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
70546 return; // no change
70549 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
70550 surface.interrupt();
70551 dispatch.call('hitMinZoom', this, map);
70552 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
70554 dispatch.call('move', this, map);
70558 projection.transform(eventTransform);
70559 var withinEditableZoom = map.withinEditableZoom();
70561 if (_lastWithinEditableZoom !== withinEditableZoom) {
70562 if (_lastWithinEditableZoom !== undefined) {
70563 // notify that the map zoomed in or out over the editable zoom threshold
70564 dispatch.call('crossEditableZoom', this, withinEditableZoom);
70567 _lastWithinEditableZoom = withinEditableZoom;
70570 var scale = k / _transformStart.k;
70571 var tX = (x / scale - _transformStart.x) * scale;
70572 var tY = (y / scale - _transformStart.y) * scale;
70574 if (context.inIntro()) {
70575 curtainProjection.transform({
70583 _lastPointerEvent = event;
70586 _isTransformed = true;
70587 _transformLast = eventTransform;
70588 utilSetTransform(supersurface, tX, tY, scale);
70590 dispatch.call('move', this, map);
70592 function isInteger(val) {
70593 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
70597 function resetTransform() {
70598 if (!_isTransformed) return false;
70599 utilSetTransform(supersurface, 0, 0);
70600 _isTransformed = false;
70602 if (context.inIntro()) {
70603 curtainProjection.transform(projection.transform());
70609 function redraw(difference, extent) {
70610 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
70611 // It would result in artifacts where differenced entities are redrawn with
70612 // one transform and unchanged entities with another.
70614 if (resetTransform()) {
70615 difference = extent = undefined;
70618 var zoom = map.zoom();
70619 var z = String(~~zoom);
70621 if (surface.attr('data-zoom') !== z) {
70622 surface.attr('data-zoom', z);
70623 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
70626 var lat = map.center()[1];
70627 var lowzoom = linear().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
70628 surface.classed('low-zoom', zoom <= lowzoom(lat));
70631 supersurface.call(context.background());
70632 wrapper.call(drawLayers);
70636 if (map.editableDataEnabled() || map.isInWideSelection()) {
70637 context.loadTiles(projection);
70638 drawEditable(difference, extent);
70643 _transformStart = projection.transform();
70647 var immediateRedraw = function immediateRedraw(difference, extent) {
70648 if (!difference && !extent) cancelPendingRedraw();
70649 redraw(difference, extent);
70652 map.lastPointerEvent = function () {
70653 return _lastPointerEvent;
70656 map.mouse = function (d3_event) {
70657 var event = d3_event || _lastPointerEvent;
70662 while (s = event.sourceEvent) {
70666 return _getMouseCoords(event);
70670 }; // returns Lng/Lat
70673 map.mouseCoordinates = function () {
70674 var coord = map.mouse() || pxCenter();
70675 return projection.invert(coord);
70678 map.dblclickZoomEnable = function (val) {
70679 if (!arguments.length) return _dblClickZoomEnabled;
70680 _dblClickZoomEnabled = val;
70684 map.redrawEnable = function (val) {
70685 if (!arguments.length) return _redrawEnabled;
70686 _redrawEnabled = val;
70690 map.isTransformed = function () {
70691 return _isTransformed;
70694 function setTransform(t2, duration, force) {
70695 var t = projection.transform();
70696 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
70699 _selection.transition().duration(duration).on('start', function () {
70701 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
70703 projection.transform(t2);
70704 _transformStart = t2;
70706 _selection.call(_zoomerPanner.transform, _transformStart);
70712 function setCenterZoom(loc2, z2, duration, force) {
70713 var c = map.center();
70714 var z = map.zoom();
70715 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
70716 var proj = geoRawMercator().transform(projection.transform()); // copy projection
70718 var k2 = clamp$1(geoZoomToScale(z2, TILESIZE), kMin, kMax);
70720 var t = proj.translate();
70721 var point = proj(loc2);
70722 var center = pxCenter();
70723 t[0] += center[0] - point[0];
70724 t[1] += center[1] - point[1];
70725 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
70728 map.pan = function (delta, duration) {
70729 var t = projection.translate();
70730 var k = projection.scale();
70735 _selection.transition().duration(duration).on('start', function () {
70737 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
70739 projection.translate(t);
70740 _transformStart = projection.transform();
70742 _selection.call(_zoomerPanner.transform, _transformStart);
70744 dispatch.call('move', this, map);
70751 map.dimensions = function (val) {
70752 if (!arguments.length) return _dimensions;
70754 drawLayers.dimensions(_dimensions);
70755 context.background().dimensions(_dimensions);
70756 projection.clipExtent([[0, 0], _dimensions]);
70757 _getMouseCoords = utilFastMouse(supersurface.node());
70762 function zoomIn(delta) {
70763 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
70766 function zoomOut(delta) {
70767 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
70770 map.zoomIn = function () {
70774 map.zoomInFurther = function () {
70778 map.canZoomIn = function () {
70779 return map.zoom() < maxZoom;
70782 map.zoomOut = function () {
70786 map.zoomOutFurther = function () {
70790 map.canZoomOut = function () {
70791 return map.zoom() > minZoom;
70794 map.center = function (loc2) {
70795 if (!arguments.length) {
70796 return projection.invert(pxCenter());
70799 if (setCenterZoom(loc2, map.zoom())) {
70800 dispatch.call('move', this, map);
70807 map.unobscuredCenterZoomEase = function (loc, zoom) {
70808 var offset = map.unobscuredOffsetPx();
70809 var proj = geoRawMercator().transform(projection.transform()); // copy projection
70810 // use the target zoom to calculate the offset center
70812 proj.scale(geoZoomToScale(zoom, TILESIZE));
70813 var locPx = proj(loc);
70814 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
70815 var offsetLoc = proj.invert(offsetLocPx);
70816 map.centerZoomEase(offsetLoc, zoom);
70819 map.unobscuredOffsetPx = function () {
70820 var openPane = context.container().select('.map-panes .map-pane.shown');
70822 if (!openPane.empty()) {
70823 return [openPane.node().offsetWidth / 2, 0];
70829 map.zoom = function (z2) {
70830 if (!arguments.length) {
70831 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
70834 if (z2 < _minzoom) {
70835 surface.interrupt();
70836 dispatch.call('hitMinZoom', this, map);
70837 z2 = context.minEditableZoom();
70840 if (setCenterZoom(map.center(), z2)) {
70841 dispatch.call('move', this, map);
70848 map.centerZoom = function (loc2, z2) {
70849 if (setCenterZoom(loc2, z2)) {
70850 dispatch.call('move', this, map);
70857 map.zoomTo = function (entity) {
70858 var extent = entity.extent(context.graph());
70859 if (!isFinite(extent.area())) return map;
70860 var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
70861 return map.centerZoom(extent.center(), z2);
70864 map.centerEase = function (loc2, duration) {
70865 duration = duration || 250;
70866 setCenterZoom(loc2, map.zoom(), duration);
70870 map.zoomEase = function (z2, duration) {
70871 duration = duration || 250;
70872 setCenterZoom(map.center(), z2, duration, false);
70876 map.centerZoomEase = function (loc2, z2, duration) {
70877 duration = duration || 250;
70878 setCenterZoom(loc2, z2, duration, false);
70882 map.transformEase = function (t2, duration) {
70883 duration = duration || 250;
70884 setTransform(t2, duration, false
70890 map.zoomToEase = function (obj, duration) {
70893 if (Array.isArray(obj)) {
70894 obj.forEach(function (entity) {
70895 var entityExtent = entity.extent(context.graph());
70898 extent = entityExtent;
70900 extent = extent.extend(entityExtent);
70904 extent = obj.extent(context.graph());
70907 if (!isFinite(extent.area())) return map;
70908 var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
70909 return map.centerZoomEase(extent.center(), z2, duration);
70912 map.startEase = function () {
70913 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
70919 map.cancelEase = function () {
70920 _selection.interrupt();
70925 map.extent = function (val) {
70926 if (!arguments.length) {
70927 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
70929 var extent = geoExtent(val);
70930 map.centerZoom(extent.center(), map.extentZoom(extent));
70934 map.trimmedExtent = function (val) {
70935 if (!arguments.length) {
70939 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
70941 var extent = geoExtent(val);
70942 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
70946 function calcExtentZoom(extent, dim) {
70947 var tl = projection([extent[0][0], extent[1][1]]);
70948 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
70950 var hFactor = (br[0] - tl[0]) / dim[0];
70951 var vFactor = (br[1] - tl[1]) / dim[1];
70952 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
70953 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
70954 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
70958 map.extentZoom = function (val) {
70959 return calcExtentZoom(geoExtent(val), _dimensions);
70962 map.trimmedExtentZoom = function (val) {
70965 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
70966 return calcExtentZoom(geoExtent(val), trimmed);
70969 map.withinEditableZoom = function () {
70970 return map.zoom() >= context.minEditableZoom();
70973 map.isInWideSelection = function () {
70974 return !map.withinEditableZoom() && context.selectedIDs().length;
70977 map.editableDataEnabled = function (skipZoomCheck) {
70978 var layer = context.layers().layer('osm');
70979 if (!layer || !layer.enabled()) return false;
70980 return skipZoomCheck || map.withinEditableZoom();
70983 map.notesEditable = function () {
70984 var layer = context.layers().layer('notes');
70985 if (!layer || !layer.enabled()) return false;
70986 return map.withinEditableZoom();
70989 map.minzoom = function (val) {
70990 if (!arguments.length) return _minzoom;
70995 map.toggleHighlightEdited = function () {
70996 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
70997 map.pan([0, 0]); // trigger a redraw
70999 dispatch.call('changeHighlighting', this);
71002 map.areaFillOptions = ['wireframe', 'partial', 'full'];
71004 map.activeAreaFill = function (val) {
71005 if (!arguments.length) return corePreferences('area-fill') || 'partial';
71006 corePreferences('area-fill', val);
71008 if (val !== 'wireframe') {
71009 corePreferences('area-fill-toggle', val);
71013 map.pan([0, 0]); // trigger a redraw
71015 dispatch.call('changeAreaFill', this);
71019 map.toggleWireframe = function () {
71020 var activeFill = map.activeAreaFill();
71022 if (activeFill === 'wireframe') {
71023 activeFill = corePreferences('area-fill-toggle') || 'partial';
71025 activeFill = 'wireframe';
71028 map.activeAreaFill(activeFill);
71031 function updateAreaFill() {
71032 var activeFill = map.activeAreaFill();
71033 map.areaFillOptions.forEach(function (opt) {
71034 surface.classed('fill-' + opt, Boolean(opt === activeFill));
71038 map.layers = function () {
71042 map.doubleUpHandler = function () {
71043 return _doubleUpHandler;
71046 return utilRebind(map, dispatch, 'on');
71049 function rendererPhotos(context) {
71050 var dispatch = dispatch$8('change');
71051 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
71052 var _allPhotoTypes = ['flat', 'panoramic'];
71054 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
71057 var _dateFilters = ['fromDate', 'toDate'];
71065 function photos() {}
71067 function updateStorage() {
71068 if (window.mocha) return;
71069 var hash = utilStringQs(window.location.hash);
71070 var enabled = context.layers().all().filter(function (d) {
71071 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
71072 }).map(function (d) {
71076 if (enabled.length) {
71077 hash.photo_overlay = enabled.join(',');
71079 delete hash.photo_overlay;
71082 window.location.replace('#' + utilQsString(hash, true));
71085 photos.overlayLayerIDs = function () {
71089 photos.allPhotoTypes = function () {
71090 return _allPhotoTypes;
71093 photos.dateFilters = function () {
71094 return _dateFilters;
71097 photos.dateFilterValue = function (val) {
71098 return val === _dateFilters[0] ? _fromDate : _toDate;
71101 photos.setDateFilter = function (type, val, updateUrl) {
71102 // validate the date
71103 var date = val && new Date(val);
71105 if (date && !isNaN(date)) {
71106 val = date.toISOString().substr(0, 10);
71111 if (type === _dateFilters[0]) {
71114 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
71115 _toDate = _fromDate;
71119 if (type === _dateFilters[1]) {
71122 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
71123 _fromDate = _toDate;
71127 dispatch.call('change', this);
71132 if (_fromDate || _toDate) {
71133 rangeString = (_fromDate || '') + '_' + (_toDate || '');
71136 setUrlFilterValue('photo_dates', rangeString);
71140 photos.setUsernameFilter = function (val, updateUrl) {
71141 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
71144 val = val.map(function (d) {
71146 }).filter(Boolean);
71154 dispatch.call('change', this);
71160 hashString = _usernames.join(',');
71163 setUrlFilterValue('photo_username', hashString);
71167 function setUrlFilterValue(property, val) {
71168 if (!window.mocha) {
71169 var hash = utilStringQs(window.location.hash);
71172 if (hash[property] === val) return;
71173 hash[property] = val;
71175 if (!(property in hash)) return;
71176 delete hash[property];
71179 window.location.replace('#' + utilQsString(hash, true));
71183 function showsLayer(id) {
71184 var layer = context.layers().layer(id);
71185 return layer && layer.supported() && layer.enabled();
71188 photos.shouldFilterByDate = function () {
71189 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
71192 photos.shouldFilterByPhotoType = function () {
71193 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
71196 photos.shouldFilterByUsername = function () {
71197 return !showsLayer('mapillary') && showsLayer('openstreetcam') && !showsLayer('streetside');
71200 photos.showsPhotoType = function (val) {
71201 if (!photos.shouldFilterByPhotoType()) return true;
71202 return _shownPhotoTypes.indexOf(val) !== -1;
71205 photos.showsFlat = function () {
71206 return photos.showsPhotoType('flat');
71209 photos.showsPanoramic = function () {
71210 return photos.showsPhotoType('panoramic');
71213 photos.fromDate = function () {
71217 photos.toDate = function () {
71221 photos.togglePhotoType = function (val) {
71222 var index = _shownPhotoTypes.indexOf(val);
71224 if (index !== -1) {
71225 _shownPhotoTypes.splice(index, 1);
71227 _shownPhotoTypes.push(val);
71230 dispatch.call('change', this);
71234 photos.usernames = function () {
71238 photos.init = function () {
71239 var hash = utilStringQs(window.location.hash);
71241 if (hash.photo_dates) {
71242 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
71243 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
71244 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
71245 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
71248 if (hash.photo_username) {
71249 this.setUsernameFilter(hash.photo_username, false);
71252 if (hash.photo_overlay) {
71253 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
71254 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
71255 hashOverlayIDs.forEach(function (id) {
71256 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
71257 if (layer && !layer.enabled()) layer.enabled(true);
71262 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
71263 var photoIds = hash.photo.replace(/;/g, ',').split(',');
71264 var photoId = photoIds.length && photoIds[0].trim();
71265 var results = /(.*)\/(.*)/g.exec(photoId);
71267 if (results && results.length >= 3) {
71268 var serviceId = results[1];
71269 var photoKey = results[2];
71270 var service = services[serviceId];
71272 if (service && service.ensureViewerLoaded) {
71273 // if we're showing a photo then make sure its layer is enabled too
71274 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
71275 if (layer && !layer.enabled()) layer.enabled(true);
71276 var baselineTime = Date.now();
71277 service.on('loadedImages.rendererPhotos', function () {
71278 // don't open the viewer if too much time has elapsed
71279 if (Date.now() - baselineTime > 45000) {
71280 service.on('loadedImages.rendererPhotos', null);
71284 if (!service.cachedImage(photoKey)) return;
71285 service.on('loadedImages.rendererPhotos', null);
71286 service.ensureViewerLoaded(context).then(function () {
71287 service.selectImage(context, photoKey).showViewer(context);
71294 context.layers().on('change.rendererPhotos', updateStorage);
71297 return utilRebind(photos, dispatch, 'on');
71300 function uiAccount(context) {
71301 var osm = context.connection();
71303 function update(selection) {
71306 if (!osm.authenticated()) {
71307 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
71311 osm.userDetails(function (err, details) {
71312 var userLink = selection.select('.userLink'),
71313 logoutLink = selection.select('.logoutLink');
71315 logoutLink.html('');
71316 if (err || !details) return;
71317 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
71319 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
71321 if (details.image_url) {
71322 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
71324 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
71328 userLinkA.append('span').attr('class', 'label').html(details.display_name);
71329 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
71330 d3_event.preventDefault();
71336 return function (selection) {
71337 selection.append('li').attr('class', 'userLink').classed('hide', true);
71338 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
71341 osm.on('change.account', function () {
71349 function uiAttribution(context) {
71350 var _selection = select(null);
71352 function render(selection, data, klass) {
71353 var div = selection.selectAll(".".concat(klass)).data([0]);
71354 div = div.enter().append('div').attr('class', klass).merge(div);
71355 var attributions = div.selectAll('.attribution').data(data, function (d) {
71358 attributions.exit().remove();
71359 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
71360 var attribution = select(nodes[i]);
71362 if (d.terms_html) {
71363 attribution.html(d.terms_html);
71368 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
71371 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
71372 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
71373 "default": d.terms_text || d.id || d.name()
71376 if (d.icon && !d.overlay) {
71377 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
71380 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
71381 }).merge(attributions);
71382 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
71383 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
71384 return notice ? [notice] : [];
71386 copyright.exit().remove();
71387 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
71388 copyright.html(String);
71391 function update() {
71392 var baselayer = context.background().baseLayerSource();
71394 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
71396 var z = context.map().zoom();
71397 var overlays = context.background().overlayLayerSources() || [];
71399 _selection.call(render, overlays.filter(function (s) {
71400 return s.validZoom(z);
71401 }), 'overlay-layer-attribution');
71404 return function (selection) {
71405 _selection = selection;
71406 context.background().on('change.attribution', update);
71407 context.map().on('move.attribution', throttle(update, 400, {
71414 function uiContributors(context) {
71415 var osm = context.connection(),
71416 debouncedUpdate = debounce(function () {
71421 wrap = select(null);
71423 function update() {
71426 entities = context.history().intersects(context.map().extent());
71427 entities.forEach(function (entity) {
71428 if (entity && entity.user) users[entity.user] = true;
71430 var u = Object.keys(users),
71431 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
71432 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
71433 var userList = select(document.createElement('span'));
71434 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
71435 return osm.userURL(d);
71436 }).attr('target', '_blank').html(String);
71438 if (u.length > limit) {
71439 var count = select(document.createElement('span'));
71440 var othersNum = u.length - limit + 1;
71441 count.append('a').attr('target', '_blank').attr('href', function () {
71442 return osm.changesetsURL(context.map().center(), context.map().zoom());
71443 }).html(othersNum);
71444 wrap.append('span').html(_t.html('contributors.truncated_list', {
71446 users: userList.html(),
71447 count: count.html()
71450 wrap.append('span').html(_t.html('contributors.list', {
71451 users: userList.html()
71457 wrap.transition().style('opacity', 0);
71458 } else if (hidden) {
71459 wrap.transition().style('opacity', 1);
71463 return function (selection) {
71467 osm.on('loaded.contributors', debouncedUpdate);
71468 context.map().on('move.contributors', debouncedUpdate);
71472 var _popoverID = 0;
71473 function uiPopover(klass) {
71474 var _id = _popoverID++;
71476 var _anchorSelection = select(null);
71478 var popover = function popover(selection) {
71479 _anchorSelection = selection;
71480 selection.each(setup);
71483 var _animation = utilFunctor(false);
71485 var _placement = utilFunctor('top'); // top, bottom, left, right
71488 var _alignment = utilFunctor('center'); // leading, center, trailing
71491 var _scrollContainer = utilFunctor(select(null));
71495 var _displayType = utilFunctor('');
71497 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
71500 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
71502 popover.displayType = function (val) {
71503 if (arguments.length) {
71504 _displayType = utilFunctor(val);
71507 return _displayType;
71511 popover.hasArrow = function (val) {
71512 if (arguments.length) {
71513 _hasArrow = utilFunctor(val);
71520 popover.placement = function (val) {
71521 if (arguments.length) {
71522 _placement = utilFunctor(val);
71529 popover.alignment = function (val) {
71530 if (arguments.length) {
71531 _alignment = utilFunctor(val);
71538 popover.scrollContainer = function (val) {
71539 if (arguments.length) {
71540 _scrollContainer = utilFunctor(val);
71543 return _scrollContainer;
71547 popover.content = function (val) {
71548 if (arguments.length) {
71556 popover.isShown = function () {
71557 var popoverSelection = _anchorSelection.select('.popover-' + _id);
71559 return !popoverSelection.empty() && popoverSelection.classed('in');
71562 popover.show = function () {
71563 _anchorSelection.each(show);
71566 popover.updateContent = function () {
71567 _anchorSelection.each(updateContent);
71570 popover.hide = function () {
71571 _anchorSelection.each(hide);
71574 popover.toggle = function () {
71575 _anchorSelection.each(toggle);
71578 popover.destroy = function (selection, selector) {
71579 // by default, just destroy the current popover
71580 selector = selector || '.popover-' + _id;
71581 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 () {
71582 return this.getAttribute('data-original-title') || this.getAttribute('title');
71583 }).attr('data-original-title', null).selectAll(selector).remove();
71586 popover.destroyAny = function (selection) {
71587 selection.call(popover.destroy, '.popover');
71591 var anchor = select(this);
71593 var animate = _animation.apply(this, arguments);
71595 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
71596 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
71597 enter.append('div').attr('class', 'popover-arrow');
71598 enter.append('div').attr('class', 'popover-inner');
71599 popoverSelection = enter.merge(popoverSelection);
71602 popoverSelection.classed('fade', true);
71605 var display = _displayType.apply(this, arguments);
71607 if (display === 'hover') {
71608 var _lastNonMouseEnterTime;
71610 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
71611 if (d3_event.pointerType) {
71612 if (d3_event.pointerType !== 'mouse') {
71613 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
71616 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
71617 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
71618 // event for non-mouse interactions right after sending
71619 // the correct type pointerenter event. Workaround by discarding
71620 // any mouse event that occurs immediately after a non-mouse event.
71623 } // don't show if buttons are pressed, e.g. during click and drag of map
71626 if (d3_event.buttons !== 0) return;
71627 show.apply(this, arguments);
71628 }).on(_pointerPrefix + 'leave.popover', function () {
71629 hide.apply(this, arguments);
71630 }) // show on focus too for better keyboard navigation support
71631 .on('focus.popover', function () {
71632 show.apply(this, arguments);
71633 }).on('blur.popover', function () {
71634 hide.apply(this, arguments);
71636 } else if (display === 'clickFocus') {
71637 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
71638 d3_event.preventDefault();
71639 d3_event.stopPropagation();
71640 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
71641 d3_event.preventDefault();
71642 d3_event.stopPropagation();
71643 }).on('click.popover', toggle);
71644 popoverSelection // This attribute lets the popover take focus
71645 .attr('tabindex', 0).on('blur.popover', function () {
71646 anchor.each(function () {
71647 hide.apply(this, arguments);
71654 var anchor = select(this);
71655 var popoverSelection = anchor.selectAll('.popover-' + _id);
71657 if (popoverSelection.empty()) {
71658 // popover was removed somehow, put it back
71659 anchor.call(popover.destroy);
71660 anchor.each(setup);
71661 popoverSelection = anchor.selectAll('.popover-' + _id);
71664 popoverSelection.classed('in', true);
71666 var displayType = _displayType.apply(this, arguments);
71668 if (displayType === 'clickFocus') {
71669 anchor.classed('active', true);
71670 popoverSelection.node().focus();
71673 anchor.each(updateContent);
71676 function updateContent() {
71677 var anchor = select(this);
71680 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
71683 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
71684 // set before the dynamic popover size is calculated by the browser
71686 updatePosition.apply(this, arguments);
71687 updatePosition.apply(this, arguments);
71690 function updatePosition() {
71691 var anchor = select(this);
71692 var popoverSelection = anchor.selectAll('.popover-' + _id);
71694 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
71696 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
71697 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
71698 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
71700 var placement = _placement.apply(this, arguments);
71702 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
71704 var alignment = _alignment.apply(this, arguments);
71706 var alignFactor = 0.5;
71708 if (alignment === 'leading') {
71710 } else if (alignment === 'trailing') {
71714 var anchorFrame = getFrame(anchor.node());
71715 var popoverFrame = getFrame(popoverSelection.node());
71718 switch (placement) {
71721 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
71722 y: anchorFrame.y - popoverFrame.h
71728 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
71729 y: anchorFrame.y + anchorFrame.h
71735 x: anchorFrame.x - popoverFrame.w,
71736 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
71742 x: anchorFrame.x + anchorFrame.w,
71743 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
71749 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
71750 var initialPosX = position.x;
71752 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
71753 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
71754 } else if (position.x < 10) {
71758 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
71760 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
71761 arrow.style('left', ~~arrowPosX + 'px');
71764 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
71766 popoverSelection.style('left', null).style('top', null);
71769 function getFrame(node) {
71770 var positionStyle = select(node).style('position');
71772 if (positionStyle === 'absolute' || positionStyle === 'static') {
71774 x: node.offsetLeft - scrollLeft,
71775 y: node.offsetTop - scrollTop,
71776 w: node.offsetWidth,
71777 h: node.offsetHeight
71783 w: node.offsetWidth,
71784 h: node.offsetHeight
71791 var anchor = select(this);
71793 if (_displayType.apply(this, arguments) === 'clickFocus') {
71794 anchor.classed('active', false);
71797 anchor.selectAll('.popover-' + _id).classed('in', false);
71800 function toggle() {
71801 if (select(this).select('.popover-' + _id).classed('in')) {
71802 hide.apply(this, arguments);
71804 show.apply(this, arguments);
71811 function uiTooltip(klass) {
71812 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
71814 var _title = function _title() {
71815 var title = this.getAttribute('data-original-title');
71820 title = this.getAttribute('title');
71821 this.removeAttribute('title');
71822 this.setAttribute('data-original-title', title);
71828 var _heading = utilFunctor(null);
71830 var _keys = utilFunctor(null);
71832 tooltip.title = function (val) {
71833 if (!arguments.length) return _title;
71834 _title = utilFunctor(val);
71838 tooltip.heading = function (val) {
71839 if (!arguments.length) return _heading;
71840 _heading = utilFunctor(val);
71844 tooltip.keys = function (val) {
71845 if (!arguments.length) return _keys;
71846 _keys = utilFunctor(val);
71850 tooltip.content(function () {
71851 var heading = _heading.apply(this, arguments);
71853 var text = _title.apply(this, arguments);
71855 var keys = _keys.apply(this, arguments);
71857 return function (selection) {
71858 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
71859 headingSelect.exit().remove();
71860 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
71861 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
71862 textSelect.exit().remove();
71863 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
71864 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
71865 keyhintWrap.exit().remove();
71866 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
71867 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
71868 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
71869 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
71877 function uiEditMenu(context) {
71878 var dispatch = dispatch$8('toggled');
71880 var _menu = select(null);
71882 var _operations = []; // the position the menu should be displayed relative to
71884 var _anchorLoc = [0, 0];
71885 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
71887 var _triggerType = '';
71888 var _vpTopMargin = 85; // viewport top margin
71890 var _vpBottomMargin = 45; // viewport bottom margin
71892 var _vpSideMargin = 35; // viewport side margin
71894 var _menuTop = false;
71898 var _menuWidth; // hardcode these values to make menu positioning easier
71901 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
71903 var _tooltipWidth = 210; // offset the menu slightly from the target location
71905 var _menuSideMargin = 10;
71906 var _tooltips = [];
71908 var editMenu = function editMenu(selection) {
71909 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
71911 var ops = _operations.filter(function (op) {
71912 return !isTouchMenu || !op.mouseOnly;
71915 if (!ops.length) return;
71916 _tooltips = []; // Position the menu above the anchor for stylus and finger input
71917 // since the mapper's hand likely obscures the screen below the anchor
71919 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
71921 var showLabels = isTouchMenu;
71922 var buttonHeight = showLabels ? 32 : 34;
71925 // Get a general idea of the width based on the length of the label
71926 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
71927 return op.title.length;
71933 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
71934 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
71936 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
71939 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
71940 return 'edit-menu-item edit-menu-item-' + d.id;
71941 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
71942 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
71943 // don't let button presses also act as map input - #1869
71944 d3_event.stopPropagation();
71945 }).on('mouseenter.highlight', function (d3_event, d) {
71946 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
71947 utilHighlightEntities(d.relatedEntityIds(), true, context);
71948 }).on('mouseleave.highlight', function (d3_event, d) {
71949 if (!d.relatedEntityIds) return;
71950 utilHighlightEntities(d.relatedEntityIds(), false, context);
71952 buttonsEnter.each(function (d) {
71953 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
71955 _tooltips.push(tooltip);
71957 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
71961 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
71967 buttonsEnter.merge(buttons).classed('disabled', function (d) {
71968 return d.disabled();
71971 var initialScale = context.projection.scale();
71972 context.map().on('move.edit-menu', function () {
71973 if (initialScale !== context.projection.scale()) {
71976 }).on('drawn.edit-menu', function (info) {
71977 if (info.full) updatePosition();
71979 var lastPointerUpType; // `pointerup` is always called before `click`
71981 function pointerup(d3_event) {
71982 lastPointerUpType = d3_event.pointerType;
71985 function click(d3_event, operation) {
71986 d3_event.stopPropagation();
71988 if (operation.relatedEntityIds) {
71989 utilHighlightEntities(operation.relatedEntityIds(), false, context);
71992 if (operation.disabled()) {
71993 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
71994 // there are no tooltips for touch interactions so flash feedback instead
71995 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
71998 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
71999 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
72006 lastPointerUpType = null;
72009 dispatch.call('toggled', this, true);
72012 function updatePosition() {
72013 if (!_menu || _menu.empty()) return;
72014 var anchorLoc = context.projection(_anchorLocLonLat);
72015 var viewport = context.surfaceRect();
72017 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
72018 // close the menu if it's gone offscreen
72023 var menuLeft = displayOnLeft(viewport);
72024 var offset = [0, 0];
72025 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
72028 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
72029 // menu is near top viewport edge, shift downward
72030 offset[1] = -anchorLoc[1] + _vpTopMargin;
72032 offset[1] = -_menuHeight;
72035 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
72036 // menu is near bottom viewport edge, shift upwards
72037 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
72043 var origin = geoVecAdd(anchorLoc, offset);
72045 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
72047 var tooltipSide = tooltipPosition(viewport, menuLeft);
72049 _tooltips.forEach(function (tooltip) {
72050 tooltip.placement(tooltipSide);
72053 function displayOnLeft(viewport) {
72054 if (_mainLocalizer.textDirection() === 'ltr') {
72055 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
72056 // right menu would be too close to the right viewport edge, go left
72058 } // prefer right menu
72064 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
72065 // left menu would be too close to the left viewport edge, go right
72067 } // prefer left menu
72074 function tooltipPosition(viewport, menuLeft) {
72075 if (_mainLocalizer.textDirection() === 'ltr') {
72077 // if there's not room for a right-side menu then there definitely
72078 // isn't room for right-side tooltips
72082 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
72083 // right tooltips would be too close to the right viewport edge, go left
72085 } // prefer right tooltips
72095 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
72096 // left tooltips would be too close to the left viewport edge, go right
72098 } // prefer left tooltips
72106 editMenu.close = function () {
72107 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
72112 dispatch.call('toggled', this, false);
72115 editMenu.anchorLoc = function (val) {
72116 if (!arguments.length) return _anchorLoc;
72118 _anchorLocLonLat = context.projection.invert(_anchorLoc);
72122 editMenu.triggerType = function (val) {
72123 if (!arguments.length) return _triggerType;
72124 _triggerType = val;
72128 editMenu.operations = function (val) {
72129 if (!arguments.length) return _operations;
72134 return utilRebind(editMenu, dispatch, 'on');
72137 function uiFeatureInfo(context) {
72138 function update(selection) {
72139 var features = context.features();
72140 var stats = features.stats();
72142 var hiddenList = features.hidden().map(function (k) {
72145 return _t('inspector.title_count', {
72146 title: _t.html('feature.' + k + '.description'),
72152 }).filter(Boolean);
72153 selection.html('');
72155 if (hiddenList.length) {
72156 var tooltipBehavior = uiTooltip().placement('top').title(function () {
72157 return hiddenList.join('<br/>');
72159 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
72161 })).call(tooltipBehavior).on('click', function (d3_event) {
72162 tooltipBehavior.hide();
72163 d3_event.preventDefault(); // open the Map Data pane
72165 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
72169 selection.classed('hide', !hiddenList.length);
72172 return function (selection) {
72174 context.features().on('change.feature_info', function () {
72180 function uiFlash(context) {
72183 var _duration = 2000;
72184 var _iconName = '#iD-icon-no';
72185 var _iconClass = 'disabled';
72190 _flashTimer.stop();
72193 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
72194 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
72195 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
72197 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
72198 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
72199 iconEnter.append('circle').attr('r', 9);
72200 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
72201 contentEnter.append('div').attr('class', 'flash-text'); // Update
72203 content = content.merge(contentEnter);
72204 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
72205 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
72206 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
72207 _flashTimer = d3_timeout(function () {
72208 _flashTimer = null;
72209 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
72210 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
72215 flash.duration = function (_) {
72216 if (!arguments.length) return _duration;
72221 flash.label = function (_) {
72222 if (!arguments.length) return _label;
72227 flash.iconName = function (_) {
72228 if (!arguments.length) return _iconName;
72233 flash.iconClass = function (_) {
72234 if (!arguments.length) return _iconClass;
72242 function uiFullScreen(context) {
72243 var element = context.container().node(); // var button = d3_select(null);
72245 function getFullScreenFn() {
72246 if (element.requestFullscreen) {
72247 return element.requestFullscreen;
72248 } else if (element.msRequestFullscreen) {
72249 return element.msRequestFullscreen;
72250 } else if (element.mozRequestFullScreen) {
72251 return element.mozRequestFullScreen;
72252 } else if (element.webkitRequestFullscreen) {
72253 return element.webkitRequestFullscreen;
72257 function getExitFullScreenFn() {
72258 if (document.exitFullscreen) {
72259 return document.exitFullscreen;
72260 } else if (document.msExitFullscreen) {
72261 return document.msExitFullscreen;
72262 } else if (document.mozCancelFullScreen) {
72263 return document.mozCancelFullScreen;
72264 } else if (document.webkitExitFullscreen) {
72265 return document.webkitExitFullscreen;
72269 function isFullScreen() {
72270 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
72273 function isSupported() {
72274 return !!getFullScreenFn();
72277 function fullScreen(d3_event) {
72278 d3_event.preventDefault();
72280 if (!isFullScreen()) {
72281 // button.classed('active', true);
72282 getFullScreenFn().apply(element);
72284 // button.classed('active', false);
72285 getExitFullScreenFn().apply(document);
72289 return function () {
72291 if (!isSupported()) return; // button = selection.append('button')
72292 // .attr('title', t('full_screen'))
72293 // .on('click', fullScreen)
72295 // button.append('span')
72296 // .attr('class', 'icon full-screen');
72298 var detected = utilDetect();
72299 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
72300 context.keybinding().on(keys, fullScreen);
72304 function uiGeolocate(context) {
72305 var _geolocationOptions = {
72306 // prioritize speed and power usage over precision
72307 enableHighAccuracy: false,
72308 // don't hang indefinitely getting the location
72309 timeout: 6000 // 6sec
72313 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
72315 var _layer = context.layers().layer('geolocate');
72323 var _button = select(null);
72326 if (context.inIntro()) return;
72328 if (!_layer.enabled() && !_locating.isShown()) {
72329 // This timeout ensures that we still call finish() even if
72330 // the user declines to share their location in Firefox
72331 _timeoutID = setTimeout(error, 10000
72334 context.container().call(_locating); // get the latest position even if we already have one
72336 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
72340 _layer.enabled(null, false);
72342 updateButtonState();
72346 function zoomTo() {
72347 context.enter(modeBrowse(context));
72348 var map = context.map();
72350 _layer.enabled(_position, true);
72352 updateButtonState();
72353 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
72356 function success(geolocation) {
72357 _position = geolocation;
72358 var coords = _position.coords;
72359 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
72366 // use the position from a previous call if we have one
72369 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
72375 function finish() {
72376 _locating.close(); // unblock ui
72380 clearTimeout(_timeoutID);
72383 _timeoutID = undefined;
72386 function updateButtonState() {
72387 _button.classed('active', _layer.enabled());
72390 return function (selection) {
72391 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
72392 _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')]));
72393 context.keybinding().on(_t('geolocate.key'), click);
72397 function uiPanelBackground(context) {
72398 var background = context.background();
72399 var _currSourceName = null;
72400 var _metadata = {};
72401 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
72403 var debouncedRedraw = debounce(redraw, 250);
72405 function redraw(selection) {
72406 var source = background.baseLayerSource();
72407 if (!source) return;
72408 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
72409 var sourceLabel = source.label();
72411 if (_currSourceName !== sourceLabel) {
72412 _currSourceName = sourceLabel;
72416 selection.html('');
72417 var list = selection.append('ul').attr('class', 'background-info');
72418 list.append('li').html(_currSourceName);
72420 _metadataKeys.forEach(function (k) {
72421 // DigitalGlobe vintage is available in raster layers for now.
72422 if (isDG && k === 'vintage') return;
72423 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]);
72426 debouncedGetMetadata(selection);
72427 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
72428 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
72429 d3_event.preventDefault();
72430 context.setDebug('tile', !context.getDebug('tile'));
72431 selection.call(redraw);
72435 var key = source.id + '-vintage';
72436 var sourceVintage = context.background().findSource(key);
72437 var showsVintage = context.background().showsLayer(sourceVintage);
72438 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
72439 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
72440 d3_event.preventDefault();
72441 context.background().toggleOverlayLayer(sourceVintage);
72442 selection.call(redraw);
72444 } // disable if necessary
72447 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
72448 if (source.id !== layerId) {
72449 var key = layerId + '-vintage';
72450 var sourceVintage = context.background().findSource(key);
72452 if (context.background().showsLayer(sourceVintage)) {
72453 context.background().toggleOverlayLayer(sourceVintage);
72459 var debouncedGetMetadata = debounce(getMetadata, 250);
72461 function getMetadata(selection) {
72462 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
72464 if (tile.empty()) return;
72465 var sourceName = _currSourceName;
72466 var d = tile.datum();
72467 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
72468 var center = context.map().center(); // update zoom
72470 _metadata.zoom = String(zoom);
72471 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
72472 if (!d || !d.length >= 3) return;
72473 background.baseLayerSource().getMetadata(center, d, function (err, result) {
72474 if (err || _currSourceName !== sourceName) return; // update vintage
72476 var vintage = result.vintage;
72477 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
72478 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
72480 _metadataKeys.forEach(function (k) {
72481 if (k === 'zoom' || k === 'vintage') return; // done already
72483 var val = result[k];
72484 _metadata[k] = val;
72485 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
72490 var panel = function panel(selection) {
72491 selection.call(redraw);
72492 context.map().on('drawn.info-background', function () {
72493 selection.call(debouncedRedraw);
72494 }).on('move.info-background', function () {
72495 selection.call(debouncedGetMetadata);
72499 panel.off = function () {
72500 context.map().on('drawn.info-background', null).on('move.info-background', null);
72503 panel.id = 'background';
72504 panel.label = _t.html('info_panels.background.title');
72505 panel.key = _t('info_panels.background.key');
72509 function uiPanelHistory(context) {
72512 function displayTimestamp(timestamp) {
72513 if (!timestamp) return _t('info_panels.history.unknown');
72522 var d = new Date(timestamp);
72523 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
72524 return d.toLocaleString(_mainLocalizer.localeCode(), options);
72527 function displayUser(selection, userName) {
72529 selection.append('span').html(_t.html('info_panels.history.unknown'));
72533 selection.append('span').attr('class', 'user-name').html(userName);
72534 var links = selection.append('div').attr('class', 'links');
72537 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
72540 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
72543 function displayChangeset(selection, changeset) {
72545 selection.append('span').html(_t.html('info_panels.history.unknown'));
72549 selection.append('span').attr('class', 'changeset-id').html(changeset);
72550 var links = selection.append('div').attr('class', 'links');
72553 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
72556 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
72557 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
72560 function redraw(selection) {
72561 var selectedNoteID = context.selectedNoteID();
72562 osm = context.connection();
72563 var selected, note, entity;
72565 if (selectedNoteID && osm) {
72567 selected = [_t('note.note') + ' ' + selectedNoteID];
72568 note = osm.getNote(selectedNoteID);
72570 // selected 1..n entities
72571 selected = context.selectedIDs().filter(function (e) {
72572 return context.hasEntity(e);
72575 if (selected.length) {
72576 entity = context.entity(selected[0]);
72580 var singular = selected.length === 1 ? selected[0] : null;
72581 selection.html('');
72582 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
72585 if (!singular) return;
72588 selection.call(redrawEntity, entity);
72590 selection.call(redrawNote, note);
72594 function redrawNote(selection, note) {
72595 if (!note || note.isNew()) {
72596 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
72600 var list = selection.append('ul');
72601 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
72603 if (note.comments.length) {
72604 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
72605 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
72609 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'));
72613 function redrawEntity(selection, entity) {
72614 if (!entity || entity.isNew()) {
72615 selection.append('div').html(_t.html('info_panels.history.no_history'));
72619 var links = selection.append('div').attr('class', 'links');
72622 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');
72625 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');
72626 var list = selection.append('ul');
72627 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
72628 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
72629 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
72630 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
72633 var panel = function panel(selection) {
72634 selection.call(redraw);
72635 context.map().on('drawn.info-history', function () {
72636 selection.call(redraw);
72638 context.on('enter.info-history', function () {
72639 selection.call(redraw);
72643 panel.off = function () {
72644 context.map().on('drawn.info-history', null);
72645 context.on('enter.info-history', null);
72648 panel.id = 'history';
72649 panel.label = _t.html('info_panels.history.title');
72650 panel.key = _t('info_panels.history.key');
72654 var OSM_PRECISION = 7;
72656 * Returns a localized representation of the given length measurement.
72658 * @param {Number} m area in meters
72659 * @param {Boolean} isImperial true for U.S. customary units; false for metric
72662 function displayLength(m, isImperial) {
72663 var d = m * (isImperial ? 3.28084 : 1);
72676 unit = 'kilometers';
72682 return _t('units.' + unit, {
72683 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
72684 maximumSignificantDigits: 4
72689 * Returns a localized representation of the given area measurement.
72691 * @param {Number} m2 area in square meters
72692 * @param {Boolean} isImperial true for U.S. customary units; false for metric
72695 function displayArea(m2, isImperial) {
72696 var locale = _mainLocalizer.localeCode();
72697 var d = m2 * (isImperial ? 10.7639111056 : 1);
72703 if (d >= 6969600) {
72704 // > 0.25mi² show mi²
72706 unit1 = 'square_miles';
72709 unit1 = 'square_feet';
72712 if (d > 4356 && d < 43560000) {
72713 // 0.1 - 1000 acres
72719 // > 0.25km² show km²
72721 unit1 = 'square_kilometers';
72724 unit1 = 'square_meters';
72727 if (d > 1000 && d < 10000000) {
72728 // 0.1 - 1000 hectares
72730 unit2 = 'hectares';
72734 area = _t('units.' + unit1, {
72735 quantity: d1.toLocaleString(locale, {
72736 maximumSignificantDigits: 4
72741 return _t('units.area_pair', {
72743 area2: _t('units.' + unit2, {
72744 quantity: d2.toLocaleString(locale, {
72745 maximumSignificantDigits: 2
72754 function wrap(x, min, max) {
72756 return ((x - min) % d + d) % d + min;
72759 function clamp(x, min, max) {
72760 return Math.max(min, Math.min(x, max));
72763 function displayCoordinate(deg, pos, neg) {
72764 var locale = _mainLocalizer.localeCode();
72765 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
72766 var sec = (min - Math.floor(min)) * 60;
72767 var displayDegrees = _t('units.arcdegrees', {
72768 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
72770 var displayCoordinate;
72772 if (Math.floor(sec) > 0) {
72773 displayCoordinate = displayDegrees + _t('units.arcminutes', {
72774 quantity: Math.floor(min).toLocaleString(locale)
72775 }) + _t('units.arcseconds', {
72776 quantity: Math.round(sec).toLocaleString(locale)
72778 } else if (Math.floor(min) > 0) {
72779 displayCoordinate = displayDegrees + _t('units.arcminutes', {
72780 quantity: Math.round(min).toLocaleString(locale)
72783 displayCoordinate = _t('units.arcdegrees', {
72784 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
72789 return displayCoordinate;
72791 return _t('units.coordinate', {
72792 coordinate: displayCoordinate,
72793 direction: _t('units.' + (deg > 0 ? pos : neg))
72798 * Returns given coordinate pair in degree-minute-second format.
72800 * @param {Array<Number>} coord longitude and latitude
72804 function dmsCoordinatePair(coord) {
72805 return _t('units.coordinate_pair', {
72806 latitude: displayCoordinate(clamp(coord[1], -90, 90), 'north', 'south'),
72807 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
72811 * Returns the given coordinate pair in decimal format.
72812 * note: unlocalized to avoid comma ambiguity - see #4765
72814 * @param {Array<Number>} coord longitude and latitude
72817 function decimalCoordinatePair(coord) {
72818 return _t('units.coordinate_pair', {
72819 latitude: clamp(coord[1], -90, 90).toFixed(OSM_PRECISION),
72820 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
72824 function uiPanelLocation(context) {
72825 var currLocation = '';
72827 function redraw(selection) {
72828 selection.html('');
72829 var list = selection.append('ul'); // Mouse coordinates
72831 var coord = context.map().mouseCoordinates();
72833 if (coord.some(isNaN)) {
72834 coord = context.map().center();
72837 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
72839 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
72840 debouncedGetLocation(selection, coord);
72843 var debouncedGetLocation = debounce(getLocation, 250);
72845 function getLocation(selection, coord) {
72846 if (!services.geocoder) {
72847 currLocation = _t('info_panels.location.unknown_location');
72848 selection.selectAll('.location-info').html(currLocation);
72850 services.geocoder.reverse(coord, function (err, result) {
72851 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
72852 selection.selectAll('.location-info').html(currLocation);
72857 var panel = function panel(selection) {
72858 selection.call(redraw);
72859 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
72860 selection.call(redraw);
72864 panel.off = function () {
72865 context.surface().on('.info-location', null);
72868 panel.id = 'location';
72869 panel.label = _t.html('info_panels.location.title');
72870 panel.key = _t('info_panels.location.key');
72874 function uiPanelMeasurement(context) {
72875 function radiansToMeters(r) {
72876 // using WGS84 authalic radius (6371007.1809 m)
72877 return r * 6371007.1809;
72880 function steradiansToSqmeters(r) {
72881 // http://gis.stackexchange.com/a/124857/40446
72882 return r / (4 * Math.PI) * 510065621724000;
72885 function toLineString(feature) {
72886 if (feature.type === 'LineString') return feature;
72888 type: 'LineString',
72892 if (feature.type === 'Polygon') {
72893 result.coordinates = feature.coordinates[0];
72894 } else if (feature.type === 'MultiPolygon') {
72895 result.coordinates = feature.coordinates[0][0];
72901 var _isImperial = !_mainLocalizer.usesMetric();
72903 function redraw(selection) {
72904 var graph = context.graph();
72905 var selectedNoteID = context.selectedNoteID();
72906 var osm = services.osm;
72907 var localeCode = _mainLocalizer.localeCode();
72909 var center, location, centroid;
72910 var closed, geometry;
72911 var totalNodeCount,
72916 if (selectedNoteID && osm) {
72918 var note = osm.getNote(selectedNoteID);
72919 heading = _t('note.note') + ' ' + selectedNoteID;
72920 location = note.loc;
72923 // selected 1..n entities
72924 var selectedIDs = context.selectedIDs().filter(function (id) {
72925 return context.hasEntity(id);
72927 var selected = selectedIDs.map(function (id) {
72928 return context.entity(id);
72930 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
72934 if (selected.length) {
72935 var extent = geoExtent();
72937 for (var i in selected) {
72938 var entity = selected[i];
72940 extent._extend(entity.extent(graph));
72942 geometry = entity.geometry(graph);
72944 if (geometry === 'line' || geometry === 'area') {
72945 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
72946 var feature = entity.asGeoJSON(graph);
72947 length += radiansToMeters(d3_geoLength(toLineString(feature)));
72948 centroid = d3_geoPath(context.projection).centroid(entity.asGeoJSON(graph));
72949 centroid = centroid && context.projection.invert(centroid);
72951 if (!centroid || !isFinite(centroid[0]) || !isFinite(centroid[1])) {
72952 centroid = entity.extent(graph).center();
72956 area += steradiansToSqmeters(entity.area(graph));
72961 if (selected.length > 1) {
72967 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
72968 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
72971 if (selected.length === 1 && selected[0].type === 'node') {
72972 location = selected[0].loc;
72974 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
72977 if (!location && !centroid) {
72978 center = extent.center();
72983 selection.html('');
72986 selection.append('h4').attr('class', 'measurement-heading').html(heading);
72989 var list = selection.append('ul');
72993 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
72996 if (totalNodeCount) {
72997 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
73001 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
73005 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
73008 if (typeof distance === 'number') {
73009 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
73013 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
73014 coordItem.append('span').html(dmsCoordinatePair(location));
73015 coordItem.append('span').html(decimalCoordinatePair(location));
73019 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
73020 coordItem.append('span').html(dmsCoordinatePair(centroid));
73021 coordItem.append('span').html(decimalCoordinatePair(centroid));
73025 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
73026 coordItem.append('span').html(dmsCoordinatePair(center));
73027 coordItem.append('span').html(decimalCoordinatePair(center));
73030 if (length || area || typeof distance === 'number') {
73031 var toggle = _isImperial ? 'imperial' : 'metric';
73032 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
73033 d3_event.preventDefault();
73034 _isImperial = !_isImperial;
73035 selection.call(redraw);
73040 var panel = function panel(selection) {
73041 selection.call(redraw);
73042 context.map().on('drawn.info-measurement', function () {
73043 selection.call(redraw);
73045 context.on('enter.info-measurement', function () {
73046 selection.call(redraw);
73050 panel.off = function () {
73051 context.map().on('drawn.info-measurement', null);
73052 context.on('enter.info-measurement', null);
73055 panel.id = 'measurement';
73056 panel.label = _t.html('info_panels.measurement.title');
73057 panel.key = _t('info_panels.measurement.key');
73061 var uiInfoPanels = {
73062 background: uiPanelBackground,
73063 history: uiPanelHistory,
73064 location: uiPanelLocation,
73065 measurement: uiPanelMeasurement
73068 function uiInfo(context) {
73069 var ids = Object.keys(uiInfoPanels);
73070 var wasActive = ['measurement'];
73072 var active = {}; // create panels
73074 ids.forEach(function (k) {
73076 panels[k] = uiInfoPanels[k](context);
73081 function info(selection) {
73082 function redraw() {
73083 var activeids = ids.filter(function (k) {
73086 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
73089 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
73090 select(this).call(panels[d].off).remove();
73092 var enter = containers.enter().append('div').attr('class', function (d) {
73093 return 'fillD2 panel-container panel-container-' + d;
73095 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
73096 var title = enter.append('div').attr('class', 'panel-title fillD2');
73097 title.append('h3').html(function (d) {
73098 return panels[d].label;
73100 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
73101 d3_event.stopImmediatePropagation();
73102 d3_event.preventDefault();
73104 }).call(svgIcon('#iD-icon-close'));
73105 enter.append('div').attr('class', function (d) {
73106 return 'panel-content panel-content-' + d;
73107 }); // redraw the panels
73109 infoPanels.selectAll('.panel-content').each(function (d) {
73110 select(this).call(panels[d]);
73114 info.toggle = function (which) {
73115 var activeids = ids.filter(function (k) {
73121 active[which] = !active[which];
73123 if (activeids.length === 1 && activeids[0] === which) {
73124 // none active anymore
73125 wasActive = [which];
73128 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
73131 if (activeids.length) {
73132 wasActive = activeids;
73133 activeids.forEach(function (k) {
73137 wasActive.forEach(function (k) {
73146 var infoPanels = selection.selectAll('.info-panels').data([0]);
73147 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
73149 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
73150 d3_event.stopImmediatePropagation();
73151 d3_event.preventDefault();
73154 ids.forEach(function (k) {
73155 var key = _t('info_panels.' + k + '.key', {
73159 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
73160 d3_event.stopImmediatePropagation();
73161 d3_event.preventDefault();
73170 function pointBox(loc, context) {
73171 var rect = context.surfaceRect();
73172 var point = context.curtainProjection(loc);
73174 left: point[0] + rect.left - 40,
73175 top: point[1] + rect.top - 60,
73180 function pad(locOrBox, padding, context) {
73183 if (locOrBox instanceof Array) {
73184 var rect = context.surfaceRect();
73185 var point = context.curtainProjection(locOrBox);
73187 left: point[0] + rect.left,
73188 top: point[1] + rect.top
73195 left: box.left - padding,
73196 top: box.top - padding,
73197 width: (box.width || 0) + 2 * padding,
73198 height: (box.width || 0) + 2 * padding
73201 function icon(name, svgklass, useklass) {
73202 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
73204 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
73205 // label replacements suitable for tutorials and documentation. Optionally supplemented
73206 // with custom `replacements`
73208 function helpHtml(id, replacements) {
73209 // only load these the first time
73210 if (!helpStringReplacements) {
73211 helpStringReplacements = {
73212 // insert icons corresponding to various UI elements
73213 point_icon: icon('#iD-icon-point', 'inline'),
73214 line_icon: icon('#iD-icon-line', 'inline'),
73215 area_icon: icon('#iD-icon-area', 'inline'),
73216 note_icon: icon('#iD-icon-note', 'inline add-note'),
73217 plus: icon('#iD-icon-plus', 'inline'),
73218 minus: icon('#iD-icon-minus', 'inline'),
73219 layers_icon: icon('#iD-icon-layers', 'inline'),
73220 data_icon: icon('#iD-icon-data', 'inline'),
73221 inspect: icon('#iD-icon-inspect', 'inline'),
73222 help_icon: icon('#iD-icon-help', 'inline'),
73223 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
73224 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
73225 save_icon: icon('#iD-icon-save', 'inline'),
73227 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
73228 continue_icon: icon('#iD-operation-continue', 'inline operation'),
73229 copy_icon: icon('#iD-operation-copy', 'inline operation'),
73230 delete_icon: icon('#iD-operation-delete', 'inline operation'),
73231 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
73232 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
73233 extract_icon: icon('#iD-operation-extract', 'inline operation'),
73234 merge_icon: icon('#iD-operation-merge', 'inline operation'),
73235 move_icon: icon('#iD-operation-move', 'inline operation'),
73236 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
73237 paste_icon: icon('#iD-operation-paste', 'inline operation'),
73238 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
73239 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
73240 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
73241 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
73242 split_icon: icon('#iD-operation-split', 'inline operation'),
73243 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
73244 // interaction icons
73245 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
73246 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
73247 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
73248 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
73249 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
73250 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
73251 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
73252 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
73253 // insert keys; may be localized and platform-dependent
73254 shift: uiCmd.display('⇧'),
73255 alt: uiCmd.display('⌥'),
73256 "return": uiCmd.display('↵'),
73257 esc: _t.html('shortcuts.key.esc'),
73258 space: _t.html('shortcuts.key.space'),
73259 add_note_key: _t.html('modes.add_note.key'),
73260 help_key: _t.html('help.key'),
73261 shortcuts_key: _t.html('shortcuts.toggle.key'),
73262 // reference localized UI labels directly so that they'll always match
73263 save: _t.html('save.title'),
73264 undo: _t.html('undo.title'),
73265 redo: _t.html('redo.title'),
73266 upload: _t.html('commit.save'),
73267 point: _t.html('modes.add_point.title'),
73268 line: _t.html('modes.add_line.title'),
73269 area: _t.html('modes.add_area.title'),
73270 note: _t.html('modes.add_note.label'),
73271 circularize: _t.html('operations.circularize.title'),
73272 "continue": _t.html('operations.continue.title'),
73273 copy: _t.html('operations.copy.title'),
73274 "delete": _t.html('operations.delete.title'),
73275 disconnect: _t.html('operations.disconnect.title'),
73276 downgrade: _t.html('operations.downgrade.title'),
73277 extract: _t.html('operations.extract.title'),
73278 merge: _t.html('operations.merge.title'),
73279 move: _t.html('operations.move.title'),
73280 orthogonalize: _t.html('operations.orthogonalize.title'),
73281 paste: _t.html('operations.paste.title'),
73282 reflect_long: _t.html('operations.reflect.title.long'),
73283 reflect_short: _t.html('operations.reflect.title.short'),
73284 reverse: _t.html('operations.reverse.title'),
73285 rotate: _t.html('operations.rotate.title'),
73286 split: _t.html('operations.split.title'),
73287 straighten: _t.html('operations.straighten.title'),
73288 map_data: _t.html('map_data.title'),
73289 osm_notes: _t.html('map_data.layers.notes.title'),
73290 fields: _t.html('inspector.fields'),
73291 tags: _t.html('inspector.tags'),
73292 relations: _t.html('inspector.relations'),
73293 new_relation: _t.html('inspector.new_relation'),
73294 turn_restrictions: _t.html('_tagging.presets.fields.restrictions.label'),
73295 background_settings: _t.html('background.description'),
73296 imagery_offset: _t.html('background.fix_misalignment'),
73297 start_the_walkthrough: _t.html('splash.walkthrough'),
73298 help: _t.html('help.title'),
73299 ok: _t.html('intro.ok')
73305 if (replacements) {
73306 reps = Object.assign(replacements, helpStringReplacements);
73308 reps = helpStringReplacements;
73311 return _t.html(id, reps) // use keyboard key styling for shortcuts
73312 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
73315 function slugify(text) {
73316 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
73317 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
73318 .replace(/\-\-+/g, '-') // Replace multiple - with single -
73319 .replace(/^-+/, '') // Trim - from start of text
73320 .replace(/-+$/, ''); // Trim - from end of text
73321 } // console warning for missing walkthrough names
73324 var missingStrings = {};
73326 function checkKey(key, text) {
73328 "default": undefined
73329 }) === undefined) {
73330 if (missingStrings.hasOwnProperty(key)) return; // warn once
73332 missingStrings[key] = text;
73333 var missing = key + ': ' + text;
73334 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
73338 function localize(obj) {
73339 var key; // Assign name if entity has one..
73341 var name = obj.tags && obj.tags.name;
73344 key = 'intro.graph.name.' + slugify(name);
73345 obj.tags.name = _t(key, {
73348 checkKey(key, name);
73349 } // Assign street name if entity has one..
73352 var street = obj.tags && obj.tags['addr:street'];
73355 key = 'intro.graph.name.' + slugify(street);
73356 obj.tags['addr:street'] = _t(key, {
73359 checkKey(key, street); // Add address details common across walkthrough..
73361 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
73362 addrTags.forEach(function (k) {
73363 var key = 'intro.graph.' + k;
73364 var tag = 'addr:' + k;
73365 var val = obj.tags && obj.tags[tag];
73366 var str = _t(key, {
73371 if (str.match(/^<.*>$/) !== null) {
73372 delete obj.tags[tag];
73374 obj.tags[tag] = str;
73381 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
73383 function isMostlySquare(points) {
73384 // note: uses 15 here instead of the 12 from actionOrthogonalize because
73385 // actionOrthogonalize can actually straighten some larger angles as it iterates
73386 var threshold = 15; // degrees within right or straight
73388 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
73390 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
73392 for (var i = 0; i < points.length; i++) {
73393 var a = points[(i - 1 + points.length) % points.length];
73394 var origin = points[i];
73395 var b = points[(i + 1) % points.length];
73396 var dotp = geoVecNormalizedDot(a, b, origin);
73397 var mag = Math.abs(dotp);
73399 if (mag > lowerBound && mag < upperBound) {
73406 function selectMenuItem(context, operation) {
73407 return context.container().select('.edit-menu .edit-menu-item-' + operation);
73409 function transitionTime(point1, point2) {
73410 var distance = geoSphericalDistance(point1, point2);
73412 if (distance === 0) {
73414 } else if (distance < 80) {
73421 // hide class, which sets display=none, and a d3 transition for opacity.
73422 // this will cause blinking when called repeatedly, so check that the
73423 // value actually changes between calls.
73425 function uiToggle(show, callback) {
73426 return function (selection) {
73427 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
73428 select(this).classed('hide', !show).style('opacity', null);
73429 if (callback) callback.apply(this);
73434 function uiCurtain(containerNode) {
73435 var surface = select(null),
73436 tooltip = select(null),
73437 darkness = select(null);
73439 function curtain(selection) {
73440 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
73441 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
73442 select(window).on('resize.curtain', resize);
73443 tooltip = selection.append('div').attr('class', 'tooltip');
73444 tooltip.append('div').attr('class', 'popover-arrow');
73445 tooltip.append('div').attr('class', 'popover-inner');
73448 function resize() {
73449 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
73450 curtain.cut(darkness.datum());
73454 * Reveal cuts the curtain to highlight the given box,
73455 * and shows a tooltip with instructions next to the box.
73457 * @param {String|ClientRect} [box] box used to cut the curtain
73458 * @param {String} [text] text for a tooltip
73459 * @param {Object} [options]
73460 * @param {string} [options.tooltipClass] optional class to add to the tooltip
73461 * @param {integer} [options.duration] transition time in milliseconds
73462 * @param {string} [options.buttonText] if set, create a button with this text label
73463 * @param {function} [options.buttonCallback] if set, the callback for the button
73464 * @param {function} [options.padding] extra margin in px to put around bbox
73465 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
73469 curtain.reveal = function (box, html, options) {
73470 options = options || {};
73472 if (typeof box === 'string') {
73473 box = select(box).node();
73476 if (box && box.getBoundingClientRect) {
73477 box = copyBox(box.getBoundingClientRect());
73478 var containerRect = containerNode.getBoundingClientRect();
73479 box.top -= containerRect.top;
73480 box.left -= containerRect.left;
73483 if (box && options.padding) {
73484 box.top -= options.padding;
73485 box.left -= options.padding;
73486 box.bottom += options.padding;
73487 box.right += options.padding;
73488 box.height += options.padding * 2;
73489 box.width += options.padding * 2;
73494 if (options.tooltipBox) {
73495 tooltipBox = options.tooltipBox;
73497 if (typeof tooltipBox === 'string') {
73498 tooltipBox = select(tooltipBox).node();
73501 if (tooltipBox && tooltipBox.getBoundingClientRect) {
73502 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
73508 if (tooltipBox && html) {
73509 if (html.indexOf('**') !== -1) {
73510 if (html.indexOf('<span') === 0) {
73511 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
73513 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
73514 } // pseudo markdown bold text for the instruction section..
73517 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
73520 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
73522 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
73524 if (options.buttonText && options.buttonCallback) {
73525 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
73528 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
73529 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
73531 if (options.buttonText && options.buttonCallback) {
73532 var button = tooltip.selectAll('.button-section .button.action');
73533 button.on('click', function (d3_event) {
73534 d3_event.preventDefault();
73535 options.buttonCallback();
73539 var tip = copyBox(tooltip.node().getBoundingClientRect()),
73540 w = containerNode.clientWidth,
73541 h = containerNode.clientHeight,
73542 tooltipWidth = 200,
73545 pos; // hack: this will have bottom placement,
73546 // so need to reserve extra space for the tooltip illustration.
73548 if (options.tooltipClass === 'intro-mouse') {
73550 } // trim box dimensions to just the portion that fits in the container..
73553 if (tooltipBox.top + tooltipBox.height > h) {
73554 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
73557 if (tooltipBox.left + tooltipBox.width > w) {
73558 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
73559 } // determine tooltip placement..
73562 if (tooltipBox.top + tooltipBox.height < 100) {
73563 // tooltip below box..
73565 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
73566 } else if (tooltipBox.top > h - 140) {
73567 // tooltip above box..
73569 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
73571 // tooltip to the side of the tooltipBox..
73572 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
73574 if (_mainLocalizer.textDirection() === 'rtl') {
73575 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
73577 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
73580 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
73583 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
73585 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
73588 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
73593 if (options.duration !== 0 || !tooltip.classed(side)) {
73594 tooltip.call(uiToggle(true));
73597 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
73598 // (doesn't affect the placement of the popover-arrow)
73602 if (side === 'left' || side === 'right') {
73604 shiftY = 60 - pos[1];
73605 } else if (pos[1] + tip.height > h - 100) {
73606 shiftY = h - pos[1] - tip.height - 100;
73610 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
73612 tooltip.classed('in', false).call(uiToggle(false));
73615 curtain.cut(box, options.duration);
73619 curtain.cut = function (datum, duration) {
73620 darkness.datum(datum).interrupt();
73623 if (duration === 0) {
73624 selection = darkness;
73626 selection = darkness.transition().duration(duration || 600).ease(linear$1);
73629 selection.attr('d', function (d) {
73630 var containerWidth = containerNode.clientWidth;
73631 var containerHeight = containerNode.clientHeight;
73632 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
73633 if (!d) return string;
73634 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';
73638 curtain.remove = function () {
73641 select(window).on('resize.curtain', null);
73642 }; // ClientRects are immutable, so copy them to an object,
73643 // in case we need to trim the height/width.
73646 function copyBox(src) {
73650 bottom: src.bottom,
73660 function uiIntroWelcome(context, reveal) {
73661 var dispatch = dispatch$8('done');
73663 title: 'intro.welcome.title'
73666 function welcome() {
73667 context.map().centerZoom([-85.63591, 41.94285], 19);
73668 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
73669 buttonText: _t.html('intro.ok'),
73670 buttonCallback: practice
73674 function practice() {
73675 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
73676 buttonText: _t.html('intro.ok'),
73677 buttonCallback: words
73682 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
73683 buttonText: _t.html('intro.ok'),
73684 buttonCallback: chapters
73688 function chapters() {
73689 dispatch.call('done');
73690 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
73691 next: _t('intro.navigation.title')
73695 chapter.enter = function () {
73699 chapter.exit = function () {
73700 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
73703 chapter.restart = function () {
73708 return utilRebind(chapter, dispatch, 'on');
73711 function uiIntroNavigation(context, reveal) {
73712 var dispatch = dispatch$8('done');
73714 var hallId = 'n2061';
73715 var townHall = [-85.63591, 41.94285];
73716 var springStreetId = 'w397';
73717 var springStreetEndId = 'n1834';
73718 var springStreet = [-85.63582, 41.94255];
73719 var onewayField = _mainPresetIndex.field('oneway');
73720 var maxspeedField = _mainPresetIndex.field('maxspeed');
73722 title: 'intro.navigation.title'
73725 function timeout(f, t) {
73726 timeouts.push(window.setTimeout(f, t));
73729 function eventCancel(d3_event) {
73730 d3_event.stopPropagation();
73731 d3_event.preventDefault();
73734 function isTownHallSelected() {
73735 var ids = context.selectedIDs();
73736 return ids.length === 1 && ids[0] === hallId;
73739 function dragMap() {
73740 context.enter(modeBrowse(context));
73741 context.history().reset('initial');
73742 var msec = transitionTime(townHall, context.map().center());
73745 reveal(null, null, {
73750 context.map().centerZoomEase(townHall, 19, msec);
73751 timeout(function () {
73752 var centerStart = context.map().center();
73753 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
73754 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
73755 reveal('.surface', dragString);
73756 context.map().on('drawn.intro', function () {
73757 reveal('.surface', dragString, {
73761 context.map().on('move.intro', function () {
73762 var centerNow = context.map().center();
73764 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
73765 context.map().on('move.intro', null);
73766 timeout(function () {
73767 continueTo(zoomMap);
73773 function continueTo(nextStep) {
73774 context.map().on('move.intro drawn.intro', null);
73779 function zoomMap() {
73780 var zoomStart = context.map().zoom();
73781 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
73782 var zoomString = helpHtml('intro.navigation.' + textId);
73783 reveal('.surface', zoomString);
73784 context.map().on('drawn.intro', function () {
73785 reveal('.surface', zoomString, {
73789 context.map().on('move.intro', function () {
73790 if (context.map().zoom() !== zoomStart) {
73791 context.map().on('move.intro', null);
73792 timeout(function () {
73793 continueTo(features);
73798 function continueTo(nextStep) {
73799 context.map().on('move.intro drawn.intro', null);
73804 function features() {
73805 var onClick = function onClick() {
73806 continueTo(pointsLinesAreas);
73809 reveal('.surface', helpHtml('intro.navigation.features'), {
73810 buttonText: _t.html('intro.ok'),
73811 buttonCallback: onClick
73813 context.map().on('drawn.intro', function () {
73814 reveal('.surface', helpHtml('intro.navigation.features'), {
73816 buttonText: _t.html('intro.ok'),
73817 buttonCallback: onClick
73821 function continueTo(nextStep) {
73822 context.map().on('drawn.intro', null);
73827 function pointsLinesAreas() {
73828 var onClick = function onClick() {
73829 continueTo(nodesWays);
73832 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
73833 buttonText: _t.html('intro.ok'),
73834 buttonCallback: onClick
73836 context.map().on('drawn.intro', function () {
73837 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
73839 buttonText: _t.html('intro.ok'),
73840 buttonCallback: onClick
73844 function continueTo(nextStep) {
73845 context.map().on('drawn.intro', null);
73850 function nodesWays() {
73851 var onClick = function onClick() {
73852 continueTo(clickTownHall);
73855 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
73856 buttonText: _t.html('intro.ok'),
73857 buttonCallback: onClick
73859 context.map().on('drawn.intro', function () {
73860 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
73862 buttonText: _t.html('intro.ok'),
73863 buttonCallback: onClick
73867 function continueTo(nextStep) {
73868 context.map().on('drawn.intro', null);
73873 function clickTownHall() {
73874 context.enter(modeBrowse(context));
73875 context.history().reset('initial');
73876 var entity = context.hasEntity(hallId);
73877 if (!entity) return;
73878 reveal(null, null, {
73881 context.map().centerZoomEase(entity.loc, 19, 500);
73882 timeout(function () {
73883 var entity = context.hasEntity(hallId);
73884 if (!entity) return;
73885 var box = pointBox(entity.loc, context);
73886 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
73887 reveal(box, helpHtml('intro.navigation.' + textId));
73888 context.map().on('move.intro drawn.intro', function () {
73889 var entity = context.hasEntity(hallId);
73890 if (!entity) return;
73891 var box = pointBox(entity.loc, context);
73892 reveal(box, helpHtml('intro.navigation.' + textId), {
73896 context.on('enter.intro', function () {
73897 if (isTownHallSelected()) continueTo(selectedTownHall);
73899 }, 550); // after centerZoomEase
73901 context.history().on('change.intro', function () {
73902 if (!context.hasEntity(hallId)) {
73903 continueTo(clickTownHall);
73907 function continueTo(nextStep) {
73908 context.on('enter.intro', null);
73909 context.map().on('move.intro drawn.intro', null);
73910 context.history().on('change.intro', null);
73915 function selectedTownHall() {
73916 if (!isTownHallSelected()) return clickTownHall();
73917 var entity = context.hasEntity(hallId);
73918 if (!entity) return clickTownHall();
73919 var box = pointBox(entity.loc, context);
73921 var onClick = function onClick() {
73922 continueTo(editorTownHall);
73925 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
73926 buttonText: _t.html('intro.ok'),
73927 buttonCallback: onClick
73929 context.map().on('move.intro drawn.intro', function () {
73930 var entity = context.hasEntity(hallId);
73931 if (!entity) return;
73932 var box = pointBox(entity.loc, context);
73933 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
73935 buttonText: _t.html('intro.ok'),
73936 buttonCallback: onClick
73939 context.history().on('change.intro', function () {
73940 if (!context.hasEntity(hallId)) {
73941 continueTo(clickTownHall);
73945 function continueTo(nextStep) {
73946 context.map().on('move.intro drawn.intro', null);
73947 context.history().on('change.intro', null);
73952 function editorTownHall() {
73953 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
73955 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
73957 var onClick = function onClick() {
73958 continueTo(presetTownHall);
73961 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
73962 buttonText: _t.html('intro.ok'),
73963 buttonCallback: onClick
73965 context.on('exit.intro', function () {
73966 continueTo(clickTownHall);
73968 context.history().on('change.intro', function () {
73969 if (!context.hasEntity(hallId)) {
73970 continueTo(clickTownHall);
73974 function continueTo(nextStep) {
73975 context.on('exit.intro', null);
73976 context.history().on('change.intro', null);
73977 context.container().select('.inspector-wrap').on('wheel.intro', null);
73982 function presetTownHall() {
73983 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
73985 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
73987 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
73989 var entity = context.entity(context.selectedIDs()[0]);
73990 var preset = _mainPresetIndex.match(entity, context.graph());
73992 var onClick = function onClick() {
73993 continueTo(fieldsTownHall);
73996 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
73997 preset: preset.name()
73999 buttonText: _t.html('intro.ok'),
74000 buttonCallback: onClick
74002 context.on('exit.intro', function () {
74003 continueTo(clickTownHall);
74005 context.history().on('change.intro', function () {
74006 if (!context.hasEntity(hallId)) {
74007 continueTo(clickTownHall);
74011 function continueTo(nextStep) {
74012 context.on('exit.intro', null);
74013 context.history().on('change.intro', null);
74014 context.container().select('.inspector-wrap').on('wheel.intro', null);
74019 function fieldsTownHall() {
74020 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
74022 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
74024 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74026 var onClick = function onClick() {
74027 continueTo(closeTownHall);
74030 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
74031 buttonText: _t.html('intro.ok'),
74032 buttonCallback: onClick
74034 context.on('exit.intro', function () {
74035 continueTo(clickTownHall);
74037 context.history().on('change.intro', function () {
74038 if (!context.hasEntity(hallId)) {
74039 continueTo(clickTownHall);
74043 function continueTo(nextStep) {
74044 context.on('exit.intro', null);
74045 context.history().on('change.intro', null);
74046 context.container().select('.inspector-wrap').on('wheel.intro', null);
74051 function closeTownHall() {
74052 if (!isTownHallSelected()) return clickTownHall();
74053 var selector = '.entity-editor-pane button.close svg use';
74054 var href = select(selector).attr('href') || '#iD-icon-close';
74055 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
74056 button: icon(href, 'inline')
74058 context.on('exit.intro', function () {
74059 continueTo(searchStreet);
74061 context.history().on('change.intro', function () {
74062 // update the close icon in the tooltip if the user edits something.
74063 var selector = '.entity-editor-pane button.close svg use';
74064 var href = select(selector).attr('href') || '#iD-icon-close';
74065 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
74066 button: icon(href, 'inline')
74072 function continueTo(nextStep) {
74073 context.on('exit.intro', null);
74074 context.history().on('change.intro', null);
74079 function searchStreet() {
74080 context.enter(modeBrowse(context));
74081 context.history().reset('initial'); // ensure spring street exists
74083 var msec = transitionTime(springStreet, context.map().center());
74086 reveal(null, null, {
74091 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
74093 timeout(function () {
74094 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
74095 name: _t('intro.graph.name.spring-street')
74097 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
74101 function checkSearchResult() {
74102 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
74104 var firstName = first.select('.entity-name');
74105 var name = _t('intro.graph.name.spring-street');
74107 if (!firstName.empty() && firstName.html() === name) {
74108 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
74113 context.on('exit.intro', function () {
74114 continueTo(selectedStreet);
74116 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
74119 function continueTo(nextStep) {
74120 context.on('exit.intro', null);
74121 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
74126 function selectedStreet() {
74127 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
74128 return searchStreet();
74131 var onClick = function onClick() {
74132 continueTo(editorStreet);
74135 var entity = context.entity(springStreetEndId);
74136 var box = pointBox(entity.loc, context);
74138 reveal(box, helpHtml('intro.navigation.selected_street', {
74139 name: _t('intro.graph.name.spring-street')
74142 buttonText: _t.html('intro.ok'),
74143 buttonCallback: onClick
74145 timeout(function () {
74146 context.map().on('move.intro drawn.intro', function () {
74147 var entity = context.hasEntity(springStreetEndId);
74148 if (!entity) return;
74149 var box = pointBox(entity.loc, context);
74151 reveal(box, helpHtml('intro.navigation.selected_street', {
74152 name: _t('intro.graph.name.spring-street')
74155 buttonText: _t.html('intro.ok'),
74156 buttonCallback: onClick
74159 }, 600); // after reveal.
74161 context.on('enter.intro', function (mode) {
74162 if (!context.hasEntity(springStreetId)) {
74163 return continueTo(searchStreet);
74166 var ids = context.selectedIDs();
74168 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
74169 // keep Spring Street selected..
74170 context.enter(modeSelect(context, [springStreetId]));
74173 context.history().on('change.intro', function () {
74174 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
74175 timeout(function () {
74176 continueTo(searchStreet);
74177 }, 300); // after any transition (e.g. if user deleted intersection)
74181 function continueTo(nextStep) {
74182 context.map().on('move.intro drawn.intro', null);
74183 context.on('enter.intro', null);
74184 context.history().on('change.intro', null);
74189 function editorStreet() {
74190 var selector = '.entity-editor-pane button.close svg use';
74191 var href = select(selector).attr('href') || '#iD-icon-close';
74192 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
74193 button: icon(href, 'inline'),
74194 field1: onewayField.label(),
74195 field2: maxspeedField.label()
74197 context.on('exit.intro', function () {
74200 context.history().on('change.intro', function () {
74201 // update the close icon in the tooltip if the user edits something.
74202 var selector = '.entity-editor-pane button.close svg use';
74203 var href = select(selector).attr('href') || '#iD-icon-close';
74204 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
74205 button: icon(href, 'inline'),
74206 field1: onewayField.label(),
74207 field2: maxspeedField.label()
74213 function continueTo(nextStep) {
74214 context.on('exit.intro', null);
74215 context.history().on('change.intro', null);
74221 dispatch.call('done');
74222 reveal('.ideditor', helpHtml('intro.navigation.play', {
74223 next: _t('intro.points.title')
74225 tooltipBox: '.intro-nav-wrap .chapter-point',
74226 buttonText: _t.html('intro.ok'),
74227 buttonCallback: function buttonCallback() {
74228 reveal('.ideditor');
74233 chapter.enter = function () {
74237 chapter.exit = function () {
74238 timeouts.forEach(window.clearTimeout);
74239 context.on('enter.intro exit.intro', null);
74240 context.map().on('move.intro drawn.intro', null);
74241 context.history().on('change.intro', null);
74242 context.container().select('.inspector-wrap').on('wheel.intro', null);
74243 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
74246 chapter.restart = function () {
74251 return utilRebind(chapter, dispatch, 'on');
74254 function uiIntroPoint(context, reveal) {
74255 var dispatch = dispatch$8('done');
74257 var intersection = [-85.63279, 41.94394];
74258 var building = [-85.632422, 41.944045];
74259 var cafePreset = _mainPresetIndex.item('amenity/cafe');
74260 var _pointID = null;
74262 title: 'intro.points.title'
74265 function timeout(f, t) {
74266 timeouts.push(window.setTimeout(f, t));
74269 function eventCancel(d3_event) {
74270 d3_event.stopPropagation();
74271 d3_event.preventDefault();
74274 function addPoint() {
74275 context.enter(modeBrowse(context));
74276 context.history().reset('initial');
74277 var msec = transitionTime(intersection, context.map().center());
74280 reveal(null, null, {
74285 context.map().centerZoomEase(intersection, 19, msec);
74286 timeout(function () {
74287 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
74289 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
74290 context.on('enter.intro', function (mode) {
74291 if (mode.id !== 'add-point') return;
74292 continueTo(placePoint);
74296 function continueTo(nextStep) {
74297 context.on('enter.intro', null);
74302 function placePoint() {
74303 if (context.mode().id !== 'add-point') {
74304 return chapter.restart();
74307 var pointBox = pad(building, 150, context);
74308 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
74309 reveal(pointBox, helpHtml('intro.points.' + textId));
74310 context.map().on('move.intro drawn.intro', function () {
74311 pointBox = pad(building, 150, context);
74312 reveal(pointBox, helpHtml('intro.points.' + textId), {
74316 context.on('enter.intro', function (mode) {
74317 if (mode.id !== 'select') return chapter.restart();
74318 _pointID = context.mode().selectedIDs()[0];
74319 continueTo(searchPreset);
74322 function continueTo(nextStep) {
74323 context.map().on('move.intro drawn.intro', null);
74324 context.on('enter.intro', null);
74329 function searchPreset() {
74330 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
74332 } // disallow scrolling
74335 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74336 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74337 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
74338 preset: cafePreset.name()
74340 context.on('enter.intro', function (mode) {
74341 if (!_pointID || !context.hasEntity(_pointID)) {
74342 return continueTo(addPoint);
74345 var ids = context.selectedIDs();
74347 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
74348 // keep the user's point selected..
74349 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
74351 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74352 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74353 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
74354 preset: cafePreset.name()
74356 context.history().on('change.intro', null);
74360 function checkPresetSearch() {
74361 var first = context.container().select('.preset-list-item:first-child');
74363 if (first.classed('preset-amenity-cafe')) {
74364 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
74365 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
74366 preset: cafePreset.name()
74370 context.history().on('change.intro', function () {
74371 continueTo(aboutFeatureEditor);
74376 function continueTo(nextStep) {
74377 context.on('enter.intro', null);
74378 context.history().on('change.intro', null);
74379 context.container().select('.inspector-wrap').on('wheel.intro', null);
74380 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74385 function aboutFeatureEditor() {
74386 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
74390 timeout(function () {
74391 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
74392 tooltipClass: 'intro-points-describe',
74393 buttonText: _t.html('intro.ok'),
74394 buttonCallback: function buttonCallback() {
74395 continueTo(addName);
74399 context.on('exit.intro', function () {
74400 // if user leaves select mode here, just continue with the tutorial.
74401 continueTo(reselectPoint);
74404 function continueTo(nextStep) {
74405 context.on('exit.intro', null);
74410 function addName() {
74411 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
74413 } // reset pane, in case user happened to change it..
74416 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
74417 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
74418 timeout(function () {
74419 // It's possible for the user to add a name in a previous step..
74420 // If so, don't tell them to add the name in this step.
74421 // Give them an OK button instead.
74422 var entity = context.entity(_pointID);
74424 if (entity.tags.name) {
74425 var tooltip = reveal('.entity-editor-pane', addNameString, {
74426 tooltipClass: 'intro-points-describe',
74427 buttonText: _t.html('intro.ok'),
74428 buttonCallback: function buttonCallback() {
74429 continueTo(addCloseEditor);
74432 tooltip.select('.instruction').style('display', 'none');
74434 reveal('.entity-editor-pane', addNameString, {
74435 tooltipClass: 'intro-points-describe'
74439 context.history().on('change.intro', function () {
74440 continueTo(addCloseEditor);
74442 context.on('exit.intro', function () {
74443 // if user leaves select mode here, just continue with the tutorial.
74444 continueTo(reselectPoint);
74447 function continueTo(nextStep) {
74448 context.on('exit.intro', null);
74449 context.history().on('change.intro', null);
74454 function addCloseEditor() {
74455 // reset pane, in case user happened to change it..
74456 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
74457 var selector = '.entity-editor-pane button.close svg use';
74458 var href = select(selector).attr('href') || '#iD-icon-close';
74459 context.on('exit.intro', function () {
74460 continueTo(reselectPoint);
74462 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
74463 button: icon(href, 'inline')
74466 function continueTo(nextStep) {
74467 context.on('exit.intro', null);
74472 function reselectPoint() {
74473 if (!_pointID) return chapter.restart();
74474 var entity = context.hasEntity(_pointID);
74475 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
74477 var oldPreset = _mainPresetIndex.match(entity, context.graph());
74478 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
74479 context.enter(modeBrowse(context));
74480 var msec = transitionTime(entity.loc, context.map().center());
74483 reveal(null, null, {
74488 context.map().centerEase(entity.loc, msec);
74489 timeout(function () {
74490 var box = pointBox(entity.loc, context);
74491 reveal(box, helpHtml('intro.points.reselect'), {
74494 timeout(function () {
74495 context.map().on('move.intro drawn.intro', function () {
74496 var entity = context.hasEntity(_pointID);
74497 if (!entity) return chapter.restart();
74498 var box = pointBox(entity.loc, context);
74499 reveal(box, helpHtml('intro.points.reselect'), {
74503 }, 600); // after reveal..
74505 context.on('enter.intro', function (mode) {
74506 if (mode.id !== 'select') return;
74507 continueTo(updatePoint);
74511 function continueTo(nextStep) {
74512 context.map().on('move.intro drawn.intro', null);
74513 context.on('enter.intro', null);
74518 function updatePoint() {
74519 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
74520 return continueTo(reselectPoint);
74521 } // reset pane, in case user happened to untag the point..
74524 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
74525 context.on('exit.intro', function () {
74526 continueTo(reselectPoint);
74528 context.history().on('change.intro', function () {
74529 continueTo(updateCloseEditor);
74531 timeout(function () {
74532 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
74533 tooltipClass: 'intro-points-describe'
74537 function continueTo(nextStep) {
74538 context.on('exit.intro', null);
74539 context.history().on('change.intro', null);
74544 function updateCloseEditor() {
74545 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
74546 return continueTo(reselectPoint);
74547 } // reset pane, in case user happened to change it..
74550 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
74551 context.on('exit.intro', function () {
74552 continueTo(rightClickPoint);
74554 timeout(function () {
74555 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
74556 button: icon('#iD-icon-close', 'inline')
74560 function continueTo(nextStep) {
74561 context.on('exit.intro', null);
74566 function rightClickPoint() {
74567 if (!_pointID) return chapter.restart();
74568 var entity = context.hasEntity(_pointID);
74569 if (!entity) return chapter.restart();
74570 context.enter(modeBrowse(context));
74571 var box = pointBox(entity.loc, context);
74572 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
74573 reveal(box, helpHtml('intro.points.' + textId), {
74576 timeout(function () {
74577 context.map().on('move.intro', function () {
74578 var entity = context.hasEntity(_pointID);
74579 if (!entity) return chapter.restart();
74580 var box = pointBox(entity.loc, context);
74581 reveal(box, helpHtml('intro.points.' + textId), {
74585 }, 600); // after reveal
74587 context.on('enter.intro', function (mode) {
74588 if (mode.id !== 'select') return;
74589 var ids = context.selectedIDs();
74590 if (ids.length !== 1 || ids[0] !== _pointID) return;
74591 timeout(function () {
74592 var node = selectMenuItem(context, 'delete').node();
74594 continueTo(enterDelete);
74595 }, 50); // after menu visible
74598 function continueTo(nextStep) {
74599 context.on('enter.intro', null);
74600 context.map().on('move.intro', null);
74605 function enterDelete() {
74606 if (!_pointID) return chapter.restart();
74607 var entity = context.hasEntity(_pointID);
74608 if (!entity) return chapter.restart();
74609 var node = selectMenuItem(context, 'delete').node();
74612 return continueTo(rightClickPoint);
74615 reveal('.edit-menu', helpHtml('intro.points.delete'), {
74618 timeout(function () {
74619 context.map().on('move.intro', function () {
74620 reveal('.edit-menu', helpHtml('intro.points.delete'), {
74625 }, 300); // after menu visible
74627 context.on('exit.intro', function () {
74628 if (!_pointID) return chapter.restart();
74629 var entity = context.hasEntity(_pointID);
74630 if (entity) return continueTo(rightClickPoint); // point still exists
74632 context.history().on('change.intro', function (changed) {
74633 if (changed.deleted().length) {
74638 function continueTo(nextStep) {
74639 context.map().on('move.intro', null);
74640 context.history().on('change.intro', null);
74641 context.on('exit.intro', null);
74647 context.history().on('change.intro', function () {
74650 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
74652 function continueTo(nextStep) {
74653 context.history().on('change.intro', null);
74659 dispatch.call('done');
74660 reveal('.ideditor', helpHtml('intro.points.play', {
74661 next: _t('intro.areas.title')
74663 tooltipBox: '.intro-nav-wrap .chapter-area',
74664 buttonText: _t.html('intro.ok'),
74665 buttonCallback: function buttonCallback() {
74666 reveal('.ideditor');
74671 chapter.enter = function () {
74675 chapter.exit = function () {
74676 timeouts.forEach(window.clearTimeout);
74677 context.on('enter.intro exit.intro', null);
74678 context.map().on('move.intro drawn.intro', null);
74679 context.history().on('change.intro', null);
74680 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74681 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74684 chapter.restart = function () {
74689 return utilRebind(chapter, dispatch, 'on');
74692 function uiIntroArea(context, reveal) {
74693 var dispatch = dispatch$8('done');
74694 var playground = [-85.63552, 41.94159];
74695 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
74696 var nameField = _mainPresetIndex.field('name');
74697 var descriptionField = _mainPresetIndex.field('description');
74703 title: 'intro.areas.title'
74706 function timeout(f, t) {
74707 timeouts.push(window.setTimeout(f, t));
74710 function eventCancel(d3_event) {
74711 d3_event.stopPropagation();
74712 d3_event.preventDefault();
74715 function revealPlayground(center, text, options) {
74716 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
74717 var box = pad(center, padding, context);
74718 reveal(box, text, options);
74721 function addArea() {
74722 context.enter(modeBrowse(context));
74723 context.history().reset('initial');
74725 var msec = transitionTime(playground, context.map().center());
74728 reveal(null, null, {
74733 context.map().centerZoomEase(playground, 19, msec);
74734 timeout(function () {
74735 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
74736 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
74737 context.on('enter.intro', function (mode) {
74738 if (mode.id !== 'add-area') return;
74739 continueTo(startPlayground);
74743 function continueTo(nextStep) {
74744 context.on('enter.intro', null);
74749 function startPlayground() {
74750 if (context.mode().id !== 'add-area') {
74751 return chapter.restart();
74755 context.map().zoomEase(19.5, 500);
74756 timeout(function () {
74757 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
74758 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
74759 revealPlayground(playground, startDrawString, {
74762 timeout(function () {
74763 context.map().on('move.intro drawn.intro', function () {
74764 revealPlayground(playground, startDrawString, {
74768 context.on('enter.intro', function (mode) {
74769 if (mode.id !== 'draw-area') return chapter.restart();
74770 continueTo(continuePlayground);
74772 }, 250); // after reveal
74773 }, 550); // after easing
74775 function continueTo(nextStep) {
74776 context.map().on('move.intro drawn.intro', null);
74777 context.on('enter.intro', null);
74782 function continuePlayground() {
74783 if (context.mode().id !== 'draw-area') {
74784 return chapter.restart();
74788 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
74791 timeout(function () {
74792 context.map().on('move.intro drawn.intro', function () {
74793 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
74797 }, 250); // after reveal
74799 context.on('enter.intro', function (mode) {
74800 if (mode.id === 'draw-area') {
74801 var entity = context.hasEntity(context.selectedIDs()[0]);
74803 if (entity && entity.nodes.length >= 6) {
74804 return continueTo(finishPlayground);
74808 } else if (mode.id === 'select') {
74809 _areaID = context.selectedIDs()[0];
74810 return continueTo(searchPresets);
74812 return chapter.restart();
74816 function continueTo(nextStep) {
74817 context.map().on('move.intro drawn.intro', null);
74818 context.on('enter.intro', null);
74823 function finishPlayground() {
74824 if (context.mode().id !== 'draw-area') {
74825 return chapter.restart();
74829 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
74830 revealPlayground(playground, finishString, {
74833 timeout(function () {
74834 context.map().on('move.intro drawn.intro', function () {
74835 revealPlayground(playground, finishString, {
74839 }, 250); // after reveal
74841 context.on('enter.intro', function (mode) {
74842 if (mode.id === 'draw-area') {
74844 } else if (mode.id === 'select') {
74845 _areaID = context.selectedIDs()[0];
74846 return continueTo(searchPresets);
74848 return chapter.restart();
74852 function continueTo(nextStep) {
74853 context.map().on('move.intro drawn.intro', null);
74854 context.on('enter.intro', null);
74859 function searchPresets() {
74860 if (!_areaID || !context.hasEntity(_areaID)) {
74864 var ids = context.selectedIDs();
74866 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
74867 context.enter(modeSelect(context, [_areaID]));
74868 } // disallow scrolling
74871 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74872 timeout(function () {
74873 // reset pane, in case user somehow happened to change it..
74874 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74875 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74876 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
74877 preset: playgroundPreset.name()
74879 }, 400); // after preset list pane visible..
74881 context.on('enter.intro', function (mode) {
74882 if (!_areaID || !context.hasEntity(_areaID)) {
74883 return continueTo(addArea);
74886 var ids = context.selectedIDs();
74888 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
74889 // keep the user's area selected..
74890 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
74892 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
74894 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74895 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74896 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
74897 preset: playgroundPreset.name()
74899 context.history().on('change.intro', null);
74903 function checkPresetSearch() {
74904 var first = context.container().select('.preset-list-item:first-child');
74906 if (first.classed('preset-leisure-playground')) {
74907 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
74908 preset: playgroundPreset.name()
74912 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
74913 context.history().on('change.intro', function () {
74914 continueTo(clickAddField);
74919 function continueTo(nextStep) {
74920 context.container().select('.inspector-wrap').on('wheel.intro', null);
74921 context.on('enter.intro', null);
74922 context.history().on('change.intro', null);
74923 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74928 function clickAddField() {
74929 if (!_areaID || !context.hasEntity(_areaID)) {
74933 var ids = context.selectedIDs();
74935 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
74936 return searchPresets();
74939 if (!context.container().select('.form-field-description').empty()) {
74940 return continueTo(describePlayground);
74941 } // disallow scrolling
74944 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74945 timeout(function () {
74946 // reset pane, in case user somehow happened to change it..
74947 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
74948 // If they did this already, just continue to next step.
74950 var entity = context.entity(_areaID);
74952 if (entity.tags.description) {
74953 return continueTo(play);
74954 } // scroll "Add field" into view
74957 var box = context.container().select('.more-fields').node().getBoundingClientRect();
74959 if (box.top > 300) {
74960 var pane = context.container().select('.entity-editor-pane .inspector-body');
74961 var start = pane.node().scrollTop;
74962 var end = start + (box.top - 300);
74963 pane.transition().duration(250).tween('scroll.inspector', function () {
74965 var i = d3_interpolateNumber(start, end);
74966 return function (t) {
74967 node.scrollTop = i(t);
74972 timeout(function () {
74973 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
74974 name: nameField.label(),
74975 description: descriptionField.label()
74979 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
74980 // Watch for the combobox to appear...
74982 watcher = window.setInterval(function () {
74983 if (!context.container().select('div.combobox').empty()) {
74984 window.clearInterval(watcher);
74985 continueTo(chooseDescriptionField);
74989 }, 300); // after "Add Field" visible
74990 }, 400); // after editor pane visible
74992 context.on('exit.intro', function () {
74993 return continueTo(searchPresets);
74996 function continueTo(nextStep) {
74997 context.container().select('.inspector-wrap').on('wheel.intro', null);
74998 context.container().select('.more-fields .combobox-input').on('click.intro', null);
74999 context.on('exit.intro', null);
75004 function chooseDescriptionField() {
75005 if (!_areaID || !context.hasEntity(_areaID)) {
75009 var ids = context.selectedIDs();
75011 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
75012 return searchPresets();
75015 if (!context.container().select('.form-field-description').empty()) {
75016 return continueTo(describePlayground);
75017 } // Make sure combobox is ready..
75020 if (context.container().select('div.combobox').empty()) {
75021 return continueTo(clickAddField);
75022 } // Watch for the combobox to go away..
75026 watcher = window.setInterval(function () {
75027 if (context.container().select('div.combobox').empty()) {
75028 window.clearInterval(watcher);
75029 timeout(function () {
75030 if (context.container().select('.form-field-description').empty()) {
75031 continueTo(retryChooseDescription);
75033 continueTo(describePlayground);
75035 }, 300); // after description field added.
75038 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
75039 field: descriptionField.label()
75043 context.on('exit.intro', function () {
75044 return continueTo(searchPresets);
75047 function continueTo(nextStep) {
75048 if (watcher) window.clearInterval(watcher);
75049 context.on('exit.intro', null);
75054 function describePlayground() {
75055 if (!_areaID || !context.hasEntity(_areaID)) {
75059 var ids = context.selectedIDs();
75061 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
75062 return searchPresets();
75063 } // reset pane, in case user happened to change it..
75066 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
75068 if (context.container().select('.form-field-description').empty()) {
75069 return continueTo(retryChooseDescription);
75072 context.on('exit.intro', function () {
75075 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
75076 button: icon('#iD-icon-close', 'inline')
75081 function continueTo(nextStep) {
75082 context.on('exit.intro', null);
75087 function retryChooseDescription() {
75088 if (!_areaID || !context.hasEntity(_areaID)) {
75092 var ids = context.selectedIDs();
75094 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
75095 return searchPresets();
75096 } // reset pane, in case user happened to change it..
75099 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
75100 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
75101 field: descriptionField.label()
75103 buttonText: _t.html('intro.ok'),
75104 buttonCallback: function buttonCallback() {
75105 continueTo(clickAddField);
75108 context.on('exit.intro', function () {
75109 return continueTo(searchPresets);
75112 function continueTo(nextStep) {
75113 context.on('exit.intro', null);
75119 dispatch.call('done');
75120 reveal('.ideditor', helpHtml('intro.areas.play', {
75121 next: _t('intro.lines.title')
75123 tooltipBox: '.intro-nav-wrap .chapter-line',
75124 buttonText: _t.html('intro.ok'),
75125 buttonCallback: function buttonCallback() {
75126 reveal('.ideditor');
75131 chapter.enter = function () {
75135 chapter.exit = function () {
75136 timeouts.forEach(window.clearTimeout);
75137 context.on('enter.intro exit.intro', null);
75138 context.map().on('move.intro drawn.intro', null);
75139 context.history().on('change.intro', null);
75140 context.container().select('.inspector-wrap').on('wheel.intro', null);
75141 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
75142 context.container().select('.more-fields .combobox-input').on('click.intro', null);
75145 chapter.restart = function () {
75150 return utilRebind(chapter, dispatch, 'on');
75153 function uiIntroLine(context, reveal) {
75154 var dispatch = dispatch$8('done');
75156 var _tulipRoadID = null;
75157 var flowerRoadID = 'w646';
75158 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
75159 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
75160 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
75161 var roadCategory = _mainPresetIndex.item('category-road_minor');
75162 var residentialPreset = _mainPresetIndex.item('highway/residential');
75163 var woodRoadID = 'w525';
75164 var woodRoadEndID = 'n2862';
75165 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
75166 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
75167 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
75168 var washingtonStreetID = 'w522';
75169 var twelfthAvenueID = 'w1';
75170 var eleventhAvenueEndID = 'n3550';
75171 var twelfthAvenueEndID = 'n5';
75172 var _washingtonSegmentID = null;
75173 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
75174 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
75175 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
75176 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
75178 title: 'intro.lines.title'
75181 function timeout(f, t) {
75182 timeouts.push(window.setTimeout(f, t));
75185 function eventCancel(d3_event) {
75186 d3_event.stopPropagation();
75187 d3_event.preventDefault();
75190 function addLine() {
75191 context.enter(modeBrowse(context));
75192 context.history().reset('initial');
75193 var msec = transitionTime(tulipRoadStart, context.map().center());
75196 reveal(null, null, {
75201 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
75202 timeout(function () {
75203 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
75204 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
75205 context.on('enter.intro', function (mode) {
75206 if (mode.id !== 'add-line') return;
75207 continueTo(startLine);
75211 function continueTo(nextStep) {
75212 context.on('enter.intro', null);
75217 function startLine() {
75218 if (context.mode().id !== 'add-line') return chapter.restart();
75219 _tulipRoadID = null;
75220 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
75221 var box = pad(tulipRoadStart, padding, context);
75222 box.height = box.height + 100;
75223 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
75224 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
75225 reveal(box, startLineString);
75226 context.map().on('move.intro drawn.intro', function () {
75227 padding = 70 * Math.pow(2, context.map().zoom() - 18);
75228 box = pad(tulipRoadStart, padding, context);
75229 box.height = box.height + 100;
75230 reveal(box, startLineString, {
75234 context.on('enter.intro', function (mode) {
75235 if (mode.id !== 'draw-line') return chapter.restart();
75236 continueTo(drawLine);
75239 function continueTo(nextStep) {
75240 context.map().on('move.intro drawn.intro', null);
75241 context.on('enter.intro', null);
75246 function drawLine() {
75247 if (context.mode().id !== 'draw-line') return chapter.restart();
75248 _tulipRoadID = context.mode().selectedIDs()[0];
75249 context.map().centerEase(tulipRoadMidpoint, 500);
75250 timeout(function () {
75251 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
75252 var box = pad(tulipRoadMidpoint, padding, context);
75253 box.height = box.height * 2;
75254 reveal(box, helpHtml('intro.lines.intersect', {
75255 name: _t('intro.graph.name.flower-street')
75257 context.map().on('move.intro drawn.intro', function () {
75258 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
75259 box = pad(tulipRoadMidpoint, padding, context);
75260 box.height = box.height * 2;
75261 reveal(box, helpHtml('intro.lines.intersect', {
75262 name: _t('intro.graph.name.flower-street')
75267 }, 550); // after easing..
75269 context.history().on('change.intro', function () {
75270 if (isLineConnected()) {
75271 continueTo(continueLine);
75274 context.on('enter.intro', function (mode) {
75275 if (mode.id === 'draw-line') {
75277 } else if (mode.id === 'select') {
75278 continueTo(retryIntersect);
75281 return chapter.restart();
75285 function continueTo(nextStep) {
75286 context.map().on('move.intro drawn.intro', null);
75287 context.history().on('change.intro', null);
75288 context.on('enter.intro', null);
75293 function isLineConnected() {
75294 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
75296 if (!entity) return false;
75297 var drawNodes = context.graph().childNodes(entity);
75298 return drawNodes.some(function (node) {
75299 return context.graph().parentWays(node).some(function (parent) {
75300 return parent.id === flowerRoadID;
75305 function retryIntersect() {
75306 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
75307 var box = pad(tulipRoadIntersection, 80, context);
75308 reveal(box, helpHtml('intro.lines.retry_intersect', {
75309 name: _t('intro.graph.name.flower-street')
75311 timeout(chapter.restart, 3000);
75314 function continueLine() {
75315 if (context.mode().id !== 'draw-line') return chapter.restart();
75317 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
75319 if (!entity) return chapter.restart();
75320 context.map().centerEase(tulipRoadIntersection, 500);
75321 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
75322 reveal('.surface', continueLineText);
75323 context.on('enter.intro', function (mode) {
75324 if (mode.id === 'draw-line') {
75326 } else if (mode.id === 'select') {
75327 return continueTo(chooseCategoryRoad);
75329 return chapter.restart();
75333 function continueTo(nextStep) {
75334 context.on('enter.intro', null);
75339 function chooseCategoryRoad() {
75340 if (context.mode().id !== 'select') return chapter.restart();
75341 context.on('exit.intro', function () {
75342 return chapter.restart();
75344 var button = context.container().select('.preset-category-road_minor .preset-list-button');
75345 if (button.empty()) return chapter.restart(); // disallow scrolling
75347 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
75348 timeout(function () {
75349 // reset pane, in case user somehow happened to change it..
75350 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
75351 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
75352 category: roadCategory.name()
75354 button.on('click.intro', function () {
75355 continueTo(choosePresetResidential);
75357 }, 400); // after editor pane visible
75359 function continueTo(nextStep) {
75360 context.container().select('.inspector-wrap').on('wheel.intro', null);
75361 context.container().select('.preset-list-button').on('click.intro', null);
75362 context.on('exit.intro', null);
75367 function choosePresetResidential() {
75368 if (context.mode().id !== 'select') return chapter.restart();
75369 context.on('exit.intro', function () {
75370 return chapter.restart();
75372 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
75373 if (subgrid.empty()) return chapter.restart();
75374 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
75375 continueTo(retryPresetResidential);
75377 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
75378 continueTo(nameRoad);
75380 timeout(function () {
75381 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
75382 preset: residentialPreset.name()
75384 tooltipBox: '.preset-highway-residential .preset-list-button',
75389 function continueTo(nextStep) {
75390 context.container().select('.preset-list-button').on('click.intro', null);
75391 context.on('exit.intro', null);
75394 } // selected wrong road type
75397 function retryPresetResidential() {
75398 if (context.mode().id !== 'select') return chapter.restart();
75399 context.on('exit.intro', function () {
75400 return chapter.restart();
75401 }); // disallow scrolling
75403 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
75404 timeout(function () {
75405 var button = context.container().select('.entity-editor-pane .preset-list-button');
75406 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
75407 preset: residentialPreset.name()
75409 button.on('click.intro', function () {
75410 continueTo(chooseCategoryRoad);
75414 function continueTo(nextStep) {
75415 context.container().select('.inspector-wrap').on('wheel.intro', null);
75416 context.container().select('.preset-list-button').on('click.intro', null);
75417 context.on('exit.intro', null);
75422 function nameRoad() {
75423 context.on('exit.intro', function () {
75424 continueTo(didNameRoad);
75426 timeout(function () {
75427 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
75428 button: icon('#iD-icon-close', 'inline')
75430 tooltipClass: 'intro-lines-name_road'
75434 function continueTo(nextStep) {
75435 context.on('exit.intro', null);
75440 function didNameRoad() {
75441 context.history().checkpoint('doneAddLine');
75442 timeout(function () {
75443 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
75444 buttonText: _t.html('intro.ok'),
75445 buttonCallback: function buttonCallback() {
75446 continueTo(updateLine);
75451 function continueTo(nextStep) {
75456 function updateLine() {
75457 context.history().reset('doneAddLine');
75459 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75460 return chapter.restart();
75463 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
75466 reveal(null, null, {
75471 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
75472 timeout(function () {
75473 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
75474 var box = pad(woodRoadDragMidpoint, padding, context);
75476 var advance = function advance() {
75477 continueTo(addNode);
75480 reveal(box, helpHtml('intro.lines.update_line'), {
75481 buttonText: _t.html('intro.ok'),
75482 buttonCallback: advance
75484 context.map().on('move.intro drawn.intro', function () {
75485 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
75486 var box = pad(woodRoadDragMidpoint, padding, context);
75487 reveal(box, helpHtml('intro.lines.update_line'), {
75489 buttonText: _t.html('intro.ok'),
75490 buttonCallback: advance
75495 function continueTo(nextStep) {
75496 context.map().on('move.intro drawn.intro', null);
75501 function addNode() {
75502 context.history().reset('doneAddLine');
75504 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75505 return chapter.restart();
75508 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
75509 var box = pad(woodRoadAddNode, padding, context);
75510 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
75511 reveal(box, addNodeString);
75512 context.map().on('move.intro drawn.intro', function () {
75513 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
75514 var box = pad(woodRoadAddNode, padding, context);
75515 reveal(box, addNodeString, {
75519 context.history().on('change.intro', function (changed) {
75520 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75521 return continueTo(updateLine);
75524 if (changed.created().length === 1) {
75525 timeout(function () {
75526 continueTo(startDragEndpoint);
75530 context.on('enter.intro', function (mode) {
75531 if (mode.id !== 'select') {
75532 continueTo(updateLine);
75536 function continueTo(nextStep) {
75537 context.map().on('move.intro drawn.intro', null);
75538 context.history().on('change.intro', null);
75539 context.on('enter.intro', null);
75544 function startDragEndpoint() {
75545 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75546 return continueTo(updateLine);
75549 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75550 var box = pad(woodRoadDragEndpoint, padding, context);
75551 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
75552 reveal(box, startDragString);
75553 context.map().on('move.intro drawn.intro', function () {
75554 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75555 return continueTo(updateLine);
75558 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75559 var box = pad(woodRoadDragEndpoint, padding, context);
75560 reveal(box, startDragString, {
75563 var entity = context.entity(woodRoadEndID);
75565 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
75566 continueTo(finishDragEndpoint);
75570 function continueTo(nextStep) {
75571 context.map().on('move.intro drawn.intro', null);
75576 function finishDragEndpoint() {
75577 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75578 return continueTo(updateLine);
75581 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75582 var box = pad(woodRoadDragEndpoint, padding, context);
75583 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
75584 reveal(box, finishDragString);
75585 context.map().on('move.intro drawn.intro', function () {
75586 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75587 return continueTo(updateLine);
75590 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75591 var box = pad(woodRoadDragEndpoint, padding, context);
75592 reveal(box, finishDragString, {
75595 var entity = context.entity(woodRoadEndID);
75597 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
75598 continueTo(startDragEndpoint);
75601 context.on('enter.intro', function () {
75602 continueTo(startDragMidpoint);
75605 function continueTo(nextStep) {
75606 context.map().on('move.intro drawn.intro', null);
75607 context.on('enter.intro', null);
75612 function startDragMidpoint() {
75613 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75614 return continueTo(updateLine);
75617 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
75618 context.enter(modeSelect(context, [woodRoadID]));
75621 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
75622 var box = pad(woodRoadDragMidpoint, padding, context);
75623 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
75624 context.map().on('move.intro drawn.intro', function () {
75625 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75626 return continueTo(updateLine);
75629 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
75630 var box = pad(woodRoadDragMidpoint, padding, context);
75631 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
75635 context.history().on('change.intro', function (changed) {
75636 if (changed.created().length === 1) {
75637 continueTo(continueDragMidpoint);
75640 context.on('enter.intro', function (mode) {
75641 if (mode.id !== 'select') {
75642 // keep Wood Road selected so midpoint triangles are drawn..
75643 context.enter(modeSelect(context, [woodRoadID]));
75647 function continueTo(nextStep) {
75648 context.map().on('move.intro drawn.intro', null);
75649 context.history().on('change.intro', null);
75650 context.on('enter.intro', null);
75655 function continueDragMidpoint() {
75656 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75657 return continueTo(updateLine);
75660 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75661 var box = pad(woodRoadDragEndpoint, padding, context);
75664 var advance = function advance() {
75665 context.history().checkpoint('doneUpdateLine');
75666 continueTo(deleteLines);
75669 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
75670 buttonText: _t.html('intro.ok'),
75671 buttonCallback: advance
75673 context.map().on('move.intro drawn.intro', function () {
75674 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
75675 return continueTo(updateLine);
75678 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
75679 var box = pad(woodRoadDragEndpoint, padding, context);
75681 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
75683 buttonText: _t.html('intro.ok'),
75684 buttonCallback: advance
75688 function continueTo(nextStep) {
75689 context.map().on('move.intro drawn.intro', null);
75694 function deleteLines() {
75695 context.history().reset('doneUpdateLine');
75696 context.enter(modeBrowse(context));
75698 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
75699 return chapter.restart();
75702 var msec = transitionTime(deleteLinesLoc, context.map().center());
75705 reveal(null, null, {
75710 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
75711 timeout(function () {
75712 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
75713 var box = pad(deleteLinesLoc, padding, context);
75717 var advance = function advance() {
75718 continueTo(rightClickIntersection);
75721 reveal(box, helpHtml('intro.lines.delete_lines', {
75722 street: _t('intro.graph.name.12th-avenue')
75724 buttonText: _t.html('intro.ok'),
75725 buttonCallback: advance
75727 context.map().on('move.intro drawn.intro', function () {
75728 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
75729 var box = pad(deleteLinesLoc, padding, context);
75732 reveal(box, helpHtml('intro.lines.delete_lines', {
75733 street: _t('intro.graph.name.12th-avenue')
75736 buttonText: _t.html('intro.ok'),
75737 buttonCallback: advance
75740 context.history().on('change.intro', function () {
75741 timeout(function () {
75742 continueTo(deleteLines);
75743 }, 500); // after any transition (e.g. if user deleted intersection)
75747 function continueTo(nextStep) {
75748 context.map().on('move.intro drawn.intro', null);
75749 context.history().on('change.intro', null);
75754 function rightClickIntersection() {
75755 context.history().reset('doneUpdateLine');
75756 context.enter(modeBrowse(context));
75757 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
75758 var rightClickString = helpHtml('intro.lines.split_street', {
75759 street1: _t('intro.graph.name.11th-avenue'),
75760 street2: _t('intro.graph.name.washington-street')
75761 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
75762 timeout(function () {
75763 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
75764 var box = pad(eleventhAvenueEnd, padding, context);
75765 reveal(box, rightClickString);
75766 context.map().on('move.intro drawn.intro', function () {
75767 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
75768 var box = pad(eleventhAvenueEnd, padding, context);
75769 reveal(box, rightClickString, {
75773 context.on('enter.intro', function (mode) {
75774 if (mode.id !== 'select') return;
75775 var ids = context.selectedIDs();
75776 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
75777 timeout(function () {
75778 var node = selectMenuItem(context, 'split').node();
75780 continueTo(splitIntersection);
75781 }, 50); // after menu visible
75783 context.history().on('change.intro', function () {
75784 timeout(function () {
75785 continueTo(deleteLines);
75786 }, 300); // after any transition (e.g. if user deleted intersection)
75790 function continueTo(nextStep) {
75791 context.map().on('move.intro drawn.intro', null);
75792 context.on('enter.intro', null);
75793 context.history().on('change.intro', null);
75798 function splitIntersection() {
75799 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
75800 return continueTo(deleteLines);
75803 var node = selectMenuItem(context, 'split').node();
75806 return continueTo(rightClickIntersection);
75809 var wasChanged = false;
75810 _washingtonSegmentID = null;
75811 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
75812 street: _t('intro.graph.name.washington-street')
75816 context.map().on('move.intro drawn.intro', function () {
75817 var node = selectMenuItem(context, 'split').node();
75819 if (!wasChanged && !node) {
75820 return continueTo(rightClickIntersection);
75823 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
75824 street: _t('intro.graph.name.washington-street')
75830 context.history().on('change.intro', function (changed) {
75832 timeout(function () {
75833 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
75836 _washingtonSegmentID = changed.created()[0].id;
75837 continueTo(didSplit);
75839 _washingtonSegmentID = null;
75840 continueTo(retrySplit);
75842 }, 300); // after any transition (e.g. if user deleted intersection)
75845 function continueTo(nextStep) {
75846 context.map().on('move.intro drawn.intro', null);
75847 context.history().on('change.intro', null);
75852 function retrySplit() {
75853 context.enter(modeBrowse(context));
75854 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
75856 var advance = function advance() {
75857 continueTo(rightClickIntersection);
75860 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
75861 var box = pad(eleventhAvenueEnd, padding, context);
75862 reveal(box, helpHtml('intro.lines.retry_split'), {
75863 buttonText: _t.html('intro.ok'),
75864 buttonCallback: advance
75866 context.map().on('move.intro drawn.intro', function () {
75867 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
75868 var box = pad(eleventhAvenueEnd, padding, context);
75869 reveal(box, helpHtml('intro.lines.retry_split'), {
75871 buttonText: _t.html('intro.ok'),
75872 buttonCallback: advance
75876 function continueTo(nextStep) {
75877 context.map().on('move.intro drawn.intro', null);
75882 function didSplit() {
75883 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
75884 return continueTo(rightClickIntersection);
75887 var ids = context.selectedIDs();
75888 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
75889 var street = _t('intro.graph.name.washington-street');
75890 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
75891 var box = pad(twelfthAvenue, padding, context);
75892 box.width = box.width / 2;
75893 reveal(box, helpHtml(string, {
75899 timeout(function () {
75900 context.map().centerZoomEase(twelfthAvenue, 18, 500);
75901 context.map().on('move.intro drawn.intro', function () {
75902 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
75903 var box = pad(twelfthAvenue, padding, context);
75904 box.width = box.width / 2;
75905 reveal(box, helpHtml(string, {
75912 }, 600); // after initial reveal and curtain cut
75914 context.on('enter.intro', function () {
75915 var ids = context.selectedIDs();
75917 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
75918 continueTo(multiSelect);
75921 context.history().on('change.intro', function () {
75922 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
75923 return continueTo(rightClickIntersection);
75927 function continueTo(nextStep) {
75928 context.map().on('move.intro drawn.intro', null);
75929 context.on('enter.intro', null);
75930 context.history().on('change.intro', null);
75935 function multiSelect() {
75936 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
75937 return continueTo(rightClickIntersection);
75940 var ids = context.selectedIDs();
75941 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
75942 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
75944 if (hasWashington && hasTwelfth) {
75945 return continueTo(multiRightClick);
75946 } else if (!hasWashington && !hasTwelfth) {
75947 return continueTo(didSplit);
75950 context.map().centerZoomEase(twelfthAvenue, 18, 500);
75951 timeout(function () {
75952 var selected, other, padding, box;
75954 if (hasWashington) {
75955 selected = _t('intro.graph.name.washington-street');
75956 other = _t('intro.graph.name.12th-avenue');
75957 padding = 60 * Math.pow(2, context.map().zoom() - 18);
75958 box = pad(twelfthAvenueEnd, padding, context);
75961 selected = _t('intro.graph.name.12th-avenue');
75962 other = _t('intro.graph.name.washington-street');
75963 padding = 200 * Math.pow(2, context.map().zoom() - 18);
75964 box = pad(twelfthAvenue, padding, context);
75968 reveal(box, helpHtml('intro.lines.multi_select', {
75969 selected: selected,
75971 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
75972 selected: selected,
75975 context.map().on('move.intro drawn.intro', function () {
75976 if (hasWashington) {
75977 selected = _t('intro.graph.name.washington-street');
75978 other = _t('intro.graph.name.12th-avenue');
75979 padding = 60 * Math.pow(2, context.map().zoom() - 18);
75980 box = pad(twelfthAvenueEnd, padding, context);
75983 selected = _t('intro.graph.name.12th-avenue');
75984 other = _t('intro.graph.name.washington-street');
75985 padding = 200 * Math.pow(2, context.map().zoom() - 18);
75986 box = pad(twelfthAvenue, padding, context);
75990 reveal(box, helpHtml('intro.lines.multi_select', {
75991 selected: selected,
75993 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
75994 selected: selected,
76000 context.on('enter.intro', function () {
76001 continueTo(multiSelect);
76003 context.history().on('change.intro', function () {
76004 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
76005 return continueTo(rightClickIntersection);
76010 function continueTo(nextStep) {
76011 context.map().on('move.intro drawn.intro', null);
76012 context.on('enter.intro', null);
76013 context.history().on('change.intro', null);
76018 function multiRightClick() {
76019 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
76020 return continueTo(rightClickIntersection);
76023 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
76024 var box = pad(twelfthAvenue, padding, context);
76025 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
76026 reveal(box, rightClickString);
76027 context.map().on('move.intro drawn.intro', function () {
76028 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
76029 var box = pad(twelfthAvenue, padding, context);
76030 reveal(box, rightClickString, {
76034 context.ui().editMenu().on('toggled.intro', function (open) {
76036 timeout(function () {
76037 var ids = context.selectedIDs();
76039 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
76040 var node = selectMenuItem(context, 'delete').node();
76042 continueTo(multiDelete);
76043 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
76044 return continueTo(multiSelect);
76046 return continueTo(didSplit);
76048 }, 300); // after edit menu visible
76050 context.history().on('change.intro', function () {
76051 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
76052 return continueTo(rightClickIntersection);
76056 function continueTo(nextStep) {
76057 context.map().on('move.intro drawn.intro', null);
76058 context.ui().editMenu().on('toggled.intro', null);
76059 context.history().on('change.intro', null);
76064 function multiDelete() {
76065 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
76066 return continueTo(rightClickIntersection);
76069 var node = selectMenuItem(context, 'delete').node();
76070 if (!node) return continueTo(multiRightClick);
76071 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
76074 context.map().on('move.intro drawn.intro', function () {
76075 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
76080 context.on('exit.intro', function () {
76081 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
76082 return continueTo(multiSelect); // left select mode but roads still exist
76085 context.history().on('change.intro', function () {
76086 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
76087 continueTo(retryDelete); // changed something but roads still exist
76093 function continueTo(nextStep) {
76094 context.map().on('move.intro drawn.intro', null);
76095 context.on('exit.intro', null);
76096 context.history().on('change.intro', null);
76101 function retryDelete() {
76102 context.enter(modeBrowse(context));
76103 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
76104 var box = pad(twelfthAvenue, padding, context);
76105 reveal(box, helpHtml('intro.lines.retry_delete'), {
76106 buttonText: _t.html('intro.ok'),
76107 buttonCallback: function buttonCallback() {
76108 continueTo(multiSelect);
76112 function continueTo(nextStep) {
76118 dispatch.call('done');
76119 reveal('.ideditor', helpHtml('intro.lines.play', {
76120 next: _t('intro.buildings.title')
76122 tooltipBox: '.intro-nav-wrap .chapter-building',
76123 buttonText: _t.html('intro.ok'),
76124 buttonCallback: function buttonCallback() {
76125 reveal('.ideditor');
76130 chapter.enter = function () {
76134 chapter.exit = function () {
76135 timeouts.forEach(window.clearTimeout);
76136 select(window).on('pointerdown.intro mousedown.intro', null, true);
76137 context.on('enter.intro exit.intro', null);
76138 context.map().on('move.intro drawn.intro', null);
76139 context.history().on('change.intro', null);
76140 context.container().select('.inspector-wrap').on('wheel.intro', null);
76141 context.container().select('.preset-list-button').on('click.intro', null);
76144 chapter.restart = function () {
76149 return utilRebind(chapter, dispatch, 'on');
76152 function uiIntroBuilding(context, reveal) {
76153 var dispatch = dispatch$8('done');
76154 var house = [-85.62815, 41.95638];
76155 var tank = [-85.62732, 41.95347];
76156 var buildingCatetory = _mainPresetIndex.item('category-building');
76157 var housePreset = _mainPresetIndex.item('building/house');
76158 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
76160 var _houseID = null;
76161 var _tankID = null;
76163 title: 'intro.buildings.title'
76166 function timeout(f, t) {
76167 timeouts.push(window.setTimeout(f, t));
76170 function eventCancel(d3_event) {
76171 d3_event.stopPropagation();
76172 d3_event.preventDefault();
76175 function revealHouse(center, text, options) {
76176 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
76177 var box = pad(center, padding, context);
76178 reveal(box, text, options);
76181 function revealTank(center, text, options) {
76182 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
76183 var box = pad(center, padding, context);
76184 reveal(box, text, options);
76187 function addHouse() {
76188 context.enter(modeBrowse(context));
76189 context.history().reset('initial');
76191 var msec = transitionTime(house, context.map().center());
76194 reveal(null, null, {
76199 context.map().centerZoomEase(house, 19, msec);
76200 timeout(function () {
76201 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
76202 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
76203 context.on('enter.intro', function (mode) {
76204 if (mode.id !== 'add-area') return;
76205 continueTo(startHouse);
76209 function continueTo(nextStep) {
76210 context.on('enter.intro', null);
76215 function startHouse() {
76216 if (context.mode().id !== 'add-area') {
76217 return continueTo(addHouse);
76221 context.map().zoomEase(20, 500);
76222 timeout(function () {
76223 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
76224 revealHouse(house, startString);
76225 context.map().on('move.intro drawn.intro', function () {
76226 revealHouse(house, startString, {
76230 context.on('enter.intro', function (mode) {
76231 if (mode.id !== 'draw-area') return chapter.restart();
76232 continueTo(continueHouse);
76234 }, 550); // after easing
76236 function continueTo(nextStep) {
76237 context.map().on('move.intro drawn.intro', null);
76238 context.on('enter.intro', null);
76243 function continueHouse() {
76244 if (context.mode().id !== 'draw-area') {
76245 return continueTo(addHouse);
76249 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
76250 revealHouse(house, continueString);
76251 context.map().on('move.intro drawn.intro', function () {
76252 revealHouse(house, continueString, {
76256 context.on('enter.intro', function (mode) {
76257 if (mode.id === 'draw-area') {
76259 } else if (mode.id === 'select') {
76260 var graph = context.graph();
76261 var way = context.entity(context.selectedIDs()[0]);
76262 var nodes = graph.childNodes(way);
76263 var points = utilArrayUniq(nodes).map(function (n) {
76264 return context.projection(n.loc);
76267 if (isMostlySquare(points)) {
76269 return continueTo(chooseCategoryBuilding);
76271 return continueTo(retryHouse);
76274 return chapter.restart();
76278 function continueTo(nextStep) {
76279 context.map().on('move.intro drawn.intro', null);
76280 context.on('enter.intro', null);
76285 function retryHouse() {
76286 var onClick = function onClick() {
76287 continueTo(addHouse);
76290 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
76291 buttonText: _t.html('intro.ok'),
76292 buttonCallback: onClick
76294 context.map().on('move.intro drawn.intro', function () {
76295 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
76297 buttonText: _t.html('intro.ok'),
76298 buttonCallback: onClick
76302 function continueTo(nextStep) {
76303 context.map().on('move.intro drawn.intro', null);
76308 function chooseCategoryBuilding() {
76309 if (!_houseID || !context.hasEntity(_houseID)) {
76313 var ids = context.selectedIDs();
76315 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
76316 context.enter(modeSelect(context, [_houseID]));
76317 } // disallow scrolling
76320 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
76321 timeout(function () {
76322 // reset pane, in case user somehow happened to change it..
76323 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
76324 var button = context.container().select('.preset-category-building .preset-list-button');
76325 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
76326 category: buildingCatetory.name()
76328 button.on('click.intro', function () {
76329 button.on('click.intro', null);
76330 continueTo(choosePresetHouse);
76332 }, 400); // after preset list pane visible..
76334 context.on('enter.intro', function (mode) {
76335 if (!_houseID || !context.hasEntity(_houseID)) {
76336 return continueTo(addHouse);
76339 var ids = context.selectedIDs();
76341 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
76342 return continueTo(chooseCategoryBuilding);
76346 function continueTo(nextStep) {
76347 context.container().select('.inspector-wrap').on('wheel.intro', null);
76348 context.container().select('.preset-list-button').on('click.intro', null);
76349 context.on('enter.intro', null);
76354 function choosePresetHouse() {
76355 if (!_houseID || !context.hasEntity(_houseID)) {
76359 var ids = context.selectedIDs();
76361 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
76362 context.enter(modeSelect(context, [_houseID]));
76363 } // disallow scrolling
76366 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
76367 timeout(function () {
76368 // reset pane, in case user somehow happened to change it..
76369 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
76370 var button = context.container().select('.preset-building-house .preset-list-button');
76371 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
76372 preset: housePreset.name()
76376 button.on('click.intro', function () {
76377 button.on('click.intro', null);
76378 continueTo(closeEditorHouse);
76380 }, 400); // after preset list pane visible..
76382 context.on('enter.intro', function (mode) {
76383 if (!_houseID || !context.hasEntity(_houseID)) {
76384 return continueTo(addHouse);
76387 var ids = context.selectedIDs();
76389 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
76390 return continueTo(chooseCategoryBuilding);
76394 function continueTo(nextStep) {
76395 context.container().select('.inspector-wrap').on('wheel.intro', null);
76396 context.container().select('.preset-list-button').on('click.intro', null);
76397 context.on('enter.intro', null);
76402 function closeEditorHouse() {
76403 if (!_houseID || !context.hasEntity(_houseID)) {
76407 var ids = context.selectedIDs();
76409 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
76410 context.enter(modeSelect(context, [_houseID]));
76413 context.history().checkpoint('hasHouse');
76414 context.on('exit.intro', function () {
76415 continueTo(rightClickHouse);
76417 timeout(function () {
76418 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
76419 button: icon('#iD-icon-close', 'inline')
76423 function continueTo(nextStep) {
76424 context.on('exit.intro', null);
76429 function rightClickHouse() {
76430 if (!_houseID) return chapter.restart();
76431 context.enter(modeBrowse(context));
76432 context.history().reset('hasHouse');
76433 var zoom = context.map().zoom();
76439 context.map().centerZoomEase(house, zoom, 500);
76440 context.on('enter.intro', function (mode) {
76441 if (mode.id !== 'select') return;
76442 var ids = context.selectedIDs();
76443 if (ids.length !== 1 || ids[0] !== _houseID) return;
76444 timeout(function () {
76445 var node = selectMenuItem(context, 'orthogonalize').node();
76447 continueTo(clickSquare);
76448 }, 50); // after menu visible
76450 context.map().on('move.intro drawn.intro', function () {
76451 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
76452 revealHouse(house, rightclickString, {
76456 context.history().on('change.intro', function () {
76457 continueTo(rightClickHouse);
76460 function continueTo(nextStep) {
76461 context.on('enter.intro', null);
76462 context.map().on('move.intro drawn.intro', null);
76463 context.history().on('change.intro', null);
76468 function clickSquare() {
76469 if (!_houseID) return chapter.restart();
76470 var entity = context.hasEntity(_houseID);
76471 if (!entity) return continueTo(rightClickHouse);
76472 var node = selectMenuItem(context, 'orthogonalize').node();
76475 return continueTo(rightClickHouse);
76478 var wasChanged = false;
76479 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
76482 context.on('enter.intro', function (mode) {
76483 if (mode.id === 'browse') {
76484 continueTo(rightClickHouse);
76485 } else if (mode.id === 'move' || mode.id === 'rotate') {
76486 continueTo(retryClickSquare);
76489 context.map().on('move.intro', function () {
76490 var node = selectMenuItem(context, 'orthogonalize').node();
76492 if (!wasChanged && !node) {
76493 return continueTo(rightClickHouse);
76496 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
76501 context.history().on('change.intro', function () {
76503 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
76505 timeout(function () {
76506 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
76509 continueTo(doneSquare);
76511 continueTo(retryClickSquare);
76513 }, 500); // after transitioned actions
76516 function continueTo(nextStep) {
76517 context.on('enter.intro', null);
76518 context.map().on('move.intro', null);
76519 context.history().on('change.intro', null);
76524 function retryClickSquare() {
76525 context.enter(modeBrowse(context));
76526 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
76527 buttonText: _t.html('intro.ok'),
76528 buttonCallback: function buttonCallback() {
76529 continueTo(rightClickHouse);
76533 function continueTo(nextStep) {
76538 function doneSquare() {
76539 context.history().checkpoint('doneSquare');
76540 revealHouse(house, helpHtml('intro.buildings.done_square'), {
76541 buttonText: _t.html('intro.ok'),
76542 buttonCallback: function buttonCallback() {
76543 continueTo(addTank);
76547 function continueTo(nextStep) {
76552 function addTank() {
76553 context.enter(modeBrowse(context));
76554 context.history().reset('doneSquare');
76556 var msec = transitionTime(tank, context.map().center());
76559 reveal(null, null, {
76564 context.map().centerZoomEase(tank, 19.5, msec);
76565 timeout(function () {
76566 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
76567 context.on('enter.intro', function (mode) {
76568 if (mode.id !== 'add-area') return;
76569 continueTo(startTank);
76573 function continueTo(nextStep) {
76574 context.on('enter.intro', null);
76579 function startTank() {
76580 if (context.mode().id !== 'add-area') {
76581 return continueTo(addTank);
76585 timeout(function () {
76586 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
76587 revealTank(tank, startString);
76588 context.map().on('move.intro drawn.intro', function () {
76589 revealTank(tank, startString, {
76593 context.on('enter.intro', function (mode) {
76594 if (mode.id !== 'draw-area') return chapter.restart();
76595 continueTo(continueTank);
76597 }, 550); // after easing
76599 function continueTo(nextStep) {
76600 context.map().on('move.intro drawn.intro', null);
76601 context.on('enter.intro', null);
76606 function continueTank() {
76607 if (context.mode().id !== 'draw-area') {
76608 return continueTo(addTank);
76612 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
76613 revealTank(tank, continueString);
76614 context.map().on('move.intro drawn.intro', function () {
76615 revealTank(tank, continueString, {
76619 context.on('enter.intro', function (mode) {
76620 if (mode.id === 'draw-area') {
76622 } else if (mode.id === 'select') {
76623 _tankID = context.selectedIDs()[0];
76624 return continueTo(searchPresetTank);
76626 return continueTo(addTank);
76630 function continueTo(nextStep) {
76631 context.map().on('move.intro drawn.intro', null);
76632 context.on('enter.intro', null);
76637 function searchPresetTank() {
76638 if (!_tankID || !context.hasEntity(_tankID)) {
76642 var ids = context.selectedIDs();
76644 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
76645 context.enter(modeSelect(context, [_tankID]));
76646 } // disallow scrolling
76649 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
76650 timeout(function () {
76651 // reset pane, in case user somehow happened to change it..
76652 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
76653 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
76654 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
76655 preset: tankPreset.name()
76657 }, 400); // after preset list pane visible..
76659 context.on('enter.intro', function (mode) {
76660 if (!_tankID || !context.hasEntity(_tankID)) {
76661 return continueTo(addTank);
76664 var ids = context.selectedIDs();
76666 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
76667 // keep the user's area selected..
76668 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
76670 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
76672 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
76673 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
76674 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
76675 preset: tankPreset.name()
76677 context.history().on('change.intro', null);
76681 function checkPresetSearch() {
76682 var first = context.container().select('.preset-list-item:first-child');
76684 if (first.classed('preset-man_made-storage_tank')) {
76685 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
76686 preset: tankPreset.name()
76690 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
76691 context.history().on('change.intro', function () {
76692 continueTo(closeEditorTank);
76697 function continueTo(nextStep) {
76698 context.container().select('.inspector-wrap').on('wheel.intro', null);
76699 context.on('enter.intro', null);
76700 context.history().on('change.intro', null);
76701 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
76706 function closeEditorTank() {
76707 if (!_tankID || !context.hasEntity(_tankID)) {
76711 var ids = context.selectedIDs();
76713 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
76714 context.enter(modeSelect(context, [_tankID]));
76717 context.history().checkpoint('hasTank');
76718 context.on('exit.intro', function () {
76719 continueTo(rightClickTank);
76721 timeout(function () {
76722 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
76723 button: icon('#iD-icon-close', 'inline')
76727 function continueTo(nextStep) {
76728 context.on('exit.intro', null);
76733 function rightClickTank() {
76734 if (!_tankID) return continueTo(addTank);
76735 context.enter(modeBrowse(context));
76736 context.history().reset('hasTank');
76737 context.map().centerEase(tank, 500);
76738 timeout(function () {
76739 context.on('enter.intro', function (mode) {
76740 if (mode.id !== 'select') return;
76741 var ids = context.selectedIDs();
76742 if (ids.length !== 1 || ids[0] !== _tankID) return;
76743 timeout(function () {
76744 var node = selectMenuItem(context, 'circularize').node();
76746 continueTo(clickCircle);
76747 }, 50); // after menu visible
76749 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
76750 revealTank(tank, rightclickString);
76751 context.map().on('move.intro drawn.intro', function () {
76752 revealTank(tank, rightclickString, {
76756 context.history().on('change.intro', function () {
76757 continueTo(rightClickTank);
76761 function continueTo(nextStep) {
76762 context.on('enter.intro', null);
76763 context.map().on('move.intro drawn.intro', null);
76764 context.history().on('change.intro', null);
76769 function clickCircle() {
76770 if (!_tankID) return chapter.restart();
76771 var entity = context.hasEntity(_tankID);
76772 if (!entity) return continueTo(rightClickTank);
76773 var node = selectMenuItem(context, 'circularize').node();
76776 return continueTo(rightClickTank);
76779 var wasChanged = false;
76780 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
76783 context.on('enter.intro', function (mode) {
76784 if (mode.id === 'browse') {
76785 continueTo(rightClickTank);
76786 } else if (mode.id === 'move' || mode.id === 'rotate') {
76787 continueTo(retryClickCircle);
76790 context.map().on('move.intro', function () {
76791 var node = selectMenuItem(context, 'circularize').node();
76793 if (!wasChanged && !node) {
76794 return continueTo(rightClickTank);
76797 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
76802 context.history().on('change.intro', function () {
76804 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
76806 timeout(function () {
76807 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
76812 continueTo(retryClickCircle);
76814 }, 500); // after transitioned actions
76817 function continueTo(nextStep) {
76818 context.on('enter.intro', null);
76819 context.map().on('move.intro', null);
76820 context.history().on('change.intro', null);
76825 function retryClickCircle() {
76826 context.enter(modeBrowse(context));
76827 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
76828 buttonText: _t.html('intro.ok'),
76829 buttonCallback: function buttonCallback() {
76830 continueTo(rightClickTank);
76834 function continueTo(nextStep) {
76840 dispatch.call('done');
76841 reveal('.ideditor', helpHtml('intro.buildings.play', {
76842 next: _t('intro.startediting.title')
76844 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
76845 buttonText: _t.html('intro.ok'),
76846 buttonCallback: function buttonCallback() {
76847 reveal('.ideditor');
76852 chapter.enter = function () {
76856 chapter.exit = function () {
76857 timeouts.forEach(window.clearTimeout);
76858 context.on('enter.intro exit.intro', null);
76859 context.map().on('move.intro drawn.intro', null);
76860 context.history().on('change.intro', null);
76861 context.container().select('.inspector-wrap').on('wheel.intro', null);
76862 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
76863 context.container().select('.more-fields .combobox-input').on('click.intro', null);
76866 chapter.restart = function () {
76871 return utilRebind(chapter, dispatch, 'on');
76874 function uiIntroStartEditing(context, reveal) {
76875 var dispatch = dispatch$8('done', 'startEditing');
76876 var modalSelection = select(null);
76878 title: 'intro.startediting.title'
76881 function showHelp() {
76882 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
76883 buttonText: _t.html('intro.ok'),
76884 buttonCallback: function buttonCallback() {
76890 function shortcuts() {
76891 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
76892 buttonText: _t.html('intro.ok'),
76893 buttonCallback: function buttonCallback() {
76899 function showSave() {
76900 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
76902 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
76903 buttonText: _t.html('intro.ok'),
76904 buttonCallback: function buttonCallback() {
76910 function showStart() {
76911 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
76913 modalSelection = uiModal(context.container());
76914 modalSelection.select('.modal').attr('class', 'modal-splash modal');
76915 modalSelection.selectAll('.close').remove();
76916 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
76917 modalSelection.remove();
76919 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
76920 startbutton.append('h2').html(_t.html('intro.startediting.start'));
76921 dispatch.call('startEditing');
76924 chapter.enter = function () {
76928 chapter.exit = function () {
76929 modalSelection.remove();
76930 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
76933 return utilRebind(chapter, dispatch, 'on');
76937 welcome: uiIntroWelcome,
76938 navigation: uiIntroNavigation,
76939 point: uiIntroPoint,
76942 building: uiIntroBuilding,
76943 startEditing: uiIntroStartEditing
76945 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
76946 function uiIntro(context) {
76947 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
76948 var _introGraph = {};
76952 function intro(selection) {
76953 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
76954 // create entities for intro graph and localize names
76955 for (var id in dataIntroGraph) {
76956 if (!_introGraph[id]) {
76957 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
76961 selection.call(startIntro);
76962 })["catch"](function () {
76967 function startIntro(selection) {
76968 context.enter(modeBrowse(context)); // Save current map state
76970 var osm = context.connection();
76971 var history = context.history().toJSON();
76972 var hash = window.location.hash;
76973 var center = context.map().center();
76974 var zoom = context.map().zoom();
76975 var background = context.background().baseLayerSource();
76976 var overlays = context.background().overlayLayerSources();
76977 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
76978 var caches = osm && osm.caches();
76979 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
76980 // (this needs to be before `context.inIntro(true)`)
76982 context.ui().sidebar.expand();
76983 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
76985 context.inIntro(true); // Load semi-real data used in intro
76988 osm.toggle(false).reset();
76991 context.history().reset();
76992 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
76993 context.history().checkpoint('initial'); // Setup imagery
76995 var imagery = context.background().findSource(INTRO_IMAGERY);
76998 context.background().baseLayerSource(imagery);
77000 context.background().bing();
77003 overlays.forEach(function (d) {
77004 return context.background().toggleOverlayLayer(d);
77005 }); // Setup data layers (only OSM)
77007 var layers = context.layers();
77008 layers.all().forEach(function (item) {
77009 // if the layer has the function `enabled`
77010 if (typeof item.layer.enabled === 'function') {
77011 item.layer.enabled(item.id === 'osm');
77014 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
77015 var curtain = uiCurtain(context.container().node());
77016 selection.call(curtain); // Store that the user started the walkthrough..
77018 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
77020 var storedProgress = corePreferences('walkthrough_progress') || '';
77021 var progress = storedProgress.split(';').filter(Boolean);
77022 var chapters = chapterFlow.map(function (chapter, i) {
77023 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
77024 buttons.filter(function (d) {
77025 return d.title === s.title;
77026 }).classed('finished', true);
77028 if (i < chapterFlow.length - 1) {
77029 var next = chapterFlow[i + 1];
77030 context.container().select("button.chapter-".concat(next)).classed('next', true);
77031 } // Store walkthrough progress..
77034 progress.push(chapter);
77035 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
77039 chapters[chapters.length - 1].on('startEditing', function () {
77040 // Store walkthrough progress..
77041 progress.push('startEditing');
77042 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
77044 var incomplete = utilArrayDifference(chapterFlow, progress);
77046 if (!incomplete.length) {
77047 corePreferences('walkthrough_completed', 'yes');
77052 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
77053 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
77056 osm.toggle(true).reset().caches(caches);
77059 context.history().reset().merge(Object.values(baseEntities));
77060 context.background().baseLayerSource(background);
77061 overlays.forEach(function (d) {
77062 return context.background().toggleOverlayLayer(d);
77066 context.history().fromJSON(history, false);
77069 context.map().centerZoom(center, zoom);
77070 window.location.replace(hash);
77071 context.inIntro(false);
77073 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
77074 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
77075 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
77076 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
77077 return "chapter chapter-".concat(chapterFlow[i]);
77078 }).on('click', enterChapter);
77079 buttons.append('span').html(function (d) {
77080 return _t.html(d.title);
77082 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
77083 enterChapter(null, chapters[0]);
77085 function enterChapter(d3_event, newChapter) {
77086 if (_currChapter) {
77087 _currChapter.exit();
77090 context.enter(modeBrowse(context));
77091 _currChapter = newChapter;
77093 _currChapter.enter();
77095 buttons.classed('next', false).classed('active', function (d) {
77096 return d.title === _currChapter.title;
77104 function uiIssuesInfo(context) {
77105 var warningsItem = {
77108 iconID: 'iD-icon-alert',
77109 descriptionID: 'issues.warnings_and_errors'
77111 var resolvedItem = {
77114 iconID: 'iD-icon-apply',
77115 descriptionID: 'issues.user_resolved_issues'
77118 function update(selection) {
77119 var shownItems = [];
77120 var liveIssues = context.validator().getIssues({
77121 what: corePreferences('validate-what') || 'edited',
77122 where: corePreferences('validate-where') || 'all'
77125 if (liveIssues.length) {
77126 warningsItem.count = liveIssues.length;
77127 shownItems.push(warningsItem);
77130 if (corePreferences('validate-what') === 'all') {
77131 var resolvedIssues = context.validator().getResolvedIssues();
77133 if (resolvedIssues.length) {
77134 resolvedItem.count = resolvedIssues.length;
77135 shownItems.push(resolvedItem);
77139 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
77142 chips.exit().remove();
77143 var enter = chips.enter().append('a').attr('class', function (d) {
77144 return 'chip ' + d.id + '-count';
77145 }).attr('href', '#').each(function (d) {
77146 var chipSelection = select(this);
77147 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
77148 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
77149 d3_event.preventDefault();
77150 tooltipBehavior.hide(select(this)); // open the Issues pane
77152 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
77154 chipSelection.call(svgIcon('#' + d.iconID));
77156 enter.append('span').attr('class', 'count');
77157 enter.merge(chips).selectAll('span.count').html(function (d) {
77158 return d.count.toString();
77162 return function (selection) {
77164 context.validator().on('validated.infobox', function () {
77170 function uiMapInMap(context) {
77171 function mapInMap(selection) {
77172 var backgroundLayer = rendererTileLayer(context);
77173 var overlayLayers = {};
77174 var projection = geoRawMercator();
77175 var dataLayer = svgData(projection, context).showLabels(false);
77176 var debugLayer = svgDebug(projection, context);
77177 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
77178 var wrap = select(null);
77179 var tiles = select(null);
77180 var viewport = select(null);
77181 var _isTransformed = false;
77182 var _isHidden = true;
77183 var _skipEvents = false;
77184 var _gesture = null;
77185 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
77187 var _dMini; // dimensions of minimap
77190 var _cMini; // center pixel of minimap
77193 var _tStart; // transform at start of gesture
77196 var _tCurr; // transform at most recent event
77201 function zoomStarted() {
77202 if (_skipEvents) return;
77203 _tStart = _tCurr = projection.transform();
77207 function zoomed(d3_event) {
77208 if (_skipEvents) return;
77209 var x = d3_event.transform.x;
77210 var y = d3_event.transform.y;
77211 var k = d3_event.transform.k;
77212 var isZooming = k !== _tStart.k;
77213 var isPanning = x !== _tStart.x || y !== _tStart.y;
77215 if (!isZooming && !isPanning) {
77216 return; // no change
77217 } // lock in either zooming or panning, don't allow both in minimap.
77221 _gesture = isZooming ? 'zoom' : 'pan';
77224 var tMini = projection.transform();
77227 if (_gesture === 'zoom') {
77228 scale = k / tMini.k;
77229 tX = (_cMini[0] / scale - _cMini[0]) * scale;
77230 tY = (_cMini[1] / scale - _cMini[1]) * scale;
77238 utilSetTransform(tiles, tX, tY, scale);
77239 utilSetTransform(viewport, 0, 0, scale);
77240 _isTransformed = true;
77241 _tCurr = identity$2.translate(x, y).scale(k);
77242 var zMain = geoScaleToZoom(context.projection.scale());
77243 var zMini = geoScaleToZoom(k);
77244 _zDiff = zMain - zMini;
77248 function zoomEnded() {
77249 if (_skipEvents) return;
77250 if (_gesture !== 'pan') return;
77251 updateProjection();
77253 context.map().center(projection.invert(_cMini)); // recenter main map..
77256 function updateProjection() {
77257 var loc = context.map().center();
77258 var tMain = context.projection.transform();
77259 var zMain = geoScaleToZoom(tMain.k);
77260 var zMini = Math.max(zMain - _zDiff, 0.5);
77261 var kMini = geoZoomToScale(zMini);
77262 projection.translate([tMain.x, tMain.y]).scale(kMini);
77263 var point = projection(loc);
77264 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
77265 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
77266 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
77267 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
77268 _tCurr = projection.transform();
77270 if (_isTransformed) {
77271 utilSetTransform(tiles, 0, 0);
77272 utilSetTransform(viewport, 0, 0);
77273 _isTransformed = false;
77276 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
77277 _skipEvents = true;
77278 wrap.call(zoom.transform, _tCurr);
77279 _skipEvents = false;
77282 function redraw() {
77283 clearTimeout(_timeoutID);
77284 if (_isHidden) return;
77285 updateProjection();
77286 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
77288 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
77289 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
77291 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
77292 var background = tiles.selectAll('.map-in-map-background').data([0]);
77293 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
77295 var overlaySources = context.background().overlayLayerSources();
77296 var activeOverlayLayers = [];
77298 for (var i = 0; i < overlaySources.length; i++) {
77299 if (overlaySources[i].validZoom(zMini)) {
77300 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
77301 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
77305 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
77306 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
77307 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
77308 return d.source().name();
77310 overlays.exit().remove();
77311 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
77312 select(this).call(layer);
77314 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
77315 dataLayers.exit().remove();
77316 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
77318 if (_gesture !== 'pan') {
77319 var getPath = d3_geoPath(projection);
77322 coordinates: [context.map().extent().polygon()]
77324 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
77325 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
77326 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
77327 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
77328 return getPath.area(d) < 30;
77333 function queueRedraw() {
77334 clearTimeout(_timeoutID);
77335 _timeoutID = setTimeout(function () {
77340 function toggle(d3_event) {
77341 if (d3_event) d3_event.preventDefault();
77342 _isHidden = !_isHidden;
77343 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
77346 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
77347 selection.selectAll('.map-in-map').style('display', 'none');
77350 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
77356 uiMapInMap.toggle = toggle;
77357 wrap = selection.selectAll('.map-in-map').data([0]);
77358 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..
77360 _dMini = [200, 150]; //utilGetDimensions(wrap);
77362 _cMini = geoVecScale(_dMini, 0.5);
77363 context.map().on('drawn.map-in-map', function (drawn) {
77364 if (drawn.full === true) {
77369 context.keybinding().on(_t('background.minimap.key'), toggle);
77375 function uiNotice(context) {
77376 return function (selection) {
77377 var div = selection.append('div').attr('class', 'notice');
77378 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
77379 context.map().zoomEase(context.minEditableZoom());
77380 }).on('wheel', function (d3_event) {
77381 // let wheel events pass through #4482
77382 var e2 = new WheelEvent(d3_event.type, d3_event);
77383 context.surface().node().dispatchEvent(e2);
77385 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
77387 function disableTooHigh() {
77388 var canEdit = context.map().zoom() >= context.minEditableZoom();
77389 div.style('display', canEdit ? 'none' : 'block');
77392 context.map().on('move.notice', debounce(disableTooHigh, 500));
77397 function uiPhotoviewer(context) {
77398 var dispatch = dispatch$8('resize');
77400 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
77402 function photoviewer(selection) {
77403 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
77404 if (services.streetside) {
77405 services.streetside.hideViewer(context);
77408 if (services.mapillary) {
77409 services.mapillary.hideViewer(context);
77412 if (services.openstreetcam) {
77413 services.openstreetcam.hideViewer(context);
77415 }).append('div').call(svgIcon('#iD-icon-close'));
77417 function preventDefault(d3_event) {
77418 d3_event.preventDefault();
77421 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
77425 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
77428 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
77432 function buildResizeListener(target, eventName, dispatch, options) {
77433 var resizeOnX = !!options.resizeOnX;
77434 var resizeOnY = !!options.resizeOnY;
77435 var minHeight = options.minHeight || 240;
77436 var minWidth = options.minWidth || 320;
77443 function startResize(d3_event) {
77444 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
77445 d3_event.preventDefault();
77446 d3_event.stopPropagation();
77447 var mapSize = context.map().dimensions();
77450 var maxWidth = mapSize[0];
77451 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
77452 target.style('width', newWidth + 'px');
77456 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
77458 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
77459 target.style('height', newHeight + 'px');
77462 dispatch.call(eventName, target, utilGetDimensions(target, true));
77465 function clamp(num, min, max) {
77466 return Math.max(min, Math.min(num, max));
77469 function stopResize(d3_event) {
77470 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
77471 d3_event.preventDefault();
77472 d3_event.stopPropagation(); // remove all the listeners we added
77474 select(window).on('.' + eventName, null);
77477 return function initResize(d3_event) {
77478 d3_event.preventDefault();
77479 d3_event.stopPropagation();
77480 pointerId = d3_event.pointerId || 'mouse';
77481 startX = d3_event.clientX;
77482 startY = d3_event.clientY;
77483 var targetRect = target.node().getBoundingClientRect();
77484 startWidth = targetRect.width;
77485 startHeight = targetRect.height;
77486 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
77488 if (_pointerPrefix === 'pointer') {
77489 select(window).on('pointercancel.' + eventName, stopResize, false);
77495 photoviewer.onMapResize = function () {
77496 var photoviewer = context.container().select('.photoviewer');
77497 var content = context.container().select('.main-content');
77498 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
77499 // (-90 preserves space at top and bottom of map used by menus)
77501 var photoDimensions = utilGetDimensions(photoviewer, true);
77503 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
77504 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
77505 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
77506 dispatch.call('resize', photoviewer, setPhotoDimensions);
77510 return utilRebind(photoviewer, dispatch, 'on');
77513 function uiRestore(context) {
77514 return function (selection) {
77515 if (!context.history().hasRestorableChanges()) return;
77516 var modalSelection = uiModal(selection, true);
77517 modalSelection.select('.modal').attr('class', 'modal fillL');
77518 var introModal = modalSelection.select('.content');
77519 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
77520 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
77521 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
77522 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
77523 context.history().restore();
77524 modalSelection.remove();
77526 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
77527 restore.append('div').html(_t.html('restore.restore'));
77528 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
77529 context.history().clearSaved();
77530 modalSelection.remove();
77532 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
77533 reset.append('div').html(_t.html('restore.reset'));
77534 restore.node().focus();
77538 function uiScale(context) {
77539 var projection = context.projection,
77540 isImperial = !_mainLocalizer.usesMetric(),
77544 function scaleDefs(loc1, loc2) {
77545 var lat = (loc2[1] + loc1[1]) / 2,
77546 conversion = isImperial ? 3.28084 : 1,
77547 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
77559 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
77561 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
77562 } // determine a user-friendly endpoint for the scale
77565 for (i = 0; i < buckets.length; i++) {
77569 scale.dist = Math.floor(dist / val) * val;
77572 scale.dist = +dist.toFixed(2);
77576 dLon = geoMetersToLon(scale.dist / conversion, lat);
77577 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
77578 scale.text = displayLength(scale.dist / conversion, isImperial);
77582 function update(selection) {
77583 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
77584 var dims = context.map().dimensions(),
77585 loc1 = projection.invert([0, dims[1]]),
77586 loc2 = projection.invert([maxLength, dims[1]]),
77587 scale = scaleDefs(loc1, loc2);
77588 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
77589 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
77592 return function (selection) {
77593 function switchUnits() {
77594 isImperial = !isImperial;
77595 selection.call(update);
77598 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
77599 scalegroup.append('path').attr('class', 'scale-path');
77600 selection.append('div').attr('class', 'scale-text');
77601 selection.call(update);
77602 context.map().on('move.scale', function () {
77608 function uiShortcuts(context) {
77609 var detected = utilDetect();
77610 var _activeTab = 0;
77612 var _modalSelection;
77614 var _selection = select(null);
77616 var _dataShortcuts;
77618 function shortcutsModal(_modalSelection) {
77619 _modalSelection.select('.modal').classed('modal-shortcuts', true);
77621 var content = _modalSelection.select('.content');
77623 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
77624 _mainFileFetcher.get('shortcuts').then(function (data) {
77625 _dataShortcuts = data;
77626 content.call(render);
77627 })["catch"](function () {
77632 function render(selection) {
77633 if (!_dataShortcuts) return;
77634 var wrapper = selection.selectAll('.wrapper').data([0]);
77635 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
77636 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
77637 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
77638 wrapper = wrapper.merge(wrapperEnter);
77639 var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
77640 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
77641 d3_event.preventDefault();
77643 var i = _dataShortcuts.indexOf(d);
77648 tabsEnter.append('span').html(function (d) {
77649 return _t.html(d.text);
77652 wrapper.selectAll('.tab').classed('active', function (d, i) {
77653 return i === _activeTab;
77655 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
77656 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
77657 return 'shortcut-tab shortcut-tab-' + d.tab;
77659 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
77661 }).enter().append('table').attr('class', 'shortcut-column');
77662 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
77664 }).enter().append('tr').attr('class', 'shortcut-row');
77665 var sectionRows = rowsEnter.filter(function (d) {
77666 return !d.shortcuts;
77668 sectionRows.append('td');
77669 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
77670 return _t.html(d.text);
77672 var shortcutRows = rowsEnter.filter(function (d) {
77673 return d.shortcuts;
77675 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
77676 var modifierKeys = shortcutKeys.filter(function (d) {
77677 return d.modifiers;
77679 modifierKeys.selectAll('kbd.modifier').data(function (d) {
77680 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
77682 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
77685 return d.modifiers;
77687 }).enter().each(function () {
77688 var selection = select(this);
77689 selection.append('kbd').attr('class', 'modifier').html(function (d) {
77690 return uiCmd.display(d);
77692 selection.append('span').html('+');
77694 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
77695 var arr = d.shortcuts;
77697 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
77699 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
77701 } // replace translations
77704 arr = arr.map(function (s) {
77705 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
77707 return utilArrayUniq(arr).map(function (s) {
77710 separator: d.separator,
77714 }).enter().each(function (d, i, nodes) {
77715 var selection = select(this);
77716 var click = d.shortcut.toLowerCase().match(/(.*).click/);
77718 if (click && click[1]) {
77719 // replace "left_click", "right_click" with mouse icon
77720 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
77721 } else if (d.shortcut.toLowerCase() === 'long-press') {
77722 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
77723 } else if (d.shortcut.toLowerCase() === 'tap') {
77724 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
77726 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
77731 if (i < nodes.length - 1) {
77732 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
77733 } else if (i === nodes.length - 1 && d.suffix) {
77734 selection.append('span').html(d.suffix);
77737 shortcutKeys.filter(function (d) {
77739 }).each(function () {
77740 var selection = select(this);
77741 selection.append('span').html('+');
77742 selection.append('span').attr('class', 'gesture').html(function (d) {
77743 return _t.html(d.gesture);
77746 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
77747 return d.text ? _t.html(d.text) : "\xA0";
77750 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
77751 return i === _activeTab ? 'flex' : 'none';
77755 return function (selection, show) {
77756 _selection = selection;
77759 _modalSelection = uiModal(selection);
77761 _modalSelection.call(shortcutsModal);
77763 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
77764 if (context.container().selectAll('.modal-shortcuts').size()) {
77766 if (_modalSelection) {
77767 _modalSelection.close();
77769 _modalSelection = null;
77772 _modalSelection = uiModal(_selection);
77774 _modalSelection.call(shortcutsModal);
77781 function uiDataHeader() {
77784 function dataHeader(selection) {
77785 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
77786 return d.__featurehash__;
77788 header.exit().remove();
77789 var headerEnter = header.enter().append('div').attr('class', 'data-header');
77790 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
77791 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
77792 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
77795 dataHeader.datum = function (val) {
77796 if (!arguments.length) return _datum;
77804 // It is keyed on the `value` of the entry. Data should be an array of objects like:
77806 // value: 'string value', // required
77807 // display: 'label html' // optional
77808 // title: 'hover text' // optional
77809 // terms: ['search terms'] // optional
77812 var _comboHideTimerID;
77814 function uiCombobox(context, klass) {
77815 var dispatch = dispatch$8('accept', 'cancel');
77816 var container = context.container();
77817 var _suggestions = [];
77820 var _selected = null;
77821 var _canAutocomplete = true;
77822 var _caseSensitive = false;
77823 var _cancelFetch = false;
77827 var _mouseEnterHandler, _mouseLeaveHandler;
77829 var _fetcher = function _fetcher(val, cb) {
77830 cb(_data.filter(function (d) {
77831 var terms = d.terms || [];
77832 terms.push(d.value);
77833 return terms.some(function (term) {
77834 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
77839 var combobox = function combobox(input, attachTo) {
77840 if (!input || input.empty()) return;
77841 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 () {
77842 var parent = this.parentNode;
77843 var sibling = this.nextSibling;
77844 select(parent).selectAll('.combobox-caret').filter(function (d) {
77845 return d === input.node();
77846 }).data([input.node()]).enter().insert('div', function () {
77848 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
77849 d3_event.preventDefault(); // don't steal focus from input
77851 input.node().focus(); // focus the input as if it was clicked
77853 mousedown(d3_event);
77854 }).on('mouseup.combo-caret', function (d3_event) {
77855 d3_event.preventDefault(); // don't steal focus from input
77861 function mousedown(d3_event) {
77862 if (d3_event.button !== 0) return; // left click only
77864 _tDown = +new Date(); // clear selection
77866 var start = input.property('selectionStart');
77867 var end = input.property('selectionEnd');
77869 if (start !== end) {
77870 var val = utilGetSetValue(input);
77871 input.node().setSelectionRange(val.length, val.length);
77875 input.on('mouseup.combo-input', mouseup);
77878 function mouseup(d3_event) {
77879 input.on('mouseup.combo-input', null);
77880 if (d3_event.button !== 0) return; // left click only
77882 if (input.node() !== document.activeElement) return; // exit if this input is not focused
77884 var start = input.property('selectionStart');
77885 var end = input.property('selectionEnd');
77886 if (start !== end) return; // exit if user is selecting
77887 // not showing or showing for a different field - try to show it.
77889 var combo = container.selectAll('.combobox');
77891 if (combo.empty() || combo.datum() !== input.node()) {
77892 var tOrig = _tDown;
77893 window.setTimeout(function () {
77894 if (tOrig !== _tDown) return; // exit if user double clicked
77896 fetchComboData('', function () {
77907 fetchComboData(''); // prefetch values (may warm taginfo cache)
77911 _comboHideTimerID = window.setTimeout(hide, 75);
77915 hide(); // remove any existing
77917 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) {
77918 // prevent moving focus out of the input field
77919 d3_event.preventDefault();
77921 container.on('scroll.combo-scroll', render, true);
77925 if (_comboHideTimerID) {
77926 window.clearTimeout(_comboHideTimerID);
77927 _comboHideTimerID = undefined;
77930 container.selectAll('.combobox').remove();
77931 container.on('scroll.combo-scroll', null);
77934 function keydown(d3_event) {
77935 var shown = !container.selectAll('.combobox').empty();
77936 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
77938 switch (d3_event.keyCode) {
77939 case 8: // ⌫ Backspace
77943 d3_event.stopPropagation();
77946 input.on('input.combo-input', function () {
77947 var start = input.property('selectionStart');
77948 input.node().setSelectionRange(start, start);
77949 input.on('input.combo-input', change);
77960 d3_event.preventDefault();
77961 d3_event.stopPropagation();
77966 if (tagName === 'textarea' && !shown) return;
77967 d3_event.preventDefault();
77969 if (tagName === 'input' && !shown) {
77978 if (tagName === 'textarea' && !shown) return;
77979 d3_event.preventDefault();
77981 if (tagName === 'input' && !shown) {
77990 function keyup(d3_event) {
77991 switch (d3_event.keyCode) {
78002 } // Called whenever the input value is changed (e.g. on typing)
78005 function change() {
78006 fetchComboData(value(), function () {
78008 var val = input.property('value');
78010 if (_suggestions.length) {
78011 if (input.property('selectionEnd') === val.length) {
78012 _selected = tryAutocomplete();
78021 var combo = container.selectAll('.combobox');
78023 if (combo.empty()) {
78032 } // Called when the user presses up/down arrows to navigate the list
78035 function nav(dir) {
78036 if (_suggestions.length) {
78037 // try to determine previously selected index..
78040 for (var i = 0; i < _suggestions.length; i++) {
78041 if (_selected && _suggestions[i].value === _selected) {
78045 } // pick new _selected
78048 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
78049 _selected = _suggestions[index].value;
78050 input.property('value', _selected);
78057 function ensureVisible() {
78058 var combo = container.selectAll('.combobox');
78059 if (combo.empty()) return;
78060 var containerRect = container.node().getBoundingClientRect();
78061 var comboRect = combo.node().getBoundingClientRect();
78063 if (comboRect.bottom > containerRect.bottom) {
78064 var node = attachTo ? attachTo.node() : input.node();
78065 node.scrollIntoView({
78066 behavior: 'instant',
78070 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
78073 var selected = combo.selectAll('.combobox-option.selected').node();
78076 selected.scrollIntoView({
78077 behavior: 'smooth',
78084 var value = input.property('value');
78085 var start = input.property('selectionStart');
78086 var end = input.property('selectionEnd');
78088 if (start && end) {
78089 value = value.substring(0, start);
78095 function fetchComboData(v, cb) {
78096 _cancelFetch = false;
78098 _fetcher.call(input, v, function (results) {
78099 // already chose a value, don't overwrite or autocomplete it
78100 if (_cancelFetch) return;
78101 _suggestions = results;
78102 results.forEach(function (d) {
78103 _fetched[d.value] = d;
78112 function tryAutocomplete() {
78113 if (!_canAutocomplete) return;
78114 var val = _caseSensitive ? value() : value().toLowerCase();
78115 if (!val) return; // Don't autocomplete if user is typing a number - #4935
78117 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
78118 var bestIndex = -1;
78120 for (var i = 0; i < _suggestions.length; i++) {
78121 var suggestion = _suggestions[i].value;
78122 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
78124 if (compare === val) {
78126 break; // otherwise lock in the first result that starts with the search string..
78127 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
78132 if (bestIndex !== -1) {
78133 var bestVal = _suggestions[bestIndex].value;
78134 input.property('value', bestVal);
78135 input.node().setSelectionRange(val.length, bestVal.length);
78140 function render() {
78141 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
78146 var shown = !container.selectAll('.combobox').empty();
78147 if (!shown) return;
78148 var combo = container.selectAll('.combobox');
78149 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
78152 options.exit().remove(); // enter/update
78154 options.enter().append('a').attr('class', function (d) {
78155 return 'combobox-option ' + (d.klass || '');
78156 }).attr('title', function (d) {
78158 }).html(function (d) {
78159 return d.display || d.value;
78160 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
78161 return d.value === _selected;
78162 }).on('click.combo-option', accept).order();
78163 var node = attachTo ? attachTo.node() : input.node();
78164 var containerRect = container.node().getBoundingClientRect();
78165 var rect = node.getBoundingClientRect();
78166 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
78167 } // Dispatches an 'accept' event
78168 // Then hides the combobox.
78171 function accept(d3_event, d) {
78172 _cancelFetch = true;
78173 var thiz = input.node();
78176 // user clicked on a suggestion
78177 utilGetSetValue(input, d.value); // replace field contents
78179 utilTriggerEvent(input, 'change');
78180 } // clear (and keep) selection
78183 var val = utilGetSetValue(input);
78184 thiz.setSelectionRange(val.length, val.length);
78186 dispatch.call('accept', thiz, d, val);
78188 } // Dispatches an 'cancel' event
78189 // Then hides the combobox.
78192 function cancel() {
78193 _cancelFetch = true;
78194 var thiz = input.node(); // clear (and remove) selection, and replace field contents
78196 var val = utilGetSetValue(input);
78197 var start = input.property('selectionStart');
78198 var end = input.property('selectionEnd');
78199 val = val.slice(0, start) + val.slice(end);
78200 utilGetSetValue(input, val);
78201 thiz.setSelectionRange(val.length, val.length);
78202 dispatch.call('cancel', thiz);
78207 combobox.canAutocomplete = function (val) {
78208 if (!arguments.length) return _canAutocomplete;
78209 _canAutocomplete = val;
78213 combobox.caseSensitive = function (val) {
78214 if (!arguments.length) return _caseSensitive;
78215 _caseSensitive = val;
78219 combobox.data = function (val) {
78220 if (!arguments.length) return _data;
78225 combobox.fetcher = function (val) {
78226 if (!arguments.length) return _fetcher;
78231 combobox.minItems = function (val) {
78232 if (!arguments.length) return _minItems;
78237 combobox.itemsMouseEnter = function (val) {
78238 if (!arguments.length) return _mouseEnterHandler;
78239 _mouseEnterHandler = val;
78243 combobox.itemsMouseLeave = function (val) {
78244 if (!arguments.length) return _mouseLeaveHandler;
78245 _mouseLeaveHandler = val;
78249 return utilRebind(combobox, dispatch, 'on');
78252 uiCombobox.off = function (input, context) {
78253 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);
78254 context.container().on('scroll.combo-scroll', null);
78257 function uiDisclosure(context, key, expandedDefault) {
78258 var dispatch = dispatch$8('toggled');
78262 var _label = utilFunctor('');
78264 var _updatePreference = true;
78266 var _content = function _content() {};
78268 var disclosure = function disclosure(selection) {
78269 if (_expanded === undefined || _expanded === null) {
78270 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
78271 var preference = corePreferences('disclosure.' + key + '.expanded');
78272 _expanded = preference === null ? !!expandedDefault : preference === 'true';
78275 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
78277 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
78278 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
78280 hideToggle = hideToggleEnter.merge(hideToggle);
78281 hideToggle.on('click', toggle).classed('expanded', _expanded);
78282 hideToggle.selectAll('.hide-toggle-text').html(_label());
78283 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
78284 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
78286 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
78289 wrap.call(_content);
78292 function toggle(d3_event) {
78293 d3_event.preventDefault();
78294 _expanded = !_expanded;
78296 if (_updatePreference) {
78297 corePreferences('disclosure.' + key + '.expanded', _expanded);
78300 hideToggle.classed('expanded', _expanded);
78301 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
78302 wrap.call(uiToggle(_expanded));
78305 wrap.call(_content);
78308 dispatch.call('toggled', this, _expanded);
78312 disclosure.label = function (val) {
78313 if (!arguments.length) return _label;
78314 _label = utilFunctor(val);
78318 disclosure.expanded = function (val) {
78319 if (!arguments.length) return _expanded;
78324 disclosure.updatePreference = function (val) {
78325 if (!arguments.length) return _updatePreference;
78326 _updatePreference = val;
78330 disclosure.content = function (val) {
78331 if (!arguments.length) return _content;
78336 return utilRebind(disclosure, dispatch, 'on');
78339 // Can be labeled and collapsible.
78341 function uiSection(id, context) {
78342 var _classes = utilFunctor('');
78344 var _shouldDisplay;
78352 var _expandedByDefault = utilFunctor(true);
78354 var _disclosureContent;
78356 var _disclosureExpanded;
78358 var _containerSelection = select(null);
78364 section.classes = function (val) {
78365 if (!arguments.length) return _classes;
78366 _classes = utilFunctor(val);
78370 section.label = function (val) {
78371 if (!arguments.length) return _label;
78372 _label = utilFunctor(val);
78376 section.expandedByDefault = function (val) {
78377 if (!arguments.length) return _expandedByDefault;
78378 _expandedByDefault = utilFunctor(val);
78382 section.shouldDisplay = function (val) {
78383 if (!arguments.length) return _shouldDisplay;
78384 _shouldDisplay = utilFunctor(val);
78388 section.content = function (val) {
78389 if (!arguments.length) return _content;
78394 section.disclosureContent = function (val) {
78395 if (!arguments.length) return _disclosureContent;
78396 _disclosureContent = val;
78400 section.disclosureExpanded = function (val) {
78401 if (!arguments.length) return _disclosureExpanded;
78402 _disclosureExpanded = val;
78404 }; // may be called multiple times
78407 section.render = function (selection) {
78408 _containerSelection = selection.selectAll('.section-' + id).data([0]);
78410 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
78412 _containerSelection = sectionEnter.merge(_containerSelection);
78414 _containerSelection.call(renderContent);
78417 section.reRender = function () {
78418 _containerSelection.call(renderContent);
78421 section.selection = function () {
78422 return _containerSelection;
78425 section.disclosure = function () {
78426 return _disclosure;
78427 }; // may be called multiple times
78430 function renderContent(selection) {
78431 if (_shouldDisplay) {
78432 var shouldDisplay = _shouldDisplay();
78434 selection.classed('hide', !shouldDisplay);
78436 if (!shouldDisplay) {
78437 selection.html('');
78442 if (_disclosureContent) {
78443 if (!_disclosure) {
78444 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
78445 /*.on('toggled', function(expanded) {
78446 if (expanded) { selection.node().parentNode.scrollTop += 200; }
78448 .content(_disclosureContent);
78451 if (_disclosureExpanded !== undefined) {
78452 _disclosure.expanded(_disclosureExpanded);
78454 _disclosureExpanded = undefined;
78457 selection.call(_disclosure);
78462 selection.call(_content);
78470 // key: 'string', // required
78471 // value: 'string' // optional
78475 // qid: 'string' // brand wikidata (e.g. 'Q37158')
78479 function uiTagReference(what) {
78480 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
78481 var tagReference = {};
78483 var _button = select(null);
78485 var _body = select(null);
78492 if (!wikibase) return;
78494 _button.classed('tag-reference-loading', true);
78496 wikibase.getDocs(what, gotDocs);
78499 function gotDocs(err, docs) {
78502 if (!docs || !docs.title) {
78503 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
78509 if (docs.imageURL) {
78510 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
78512 }).on('error', function () {
78513 select(this).remove();
78520 _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'));
78523 _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));
78524 } // Add link to info about "good changeset comments" - #2923
78527 if (what.key === 'comment') {
78528 _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'));
78535 _button.classed('tag-reference-loading', false);
78537 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
78541 _button.selectAll('svg.icon use').each(function () {
78542 var iconUse = select(this);
78544 if (iconUse.attr('href') === '#iD-icon-info') {
78545 iconUse.attr('href', '#iD-icon-info-filled');
78551 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
78552 _body.classed('expanded', false);
78557 _button.selectAll('svg.icon use').each(function () {
78558 var iconUse = select(this);
78560 if (iconUse.attr('href') === '#iD-icon-info-filled') {
78561 iconUse.attr('href', '#iD-icon-info');
78566 tagReference.button = function (selection, klass, iconName) {
78567 _button = selection.selectAll('.tag-reference-button').data([0]);
78568 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
78570 _button.on('click', function (d3_event) {
78571 d3_event.stopPropagation();
78572 d3_event.preventDefault();
78573 this.blur(); // avoid keeping focus on the button - #4641
78577 } else if (_loaded) {
78585 tagReference.body = function (selection) {
78586 var itemID = what.qid || what.key + '-' + (what.value || '');
78587 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
78591 _body.exit().remove();
78593 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
78595 if (_showing === false) {
78600 tagReference.showing = function (val) {
78601 if (!arguments.length) return _showing;
78603 return tagReference;
78606 return tagReference;
78609 function uiSectionRawTagEditor(id, context) {
78610 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
78611 var count = Object.keys(_tags).filter(function (d) {
78614 return _t('inspector.title_count', {
78615 title: _t.html('inspector.tags'),
78618 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
78619 var taginfo = services.taginfo;
78620 var dispatch = dispatch$8('change');
78621 var availableViews = [{
78623 icon: '#fas-th-list'
78626 icon: '#fas-i-cursor'
78629 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
78632 var _readOnlyTags = []; // the keys in the order we want them to display
78634 var _orderedKeys = [];
78635 var _showBlank = false;
78636 var _pendingChange = null;
78646 var _didInteract = false;
78648 function interacted() {
78649 _didInteract = true;
78652 function renderDisclosureContent(wrap) {
78653 // remove deleted keys
78654 _orderedKeys = _orderedKeys.filter(function (key) {
78655 return _tags[key] !== undefined;
78656 }); // When switching to a different entity or changing the state (hover/select)
78657 // reorder the keys alphabetically.
78658 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
78659 // Otherwise leave their order alone - #5857, #5927
78661 var all = Object.keys(_tags).sort();
78662 var missingKeys = utilArrayDifference(all, _orderedKeys);
78664 for (var i in missingKeys) {
78665 _orderedKeys.push(missingKeys[i]);
78666 } // assemble row data
78669 var rowData = _orderedKeys.map(function (key, i) {
78675 }); // append blank row last, if necessary
78678 if (!rowData.length || _showBlank) {
78679 _showBlank = false;
78681 index: rowData.length,
78688 var options = wrap.selectAll('.raw-tag-options').data([0]);
78689 options.exit().remove();
78690 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
78691 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
78694 optionEnter.append('button').attr('class', function (d) {
78695 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
78696 }).attr('title', function (d) {
78697 return _t('icons.' + d.id);
78698 }).on('click', function (d3_event, d) {
78700 corePreferences('raw-tag-editor-view', d.id);
78701 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
78702 return datum === d;
78704 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
78705 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
78706 }).each(function (d) {
78707 select(this).call(svgIcon(d.icon));
78708 }); // View as Text
78710 var textData = rowsToText(rowData);
78711 var textarea = wrap.selectAll('.tag-text').data([0]);
78712 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);
78713 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
78715 var list = wrap.selectAll('.tag-list').data([0]);
78716 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
78718 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
78719 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
78720 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
78722 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
78725 var items = list.selectAll('.tag-row').data(rowData, function (d) {
78728 items.exit().each(unbind).remove(); // Enter
78730 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
78731 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
78732 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);
78733 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);
78734 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
78736 items = items.merge(itemsEnter).sort(function (a, b) {
78737 return a.index - b.index;
78739 items.each(function (d) {
78740 var row = select(this);
78741 var key = row.select('input.key'); // propagate bound data
78743 var value = row.select('input.value'); // propagate bound data
78745 if (_entityIDs && taginfo && _state !== 'hover') {
78746 bindTypeahead(key, value);
78749 var referenceOptions = {
78753 if (typeof d.value === 'string') {
78754 referenceOptions.value = d.value;
78757 var reference = uiTagReference(referenceOptions);
78759 if (_state === 'hover') {
78760 reference.showing(false);
78763 row.select('.inner-wrap') // propagate bound data
78764 .call(reference.button);
78765 row.call(reference.body);
78766 row.select('button.remove'); // propagate bound data
78768 items.selectAll('input.key').attr('title', function (d) {
78770 }).call(utilGetSetValue, function (d) {
78772 }).attr('readonly', function (d) {
78773 return isReadOnly(d) || typeof d.value !== 'string' || null;
78775 items.selectAll('input.value').attr('title', function (d) {
78776 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
78777 }).classed('mixed', function (d) {
78778 return Array.isArray(d.value);
78779 }).attr('placeholder', function (d) {
78780 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
78781 }).call(utilGetSetValue, function (d) {
78782 return typeof d.value === 'string' ? d.value : '';
78783 }).attr('readonly', function (d) {
78784 return isReadOnly(d) || null;
78786 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
78789 function isReadOnly(d) {
78790 for (var i = 0; i < _readOnlyTags.length; i++) {
78791 if (d.key.match(_readOnlyTags[i]) !== null) {
78799 function setTextareaHeight() {
78800 if (_tagView !== 'text') return;
78801 var selection = select(this);
78802 var matches = selection.node().value.match(/\n/g);
78803 var lineCount = 2 + Number(matches && matches.length);
78804 var lineHeight = 20;
78805 selection.style('height', lineCount * lineHeight + 'px');
78808 function stringify(s) {
78809 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
78812 function unstringify(s) {
78816 if (s.length < 1 || s.charAt(0) !== '"') {
78820 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
78824 return JSON.parse(leading + s + trailing);
78827 function rowsToText(rows) {
78828 var str = rows.filter(function (row) {
78829 return row.key && row.key.trim() !== '';
78830 }).map(function (row) {
78831 var rawVal = row.value;
78832 if (typeof rawVal !== 'string') rawVal = '*';
78833 var val = rawVal ? stringify(rawVal) : '';
78834 return stringify(row.key) + '=' + val;
78837 if (_state !== 'hover' && str.length) {
78844 function textChanged() {
78845 var newText = this.value.trim();
78847 newText.split('\n').forEach(function (row) {
78848 var m = row.match(/^\s*([^=]+)=(.*)$/);
78851 var k = context.cleanTagKey(unstringify(m[1].trim()));
78852 var v = context.cleanTagValue(unstringify(m[2].trim()));
78856 var tagDiff = utilTagDiff(_tags, newTags);
78857 if (!tagDiff.length) return;
78858 _pendingChange = _pendingChange || {};
78859 tagDiff.forEach(function (change) {
78862 })) return; // skip unchanged multiselection placeholders
78864 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
78866 if (change.type === '-') {
78867 _pendingChange[change.key] = undefined;
78868 } else if (change.type === '+') {
78869 _pendingChange[change.key] = change.newVal || '';
78873 if (Object.keys(_pendingChange).length === 0) {
78874 _pendingChange = null;
78881 function pushMore(d3_event) {
78882 // if pressing Tab on the last value field with content, add a blank row
78883 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
78888 function bindTypeahead(key, value) {
78889 if (isReadOnly(key.datum())) return;
78891 if (Array.isArray(value.datum().value)) {
78892 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
78893 var keyString = utilGetSetValue(key);
78894 if (!_tags[keyString]) return;
78896 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
78908 var geometry = context.graph().geometry(_entityIDs[0]);
78909 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
78912 geometry: geometry,
78914 }, function (err, data) {
78916 var filtered = data.filter(function (d) {
78917 return _tags[d.value] === undefined;
78919 callback(sort(value, filtered));
78923 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
78926 key: utilGetSetValue(key),
78927 geometry: geometry,
78929 }, function (err, data) {
78930 if (!err) callback(sort(value, data));
78934 function sort(value, data) {
78935 var sameletter = [];
78938 for (var i = 0; i < data.length; i++) {
78939 if (data[i].value.substring(0, value.length) === value) {
78940 sameletter.push(data[i]);
78942 other.push(data[i]);
78946 return sameletter.concat(other);
78950 function unbind() {
78951 var row = select(this);
78952 row.selectAll('input.key').call(uiCombobox.off, context);
78953 row.selectAll('input.value').call(uiCombobox.off, context);
78956 function keyChange(d3_event, d) {
78957 if (select(this).attr('readonly')) return;
78958 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
78960 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
78961 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
78970 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
78971 // new key is already in use, switch focus to the existing row
78972 this.value = kOld; // reset the key
78974 section.selection().selectAll('.tag-list input.value').each(function (d) {
78975 if (d.key === kNew) {
78976 // send focus to that other value combo instead
78977 var input = select(this).node();
78985 var row = this.parentNode.parentNode;
78986 var inputVal = select(row).selectAll('input.value');
78987 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
78988 _pendingChange = _pendingChange || {};
78991 _pendingChange[kOld] = undefined;
78994 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
78996 var existingKeyIndex = _orderedKeys.indexOf(kOld);
78998 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
78999 d.key = kNew; // update datum to avoid exit/enter on tag update
79003 utilGetSetValue(inputVal, vNew);
79007 function valueChange(d3_event, d) {
79008 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
79010 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
79012 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
79013 _pendingChange = _pendingChange || {};
79014 _pendingChange[d.key] = context.cleanTagValue(this.value);
79018 function removeTag(d3_event, d) {
79019 if (isReadOnly(d)) return;
79021 if (d.key === '') {
79022 // removing the blank row
79023 _showBlank = false;
79024 section.reRender();
79026 // remove the key from the ordered key index
79027 _orderedKeys = _orderedKeys.filter(function (key) {
79028 return key !== d.key;
79030 _pendingChange = _pendingChange || {};
79031 _pendingChange[d.key] = undefined;
79036 function addTag() {
79037 // Delay render in case this click is blurring an edited combo.
79038 // Without the setTimeout, the `content` render would wipe out the pending tag change.
79039 window.setTimeout(function () {
79041 section.reRender();
79042 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
79046 function scheduleChange() {
79047 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
79048 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
79050 window.setTimeout(function () {
79051 if (!_pendingChange) return;
79052 dispatch.call('change', this, entityIDs, _pendingChange);
79053 _pendingChange = null;
79057 section.state = function (val) {
79058 if (!arguments.length) return _state;
79060 if (_state !== val) {
79068 section.presets = function (val) {
79069 if (!arguments.length) return _presets;
79072 if (_presets && _presets.length && _presets[0].isFallback()) {
79073 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
79074 } else if (!_didInteract) {
79075 section.disclosureExpanded(null);
79081 section.tags = function (val) {
79082 if (!arguments.length) return _tags;
79087 section.entityIDs = function (val) {
79088 if (!arguments.length) return _entityIDs;
79090 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
79096 }; // pass an array of regular expressions to test against the tag key
79099 section.readOnlyTags = function (val) {
79100 if (!arguments.length) return _readOnlyTags;
79101 _readOnlyTags = val;
79105 return utilRebind(section, dispatch, 'on');
79108 function uiDataEditor(context) {
79109 var dataHeader = uiDataHeader();
79110 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
79114 function dataEditor(selection) {
79115 var header = selection.selectAll('.header').data([0]);
79116 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
79117 headerEnter.append('button').attr('class', 'close').on('click', function () {
79118 context.enter(modeBrowse(context));
79119 }).call(svgIcon('#iD-icon-close'));
79120 headerEnter.append('h3').html(_t.html('map_data.title'));
79121 var body = selection.selectAll('.body').data([0]);
79122 body = body.enter().append('div').attr('class', 'body').merge(body);
79123 var editor = body.selectAll('.data-editor').data([0]); // enter/update
79125 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
79126 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
79128 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);
79131 dataEditor.datum = function (val) {
79132 if (!arguments.length) return _datum;
79142 function search(input, dims) {
79143 if (!dims) dims = 'NSEW';
79144 if (typeof input !== 'string') return null;
79145 input = input.toUpperCase();
79146 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
79147 var m = input.match(regex);
79148 if (!m) return null; // no match
79150 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
79154 if (m[1] && m[5]) {
79155 // if matched both..
79156 dim = m[1]; // keep leading
79158 matched = matched.slice(0, -1); // remove trailing dimension from match
79160 dim = m[1] || m[5];
79161 } // if unrecognized dimension
79164 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
79166 var deg = m[2] ? parseFloat(m[2]) : 0;
79167 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
79168 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
79169 var sign = deg < 0 ? -1 : 1;
79170 if (dim === 'S' || dim === 'W') sign *= -1;
79172 val: (Math.abs(deg) + min + sec) * sign,
79175 remain: input.slice(matched.length)
79179 function pair(input, dims) {
79180 input = input.trim();
79181 var one = search(input, dims);
79182 if (!one) return null;
79183 input = one.remain.trim();
79184 var two = search(input, dims);
79185 if (!two || two.remain) return null;
79188 return swapdim(one.val, two.val, one.dim);
79190 return [one.val, two.val];
79194 function swapdim(a, b, dim) {
79195 if (dim === 'N' || dim === 'S') return [a, b];
79196 if (dim === 'W' || dim === 'E') return [b, a];
79199 function uiFeatureList(context) {
79200 var _geocodeResults;
79202 function featureList(selection) {
79203 var header = selection.append('div').attr('class', 'header fillL');
79204 header.append('h3').html(_t.html('inspector.feature_list'));
79205 var searchWrap = selection.append('div').attr('class', 'search-header');
79206 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
79207 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
79208 var listWrap = selection.append('div').attr('class', 'inspector-body');
79209 var list = listWrap.append('div').attr('class', 'feature-list');
79210 context.on('exit.feature-list', clearSearch);
79211 context.map().on('drawn.feature-list', mapDrawn);
79212 context.keybinding().on(uiCmd('⌘F'), focusSearch);
79214 function focusSearch(d3_event) {
79215 var mode = context.mode() && context.mode().id;
79216 if (mode !== 'browse') return;
79217 d3_event.preventDefault();
79218 search.node().focus();
79221 function keydown(d3_event) {
79222 if (d3_event.keyCode === 27) {
79224 search.node().blur();
79228 function keypress(d3_event) {
79229 var q = search.property('value'),
79230 items = list.selectAll('.feature-list-item');
79232 if (d3_event.keyCode === 13 && // ↩ Return
79233 q.length && items.size()) {
79234 click(d3_event, items.datum());
79238 function inputevent() {
79239 _geocodeResults = undefined;
79243 function clearSearch() {
79244 search.property('value', '');
79248 function mapDrawn(e) {
79254 function features() {
79256 var graph = context.graph();
79257 var visibleCenter = context.map().extent().center();
79258 var q = search.property('value').toLowerCase();
79259 if (!q) return result;
79260 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
79262 if (locationMatch) {
79263 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
79267 type: _t('inspector.location'),
79268 name: dmsCoordinatePair([loc[1], loc[0]]),
79271 } // A location search takes priority over an ID search
79274 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
79277 var elemType = idMatch[1].charAt(0);
79278 var elemId = idMatch[2];
79280 id: elemType + elemId,
79281 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
79282 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
79287 var allEntities = graph.entities;
79288 var localResults = [];
79290 for (var id in allEntities) {
79291 var entity = allEntities[id];
79292 if (!entity) continue;
79293 var name = utilDisplayName(entity) || '';
79294 if (name.toLowerCase().indexOf(q) < 0) continue;
79295 var matched = _mainPresetIndex.match(entity, graph);
79296 var type = matched && matched.name() || utilDisplayType(entity.id);
79297 var extent = entity.extent(graph);
79298 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
79299 localResults.push({
79302 geometry: entity.geometry(graph),
79307 if (localResults.length > 100) break;
79310 localResults = localResults.sort(function byDistance(a, b) {
79311 return a.distance - b.distance;
79313 result = result.concat(localResults);
79315 (_geocodeResults || []).forEach(function (d) {
79316 if (d.osm_type && d.osm_id) {
79317 // some results may be missing these - #1890
79318 // Make a temporary osmEntity so we can preset match
79319 // and better localize the search result - #4725
79320 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
79322 tags[d["class"]] = d.type;
79329 if (d.osm_type === 'way') {
79330 // for ways, add some fake closed nodes
79331 attrs.nodes = ['a', 'a']; // so that geometry area is possible
79334 var tempEntity = osmEntity(attrs);
79335 var tempGraph = coreGraph([tempEntity]);
79336 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
79337 var type = matched && matched.name() || utilDisplayType(id);
79340 geometry: tempEntity.geometry(tempGraph),
79342 name: d.display_name,
79343 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
79348 if (q.match(/^[0-9]+$/)) {
79349 // if query is just a number, possibly an OSM ID without a prefix
79353 type: _t('inspector.node'),
79359 type: _t('inspector.way'),
79364 geometry: 'relation',
79365 type: _t('inspector.relation'),
79373 function drawList() {
79374 var value = search.property('value');
79375 var results = features();
79376 list.classed('filtered', value.length);
79377 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'));
79378 resultsIndicator.append('span').attr('class', 'entity-name');
79379 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
79381 if (services.geocoder) {
79382 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'));
79385 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
79386 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
79387 list.selectAll('.feature-list-item').data([-1]).remove();
79388 var items = list.selectAll('.feature-list-item').data(results, function (d) {
79391 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
79392 var label = enter.append('div').attr('class', 'label');
79393 label.each(function (d) {
79394 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
79396 label.append('span').attr('class', 'entity-type').html(function (d) {
79399 label.append('span').attr('class', 'entity-name').html(function (d) {
79402 enter.style('opacity', 0).transition().style('opacity', 1);
79404 items.exit().remove();
79407 function mouseover(d3_event, d) {
79408 if (d.id === -1) return;
79409 utilHighlightEntities([d.id], true, context);
79412 function mouseout(d3_event, d) {
79413 if (d.id === -1) return;
79414 utilHighlightEntities([d.id], false, context);
79417 function click(d3_event, d) {
79418 d3_event.preventDefault();
79421 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
79422 } else if (d.entity) {
79423 utilHighlightEntities([d.id], false, context);
79424 context.enter(modeSelect(context, [d.entity.id]));
79425 context.map().zoomToEase(d.entity);
79427 // download, zoom to, and select the entity with the given ID
79428 context.zoomToEntity(d.id);
79432 function geocoderSearch() {
79433 services.geocoder.search(search.property('value'), function (err, resp) {
79434 _geocodeResults = resp || [];
79440 return featureList;
79443 var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
79450 // eslint-disable-next-line es/no-string-prototype-startswith -- safe
79451 var $startsWith = ''.startsWith;
79452 var min$1 = Math.min;
79454 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
79455 // https://github.com/zloirock/core-js/pull/702
79456 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
79457 var descriptor = getOwnPropertyDescriptor$1(String.prototype, 'startsWith');
79458 return descriptor && !descriptor.writable;
79461 // `String.prototype.startsWith` method
79462 // https://tc39.es/ecma262/#sec-string.prototype.startswith
79463 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
79464 startsWith: function startsWith(searchString /* , position = 0 */) {
79465 var that = String(requireObjectCoercible(this));
79466 notARegexp(searchString);
79467 var index = toLength(min$1(arguments.length > 1 ? arguments[1] : undefined, that.length));
79468 var search = String(searchString);
79470 ? $startsWith.call(that, search, index)
79471 : that.slice(index, index + search.length) === search;
79475 function uiSectionEntityIssues(context) {
79476 // Does the user prefer to expand the active issue? Useful for viewing tag diff.
79477 // Expand by default so first timers see it - #6408, #8143
79478 var preference = corePreferences('entity-issues.reference.expanded');
79480 var _expanded = preference === null ? true : preference === 'true';
79482 var _entityIDs = [];
79485 var _activeIssueID;
79487 var section = uiSection('entity-issues', context).shouldDisplay(function () {
79488 return _issues.length > 0;
79489 }).label(function () {
79490 return _t('inspector.title_count', {
79491 title: _t.html('issues.list_title'),
79492 count: _issues.length
79494 }).disclosureContent(renderDisclosureContent);
79495 context.validator().on('validated.entity_issues', function () {
79496 // Refresh on validated events
79498 section.reRender();
79499 }).on('focusedIssue.entity_issues', function (issue) {
79500 makeActiveIssue(issue.id);
79503 function reloadIssues() {
79504 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
79505 includeDisabledRules: true
79509 function makeActiveIssue(issueID) {
79510 _activeIssueID = issueID;
79511 section.selection().selectAll('.issue-container').classed('active', function (d) {
79512 return d.id === _activeIssueID;
79516 function renderDisclosureContent(selection) {
79517 selection.classed('grouped-items-area', true);
79518 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
79519 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
79523 containers.exit().remove(); // Enter
79525 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
79526 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
79527 return 'issue severity-' + d.severity;
79528 }).on('mouseover.highlight', function (d3_event, d) {
79529 // don't hover-highlight the selected entity
79530 var ids = d.entityIds.filter(function (e) {
79531 return _entityIDs.indexOf(e) === -1;
79533 utilHighlightEntities(ids, true, context);
79534 }).on('mouseout.highlight', function (d3_event, d) {
79535 var ids = d.entityIds.filter(function (e) {
79536 return _entityIDs.indexOf(e) === -1;
79538 utilHighlightEntities(ids, false, context);
79540 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
79541 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
79542 makeActiveIssue(d.id); // expand only the clicked item
79544 var extent = d.extent(context.graph());
79547 var setZoom = Math.max(context.map().zoom(), 19);
79548 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
79551 textEnter.each(function (d) {
79552 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
79553 select(this).call(svgIcon(iconName, 'issue-icon'));
79555 textEnter.append('span').attr('class', 'issue-message');
79556 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
79557 infoButton.on('click', function (d3_event) {
79558 d3_event.stopPropagation();
79559 d3_event.preventDefault();
79560 this.blur(); // avoid keeping focus on the button - #4641
79562 var container = select(this.parentNode.parentNode.parentNode);
79563 var info = container.selectAll('.issue-info');
79564 var isExpanded = info.classed('expanded');
79565 _expanded = !isExpanded;
79566 corePreferences('entity-issues.reference.expanded', _expanded); // update preference
79569 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
79570 info.classed('expanded', false);
79573 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
79574 info.style('max-height', null);
79578 itemsEnter.append('ul').attr('class', 'issue-fix-list');
79579 containersEnter.append('div').attr('class', 'issue-info' + (_expanded ? ' expanded' : '')).style('max-height', _expanded ? null : '0').style('opacity', _expanded ? '1' : '0').each(function (d) {
79580 if (typeof d.reference === 'function') {
79581 select(this).call(d.reference);
79583 select(this).html(_t.html('inspector.no_documentation_key'));
79587 containers = containers.merge(containersEnter).classed('active', function (d) {
79588 return d.id === _activeIssueID;
79590 containers.selectAll('.issue-message').html(function (d) {
79591 return d.message(context);
79594 var fixLists = containers.selectAll('.issue-fix-list');
79595 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
79596 return d.fixes ? d.fixes(context) : [];
79597 }, function (fix) {
79600 fixes.exit().remove();
79601 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
79602 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
79603 // not all fixes are actionable
79604 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
79605 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
79607 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
79608 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
79610 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
79611 new Promise(function (resolve, reject) {
79612 d.onClick(context, resolve, reject);
79614 if (d.onClick.length <= 1) {
79615 // if the fix doesn't take any completion parameters then consider it resolved
79618 }).then(function () {
79619 // revalidate whenever the fix has finished running successfully
79620 context.validator().validate();
79622 }).on('mouseover.highlight', function (d3_event, d) {
79623 utilHighlightEntities(d.entityIds, true, context);
79624 }).on('mouseout.highlight', function (d3_event, d) {
79625 utilHighlightEntities(d.entityIds, false, context);
79627 buttons.each(function (d) {
79628 var iconName = d.icon || 'iD-icon-wrench';
79630 if (iconName.startsWith('maki')) {
79634 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
79636 buttons.append('span').attr('class', 'fix-message').html(function (d) {
79639 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
79641 }).attr('disabled', function (d) {
79642 return d.onClick ? null : 'true';
79643 }).attr('title', function (d) {
79644 if (d.disabledReason) {
79645 return d.disabledReason;
79652 section.entityIDs = function (val) {
79653 if (!arguments.length) return _entityIDs;
79655 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
79657 _activeIssueID = null;
79667 function uiPresetIcon() {
79672 var _sizeClass = 'medium';
79674 function isSmall() {
79675 return _sizeClass === 'small';
79678 function presetIcon(selection) {
79679 selection.each(render);
79682 function getIcon(p, geom) {
79683 if (isSmall() && p.isFallback && p.isFallback()) return 'iD-icon-' + p.id;
79684 if (p.icon) return p.icon;
79685 if (geom === 'line') return 'iD-other-line';
79686 if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex';
79687 if (isSmall() && geom === 'point') return '';
79688 return 'maki-marker-stroked';
79691 function renderPointBorder(container, drawPoint) {
79692 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
79693 pointBorder.exit().remove();
79694 var pointBorderEnter = pointBorder.enter();
79697 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');
79698 pointBorder = pointBorderEnter.merge(pointBorder);
79701 function renderCategoryBorder(container, category) {
79702 var categoryBorder = container.selectAll('.preset-icon-category-border').data(category ? [0] : []);
79703 categoryBorder.exit().remove();
79704 var categoryBorderEnter = categoryBorder.enter();
79706 var svgEnter = categoryBorderEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-category-border').attr('width', d).attr('height', d).attr('viewBox', "0 0 ".concat(d, " ").concat(d));
79707 ['fill', 'stroke'].forEach(function (klass) {
79708 svgEnter.append('path').attr('class', "area ".concat(klass)).attr('d', 'M9.5,7.5 L25.5,7.5 L28.5,12.5 L49.5,12.5 C51.709139,12.5 53.5,14.290861 53.5,16.5 L53.5,43.5 C53.5,45.709139 51.709139,47.5 49.5,47.5 L10.5,47.5 C8.290861,47.5 6.5,45.709139 6.5,43.5 L6.5,12.5 L9.5,7.5 Z');
79710 categoryBorder = categoryBorderEnter.merge(categoryBorder);
79713 var tagClasses = svgTagClasses().getClassesString(category.members.collection[0].addTags, '');
79714 categoryBorder.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
79715 categoryBorder.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
79719 function renderCircleFill(container, drawVertex) {
79720 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
79721 vertexFill.exit().remove();
79722 var vertexFillEnter = vertexFill.enter();
79726 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);
79727 vertexFill = vertexFillEnter.merge(vertexFill);
79730 function renderSquareFill(container, drawArea, tagClasses) {
79731 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
79732 fill.exit().remove();
79733 var fillEnter = fill.enter();
79734 var d = isSmall() ? 40 : 60;
79738 var c1 = (w - l) / 2;
79740 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));
79741 ['fill', 'stroke'].forEach(function (klass) {
79742 fillEnter.append('path').attr('d', "M".concat(c1, " ").concat(c1, " L").concat(c1, " ").concat(c2, " L").concat(c2, " ").concat(c2, " L").concat(c2, " ").concat(c1, " Z")).attr('class', "area ".concat(klass));
79745 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
79746 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
79750 var rMidpoint = 1.25;
79751 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
79752 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
79756 fill = fillEnter.merge(fill);
79757 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
79758 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
79761 function renderLine(container, drawLine, tagClasses) {
79762 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
79763 line.exit().remove();
79764 var lineEnter = line.enter();
79765 var d = isSmall() ? 40 : 60; // draw the line parametrically
79769 var y = Math.round(d * 0.72);
79770 var l = Math.round(d * 0.6);
79772 var x1 = (w - l) / 2;
79774 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
79775 ['casing', 'stroke'].forEach(function (klass) {
79776 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
79778 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
79779 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
79781 line = lineEnter.merge(line);
79782 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
79783 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
79786 function renderRoute(container, drawRoute, p) {
79787 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
79788 route.exit().remove();
79789 var routeEnter = route.enter();
79790 var d = isSmall() ? 40 : 60; // draw the route parametrically
79794 var y1 = Math.round(d * 0.80);
79795 var y2 = Math.round(d * 0.68);
79796 var l = Math.round(d * 0.6);
79798 var x1 = (w - l) / 2;
79799 var x2 = x1 + l / 3;
79800 var x3 = x2 + l / 3;
79801 var x4 = x3 + l / 3;
79802 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
79803 ['casing', 'stroke'].forEach(function (klass) {
79804 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
79805 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
79806 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
79808 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
79809 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
79811 route = routeEnter.merge(route);
79814 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
79815 var segmentPresetIDs = routeSegments[routeType];
79817 for (var i in segmentPresetIDs) {
79818 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
79819 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
79820 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
79821 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
79826 function renderSvgIcon(container, picon, geom, isFramed, category, tagClasses) {
79827 var isMaki = picon && /^maki-/.test(picon);
79828 var isTemaki = picon && /^temaki-/.test(picon);
79829 var isFa = picon && /^fa[srb]-/.test(picon);
79830 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
79831 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
79832 icon.exit().remove();
79833 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
79834 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('category', category).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
79835 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
79839 suffix = isSmall() && geom === 'point' ? '-11' : '-15';
79842 icon.selectAll('use').attr('href', '#' + picon + suffix);
79845 function renderImageIcon(container, imageURL) {
79846 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
79847 imageIcon.exit().remove();
79848 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
79849 return container.classed('showing-img', true);
79850 }).on('error', function () {
79851 return container.classed('showing-img', false);
79852 }).merge(imageIcon);
79853 imageIcon.attr('src', imageURL);
79854 } // Route icons are drawn with a zigzag annotation underneath:
79858 // This dataset defines the styles that are used to draw the zigzag segments.
79861 var routeSegments = {
79862 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
79863 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
79864 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
79865 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
79866 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
79867 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
79868 hiking: ['highway/path', 'highway/path', 'highway/path'],
79869 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
79870 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
79871 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
79872 mtb: ['highway/path', 'highway/track', 'highway/bridleway'],
79873 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
79874 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
79875 power: ['power/line', 'power/line', 'power/line'],
79876 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
79877 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
79878 train: ['railway/rail', 'railway/rail', 'railway/rail'],
79879 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
79880 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
79883 function render() {
79884 var p = _preset.apply(this, arguments);
79886 var geom = _geometry ? _geometry.apply(this, arguments) : null;
79888 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
79892 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
79893 var isFallback = isSmall() && p.isFallback && p.isFallback();
79894 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
79895 var picon = getIcon(p, geom);
79896 var isCategory = !p.setTags;
79897 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
79898 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
79899 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
79900 var drawArea = picon && geom === 'area' && !isFallback && !isCategory;
79901 var drawRoute = picon && geom === 'route';
79902 var isFramed = drawVertex || drawArea || drawLine || drawRoute || isCategory;
79903 var tags = !isCategory ? p.setTags({}, geom) : {};
79905 for (var k in tags) {
79906 if (tags[k] === '*') {
79911 var tagClasses = svgTagClasses().getClassesString(tags, '');
79912 var selection = select(this);
79913 var container = selection.selectAll('.preset-icon-container').data([0]);
79914 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
79915 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
79916 renderCategoryBorder(container, isCategory && p);
79917 renderPointBorder(container, drawPoint);
79918 renderCircleFill(container, drawVertex);
79919 renderSquareFill(container, drawArea, tagClasses);
79920 renderLine(container, drawLine, tagClasses);
79921 renderRoute(container, drawRoute, p);
79922 renderSvgIcon(container, picon, geom, isFramed, isCategory, tagClasses);
79923 renderImageIcon(container, imageURL);
79926 presetIcon.preset = function (val) {
79927 if (!arguments.length) return _preset;
79928 _preset = utilFunctor(val);
79932 presetIcon.geometry = function (val) {
79933 if (!arguments.length) return _geometry;
79934 _geometry = utilFunctor(val);
79938 presetIcon.sizeClass = function (val) {
79939 if (!arguments.length) return _sizeClass;
79947 function uiSectionFeatureType(context) {
79948 var dispatch = dispatch$8('choose');
79949 var _entityIDs = [];
79954 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
79956 function renderDisclosureContent(selection) {
79957 selection.classed('preset-list-item', true);
79958 selection.classed('mixed-types', _presets.length > 1);
79959 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
79960 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
79961 presetButton.append('div').attr('class', 'preset-icon-container');
79962 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
79963 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
79964 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
79965 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
79967 if (_tagReference) {
79968 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
79969 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
79972 selection.selectAll('.preset-reset').on('click', function () {
79973 dispatch.call('choose', this, _presets);
79974 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
79975 d3_event.preventDefault();
79976 d3_event.stopPropagation();
79978 var geometries = entityGeometries();
79979 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')));
79980 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
79981 var label = selection.select('.label-inner');
79982 var nameparts = label.selectAll('.namepart').data(names, function (d) {
79985 nameparts.exit().remove();
79986 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
79991 section.entityIDs = function (val) {
79992 if (!arguments.length) return _entityIDs;
79997 section.presets = function (val) {
79998 if (!arguments.length) return _presets; // don't reload the same preset
80000 if (!utilArrayIdentical(val, _presets)) {
80003 if (_presets.length === 1) {
80004 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
80011 function entityGeometries() {
80014 for (var i in _entityIDs) {
80015 var geometry = context.graph().geometry(_entityIDs[i]);
80016 if (!counts[geometry]) counts[geometry] = 0;
80017 counts[geometry] += 1;
80020 return Object.keys(counts).sort(function (geom1, geom2) {
80021 return counts[geom2] - counts[geom1];
80025 return utilRebind(section, dispatch, 'on');
80028 // It borrows some code from uiHelp
80030 function uiFieldHelp(context, fieldName) {
80031 var fieldHelp = {};
80033 var _inspector = select(null);
80035 var _wrap = select(null);
80037 var _body = select(null);
80039 var fieldHelpKeys = {
80040 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']]]
80042 var fieldHelpHeadings = {};
80043 var replacements = {
80044 distField: _t.html('restriction.controls.distance'),
80045 viaField: _t.html('restriction.controls.via'),
80046 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
80047 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
80048 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
80049 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
80050 allowTurn: icon('#iD-turn-yes', 'inline turn'),
80051 restrictTurn: icon('#iD-turn-no', 'inline turn'),
80052 onlyTurn: icon('#iD-turn-only', 'inline turn')
80053 }; // For each section, squash all the texts into a single markdown document
80055 var docs = fieldHelpKeys[fieldName].map(function (key) {
80056 var helpkey = 'help.field.' + fieldName + '.' + key[0];
80057 var text = key[1].reduce(function (all, part) {
80058 var subkey = helpkey + '.' + part;
80059 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
80061 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
80063 return all + hhh + _t.html(subkey, replacements) + '\n\n';
80067 title: _t.html(helpkey + '.title'),
80068 html: marked_1(text.trim())
80075 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
80079 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
80080 _body.classed('hide', true);
80084 function clickHelp(index) {
80085 var d = docs[index];
80086 var tkeys = fieldHelpKeys[fieldName][index][1];
80088 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
80089 return i === index;
80092 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
80095 content.selectAll('p').attr('class', function (d, i) {
80097 }); // insert special content for certain help sections
80099 if (d.key === 'help.field.restrictions.inspecting') {
80100 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
80101 } else if (d.key === 'help.field.restrictions.modifying') {
80102 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
80106 fieldHelp.button = function (selection) {
80107 if (_body.empty()) return;
80108 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
80110 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
80111 d3_event.stopPropagation();
80112 d3_event.preventDefault();
80114 if (_body.classed('hide')) {
80122 function updatePosition() {
80123 var wrap = _wrap.node();
80125 var inspector = _inspector.node();
80127 var wRect = wrap.getBoundingClientRect();
80128 var iRect = inspector.getBoundingClientRect();
80130 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
80133 fieldHelp.body = function (selection) {
80134 // This control expects the field to have a form-field-input-wrap div
80135 _wrap = selection.selectAll('.form-field-input-wrap');
80136 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
80138 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
80139 if (_inspector.empty()) return;
80140 _body = _inspector.selectAll('.field-help-body').data([0]);
80142 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
80145 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
80146 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
80147 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
80148 d3_event.stopPropagation();
80149 d3_event.preventDefault();
80151 }).call(svgIcon('#iD-icon-close'));
80152 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
80153 var titles = docs.map(function (d) {
80156 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
80158 }).on('click', function (d3_event, d) {
80159 d3_event.stopPropagation();
80160 d3_event.preventDefault();
80161 clickHelp(titles.indexOf(d));
80163 enter.append('div').attr('class', 'field-help-content');
80164 _body = _body.merge(enter);
80171 function uiFieldCheck(field, context) {
80172 var dispatch = dispatch$8('change');
80173 var options = field.options;
80179 var input = select(null);
80180 var text = select(null);
80181 var label = select(null);
80182 var reverser = select(null);
80186 var _entityIDs = [];
80191 for (var i in options) {
80192 var v = options[i];
80193 values.push(v === 'undefined' ? undefined : v);
80194 texts.push(field.t.html('options.' + v, {
80199 values = [undefined, 'yes'];
80200 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
80202 if (field.type !== 'defaultCheck') {
80204 texts.push(_t.html('inspector.check.no'));
80206 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
80209 function checkImpliedYes() {
80210 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
80211 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
80213 if (field.id === 'oneway') {
80214 var entity = context.entity(_entityIDs[0]);
80216 for (var key in entity.tags) {
80217 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
80218 _impliedYes = true;
80219 texts[0] = _t.html('_tagging.presets.fields.oneway_yes.options.undefined');
80226 function reverserHidden() {
80227 if (!context.container().select('div.inspector-hover').empty()) return true;
80228 return !(_value === 'yes' || _impliedYes && !_value);
80231 function reverserSetText(selection) {
80232 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
80233 if (reverserHidden() || !entity) return selection;
80234 var first = entity.first();
80235 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
80236 var pseudoDirection = first < last;
80237 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
80238 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
80242 var check = function check(selection) {
80244 label = selection.selectAll('.form-field-input-wrap').data([0]);
80245 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
80246 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
80247 enter.append('span').html(texts[0]).attr('class', 'value');
80249 if (field.type === 'onewayCheck') {
80250 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
80253 label = label.merge(enter);
80254 input = label.selectAll('input');
80255 text = label.selectAll('span.value');
80256 input.on('click', function (d3_event) {
80257 d3_event.stopPropagation();
80260 if (Array.isArray(_tags[field.key])) {
80261 if (values.indexOf('yes') !== -1) {
80262 t[field.key] = 'yes';
80264 t[field.key] = values[0];
80267 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
80268 } // Don't cycle through `alternating` or `reversible` states - #4970
80269 // (They are supported as translated strings, but should not toggle with clicks)
80272 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
80273 t[field.key] = values[0];
80276 dispatch.call('change', this, t);
80279 if (field.type === 'onewayCheck') {
80280 reverser = label.selectAll('.reverser');
80281 reverser.call(reverserSetText).on('click', function (d3_event) {
80282 d3_event.preventDefault();
80283 d3_event.stopPropagation();
80284 context.perform(function (graph) {
80285 for (var i in _entityIDs) {
80286 graph = actionReverse(_entityIDs[i])(graph);
80290 }, _t('operations.reverse.annotation.line', {
80292 })); // must manually revalidate since no 'change' event was called
80294 context.validator().validate();
80295 select(this).call(reverserSetText);
80300 check.entityIDs = function (val) {
80301 if (!arguments.length) return _entityIDs;
80306 check.tags = function (tags) {
80309 function isChecked(val) {
80310 return val !== 'no' && val !== '' && val !== undefined && val !== null;
80313 function textFor(val) {
80314 if (val === '') val = undefined;
80315 var index = values.indexOf(val);
80316 return index !== -1 ? texts[index] : '"' + val + '"';
80320 var isMixed = Array.isArray(tags[field.key]);
80321 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
80323 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
80327 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
80328 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
80329 label.classed('set', !!_value);
80331 if (field.type === 'onewayCheck') {
80332 reverser.classed('hide', reverserHidden()).call(reverserSetText);
80336 check.focus = function () {
80337 input.node().focus();
80340 return utilRebind(check, dispatch, 'on');
80343 function uiFieldCombo(field, context) {
80344 var dispatch = dispatch$8('change');
80346 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
80348 var _isNetwork = field.type === 'networkCombo';
80350 var _isSemi = field.type === 'semiCombo';
80352 var _optarray = field.options;
80354 var _showTagInfoSuggestions = field.type !== 'manyCombo' && field.autoSuggestions !== false;
80356 var _allowCustomValues = field.type !== 'manyCombo' && field.customValues !== false;
80358 var _snake_case = field.snake_case || field.snake_case === undefined;
80360 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
80362 var _container = select(null);
80364 var _inputWrap = select(null);
80366 var _input = select(null);
80368 var _comboData = [];
80369 var _multiData = [];
80370 var _entityIDs = [];
80376 var _staticPlaceholder; // initialize deprecated tags array
80379 var _dataDeprecated = [];
80380 _mainFileFetcher.get('deprecated').then(function (d) {
80381 _dataDeprecated = d;
80382 })["catch"](function () {
80384 }); // ensure multiCombo field.key ends with a ':'
80386 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
80390 function snake(s) {
80391 return s.replace(/\s+/g, '_').toLowerCase();
80394 function clean(s) {
80395 return s.split(';').map(function (s) {
80398 } // returns the tag value for a display value
80399 // (for multiCombo, dval should be the key suffix, not the entire key)
80402 function tagValue(dval) {
80403 dval = clean(dval || '');
80405 var found = _comboData.find(function (o) {
80406 return o.key && clean(o.value) === dval;
80409 if (found) return found.key;
80411 if (field.type === 'typeCombo' && !dval) {
80415 return (_snake_case ? snake(dval) : dval) || undefined;
80416 } // returns the display value for a tag value
80417 // (for multiCombo, tval should be the key suffix, not the entire key)
80420 function displayValue(tval) {
80423 if (field.hasTextForStringId('options.' + tval)) {
80424 return field.t('options.' + tval, {
80429 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
80434 } // Compute the difference between arrays of objects by `value` property
80436 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
80437 // > [{value:1}, {value:3}]
80441 function objectDifference(a, b) {
80442 return a.filter(function (d1) {
80443 return !b.some(function (d2) {
80444 return !d2.isMixed && d1.value === d2.value;
80449 function initCombo(selection, attachTo) {
80450 if (!_allowCustomValues) {
80451 selection.attr('readonly', 'readonly');
80454 if (_showTagInfoSuggestions && services.taginfo) {
80455 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
80456 setTaginfoValues('', setPlaceholder);
80458 selection.call(_combobox, attachTo);
80459 setStaticValues(setPlaceholder);
80463 function setStaticValues(callback) {
80464 if (!_optarray) return;
80465 _comboData = _optarray.map(function (v) {
80468 value: field.t('options.' + v, {
80472 display: field.t.html('options.' + v, {
80475 klass: field.hasTextForStringId('options.' + v) ? '' : 'raw-option'
80479 _combobox.data(objectDifference(_comboData, _multiData));
80481 if (callback) callback(_comboData);
80484 function setTaginfoValues(q, callback) {
80485 var fn = _isMulti ? 'multikeys' : 'values';
80486 var query = (_isMulti ? field.key : '') + q;
80487 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
80489 if (hasCountryPrefix) {
80490 query = _countryCode + ':';
80494 debounce: q !== '',
80499 if (_entityIDs.length) {
80500 params.geometry = context.graph().geometry(_entityIDs[0]);
80503 services.taginfo[fn](params, function (err, data) {
80505 data = data.filter(function (d) {
80506 if (field.type === 'typeCombo' && d.value === 'yes') {
80507 // don't show the fallback value
80509 } // don't show values with very low usage
80512 return !d.count || d.count > 10;
80514 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
80516 if (deprecatedValues) {
80517 // don't suggest deprecated tag values
80518 data = data.filter(function (d) {
80519 return deprecatedValues.indexOf(d.value) === -1;
80523 if (hasCountryPrefix) {
80524 data = data.filter(function (d) {
80525 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
80527 } // hide the caret if there are no suggestions
80530 _container.classed('empty-combobox', data.length === 0);
80532 _comboData = data.map(function (d) {
80534 if (_isMulti) k = k.replace(field.key, '');
80535 var label = field.t('options.' + k, {
80541 display: field.t.html('options.' + k, {
80544 title: d.title || label,
80545 klass: field.hasTextForStringId('options.' + k) ? '' : 'raw-option'
80548 _comboData = objectDifference(_comboData, _multiData);
80549 if (callback) callback(_comboData);
80553 function setPlaceholder(values) {
80554 if (_isMulti || _isSemi) {
80555 _staticPlaceholder = field.placeholder() || _t('inspector.add');
80557 var vals = values.map(function (d) {
80559 }).filter(function (s) {
80560 return s.length < 20;
80562 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
80565 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
80568 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
80569 _staticPlaceholder += '…';
80574 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
80575 ph = _t('inspector.multiple_values');
80577 ph = _staticPlaceholder;
80580 _container.selectAll('input').attr('placeholder', ph);
80583 function change() {
80587 if (_isMulti || _isSemi) {
80588 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
80590 _container.classed('active', false);
80592 utilGetSetValue(_input, '');
80593 var vals = val.split(';').filter(Boolean);
80594 if (!vals.length) return;
80597 utilArrayUniq(vals).forEach(function (v) {
80598 var key = (field.key || '') + v;
80601 // don't set a multicombo value to 'yes' if it already has a non-'no' value
80602 // e.g. `language:de=main`
80603 var old = _tags[key];
80604 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
80607 key = context.cleanTagKey(key);
80608 field.keys.push(key);
80611 } else if (_isSemi) {
80612 var arr = _multiData.map(function (d) {
80616 arr = arr.concat(vals);
80617 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
80620 window.setTimeout(function () {
80621 _input.node().focus();
80624 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
80626 if (!rawValue && Array.isArray(_tags[field.key])) return;
80627 val = context.cleanTagValue(tagValue(rawValue));
80628 t[field.key] = val || undefined;
80631 dispatch.call('change', this, t);
80634 function removeMultikey(d3_event, d) {
80635 d3_event.preventDefault();
80636 d3_event.stopPropagation();
80640 t[d.key] = undefined;
80641 } else if (_isSemi) {
80642 var arr = _multiData.map(function (md) {
80643 return md.key === d.key ? null : md.key;
80644 }).filter(Boolean);
80646 arr = utilArrayUniq(arr);
80647 t[field.key] = arr.length ? arr.join(';') : undefined;
80650 dispatch.call('change', this, t);
80653 function combo(selection) {
80654 _container = selection.selectAll('.form-field-input-wrap').data([0]);
80655 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
80656 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
80658 if (_isMulti || _isSemi) {
80659 _container = _container.selectAll('.chiplist').data([0]);
80660 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations and Via fields
80661 // to mimic highway exit signs
80663 if (field.key === 'destination' || field.key === 'via') {
80664 listClass += ' full-line-chips';
80667 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
80668 window.setTimeout(function () {
80669 _input.node().focus();
80671 }).merge(_container);
80672 _inputWrap = _container.selectAll('.input-wrap').data([0]);
80673 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
80674 _input = _inputWrap.selectAll('input').data([0]);
80676 _input = _container.selectAll('input').data([0]);
80679 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
80682 var extent = combinedEntityExtent();
80683 var countryCode = extent && iso1A2Code(extent.center());
80684 _countryCode = countryCode && countryCode.toLowerCase();
80687 _input.on('change', change).on('blur', change);
80689 _input.on('keydown.field', function (d3_event) {
80690 switch (d3_event.keyCode) {
80693 _input.node().blur(); // blurring also enters the value
80696 d3_event.stopPropagation();
80701 if (_isMulti || _isSemi) {
80702 _combobox.on('accept', function () {
80703 _input.node().blur();
80705 _input.node().focus();
80708 _input.on('focus', function () {
80709 _container.classed('active', true);
80714 combo.tags = function (tags) {
80717 if (_isMulti || _isSemi) {
80722 // Build _multiData array containing keys already set..
80723 for (var k in tags) {
80724 if (field.key && k.indexOf(field.key) !== 0) continue;
80725 if (!field.key && field.keys.indexOf(k) === -1) continue;
80727 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
80728 var suffix = field.key ? k.substr(field.key.length) : k;
80732 value: displayValue(suffix),
80733 isMixed: Array.isArray(v)
80738 // Set keys for form-field modified (needed for undo and reset buttons)..
80739 field.keys = _multiData.map(function (d) {
80741 }); // limit the input length so it fits after prepending the key prefix
80743 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
80745 maxLength = context.maxCharsForTagKey();
80747 } else if (_isSemi) {
80748 var allValues = [];
80751 if (Array.isArray(tags[field.key])) {
80752 tags[field.key].forEach(function (tagVal) {
80753 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
80754 allValues = allValues.concat(thisVals);
80756 if (!commonValues) {
80757 commonValues = thisVals;
80759 commonValues = commonValues.filter(function (value) {
80760 return thisVals.includes(value);
80764 allValues = utilArrayUniq(allValues).filter(Boolean);
80766 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
80767 commonValues = allValues;
80770 _multiData = allValues.map(function (v) {
80773 value: displayValue(v),
80774 isMixed: !commonValues.includes(v)
80777 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
80779 maxLength = context.maxCharsForTagValue() - currLength;
80781 if (currLength > 0) {
80782 // account for the separator if a new value will be appended to existing
80785 } // a negative maxlength doesn't make sense
80788 maxLength = Math.max(0, maxLength);
80789 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
80790 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
80792 var available = objectDifference(_comboData, _multiData);
80794 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
80795 // options and they're all currently used,
80796 // or if the field is already at its character limit
80799 var hideAdd = !_allowCustomValues && !available.length || maxLength <= 0;
80801 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
80804 var chips = _container.selectAll('.chip').data(_multiData);
80806 chips.exit().remove();
80807 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
80808 enter.append('span');
80810 chips = chips.merge(enter).order().classed('raw-value', function (d) {
80812 if (_isMulti) k = k.replace(field.key, '');
80813 return !field.hasTextForStringId('options.' + k);
80814 }).classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
80816 }).attr('title', function (d) {
80817 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
80820 if (allowDragAndDrop) {
80821 registerDragAndDrop(chips);
80824 chips.select('span').html(function (d) {
80827 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
80829 var isMixed = Array.isArray(tags[field.key]);
80830 var mixedValues = isMixed && tags[field.key].map(function (val) {
80831 return displayValue(val);
80832 }).filter(Boolean);
80833 var showsValue = !isMixed && tags[field.key] && !(field.type === 'typeCombo' && tags[field.key] === 'yes');
80834 var isRawValue = showsValue && !field.hasTextForStringId('options.' + tags[field.key]);
80835 var isKnownValue = showsValue && !isRawValue;
80836 var isReadOnly = !_allowCustomValues || isKnownValue;
80837 utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '').classed('raw-value', isRawValue).classed('known-value', isKnownValue).attr('readonly', isReadOnly ? 'readonly' : undefined).attr('title', isMixed ? mixedValues.join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '').classed('mixed', isMixed).on('keydown.deleteCapture', function (d3_event) {
80838 if (isReadOnly && isKnownValue && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
80839 d3_event.preventDefault();
80840 d3_event.stopPropagation();
80842 t[field.key] = undefined;
80843 dispatch.call('change', this, t);
80849 function registerDragAndDrop(selection) {
80850 // allow drag and drop re-ordering of chips
80851 var dragOrigin, targetIndex;
80852 selection.call(d3_drag().on('start', function (d3_event) {
80857 targetIndex = null;
80858 }).on('drag', function (d3_event) {
80859 var x = d3_event.x - dragOrigin.x,
80860 y = d3_event.y - dragOrigin.y;
80861 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
80862 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
80863 var index = selection.nodes().indexOf(this);
80864 select(this).classed('dragging', true);
80865 targetIndex = null;
80866 var targetIndexOffsetTop = null;
80867 var draggedTagWidth = select(this).node().offsetWidth;
80869 if (field.key === 'destination' || field.key === 'via') {
80870 // meaning tags are full width
80871 _container.selectAll('.chip').style('transform', function (d2, index2) {
80872 var node = select(this).node();
80874 if (index === index2) {
80875 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
80876 } else if (index2 > index && d3_event.y > node.offsetTop) {
80877 if (targetIndex === null || index2 > targetIndex) {
80878 targetIndex = index2;
80881 return 'translateY(-100%)'; // move the dragged tag down the order
80882 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
80883 if (targetIndex === null || index2 < targetIndex) {
80884 targetIndex = index2;
80887 return 'translateY(100%)';
80893 _container.selectAll('.chip').each(function (d2, index2) {
80894 var node = select(this).node(); // check the cursor is in the bounding box
80896 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) {
80897 targetIndex = index2;
80898 targetIndexOffsetTop = node.offsetTop;
80900 }).style('transform', function (d2, index2) {
80901 var node = select(this).node();
80903 if (index === index2) {
80904 return 'translate(' + x + 'px, ' + y + 'px)';
80905 } // only translate tags in the same row
80908 if (node.offsetTop === targetIndexOffsetTop) {
80909 if (index2 < index && index2 >= targetIndex) {
80910 return 'translateX(' + draggedTagWidth + 'px)';
80911 } else if (index2 > index && index2 <= targetIndex) {
80912 return 'translateX(-' + draggedTagWidth + 'px)';
80919 }).on('end', function () {
80920 if (!select(this).classed('dragging')) {
80924 var index = selection.nodes().indexOf(this);
80925 select(this).classed('dragging', false);
80927 _container.selectAll('.chip').style('transform', null);
80929 if (typeof targetIndex === 'number') {
80930 var element = _multiData[index];
80932 _multiData.splice(index, 1);
80934 _multiData.splice(targetIndex, 0, element);
80938 if (_multiData.length) {
80939 t[field.key] = _multiData.map(function (element) {
80940 return element.key;
80943 t[field.key] = undefined;
80946 dispatch.call('change', this, t);
80949 dragOrigin = undefined;
80950 targetIndex = undefined;
80954 combo.focus = function () {
80955 _input.node().focus();
80958 combo.entityIDs = function (val) {
80959 if (!arguments.length) return _entityIDs;
80964 function combinedEntityExtent() {
80965 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
80968 return utilRebind(combo, dispatch, 'on');
80971 function uiFieldText(field, context) {
80972 var dispatch = dispatch$8('change');
80973 var input = select(null);
80974 var outlinkButton = select(null);
80975 var _entityIDs = [];
80979 var _phoneFormats = {};
80981 if (field.type === 'tel') {
80982 _mainFileFetcher.get('phone_formats').then(function (d) {
80984 updatePhonePlaceholder();
80985 })["catch"](function () {
80990 function calcLocked() {
80991 // Protect certain fields that have a companion `*:wikidata` value
80992 var isLocked = (field.id === 'brand' || field.id === 'network' || field.id === 'operator' || field.id === 'flag') && _entityIDs.length && _entityIDs.some(function (entityID) {
80993 var entity = context.graph().hasEntity(entityID);
80994 if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
80996 if (entity.tags.wikidata) return true;
80997 var preset = _mainPresetIndex.match(entity, context.graph());
80998 var isSuggestion = preset && preset.suggestion; // Lock the field if there is a value and a companion `*:wikidata` value
81000 var which = field.id; // 'brand', 'network', 'operator', 'flag'
81002 return isSuggestion && !!entity.tags[which] && !!entity.tags[which + ':wikidata'];
81005 field.locked(isLocked);
81008 function i(selection) {
81010 var isLocked = field.locked();
81011 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81012 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
81013 input = wrap.selectAll('input').data([0]);
81014 input = input.enter().append('input').attr('type', field.type === 'identifier' || field.type === 'roadheight' ? 'text' : field.type).attr('id', field.domId).classed(field.type, true).call(utilNoAuto).merge(input);
81015 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
81017 if (field.type === 'tel') {
81018 updatePhonePlaceholder();
81019 } else if (field.type === 'number') {
81020 var rtl = _mainLocalizer.textDirection() === 'rtl';
81021 input.attr('type', 'text');
81022 var inc = field.increment;
81023 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
81024 buttons.enter().append('button').attr('class', function (d) {
81025 var which = d > 0 ? 'increment' : 'decrement';
81026 return 'form-field-button ' + which;
81027 }).merge(buttons).on('click', function (d3_event, d) {
81028 d3_event.preventDefault();
81029 var raw_vals = input.node().value || '0';
81030 var vals = raw_vals.split(';');
81031 vals = vals.map(function (v) {
81032 var num = parseFloat(v.trim(), 10);
81033 return isFinite(num) ? clamped(num + d) : v.trim();
81035 input.node().value = vals.join(';');
81038 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
81039 input.attr('type', 'text');
81040 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
81041 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
81042 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
81044 if (domainResults.length >= 2 && domainResults[1]) {
81045 var domain = domainResults[1];
81046 return _t('icons.view_on', {
81052 }).on('click', function (d3_event) {
81053 d3_event.preventDefault();
81054 var value = validIdentifierValueForLink();
81057 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
81058 window.open(url, '_blank');
81060 }).merge(outlinkButton);
81064 function updatePhonePlaceholder() {
81065 if (input.empty() || !Object.keys(_phoneFormats).length) return;
81066 var extent = combinedEntityExtent();
81067 var countryCode = extent && iso1A2Code(extent.center());
81069 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
81071 if (format) input.attr('placeholder', format);
81074 function validIdentifierValueForLink() {
81075 if (field.type === 'identifier' && field.pattern) {
81076 var value = utilGetSetValue(input).trim().split(';')[0];
81077 return value && value.match(new RegExp(field.pattern));
81081 } // clamp number to min/max
81084 function clamped(num) {
81085 if (field.minValue !== undefined) {
81086 num = Math.max(num, field.minValue);
81089 if (field.maxValue !== undefined) {
81090 num = Math.min(num, field.maxValue);
81096 function change(onInput) {
81097 return function () {
81099 var val = utilGetSetValue(input);
81100 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
81102 if (!val && Array.isArray(_tags[field.key])) return;
81105 if (field.type === 'number' && val) {
81106 var vals = val.split(';');
81107 vals = vals.map(function (v) {
81108 var num = parseFloat(v.trim(), 10);
81109 return isFinite(num) ? clamped(num) : v.trim();
81111 val = vals.join(';');
81114 utilGetSetValue(input, val);
81117 t[field.key] = val || undefined;
81118 dispatch.call('change', this, t, onInput);
81122 i.entityIDs = function (val) {
81123 if (!arguments.length) return _entityIDs;
81128 i.tags = function (tags) {
81130 var isMixed = Array.isArray(tags[field.key]);
81131 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);
81133 if (outlinkButton && !outlinkButton.empty()) {
81134 var disabled = !validIdentifierValueForLink();
81135 outlinkButton.classed('disabled', disabled);
81139 i.focus = function () {
81140 var node = input.node();
81141 if (node) node.focus();
81144 function combinedEntityExtent() {
81145 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
81148 return utilRebind(i, dispatch, 'on');
81151 function uiFieldAccess(field, context) {
81152 var dispatch = dispatch$8('change');
81153 var items = select(null);
81157 function access(selection) {
81158 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81159 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
81160 var list = wrap.selectAll('ul').data([0]);
81161 list = list.enter().append('ul').attr('class', 'rows').merge(list);
81162 items = list.selectAll('li').data(field.keys); // Enter
81164 var enter = items.enter().append('li').attr('class', function (d) {
81165 return 'labeled-input preset-access-' + d;
81167 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
81168 return 'preset-input-access-' + d;
81169 }).html(function (d) {
81170 return field.t.html('types.' + d);
81172 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
81173 return 'preset-input-access preset-input-access-' + d;
81174 }).call(utilNoAuto).each(function (d) {
81175 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
81178 items = items.merge(enter);
81179 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
81182 function change(d3_event, d) {
81184 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
81186 if (!value && typeof _tags[d] !== 'string') return;
81187 tag[d] = value || undefined;
81188 dispatch.call('change', this, tag);
81191 access.options = function (type) {
81192 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
81194 if (type !== 'access') {
81195 options.unshift('yes');
81196 options.push('designated');
81198 if (type === 'bicycle') {
81199 options.push('dismount');
81203 return options.map(function (option) {
81205 title: field.t('options.' + option + '.description'),
81211 var placeholdersByHighway = {
81213 foot: 'designated',
81214 motor_vehicle: 'no'
81218 motor_vehicle: 'no',
81224 motor_vehicle: 'no'
81227 motor_vehicle: 'no',
81228 bicycle: 'designated'
81231 motor_vehicle: 'no',
81232 horse: 'designated'
81236 motor_vehicle: 'no',
81242 motor_vehicle: 'yes',
81247 motor_vehicle: 'yes'
81251 motor_vehicle: 'yes',
81257 motor_vehicle: 'yes',
81263 motor_vehicle: 'yes',
81269 motor_vehicle: 'yes',
81275 motor_vehicle: 'yes',
81281 motor_vehicle: 'yes',
81287 motor_vehicle: 'yes',
81292 motor_vehicle: 'yes'
81296 motor_vehicle: 'yes',
81302 motor_vehicle: 'yes',
81308 motor_vehicle: 'yes',
81314 access.tags = function (tags) {
81316 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
81317 return typeof tags[d] === 'string' ? tags[d] : '';
81318 }).classed('mixed', function (d) {
81319 return tags[d] && Array.isArray(tags[d]);
81320 }).attr('title', function (d) {
81321 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
81322 }).attr('placeholder', function (d) {
81323 if (tags[d] && Array.isArray(tags[d])) {
81324 return _t('inspector.multiple_values');
81327 if (d === 'access') {
81331 if (tags.access && typeof tags.access === 'string') {
81332 return tags.access;
81335 if (tags.highway) {
81336 if (typeof tags.highway === 'string') {
81337 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
81338 return placeholdersByHighway[tags.highway][d];
81341 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
81342 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
81343 }).filter(Boolean);
81345 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
81346 // if all the highway values have the same implied access for this type then use that
81347 return impliedAccesses[0];
81352 return field.placeholder();
81356 access.focus = function () {
81357 items.selectAll('.preset-input-access').node().focus();
81360 return utilRebind(access, dispatch, 'on');
81363 function uiFieldAddress(field, context) {
81364 var dispatch = dispatch$8('change');
81366 var _selection = select(null);
81368 var _wrap = select(null);
81370 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
81372 var _entityIDs = [];
81378 var _addressFormats = [{
81379 format: [['housenumber', 'street'], ['city', 'postcode']]
81381 _mainFileFetcher.get('address_formats').then(function (d) {
81382 _addressFormats = d;
81384 if (!_selection.empty()) {
81385 _selection.call(address);
81387 })["catch"](function () {
81391 function getNearStreets() {
81392 var extent = combinedEntityExtent();
81393 var l = extent.center();
81394 var box = geoExtent(l).padByMeters(200);
81395 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
81396 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
81397 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
81399 title: d.tags.name,
81400 value: d.tags.name,
81401 dist: choice.distance
81403 }).sort(function (a, b) {
81404 return a.dist - b.dist;
81406 return utilArrayUniqBy(streets, 'value');
81408 function isAddressable(d) {
81409 return d.tags.highway && d.tags.name && d.type === 'way';
81413 function getNearCities() {
81414 var extent = combinedEntityExtent();
81415 var l = extent.center();
81416 var box = geoExtent(l).padByMeters(200);
81417 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
81419 title: d.tags['addr:city'] || d.tags.name,
81420 value: d.tags['addr:city'] || d.tags.name,
81421 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
81423 }).sort(function (a, b) {
81424 return a.dist - b.dist;
81426 return utilArrayUniqBy(cities, 'value');
81428 function isAddressable(d) {
81430 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
81431 if (d.tags.border_type === 'city') return true;
81432 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
81435 if (d.tags['addr:city']) return true;
81440 function getNearValues(key) {
81441 var extent = combinedEntityExtent();
81442 var l = extent.center();
81443 var box = geoExtent(l).padByMeters(200);
81444 var results = context.history().intersects(box).filter(function hasTag(d) {
81445 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
81446 }).map(function (d) {
81448 title: d.tags[key],
81449 value: d.tags[key],
81450 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
81452 }).sort(function (a, b) {
81453 return a.dist - b.dist;
81455 return utilArrayUniqBy(results, 'value');
81458 function updateForCountryCode() {
81459 if (!_countryCode) return;
81462 for (var i = 0; i < _addressFormats.length; i++) {
81463 var format = _addressFormats[i];
81465 if (!format.countryCodes) {
81466 addressFormat = format; // choose the default format, keep going
81467 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
81468 addressFormat = format; // choose the country format, stop here
81474 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
81475 var widths = addressFormat.widths || {
81476 housenumber: 1 / 3,
81484 // Normalize widths.
81485 var total = r.reduce(function (sum, key) {
81486 return sum + (widths[key] || 0.5);
81488 return r.map(function (key) {
81491 width: (widths[key] || 0.5) / total
81496 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
81497 return d.toString();
81500 rows.exit().remove();
81501 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
81502 return 'addr-' + d.id;
81503 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
81504 return d.width * 100 + '%';
81507 function addDropdown(d) {
81508 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
81510 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
81511 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
81512 callback(nearValues('addr:' + d.id));
81516 _wrap.selectAll('input').on('blur', change()).on('change', change());
81518 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
81520 if (_tags) updateTags(_tags);
81523 function address(selection) {
81524 _selection = selection;
81525 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81526 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
81527 var extent = combinedEntityExtent();
81532 if (context.inIntro()) {
81533 // localize the address format for the walkthrough
81534 countryCode = _t('intro.graph.countrycode');
81536 var center = extent.center();
81537 countryCode = iso1A2Code(center);
81541 _countryCode = countryCode.toLowerCase();
81542 updateForCountryCode();
81547 function change(onInput) {
81548 return function () {
81551 _wrap.selectAll('input').each(function (subfield) {
81552 var key = field.key + ':' + subfield.id;
81553 var value = this.value;
81554 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
81556 if (Array.isArray(_tags[key]) && !value) return;
81557 tags[key] = value || undefined;
81560 dispatch.call('change', this, tags, onInput);
81564 function updatePlaceholder(inputSelection) {
81565 return inputSelection.attr('placeholder', function (subfield) {
81566 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
81567 return _t('inspector.multiple_values');
81570 if (_countryCode) {
81571 var localkey = subfield.id + '!' + _countryCode;
81572 var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : subfield.id;
81573 return addrField.t('placeholders.' + tkey);
81578 function updateTags(tags) {
81579 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
81580 var val = tags[field.key + ':' + subfield.id];
81581 return typeof val === 'string' ? val : '';
81582 }).attr('title', function (subfield) {
81583 var val = tags[field.key + ':' + subfield.id];
81584 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
81585 }).classed('mixed', function (subfield) {
81586 return Array.isArray(tags[field.key + ':' + subfield.id]);
81587 }).call(updatePlaceholder);
81590 function combinedEntityExtent() {
81591 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
81594 address.entityIDs = function (val) {
81595 if (!arguments.length) return _entityIDs;
81600 address.tags = function (tags) {
81605 address.focus = function () {
81606 var node = _wrap.selectAll('input').node();
81608 if (node) node.focus();
81611 return utilRebind(address, dispatch, 'on');
81614 function uiFieldCycleway(field, context) {
81615 var dispatch = dispatch$8('change');
81616 var items = select(null);
81617 var wrap = select(null);
81621 function cycleway(selection) {
81622 function stripcolon(s) {
81623 return s.replace(':', '');
81626 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81627 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
81628 var div = wrap.selectAll('ul').data([0]);
81629 div = div.enter().append('ul').attr('class', 'rows').merge(div);
81630 var keys = ['cycleway:left', 'cycleway:right'];
81631 items = div.selectAll('li').data(keys);
81632 var enter = items.enter().append('li').attr('class', function (d) {
81633 return 'labeled-input preset-cycleway-' + stripcolon(d);
81635 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
81636 return 'preset-input-cycleway-' + stripcolon(d);
81637 }).html(function (d) {
81638 return field.t.html('types.' + d);
81640 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
81641 return 'preset-input-cycleway preset-input-' + stripcolon(d);
81642 }).call(utilNoAuto).each(function (d) {
81643 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
81645 items = items.merge(enter); // Update
81647 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
81650 function change(d3_event, key) {
81651 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
81653 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
81655 if (newValue === 'none' || newValue === '') {
81656 newValue = undefined;
81659 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
81660 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
81662 if (otherValue && Array.isArray(otherValue)) {
81663 // we must always have an explicit value for comparison
81664 otherValue = otherValue[0];
81667 if (otherValue === 'none' || otherValue === '') {
81668 otherValue = undefined;
81671 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
81672 // sides the same way
81674 if (newValue === otherValue) {
81676 cycleway: newValue,
81677 'cycleway:left': undefined,
81678 'cycleway:right': undefined
81681 // Always set both left and right as changing one can affect the other
81683 cycleway: undefined
81685 tag[key] = newValue;
81686 tag[otherKey] = otherValue;
81689 dispatch.call('change', this, tag);
81692 cycleway.options = function () {
81693 return field.options.map(function (option) {
81695 title: field.t('options.' + option + '.description'),
81701 cycleway.tags = function (tags) {
81702 _tags = tags; // If cycleway is set, use that instead of individual values
81704 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
81705 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
81706 if (commonValue) return commonValue;
81707 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
81708 }).attr('title', function (d) {
81709 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
81712 if (Array.isArray(tags.cycleway)) {
81713 vals = vals.concat(tags.cycleway);
81716 if (Array.isArray(tags[d])) {
81717 vals = vals.concat(tags[d]);
81720 return vals.filter(Boolean).join('\n');
81724 }).attr('placeholder', function (d) {
81725 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
81726 return _t('inspector.multiple_values');
81729 return field.placeholder();
81730 }).classed('mixed', function (d) {
81731 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
81735 cycleway.focus = function () {
81736 var node = wrap.selectAll('input').node();
81737 if (node) node.focus();
81740 return utilRebind(cycleway, dispatch, 'on');
81743 function uiFieldLanes(field, context) {
81744 var dispatch = dispatch$8('change');
81745 var LANE_WIDTH = 40;
81746 var LANE_HEIGHT = 200;
81747 var _entityIDs = [];
81749 function lanes(selection) {
81750 var lanesData = context.entity(_entityIDs[0]).lanes();
81752 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
81753 selection.call(lanes.off);
81757 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81758 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
81759 var surface = wrap.selectAll('.surface').data([0]);
81760 var d = utilGetDimensions(wrap);
81761 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
81762 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
81763 var lanesSelection = surface.selectAll('.lanes').data([0]);
81764 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
81765 lanesSelection.attr('transform', function () {
81766 return 'translate(' + freeSpace / 2 + ', 0)';
81768 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
81769 lane.exit().remove();
81770 var enter = lane.enter().append('g').attr('class', 'lane');
81771 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
81772 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
81773 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
81774 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
81775 lane = lane.merge(enter);
81776 lane.attr('transform', function (d) {
81777 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
81779 lane.select('.forward').style('visibility', function (d) {
81780 return d.direction === 'forward' ? 'visible' : 'hidden';
81782 lane.select('.bothways').style('visibility', function (d) {
81783 return d.direction === 'bothways' ? 'visible' : 'hidden';
81785 lane.select('.backward').style('visibility', function (d) {
81786 return d.direction === 'backward' ? 'visible' : 'hidden';
81790 lanes.entityIDs = function (val) {
81794 lanes.tags = function () {};
81796 lanes.focus = function () {};
81798 lanes.off = function () {};
81800 return utilRebind(lanes, dispatch, 'on');
81802 uiFieldLanes.supportsMultiselection = false;
81804 var _languagesArray = [];
81805 function uiFieldLocalized(field, context) {
81806 var dispatch = dispatch$8('change', 'input');
81807 var wikipedia = services.wikipedia;
81808 var input = select(null);
81809 var localizedInputs = select(null);
81813 var _tags; // A concern here in switching to async data means that _languagesArray will not
81814 // be available the first time through, so things like the fetchers and
81815 // the language() function will not work immediately.
81818 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
81821 var _territoryLanguages = {};
81822 _mainFileFetcher.get('territory_languages').then(function (d) {
81823 _territoryLanguages = d;
81824 })["catch"](function () {
81826 }); // reuse these combos
81828 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
81830 var _selection = select(null);
81832 var _multilingual = [];
81834 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
81838 var _entityIDs = [];
81840 function loadLanguagesArray(dataLanguages) {
81841 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
81843 var replacements = {
81845 // in OSM, `sr` implies Cyrillic
81846 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
81850 for (var code in dataLanguages) {
81851 if (replacements[code] === false) continue;
81852 var metaCode = code;
81853 if (replacements[code]) metaCode = replacements[code];
81855 _languagesArray.push({
81856 localName: _mainLocalizer.languageName(metaCode, {
81859 nativeName: dataLanguages[metaCode].nativeName,
81861 label: _mainLocalizer.languageName(metaCode)
81866 function calcLocked() {
81867 // Protect name field for suggestion presets that don't display a brand/operator field
81868 var isLocked = field.id === 'name' && _entityIDs.length && _entityIDs.some(function (entityID) {
81869 var entity = context.graph().hasEntity(entityID);
81870 if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
81872 if (entity.tags.wikidata) return true; // Assume the name has already been confirmed if its source has been researched
81874 if (entity.tags['name:etymology:wikidata']) return true; // Lock the `name` if this is a suggestion preset that assigns the name,
81875 // and the preset does not display a `brand` or `operator` field.
81876 // (For presets like hotels, car dealerships, post offices, the `name` should remain editable)
81877 // see also similar logic in `outdated_tags.js`
81879 var preset = _mainPresetIndex.match(entity, context.graph());
81882 var isSuggestion = preset.suggestion;
81883 var fields = preset.fields();
81884 var showsBrandField = fields.some(function (d) {
81885 return d.id === 'brand';
81887 var showsOperatorField = fields.some(function (d) {
81888 return d.id === 'operator';
81890 var setsName = preset.addTags.name;
81891 var setsBrandWikidata = preset.addTags['brand:wikidata'];
81892 var setsOperatorWikidata = preset.addTags['operator:wikidata'];
81893 return isSuggestion && setsName && (setsBrandWikidata && !showsBrandField || setsOperatorWikidata && !showsOperatorField);
81899 field.locked(isLocked);
81900 } // update _multilingual, maintaining the existing order
81903 function calcMultilingual(tags) {
81904 var existingLangsOrdered = _multilingual.map(function (item) {
81908 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
81910 for (var k in tags) {
81911 var m = k.match(/^(.*):(.*)$/);
81913 if (m && m[1] === field.key && m[2]) {
81919 if (existingLangs.has(item.lang)) {
81920 // update the value
81921 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
81922 existingLangs["delete"](item.lang);
81924 _multilingual.push(item);
81927 } // Don't remove items based on deleted tags, since this makes the UI
81928 // disappear unexpectedly when clearing values - #8164
81931 _multilingual.forEach(function (item) {
81932 if (item.lang && existingLangs.has(item.lang)) {
81938 function localized(selection) {
81939 _selection = selection;
81941 var isLocked = field.locked();
81942 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
81944 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
81945 input = wrap.selectAll('.localized-main').data([0]); // enter/update
81947 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
81948 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
81949 var translateButton = wrap.selectAll('.localized-add').data([0]);
81950 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
81951 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
81953 if (_tags && !_multilingual.length) {
81954 calcMultilingual(_tags);
81957 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
81958 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
81959 localizedInputs.call(renderMultilingual);
81960 localizedInputs.selectAll('button, input').classed('disabled', !!isLocked).attr('readonly', isLocked || null);
81962 function addNew(d3_event) {
81963 d3_event.preventDefault();
81964 if (field.locked()) return;
81965 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
81967 var langExists = _multilingual.find(function (datum) {
81968 return datum.lang === defaultLang;
81971 var isLangEn = defaultLang.indexOf('en') > -1;
81973 if (isLangEn || langExists) {
81975 langExists = _multilingual.find(function (datum) {
81976 return datum.lang === defaultLang;
81981 // prepend the value so it appears at the top
81982 _multilingual.unshift({
81987 localizedInputs.call(renderMultilingual);
81991 function change(onInput) {
81992 return function (d3_event) {
81993 if (field.locked()) {
81994 d3_event.preventDefault();
81998 var val = utilGetSetValue(select(this));
81999 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
82001 if (!val && Array.isArray(_tags[field.key])) return;
82003 t[field.key] = val || undefined;
82004 dispatch.call('change', this, t, onInput);
82009 function key(lang) {
82010 return field.key + ':' + lang;
82013 function changeLang(d3_event, d) {
82014 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
82016 var lang = utilGetSetValue(select(this)).toLowerCase();
82018 var language = _languagesArray.find(function (d) {
82019 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
82022 if (language) lang = language.code;
82024 if (d.lang && d.lang !== lang) {
82025 tags[key(d.lang)] = undefined;
82028 var newKey = lang && context.cleanTagKey(key(lang));
82029 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
82031 if (newKey && value) {
82032 tags[newKey] = value;
82033 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
82034 tags[newKey] = _wikiTitles[d.lang];
82038 dispatch.call('change', this, tags);
82041 function changeValue(d3_event, d) {
82042 if (!d.lang) return;
82043 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
82045 if (!value && Array.isArray(d.value)) return;
82047 t[key(d.lang)] = value;
82049 dispatch.call('change', this, t);
82052 function fetchLanguages(value, cb) {
82053 var v = value.toLowerCase(); // show the user's language first
82055 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
82057 if (_countryCode && _territoryLanguages[_countryCode]) {
82058 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
82061 var langItems = [];
82062 langCodes.forEach(function (code) {
82063 var langItem = _languagesArray.find(function (item) {
82064 return item.code === code;
82067 if (langItem) langItems.push(langItem);
82069 langItems = utilArrayUniq(langItems.concat(_languagesArray));
82070 cb(langItems.filter(function (d) {
82071 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;
82072 }).map(function (d) {
82079 function renderMultilingual(selection) {
82080 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
82083 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
82084 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
82085 var wrap = select(this);
82086 var domId = utilUniqueDomId(index);
82087 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
82088 var text = label.append('span').attr('class', 'label-text');
82089 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
82090 text.append('span').attr('class', 'label-textannotation');
82091 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
82092 if (field.locked()) return;
82093 d3_event.preventDefault(); // remove the UI item manually
82095 _multilingual.splice(_multilingual.indexOf(d), 1);
82097 var langKey = d.lang && key(d.lang);
82099 if (langKey && langKey in _tags) {
82100 delete _tags[langKey]; // remove from entity tags
82103 t[langKey] = undefined;
82104 dispatch.call('change', this, t);
82108 renderMultilingual(selection);
82109 }).call(svgIcon('#iD-operation-delete'));
82110 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);
82111 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
82113 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 () {
82114 select(this).style('max-height', '').style('overflow', 'visible');
82116 entries = entries.merge(entriesEnter);
82117 entries.order(); // allow removing the entry UIs even if there isn't a tag to remove
82119 entries.classed('present', true);
82120 utilGetSetValue(entries.select('.localized-lang'), function (d) {
82121 var langItem = _languagesArray.find(function (item) {
82122 return item.code === d.lang;
82125 if (langItem) return langItem.label;
82128 utilGetSetValue(entries.select('.localized-value'), function (d) {
82129 return typeof d.value === 'string' ? d.value : '';
82130 }).attr('title', function (d) {
82131 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
82132 }).attr('placeholder', function (d) {
82133 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
82134 }).classed('mixed', function (d) {
82135 return Array.isArray(d.value);
82139 localized.tags = function (tags) {
82140 _tags = tags; // Fetch translations from wikipedia
82142 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
82144 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
82146 if (wm && wm[0] && wm[1]) {
82147 wikipedia.translations(wm[1], wm[2], function (err, d) {
82148 if (err || !d) return;
82154 var isMixed = Array.isArray(tags[field.key]);
82155 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);
82156 calcMultilingual(tags);
82158 _selection.call(localized);
82161 localized.focus = function () {
82162 input.node().focus();
82165 localized.entityIDs = function (val) {
82166 if (!arguments.length) return _entityIDs;
82168 _multilingual = [];
82173 function loadCountryCode() {
82174 var extent = combinedEntityExtent();
82175 var countryCode = extent && iso1A2Code(extent.center());
82176 _countryCode = countryCode && countryCode.toLowerCase();
82179 function combinedEntityExtent() {
82180 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
82183 return utilRebind(localized, dispatch, 'on');
82186 function uiFieldRoadspeed(field, context) {
82187 var dispatch = dispatch$8('change');
82188 var unitInput = select(null);
82189 var input = select(null);
82190 var _entityIDs = [];
82196 var speedCombo = uiCombobox(context, 'roadspeed');
82197 var unitCombo = uiCombobox(context, 'roadspeed-unit').data(['km/h', 'mph'].map(comboValues));
82198 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
82199 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
82201 function roadspeed(selection) {
82202 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
82203 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
82204 input = wrap.selectAll('input.roadspeed-number').data([0]);
82205 input = input.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
82206 input.on('change', change).on('blur', change);
82207 var loc = combinedEntityExtent().center();
82208 _isImperial = roadSpeedUnit(loc) === 'mph';
82209 unitInput = wrap.selectAll('input.roadspeed-unit').data([0]);
82210 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-unit').call(unitCombo).merge(unitInput);
82211 unitInput.on('blur', changeUnits).on('change', changeUnits);
82213 function changeUnits() {
82214 _isImperial = utilGetSetValue(unitInput) === 'mph';
82215 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
82216 setUnitSuggestions();
82221 function setUnitSuggestions() {
82222 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
82223 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
82226 function comboValues(d) {
82228 value: d.toString(),
82229 title: d.toString()
82233 function change() {
82235 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
82237 if (!value && Array.isArray(_tags[field.key])) return;
82240 tag[field.key] = undefined;
82241 } else if (isNaN(value) || !_isImperial) {
82242 tag[field.key] = context.cleanTagValue(value);
82244 tag[field.key] = context.cleanTagValue(value + ' mph');
82247 dispatch.call('change', this, tag);
82250 roadspeed.tags = function (tags) {
82252 var value = tags[field.key];
82253 var isMixed = Array.isArray(value);
82256 if (value && value.indexOf('mph') >= 0) {
82257 value = parseInt(value, 10).toString();
82258 _isImperial = true;
82259 } else if (value) {
82260 _isImperial = false;
82264 setUnitSuggestions();
82265 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);
82268 roadspeed.focus = function () {
82269 input.node().focus();
82272 roadspeed.entityIDs = function (val) {
82276 function combinedEntityExtent() {
82277 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
82280 return utilRebind(roadspeed, dispatch, 'on');
82283 function uiFieldRadio(field, context) {
82284 var dispatch = dispatch$8('change');
82285 var placeholder = select(null);
82286 var wrap = select(null);
82287 var labels = select(null);
82288 var radios = select(null);
82289 var radioData = (field.options || field.keys).slice(); // shallow copy
82294 var _entityIDs = [];
82296 function selectedKey() {
82297 var node = wrap.selectAll('.form-field-input-radio label.active input');
82298 return !node.empty() && node.datum();
82301 function radio(selection) {
82302 selection.classed('preset-radio', true);
82303 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
82304 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
82305 enter.append('span').attr('class', 'placeholder');
82306 wrap = wrap.merge(enter);
82307 placeholder = wrap.selectAll('.placeholder');
82308 labels = wrap.selectAll('label').data(radioData);
82309 enter = labels.enter().append('label');
82310 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
82311 return field.t('options.' + d, {
82314 }).attr('checked', false);
82315 enter.append('span').html(function (d) {
82316 return field.t.html('options.' + d, {
82320 labels = labels.merge(enter);
82321 radios = labels.selectAll('input').on('change', changeRadio);
82324 function structureExtras(selection, tags) {
82325 var selected = selectedKey() || tags.layer !== undefined;
82326 var type = _mainPresetIndex.field(selected);
82327 var layer = _mainPresetIndex.field('layer');
82328 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
82329 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
82330 extrasWrap.exit().remove();
82331 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
82332 var list = extrasWrap.selectAll('ul').data([0]);
82333 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
82336 if (!typeField || typeField.id !== selected) {
82337 typeField = uiField(context, type, _entityIDs, {
82339 }).on('change', changeType);
82342 typeField.tags(tags);
82347 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
82351 typeItem.exit().remove(); // Enter
82353 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
82354 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
82355 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
82357 typeItem = typeItem.merge(typeEnter);
82360 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
82364 if (layer && showLayer) {
82366 layerField = uiField(context, layer, _entityIDs, {
82368 }).on('change', changeLayer);
82371 layerField.tags(tags);
82372 field.keys = utilArrayUnion(field.keys, ['layer']);
82375 field.keys = field.keys.filter(function (k) {
82376 return k !== 'layer';
82380 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
82382 layerItem.exit().remove(); // Enter
82384 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
82385 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
82386 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
82388 layerItem = layerItem.merge(layerEnter);
82391 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
82395 function changeType(t, onInput) {
82396 var key = selectedKey();
82400 if (val !== 'no') {
82401 _oldType[key] = val;
82404 if (field.type === 'structureRadio') {
82405 // remove layer if it should not be set
82406 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
82407 t.layer = undefined;
82408 } // add layer if it should be set
82411 if (t.layer === undefined) {
82412 if (key === 'bridge' && val !== 'no') {
82416 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
82422 dispatch.call('change', this, t, onInput);
82425 function changeLayer(t, onInput) {
82426 if (t.layer === '0') {
82427 t.layer = undefined;
82430 dispatch.call('change', this, t, onInput);
82433 function changeRadio() {
82438 t[field.key] = undefined;
82441 radios.each(function (d) {
82442 var active = select(this).property('checked');
82443 if (active) activeKey = d;
82446 if (active) t[field.key] = d;
82448 var val = _oldType[activeKey] || 'yes';
82449 t[d] = active ? val : undefined;
82453 if (field.type === 'structureRadio') {
82454 if (activeKey === 'bridge') {
82456 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
82459 t.layer = undefined;
82463 dispatch.call('change', this, t);
82466 radio.tags = function (tags) {
82467 radios.property('checked', function (d) {
82469 return tags[field.key] === d;
82472 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
82475 function isMixed(d) {
82477 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
82480 return Array.isArray(tags[d]);
82483 labels.classed('active', function (d) {
82485 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
82488 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
82489 }).classed('mixed', isMixed).attr('title', function (d) {
82490 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
82492 var selection = radios.filter(function () {
82493 return this.checked;
82496 if (selection.empty()) {
82497 placeholder.html(_t.html('inspector.none'));
82499 placeholder.html(selection.attr('value'));
82500 _oldType[selection.datum()] = tags[selection.datum()];
82503 if (field.type === 'structureRadio') {
82504 // For waterways without a tunnel tag, set 'culvert' as
82505 // the _oldType to default to if the user picks 'tunnel'
82506 if (!!tags.waterway && !_oldType.tunnel) {
82507 _oldType.tunnel = 'culvert';
82510 wrap.call(structureExtras, tags);
82514 radio.focus = function () {
82515 radios.node().focus();
82518 radio.entityIDs = function (val) {
82519 if (!arguments.length) return _entityIDs;
82525 radio.isAllowed = function () {
82526 return _entityIDs.length === 1;
82529 return utilRebind(radio, dispatch, 'on');
82532 function uiFieldRestrictions(field, context) {
82533 var dispatch = dispatch$8('change');
82534 var breathe = behaviorBreathe();
82535 corePreferences('turn-restriction-via-way', null); // remove old key
82537 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
82539 var storedDistance = corePreferences('turn-restriction-distance');
82541 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
82543 var _maxDistance = storedDistance ? +storedDistance : 30;
82545 var _initialized = false;
82547 var _parent = select(null); // the entire field
82550 var _container = select(null); // just the map
82565 function restrictions(selection) {
82566 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
82568 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
82569 _graph = context.graph();
82570 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
82571 } // It's possible for there to be no actual intersection here.
82572 // for example, a vertex of two `highway=path`
82573 // In this case, hide the field.
82576 var isOK = _intersection && _intersection.vertices.length && // has vertices
82577 _intersection.vertices // has the vertex that the user selected
82578 .filter(function (vertex) {
82579 return vertex.id === _vertexID;
82580 }).length && _intersection.ways.length > 2 && // has more than 2 ways
82581 _intersection.ways // has more than 1 TO way
82582 .filter(function (way) {
82584 }).length > 1; // Also hide in the case where
82586 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
82588 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
82589 selection.call(restrictions.off);
82593 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
82594 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
82595 var container = wrap.selectAll('.restriction-container').data([0]); // enter
82597 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
82598 containerEnter.append('div').attr('class', 'restriction-help'); // update
82600 _container = containerEnter.merge(container).call(renderViewer);
82601 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
82603 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
82606 function renderControls(selection) {
82607 var distControl = selection.selectAll('.restriction-distance').data([0]);
82608 distControl.exit().remove();
82609 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
82610 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
82611 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
82612 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
82614 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
82615 var val = select(this).property('value');
82616 _maxDistance = +val;
82617 _intersection = null;
82619 _container.selectAll('.layer-osm .layer-turns *').remove();
82621 corePreferences('turn-restriction-distance', _maxDistance);
82623 _parent.call(restrictions);
82625 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
82626 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
82627 viaControl.exit().remove();
82628 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
82629 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
82630 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
82631 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
82633 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
82634 var val = select(this).property('value');
82637 _container.selectAll('.layer-osm .layer-turns *').remove();
82639 corePreferences('turn-restriction-via-way0', _maxViaWay);
82641 _parent.call(restrictions);
82643 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
82646 function renderViewer(selection) {
82647 if (!_intersection) return;
82648 var vgraph = _intersection.graph;
82649 var filter = utilFunctor(true);
82650 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
82651 // Instead of asking the restriction-container for its dimensions,
82652 // we can ask the .sidebar, which can have its dimensions cached.
82653 // width: calc as sidebar - padding
82654 // height: hardcoded (from `80_app.css`)
82655 // var d = utilGetDimensions(selection);
82657 var sdims = utilGetDimensions(context.container().select('.sidebar'));
82658 var d = [sdims[0] - 50, 370];
82659 var c = geoVecScale(d, 0.5);
82661 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
82663 var extent = geoExtent();
82665 for (var i = 0; i < _intersection.vertices.length; i++) {
82666 extent._extend(_intersection.vertices[i].extent());
82667 } // If this is a large intersection, adjust zoom to fit extent
82670 if (_intersection.vertices.length > 1) {
82671 var padding = 180; // in z22 pixels
82673 var tl = projection([extent[0][0], extent[1][1]]);
82674 var br = projection([extent[1][0], extent[0][1]]);
82675 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
82676 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
82677 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
82678 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
82679 z = z - Math.max(hZoomDiff, vZoomDiff);
82680 projection.scale(geoZoomToScale(z));
82683 var padTop = 35; // reserve top space for hint text
82685 var extentCenter = projection(extent.center());
82686 extentCenter[1] = extentCenter[1] - padTop;
82687 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
82688 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
82689 var drawVertices = svgVertices(projection, context);
82690 var drawLines = svgLines(projection, context);
82691 var drawTurns = svgTurns(projection, context);
82692 var firstTime = selection.selectAll('.surface').empty();
82693 selection.call(drawLayers);
82694 var surface = selection.selectAll('.surface').classed('tr', true);
82697 _initialized = true;
82698 surface.call(breathe);
82699 } // This can happen if we've lowered the detail while a FROM way
82700 // is selected, and that way is no longer part of the intersection.
82703 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
82708 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));
82709 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
82710 surface.selectAll('.selected').classed('selected', false);
82711 surface.selectAll('.related').classed('related', false);
82715 way = vgraph.entity(_fromWayID);
82716 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
82719 document.addEventListener('resizeWindow', function () {
82720 utilSetDimensions(_container, null);
82725 function click(d3_event) {
82726 surface.call(breathe.off).call(breathe);
82727 var datum = d3_event.target.__data__;
82728 var entity = datum && datum.properties && datum.properties.entity;
82734 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
82735 _fromWayID = datum.id;
82738 } else if (datum instanceof osmTurn) {
82739 var actions, extraActions, turns, i;
82740 var restrictionType = osmInferRestriction(vgraph, datum, projection);
82742 if (datum.restrictionID && !datum.direct) {
82744 } else if (datum.restrictionID && !datum.only) {
82747 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
82749 datumOnly.only = true; // but change this property
82751 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
82752 // We will remember them in _oldTurns, and restore them if the user clicks again.
82754 turns = _intersection.turns(_fromWayID, 2);
82758 for (i = 0; i < turns.length; i++) {
82759 var turn = turns[i];
82760 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
82762 if (turn.direct && turn.path[1] === datum.path[1]) {
82763 seen[turns[i].restrictionID] = true;
82764 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
82766 _oldTurns.push(turn);
82768 extraActions.push(actionUnrestrictTurn(turn));
82772 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
82773 } else if (datum.restrictionID) {
82775 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
82776 // This relies on the assumption that the intersection was already split up when we
82777 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
82778 turns = _oldTurns || [];
82781 for (i = 0; i < turns.length; i++) {
82782 if (turns[i].key !== datum.key) {
82783 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
82788 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
82791 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
82794 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
82795 // Refresh it and update the help..
82797 var s = surface.selectAll('.' + datum.key);
82798 datum = s.empty() ? null : s.datum();
82799 updateHints(datum);
82807 function mouseover(d3_event) {
82808 var datum = d3_event.target.__data__;
82809 updateHints(datum);
82812 _lastXPos = _lastXPos || sdims[0];
82814 function redraw(minChange) {
82818 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
82821 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
82822 if (context.hasEntity(_vertexID)) {
82825 _container.call(renderViewer);
82830 function highlightPathsFrom(wayID) {
82831 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
82832 surface.selectAll('.' + wayID).classed('related', true);
82835 var turns = _intersection.turns(wayID, _maxViaWay);
82837 for (var i = 0; i < turns.length; i++) {
82838 var turn = turns[i];
82839 var ids = [turn.to.way];
82840 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
82842 if (turn.only || turns.length === 1) {
82843 if (turn.via.ways) {
82844 ids = ids.concat(turn.via.ways);
82846 } else if (turn.to.way === wayID) {
82850 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
82855 function updateHints(datum) {
82856 var help = _container.selectAll('.restriction-help').html('');
82858 var placeholders = {};
82859 ['from', 'via', 'to'].forEach(function (k) {
82860 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
82862 var entity = datum && datum.properties && datum.properties.entity;
82869 way = vgraph.entity(_fromWayID);
82870 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
82871 } // Hovering a way
82874 if (datum instanceof osmWay && datum.__from) {
82876 highlightPathsFrom(_fromWayID ? null : way.id);
82877 surface.selectAll('.' + way.id).classed('related', true);
82878 var clickSelect = !_fromWayID || _fromWayID !== way.id;
82879 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
82880 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
82881 from: placeholders.from,
82882 fromName: displayName(way.id, vgraph)
82883 })); // Hovering a turn arrow
82884 } else if (datum instanceof osmTurn) {
82885 var restrictionType = osmInferRestriction(vgraph, datum, projection);
82886 var turnType = restrictionType.replace(/^(only|no)\_/, '');
82887 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
82888 var klass, turnText, nextText;
82891 klass = 'restrict';
82892 turnText = _t.html('restriction.help.turn.no_' + turnType, {
82895 nextText = _t.html('restriction.help.turn.only_' + turnType, {
82898 } else if (datum.only) {
82900 turnText = _t.html('restriction.help.turn.only_' + turnType, {
82903 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
82908 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
82911 nextText = _t.html('restriction.help.turn.no_' + turnType, {
82916 help.append('div') // "NO Right Turn (indirect)"
82917 .attr('class', 'qualifier ' + klass).html(turnText);
82918 help.append('div') // "FROM {fromName} TO {toName}"
82919 .html(_t.html('restriction.help.from_name_to_name', {
82920 from: placeholders.from,
82921 fromName: displayName(datum.from.way, vgraph),
82922 to: placeholders.to,
82923 toName: displayName(datum.to.way, vgraph)
82926 if (datum.via.ways && datum.via.ways.length) {
82929 for (var i = 0; i < datum.via.ways.length; i++) {
82930 var prev = names[names.length - 1];
82931 var curr = displayName(datum.via.ways[i], vgraph);
82933 if (!prev || curr !== prev) {
82934 // collapse identical names
82939 help.append('div') // "VIA {viaNames}"
82940 .html(_t.html('restriction.help.via_names', {
82941 via: placeholders.via,
82942 viaNames: names.join(', ')
82947 help.append('div') // Click for "No Right Turn"
82948 .html(_t.html('restriction.help.toggle', {
82949 turn: nextText.trim()
82953 highlightPathsFrom(null);
82954 var alongIDs = datum.path.slice();
82955 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
82957 highlightPathsFrom(null);
82960 help.append('div') // "FROM {fromName}"
82961 .html(_t.html('restriction.help.from_name', {
82962 from: placeholders.from,
82963 fromName: displayName(_fromWayID, vgraph)
82966 help.append('div') // "Click to select a FROM segment."
82967 .html(_t.html('restriction.help.select_from', {
82968 from: placeholders.from
82975 function displayMaxDistance(maxDist) {
82976 var isImperial = !_mainLocalizer.usesMetric();
82981 // imprecise conversion for prettier display
82991 distance: _t('units.feet', {
82992 quantity: distToFeet
82997 distance: _t('units.meters', {
83003 return _t.html('restriction.controls.distance_up_to', opts);
83006 function displayMaxVia(maxVia) {
83007 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');
83010 function displayName(entityID, graph) {
83011 var entity = graph.entity(entityID);
83012 var name = utilDisplayName(entity) || '';
83013 var matched = _mainPresetIndex.match(entity, graph);
83014 var type = matched && matched.name() || utilDisplayType(entity.id);
83015 return name || type;
83018 restrictions.entityIDs = function (val) {
83019 _intersection = null;
83022 _vertexID = val[0];
83025 restrictions.tags = function () {};
83027 restrictions.focus = function () {};
83029 restrictions.off = function (selection) {
83030 if (!_initialized) return;
83031 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
83032 select(window).on('resize.restrictions', null);
83035 return utilRebind(restrictions, dispatch, 'on');
83037 uiFieldRestrictions.supportsMultiselection = false;
83039 function uiFieldTextarea(field, context) {
83040 var dispatch = dispatch$8('change');
83041 var input = select(null);
83045 function textarea(selection) {
83046 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
83047 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
83048 input = wrap.selectAll('textarea').data([0]);
83049 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
83052 function change(onInput) {
83053 return function () {
83054 var val = utilGetSetValue(input);
83055 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
83057 if (!val && Array.isArray(_tags[field.key])) return;
83059 t[field.key] = val || undefined;
83060 dispatch.call('change', this, t, onInput);
83064 textarea.tags = function (tags) {
83066 var isMixed = Array.isArray(tags[field.key]);
83067 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);
83070 textarea.focus = function () {
83071 input.node().focus();
83074 return utilRebind(textarea, dispatch, 'on');
83077 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
83084 // eslint-disable-next-line es/no-string-prototype-endswith -- safe
83085 var $endsWith = ''.endsWith;
83086 var min = Math.min;
83088 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
83089 // https://github.com/zloirock/core-js/pull/702
83090 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
83091 var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith');
83092 return descriptor && !descriptor.writable;
83095 // `String.prototype.endsWith` method
83096 // https://tc39.es/ecma262/#sec-string.prototype.endswith
83097 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
83098 endsWith: function endsWith(searchString /* , endPosition = @length */) {
83099 var that = String(requireObjectCoercible(this));
83100 notARegexp(searchString);
83101 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
83102 var len = toLength(that.length);
83103 var end = endPosition === undefined ? len : min(toLength(endPosition), len);
83104 var search = String(searchString);
83106 ? $endsWith.call(that, search, end)
83107 : that.slice(end - search.length, end) === search;
83111 function uiFieldWikidata(field, context) {
83112 var wikidata = services.wikidata;
83113 var dispatch = dispatch$8('change');
83115 var _selection = select(null);
83117 var _searchInput = select(null);
83120 var _wikidataEntity = null;
83122 var _entityIDs = [];
83124 var _wikipediaKey = field.keys && field.keys.find(function (key) {
83125 return key.includes('wikipedia');
83127 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
83129 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
83131 function wiki(selection) {
83132 _selection = selection;
83133 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
83134 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
83135 var list = wrap.selectAll('ul').data([0]);
83136 list = list.enter().append('ul').attr('class', 'rows').merge(list);
83137 var searchRow = list.selectAll('li.wikidata-search').data([0]);
83138 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
83139 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
83140 var node = select(this).node();
83141 node.setSelectionRange(0, node.value.length);
83142 }).on('blur', function () {
83143 setLabelForEntity();
83144 }).call(combobox.fetcher(fetchWikidataItems));
83145 combobox.on('accept', function (d) {
83150 }).on('cancel', function () {
83151 setLabelForEntity();
83153 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
83154 domain: 'wikidata.org'
83155 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
83156 d3_event.preventDefault();
83157 if (_wikiURL) window.open(_wikiURL, '_blank');
83159 searchRow = searchRow.merge(searchRowEnter);
83160 _searchInput = searchRow.select('input');
83161 var wikidataProperties = ['description', 'identifier'];
83162 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
83164 var enter = items.enter().append('li').attr('class', function (d) {
83165 return 'labeled-input preset-wikidata-' + d;
83167 enter.append('span').attr('class', 'label').html(function (d) {
83168 return _t.html('wikidata.' + d);
83170 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
83171 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
83172 d3_event.preventDefault();
83173 select(this.parentNode).select('input').node().select();
83174 document.execCommand('copy');
83178 function fetchWikidataItems(q, callback) {
83179 if (!q && _hintKey) {
83180 // other tags may be good search terms
83181 for (var i in _entityIDs) {
83182 var entity = context.hasEntity(_entityIDs[i]);
83184 if (entity.tags[_hintKey]) {
83185 q = entity.tags[_hintKey];
83191 wikidata.itemsForSearchQuery(q, function (err, data) {
83194 for (var i in data) {
83195 data[i].value = data[i].label + ' (' + data[i].id + ')';
83196 data[i].title = data[i].description;
83199 if (callback) callback(data);
83203 function change() {
83205 syncTags[field.key] = _qid;
83206 dispatch.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
83208 var initGraph = context.graph();
83209 var initEntityIDs = _entityIDs;
83210 wikidata.entityByQID(_qid, function (err, entity) {
83211 if (err) return; // If graph has changed, we can't apply this update.
83213 if (context.graph() !== initGraph) return;
83214 if (!entity.sitelinks) return;
83215 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
83217 ['labels', 'descriptions'].forEach(function (key) {
83218 if (!entity[key]) return;
83219 var valueLangs = Object.keys(entity[key]);
83220 if (valueLangs.length === 0) return;
83221 var valueLang = valueLangs[0];
83223 if (langs.indexOf(valueLang) === -1) {
83224 langs.push(valueLang);
83227 var newWikipediaValue;
83229 if (_wikipediaKey) {
83230 var foundPreferred;
83232 for (var i in langs) {
83233 var lang = langs[i];
83234 var siteID = lang.replace('-', '_') + 'wiki';
83236 if (entity.sitelinks[siteID]) {
83237 foundPreferred = true;
83238 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
83244 if (!foundPreferred) {
83245 // No wikipedia sites available in the user's language or the fallback languages,
83246 // default to any wikipedia sitelink
83247 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
83248 return site.endsWith('wiki');
83251 if (wikiSiteKeys.length === 0) {
83252 // if no wikipedia pages are linked to this wikidata entity, delete that tag
83253 newWikipediaValue = null;
83255 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
83256 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
83257 newWikipediaValue = wikiLang + ':' + wikiTitle;
83262 if (newWikipediaValue) {
83263 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
83266 if (typeof newWikipediaValue === 'undefined') return;
83267 var actions = initEntityIDs.map(function (entityID) {
83268 var entity = context.hasEntity(entityID);
83269 if (!entity) return null;
83270 var currTags = Object.assign({}, entity.tags); // shallow copy
83272 if (newWikipediaValue === null) {
83273 if (!currTags[_wikipediaKey]) return null;
83274 delete currTags[_wikipediaKey];
83276 currTags[_wikipediaKey] = newWikipediaValue;
83279 return actionChangeTags(entityID, currTags);
83280 }).filter(Boolean);
83281 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
83283 context.overwrite(function actionUpdateWikipediaTags(graph) {
83284 actions.forEach(function (action) {
83285 graph = action(graph);
83288 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
83289 // changeTags() is not intended to be called asynchronously
83293 function setLabelForEntity() {
83296 if (_wikidataEntity) {
83297 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
83299 if (label.length === 0) {
83300 label = _wikidataEntity.id.toString();
83304 utilGetSetValue(_searchInput, label);
83307 wiki.tags = function (tags) {
83308 var isMixed = Array.isArray(tags[field.key]);
83310 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
83312 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
83314 if (!/^Q[0-9]*$/.test(_qid)) {
83315 // not a proper QID
83318 } // QID value in correct format
83321 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
83322 wikidata.entityByQID(_qid, function (err, entity) {
83328 _wikidataEntity = entity;
83329 setLabelForEntity();
83330 var description = entityPropertyForDisplay(entity, 'descriptions');
83332 _selection.select('button.wiki-link').classed('disabled', false);
83334 _selection.select('.preset-wikidata-description').style('display', function () {
83335 return description.length > 0 ? 'flex' : 'none';
83336 }).select('input').attr('value', description);
83338 _selection.select('.preset-wikidata-identifier').style('display', function () {
83339 return entity.id ? 'flex' : 'none';
83340 }).select('input').attr('value', entity.id);
83341 }); // not a proper QID
83343 function unrecognized() {
83344 _wikidataEntity = null;
83345 setLabelForEntity();
83347 _selection.select('.preset-wikidata-description').style('display', 'none');
83349 _selection.select('.preset-wikidata-identifier').style('display', 'none');
83351 _selection.select('button.wiki-link').classed('disabled', true);
83353 if (_qid && _qid !== '') {
83354 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
83361 function entityPropertyForDisplay(wikidataEntity, propKey) {
83362 if (!wikidataEntity[propKey]) return '';
83363 var propObj = wikidataEntity[propKey];
83364 var langKeys = Object.keys(propObj);
83365 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
83367 var langs = wikidata.languagesToQuery();
83369 for (var i in langs) {
83370 var lang = langs[i];
83371 var valueObj = propObj[lang];
83372 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
83373 } // default to any available value
83376 return propObj[langKeys[0]].value;
83379 wiki.entityIDs = function (val) {
83380 if (!arguments.length) return _entityIDs;
83385 wiki.focus = function () {
83386 _searchInput.node().focus();
83389 return utilRebind(wiki, dispatch, 'on');
83392 function uiFieldWikipedia(field, context) {
83393 var _arguments = arguments;
83394 var dispatch = dispatch$8('change');
83395 var wikipedia = services.wikipedia;
83396 var wikidata = services.wikidata;
83398 var _langInput = select(null);
83400 var _titleInput = select(null);
83408 var _dataWikipedia = [];
83409 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
83410 _dataWikipedia = d;
83411 if (_tags) updateForTags(_tags);
83412 })["catch"](function () {
83415 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
83416 var v = value.toLowerCase();
83417 callback(_dataWikipedia.filter(function (d) {
83418 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
83419 }).map(function (d) {
83425 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
83429 for (var i in _entityIDs) {
83430 var entity = context.hasEntity(_entityIDs[i]);
83432 if (entity.tags.name) {
83433 value = entity.tags.name;
83439 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
83440 searchfn(language()[2], value, function (query, data) {
83441 callback(data.map(function (d) {
83449 function wiki(selection) {
83450 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
83451 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
83452 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
83453 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
83454 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
83455 _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);
83457 _langInput.on('blur', changeLang).on('change', changeLang);
83459 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
83460 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
83461 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
83462 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
83464 _titleInput.on('blur', function () {
83466 }).on('change', function () {
83470 var link = titleContainer.selectAll('.wiki-link').data([0]);
83471 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
83472 domain: 'wikipedia.org'
83473 })).call(svgIcon('#iD-icon-out-link')).merge(link);
83474 link.on('click', function (d3_event) {
83475 d3_event.preventDefault();
83476 if (_wikiURL) window.open(_wikiURL, '_blank');
83480 function defaultLanguageInfo(skipEnglishFallback) {
83481 var langCode = _mainLocalizer.languageCode().toLowerCase();
83483 for (var i in _dataWikipedia) {
83484 var d = _dataWikipedia[i]; // default to the language of iD's current locale
83486 if (d[2] === langCode) return d;
83487 } // fallback to English
83490 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
83493 function language(skipEnglishFallback) {
83494 var value = utilGetSetValue(_langInput).toLowerCase();
83496 for (var i in _dataWikipedia) {
83497 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
83499 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
83500 } // fallback to English
83503 return defaultLanguageInfo(skipEnglishFallback);
83506 function changeLang() {
83507 utilGetSetValue(_langInput, language()[1]);
83511 function change(skipWikidata) {
83512 var value = utilGetSetValue(_titleInput);
83513 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
83515 var langInfo = m && _dataWikipedia.find(function (d) {
83516 return m[1] === d[2];
83522 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
83524 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
83527 var anchor; // try {
83528 // leave this out for now - #6232
83529 // Best-effort `anchordecode:` implementation
83530 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
83533 anchor = decodeURIComponent(m[3]); // }
83535 value += '#' + anchor.replace(/_/g, ' ');
83538 value = value.slice(0, 1).toUpperCase() + value.slice(1);
83539 utilGetSetValue(_langInput, nativeLangName);
83540 utilGetSetValue(_titleInput, value);
83544 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
83546 syncTags.wikipedia = undefined;
83549 dispatch.call('change', this, syncTags);
83550 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
83552 var initGraph = context.graph();
83553 var initEntityIDs = _entityIDs;
83554 wikidata.itemsByTitle(language()[2], value, function (err, data) {
83555 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
83557 if (context.graph() !== initGraph) return;
83558 var qids = Object.keys(data);
83559 var value = qids && qids.find(function (id) {
83560 return id.match(/^Q\d+$/);
83562 var actions = initEntityIDs.map(function (entityID) {
83563 var entity = context.entity(entityID).tags;
83564 var currTags = Object.assign({}, entity); // shallow copy
83566 if (currTags.wikidata !== value) {
83567 currTags.wikidata = value;
83568 return actionChangeTags(entityID, currTags);
83572 }).filter(Boolean);
83573 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
83575 context.overwrite(function actionUpdateWikidataTags(graph) {
83576 actions.forEach(function (action) {
83577 graph = action(graph);
83580 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
83581 // changeTags() is not intended to be called asynchronously
83585 wiki.tags = function (tags) {
83587 updateForTags(tags);
83590 function updateForTags(tags) {
83591 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
83592 // optional suffix of `#anchor`
83594 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
83595 var tagLang = m && m[1];
83596 var tagArticleTitle = m && m[2];
83597 var anchor = m && m[3];
83599 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
83600 return tagLang === d[2];
83601 }); // value in correct format
83605 var nativeLangName = tagLangInfo[1];
83606 utilGetSetValue(_langInput, nativeLangName);
83607 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
83611 // Best-effort `anchorencode:` implementation
83612 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
83614 anchor = anchor.replace(/ /g, '_');
83618 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
83620 utilGetSetValue(_titleInput, value);
83622 if (value && value !== '') {
83623 utilGetSetValue(_langInput, '');
83624 var defaultLangInfo = defaultLanguageInfo();
83625 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
83627 var shownOrDefaultLangInfo = language(true
83628 /* skipEnglishFallback */
83630 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
83636 wiki.entityIDs = function (val) {
83637 if (!_arguments.length) return _entityIDs;
83642 wiki.focus = function () {
83643 _titleInput.node().focus();
83646 return utilRebind(wiki, dispatch, 'on');
83648 uiFieldWikipedia.supportsMultiselection = false;
83651 access: uiFieldAccess,
83652 address: uiFieldAddress,
83653 check: uiFieldCheck,
83654 combo: uiFieldCombo,
83655 cycleway: uiFieldCycleway,
83656 defaultCheck: uiFieldCheck,
83657 email: uiFieldText,
83658 identifier: uiFieldText,
83659 lanes: uiFieldLanes,
83660 localized: uiFieldLocalized,
83661 roadspeed: uiFieldRoadspeed,
83662 roadheight: uiFieldText,
83663 manyCombo: uiFieldCombo,
83664 multiCombo: uiFieldCombo,
83665 networkCombo: uiFieldCombo,
83666 number: uiFieldText,
83667 onewayCheck: uiFieldCheck,
83668 radio: uiFieldRadio,
83669 restrictions: uiFieldRestrictions,
83670 semiCombo: uiFieldCombo,
83671 structureRadio: uiFieldRadio,
83674 textarea: uiFieldTextarea,
83675 typeCombo: uiFieldCombo,
83677 wikidata: uiFieldWikidata,
83678 wikipedia: uiFieldWikipedia
83681 function uiField(context, presetField, entityIDs, options) {
83682 options = Object.assign({
83689 var dispatch = dispatch$8('change', 'revert');
83690 var field = Object.assign({}, presetField); // shallow copy
83692 field.domId = utilUniqueDomId('form-field-' + field.safeid);
83693 var _show = options.show;
83699 if (entityIDs && entityIDs.length) {
83700 _entityExtent = entityIDs.reduce(function (extent, entityID) {
83701 var entity = context.graph().entity(entityID);
83702 return extent.extend(entity.extent(context.graph()));
83706 var _locked = false;
83708 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
83710 })).placement('bottom');
83712 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
83714 if (_show && !field.impl) {
83716 } // Creates the field.. This is done lazily,
83717 // once we know that the field will be shown.
83720 function createField() {
83721 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
83722 dispatch.call('change', field, t, onInput);
83726 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
83728 if (field.impl.entityIDs) {
83729 field.impl.entityIDs(entityIDs);
83734 function isModified() {
83735 if (!entityIDs || !entityIDs.length) return false;
83736 return entityIDs.some(function (entityID) {
83737 var original = context.graph().base().entities[entityID];
83738 var latest = context.graph().entity(entityID);
83739 return field.keys.some(function (key) {
83740 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
83745 function tagsContainFieldKey() {
83746 return field.keys.some(function (key) {
83747 if (field.type === 'multiCombo') {
83748 for (var tagKey in _tags) {
83749 if (tagKey.indexOf(key) === 0) {
83757 return _tags[key] !== undefined;
83761 function revert(d3_event, d) {
83762 d3_event.stopPropagation();
83763 d3_event.preventDefault();
83764 if (!entityIDs || _locked) return;
83765 dispatch.call('revert', d, d.keys);
83768 function remove(d3_event, d) {
83769 d3_event.stopPropagation();
83770 d3_event.preventDefault();
83771 if (_locked) return;
83773 d.keys.forEach(function (key) {
83774 t[key] = undefined;
83776 dispatch.call('change', d, t);
83779 field.render = function (selection) {
83780 var container = selection.selectAll('.form-field').data([field]); // Enter
83782 var enter = container.enter().append('div').attr('class', function (d) {
83783 return 'form-field form-field-' + d.safeid;
83784 }).classed('nowrap', !options.wrap);
83786 if (options.wrap) {
83787 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
83790 var textEnter = labelEnter.append('span').attr('class', 'label-text');
83791 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
83794 textEnter.append('span').attr('class', 'label-textannotation');
83796 if (options.remove) {
83797 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
83800 if (options.revert) {
83801 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
83806 container = container.merge(enter);
83807 container.select('.field-label > .remove-icon') // propagate bound data
83808 .on('click', remove);
83809 container.select('.field-label > .modified-icon') // propagate bound data
83810 .on('click', revert);
83811 container.each(function (d) {
83812 var selection = select(this);
83818 var reference, help; // instantiate field help
83820 if (options.wrap && field.type === 'restrictions') {
83821 help = uiFieldHelp(context, 'restrictions');
83822 } // instantiate tag reference
83825 if (options.wrap && options.info) {
83826 var referenceKey = d.key || '';
83828 if (d.type === 'multiCombo') {
83829 // lookup key without the trailing ':'
83830 referenceKey = referenceKey.replace(/:$/, '');
83833 reference = uiTagReference(d.reference || {
83837 if (_state === 'hover') {
83838 reference.showing(false);
83842 selection.call(d.impl); // add field help components
83845 selection.call(help.body).select('.field-label').call(help.button);
83846 } // add tag reference components
83850 selection.call(reference.body).select('.field-label').call(reference.button);
83853 d.impl.tags(_tags);
83855 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
83857 var annotation = container.selectAll('.field-label .label-textannotation');
83858 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
83859 icon.exit().remove();
83860 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
83861 container.call(_locked ? _lockedTip : _lockedTip.destroy);
83864 field.state = function (val) {
83865 if (!arguments.length) return _state;
83870 field.tags = function (val) {
83871 if (!arguments.length) return _tags;
83874 if (tagsContainFieldKey() && !_show) {
83875 // always show a field if it has a value to display
83886 field.locked = function (val) {
83887 if (!arguments.length) return _locked;
83892 field.show = function () {
83899 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
83901 t[field.key] = field["default"];
83902 dispatch.call('change', this, t);
83904 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
83907 field.isShown = function () {
83909 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
83910 // A non-allowed field is hidden from the user altogether
83913 field.isAllowed = function () {
83914 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
83915 if (field.geometry && !entityIDs.every(function (entityID) {
83916 return field.matchGeometry(context.graph().geometry(entityID));
83919 if (entityIDs && _entityExtent && field.locationSetID) {
83920 // is field allowed in this location?
83921 var validLocations = _mainLocations.locationsAt(_entityExtent.center());
83922 if (!validLocations[field.locationSetID]) return false;
83925 var prerequisiteTag = field.prerequisiteTag;
83927 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
83929 if (!entityIDs.every(function (entityID) {
83930 var entity = context.graph().entity(entityID);
83932 if (prerequisiteTag.key) {
83933 var value = entity.tags[prerequisiteTag.key];
83934 if (!value) return false;
83936 if (prerequisiteTag.valueNot) {
83937 return prerequisiteTag.valueNot !== value;
83940 if (prerequisiteTag.value) {
83941 return prerequisiteTag.value === value;
83943 } else if (prerequisiteTag.keyNot) {
83944 if (entity.tags[prerequisiteTag.keyNot]) return false;
83954 field.focus = function () {
83956 field.impl.focus();
83960 return utilRebind(field, dispatch, 'on');
83963 function uiFormFields(context) {
83964 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
83965 var _fieldsArr = [];
83966 var _lastPlaceholder = '';
83970 function formFields(selection) {
83971 var allowedFields = _fieldsArr.filter(function (field) {
83972 return field.isAllowed();
83975 var shown = allowedFields.filter(function (field) {
83976 return field.isShown();
83978 var notShown = allowedFields.filter(function (field) {
83979 return !field.isShown();
83981 var container = selection.selectAll('.form-fields-container').data([0]);
83982 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
83983 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
83984 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
83986 fields.exit().remove(); // Enter
83988 var enter = fields.enter().append('div').attr('class', function (d) {
83989 return 'wrap-form-field wrap-form-field-' + d.safeid;
83992 fields = fields.merge(enter);
83993 fields.order().each(function (d) {
83994 select(this).call(d.render);
83997 var moreFields = notShown.map(function (field) {
83998 var title = field.title();
83999 titles.push(title);
84000 var terms = field.terms();
84001 if (field.key) terms.push(field.key);
84002 if (field.keys) terms = terms.concat(field.keys);
84004 display: field.label(),
84011 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
84012 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
84013 more.exit().remove();
84014 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
84015 moreEnter.append('span').html(_t.html('inspector.add_fields'));
84016 more = moreEnter.merge(more);
84017 var input = more.selectAll('.value').data([0]);
84018 input.exit().remove();
84019 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
84020 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
84021 if (!d) return; // user entered something that was not matched
84023 var field = d.field;
84025 selection.call(formFields); // rerender
84028 })); // avoid updating placeholder excessively (triggers style recalc)
84030 if (_lastPlaceholder !== placeholder) {
84031 input.attr('placeholder', placeholder);
84032 _lastPlaceholder = placeholder;
84036 formFields.fieldsArr = function (val) {
84037 if (!arguments.length) return _fieldsArr;
84038 _fieldsArr = val || [];
84042 formFields.state = function (val) {
84043 if (!arguments.length) return _state;
84048 formFields.klass = function (val) {
84049 if (!arguments.length) return _klass;
84057 function uiSectionPresetFields(context) {
84058 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
84059 var dispatch = dispatch$8('change', 'revert');
84060 var formFields = uiFormFields(context);
84072 function renderDisclosureContent(selection) {
84074 var graph = context.graph();
84075 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
84076 geoms[graph.entity(entityID).geometry(graph)] = true;
84079 var presetsManager = _mainPresetIndex;
84080 var allFields = [];
84081 var allMoreFields = [];
84082 var sharedTotalFields;
84084 _presets.forEach(function (preset) {
84085 var fields = preset.fields();
84086 var moreFields = preset.moreFields();
84087 allFields = utilArrayUnion(allFields, fields);
84088 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
84090 if (!sharedTotalFields) {
84091 sharedTotalFields = utilArrayUnion(fields, moreFields);
84093 sharedTotalFields = sharedTotalFields.filter(function (field) {
84094 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
84099 var sharedFields = allFields.filter(function (field) {
84100 return sharedTotalFields.indexOf(field) !== -1;
84102 var sharedMoreFields = allMoreFields.filter(function (field) {
84103 return sharedTotalFields.indexOf(field) !== -1;
84106 sharedFields.forEach(function (field) {
84107 if (field.matchAllGeometry(geometries)) {
84108 _fieldsArr.push(uiField(context, field, _entityIDs));
84111 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
84113 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
84114 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
84117 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
84118 additionalFields.sort(function (field1, field2) {
84119 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
84121 additionalFields.forEach(function (field) {
84122 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
84123 _fieldsArr.push(uiField(context, field, _entityIDs, {
84129 _fieldsArr.forEach(function (field) {
84130 field.on('change', function (t, onInput) {
84131 dispatch.call('change', field, _entityIDs, t, onInput);
84132 }).on('revert', function (keys) {
84133 dispatch.call('revert', field, keys);
84138 _fieldsArr.forEach(function (field) {
84139 field.state(_state).tags(_tags);
84142 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
84143 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
84144 // if user presses enter, and combobox is not active, accept edits..
84145 if (d3_event.keyCode === 13 && // ↩ Return
84146 context.container().select('.combobox').empty()) {
84147 context.enter(modeBrowse(context));
84152 section.presets = function (val) {
84153 if (!arguments.length) return _presets;
84155 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
84163 section.state = function (val) {
84164 if (!arguments.length) return _state;
84169 section.tags = function (val) {
84170 if (!arguments.length) return _tags;
84171 _tags = val; // Don't reset _fieldsArr here.
84176 section.entityIDs = function (val) {
84177 if (!arguments.length) return _entityIDs;
84179 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
84187 return utilRebind(section, dispatch, 'on');
84190 function uiSectionRawMemberEditor(context) {
84191 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
84192 if (!_entityIDs || _entityIDs.length !== 1) return false;
84193 var entity = context.hasEntity(_entityIDs[0]);
84194 return entity && entity.type === 'relation';
84195 }).label(function () {
84196 var entity = context.hasEntity(_entityIDs[0]);
84197 if (!entity) return '';
84198 var gt = entity.members.length > _maxMembers ? '>' : '';
84199 var count = gt + entity.members.slice(0, _maxMembers).length;
84200 return _t('inspector.title_count', {
84201 title: _t.html('inspector.members'),
84204 }).disclosureContent(renderDisclosureContent);
84205 var taginfo = services.taginfo;
84209 var _maxMembers = 1000;
84211 function downloadMember(d3_event, d) {
84212 d3_event.preventDefault(); // display the loading indicator
84214 select(this.parentNode).classed('tag-reference-loading', true);
84215 context.loadEntity(d.id, function () {
84216 section.reRender();
84220 function zoomToMember(d3_event, d) {
84221 d3_event.preventDefault();
84222 var entity = context.entity(d.id);
84223 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
84225 utilHighlightEntities([d.id], true, context);
84228 function selectMember(d3_event, d) {
84229 d3_event.preventDefault(); // remove the hover-highlight styling
84231 utilHighlightEntities([d.id], false, context);
84232 var entity = context.entity(d.id);
84233 var mapExtent = context.map().extent();
84235 if (!entity.intersects(mapExtent, context.graph())) {
84236 // zoom to the entity if its extent is not visible now
84237 context.map().zoomToEase(entity);
84240 context.enter(modeSelect(context, [d.id]));
84243 function changeRole(d3_event, d) {
84244 var oldRole = d.role;
84245 var newRole = context.cleanRelationRole(select(this).property('value'));
84247 if (oldRole !== newRole) {
84253 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
84256 context.validator().validate();
84260 function deleteMember(d3_event, d) {
84261 // remove the hover-highlight styling
84262 utilHighlightEntities([d.id], false, context);
84263 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
84267 if (!context.hasEntity(d.relation.id)) {
84268 // Removing the last member will also delete the relation.
84269 // If this happens we need to exit the selection mode
84270 context.enter(modeBrowse(context));
84272 // Changing the mode also runs `validate`, but otherwise we need to
84273 // rerun it manually
84274 context.validator().validate();
84278 function renderDisclosureContent(selection) {
84279 var entityID = _entityIDs[0];
84280 var memberships = [];
84281 var entity = context.entity(entityID);
84282 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
84289 member: context.hasEntity(member.id),
84290 domId: utilUniqueDomId(entityID + '-member-' + index)
84293 var list = selection.selectAll('.member-list').data([0]);
84294 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
84295 var items = list.selectAll('li').data(memberships, function (d) {
84296 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
84298 items.exit().each(unbind).remove();
84299 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
84302 itemsEnter.each(function (d) {
84303 var item = select(this);
84304 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
84307 // highlight the member feature in the map while hovering on the list item
84308 item.on('mouseover', function () {
84309 utilHighlightEntities([d.id], true, context);
84310 }).on('mouseout', function () {
84311 utilHighlightEntities([d.id], false, context);
84313 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
84314 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
84315 var matched = _mainPresetIndex.match(d.member, context.graph());
84316 return matched && matched.name() || utilDisplayType(d.member.id);
84318 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
84319 return utilDisplayName(d.member);
84321 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
84322 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
84324 var labelText = label.append('span').attr('class', 'label-text');
84325 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
84328 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
84331 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
84334 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
84335 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
84337 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
84340 wrapEnter.each(bindTypeahead);
84344 items = items.merge(itemsEnter).order();
84345 items.select('input.member-role').property('value', function (d) {
84347 }).on('blur', changeRole).on('change', changeRole);
84348 items.select('button.member-delete').on('click', deleteMember);
84349 var dragOrigin, targetIndex;
84350 items.call(d3_drag().on('start', function (d3_event) {
84355 targetIndex = null;
84356 }).on('drag', function (d3_event) {
84357 var x = d3_event.x - dragOrigin.x,
84358 y = d3_event.y - dragOrigin.y;
84359 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
84360 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84361 var index = items.nodes().indexOf(this);
84362 select(this).classed('dragging', true);
84363 targetIndex = null;
84364 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
84365 var node = select(this).node();
84367 if (index === index2) {
84368 return 'translate(' + x + 'px, ' + y + 'px)';
84369 } else if (index2 > index && d3_event.y > node.offsetTop) {
84370 if (targetIndex === null || index2 > targetIndex) {
84371 targetIndex = index2;
84374 return 'translateY(-100%)';
84375 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
84376 if (targetIndex === null || index2 < targetIndex) {
84377 targetIndex = index2;
84380 return 'translateY(100%)';
84385 }).on('end', function (d3_event, d) {
84386 if (!select(this).classed('dragging')) return;
84387 var index = items.nodes().indexOf(this);
84388 select(this).classed('dragging', false);
84389 selection.selectAll('li.member-row').style('transform', null);
84391 if (targetIndex !== null) {
84392 // dragged to a new position, reorder
84393 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
84394 context.validator().validate();
84398 function bindTypeahead(d) {
84399 var row = select(this);
84400 var role = row.selectAll('input.member-role');
84401 var origValue = role.property('value');
84403 function sort(value, data) {
84404 var sameletter = [];
84407 for (var i = 0; i < data.length; i++) {
84408 if (data[i].value.substring(0, value.length) === value) {
84409 sameletter.push(data[i]);
84411 other.push(data[i]);
84415 return sameletter.concat(other);
84418 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
84419 // The `geometry` param is used in the `taginfo.js` interface for
84420 // filtering results, as a key into the `tag_members_fractions`
84421 // object. If we don't know the geometry because the member is
84422 // not yet downloaded, it's ok to guess based on type.
84426 geometry = context.graph().geometry(d.member.id);
84427 } else if (d.type === 'relation') {
84428 geometry = 'relation';
84429 } else if (d.type === 'way') {
84432 geometry = 'point';
84435 var rtype = entity.tags.type;
84438 rtype: rtype || '',
84439 geometry: geometry,
84441 }, function (err, data) {
84442 if (!err) callback(sort(role, data));
84444 }).on('cancel', function () {
84445 role.property('value', origValue);
84449 function unbind() {
84450 var row = select(this);
84451 row.selectAll('input.member-role').call(uiCombobox.off, context);
84455 section.entityIDs = function (val) {
84456 if (!arguments.length) return _entityIDs;
84464 function actionDeleteMembers(relationId, memberIndexes) {
84465 return function (graph) {
84466 // Remove the members in descending order so removals won't shift what members
84467 // are at the remaining indexes
84468 memberIndexes.sort(function (a, b) {
84472 for (var i in memberIndexes) {
84473 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
84480 function uiSectionRawMembershipEditor(context) {
84481 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
84482 return _entityIDs && _entityIDs.length;
84483 }).label(function () {
84484 var parents = getSharedParentRelations();
84485 var gt = parents.length > _maxMemberships ? '>' : '';
84486 var count = gt + parents.slice(0, _maxMemberships).length;
84487 return _t('inspector.title_count', {
84488 title: _t.html('inspector.relations'),
84491 }).disclosureContent(renderDisclosureContent);
84492 var taginfo = services.taginfo;
84493 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
84494 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
84495 }).itemsMouseLeave(function (d3_event, d) {
84496 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
84498 var _inChange = false;
84499 var _entityIDs = [];
84503 var _maxMemberships = 1000;
84505 function getSharedParentRelations() {
84508 for (var i = 0; i < _entityIDs.length; i++) {
84509 var entity = context.graph().hasEntity(_entityIDs[i]);
84510 if (!entity) continue;
84513 parents = context.graph().parentRelations(entity);
84515 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
84518 if (!parents.length) break;
84524 function getMemberships() {
84525 var memberships = [];
84526 var relations = getSharedParentRelations().slice(0, _maxMemberships);
84527 var isMultiselect = _entityIDs.length > 1;
84528 var i, relation, membership, index, member, indexedMember;
84530 for (i = 0; i < relations.length; i++) {
84531 relation = relations[i];
84533 relation: relation,
84535 hash: osmEntity.key(relation)
84538 for (index = 0; index < relation.members.length; index++) {
84539 member = relation.members[index];
84541 if (_entityIDs.indexOf(member.id) !== -1) {
84542 indexedMember = Object.assign({}, member, {
84545 membership.members.push(indexedMember);
84546 membership.hash += ',' + index.toString();
84548 if (!isMultiselect) {
84549 // For single selections, list one entry per membership per relation.
84550 // For multiselections, list one entry per relation.
84551 memberships.push(membership);
84553 relation: relation,
84555 hash: osmEntity.key(relation)
84561 if (membership.members.length) memberships.push(membership);
84564 memberships.forEach(function (membership) {
84565 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
84567 membership.members.forEach(function (member) {
84568 if (roles.indexOf(member.role) === -1) roles.push(member.role);
84570 membership.role = roles.length === 1 ? roles[0] : roles;
84572 return memberships;
84575 function selectRelation(d3_event, d) {
84576 d3_event.preventDefault(); // remove the hover-highlight styling
84578 utilHighlightEntities([d.relation.id], false, context);
84579 context.enter(modeSelect(context, [d.relation.id]));
84582 function zoomToRelation(d3_event, d) {
84583 d3_event.preventDefault();
84584 var entity = context.entity(d.relation.id);
84585 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
84587 utilHighlightEntities([d.relation.id], true, context);
84590 function changeRole(d3_event, d) {
84591 if (d === 0) return; // called on newrow (shouldn't happen)
84593 if (_inChange) return; // avoid accidental recursive call #5731
84595 var newRole = context.cleanRelationRole(select(this).property('value'));
84596 if (!newRole.trim() && typeof d.role !== 'string') return;
84597 var membersToUpdate = d.members.filter(function (member) {
84598 return member.role !== newRole;
84601 if (membersToUpdate.length) {
84603 context.perform(function actionChangeMemberRoles(graph) {
84604 membersToUpdate.forEach(function (member) {
84605 var newMember = Object.assign({}, member, {
84608 delete newMember.index;
84609 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
84612 }, _t('operations.change_role.annotation', {
84613 n: membersToUpdate.length
84615 context.validator().validate();
84621 function addMembership(d, role) {
84622 this.blur(); // avoid keeping focus on the button
84624 _showBlank = false;
84626 function actionAddMembers(relationId, ids, role) {
84627 return function (graph) {
84628 for (var i in ids) {
84631 type: graph.entity(ids[i]).type,
84634 graph = actionAddMember(relationId, member)(graph);
84642 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
84643 n: _entityIDs.length
84645 context.validator().validate();
84647 var relation = osmRelation();
84648 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
84650 context.enter(modeSelect(context, [relation.id]).newFeature(true));
84654 function deleteMembership(d3_event, d) {
84655 this.blur(); // avoid keeping focus on the button
84657 if (d === 0) return; // called on newrow (shouldn't happen)
84658 // remove the hover-highlight styling
84660 utilHighlightEntities([d.relation.id], false, context);
84661 var indexes = d.members.map(function (member) {
84662 return member.index;
84664 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
84665 n: _entityIDs.length
84667 context.validator().validate();
84670 function fetchNearbyRelations(q, callback) {
84671 var newRelation = {
84673 value: _t('inspector.new_relation'),
84674 display: _t.html('inspector.new_relation')
84676 var entityID = _entityIDs[0];
84678 var graph = context.graph();
84680 function baseDisplayLabel(entity) {
84681 var matched = _mainPresetIndex.match(entity, graph);
84682 var presetName = matched && matched.name() || _t('inspector.relation');
84683 var entityName = utilDisplayName(entity) || '';
84684 return presetName + ' ' + entityName;
84687 var explicitRelation = q && context.hasEntity(q.toLowerCase());
84689 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
84690 // loaded relation is specified explicitly, only show that
84692 relation: explicitRelation,
84693 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
84696 context.history().intersects(context.map().extent()).forEach(function (entity) {
84697 if (entity.type !== 'relation' || entity.id === entityID) return;
84698 var value = baseDisplayLabel(entity);
84699 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
84705 result.sort(function (a, b) {
84706 return osmRelation.creationOrder(a.relation, b.relation);
84707 }); // Dedupe identical names by appending relation id - see #2891
84709 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
84710 return v.length > 1;
84712 dupeGroups.forEach(function (group) {
84713 group.forEach(function (obj) {
84714 obj.value += ' ' + obj.relation.id;
84719 result.forEach(function (obj) {
84720 obj.title = obj.value;
84722 result.unshift(newRelation);
84726 function renderDisclosureContent(selection) {
84727 var memberships = getMemberships();
84728 var list = selection.selectAll('.member-list').data([0]);
84729 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
84730 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
84733 items.exit().each(unbind).remove(); // Enter
84735 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
84737 itemsEnter.on('mouseover', function (d3_event, d) {
84738 utilHighlightEntities([d.relation.id], true, context);
84739 }).on('mouseout', function (d3_event, d) {
84740 utilHighlightEntities([d.relation.id], false, context);
84742 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
84745 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
84746 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
84747 var matched = _mainPresetIndex.match(d.relation, context.graph());
84748 return matched && matched.name() || _t('inspector.relation');
84750 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
84751 return utilDisplayName(d.relation);
84753 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
84754 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
84755 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
84756 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
84758 }).property('type', 'text').property('value', function (d) {
84759 return typeof d.role === 'string' ? d.role : '';
84760 }).attr('title', function (d) {
84761 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
84762 }).attr('placeholder', function (d) {
84763 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
84764 }).classed('mixed', function (d) {
84765 return Array.isArray(d.role);
84766 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
84769 wrapEnter.each(bindTypeahead);
84772 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
84774 newMembership.exit().remove(); // Enter
84776 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
84777 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
84778 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
84779 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
84780 list.selectAll('.member-row-new').remove();
84782 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
84783 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
84785 newMembership = newMembership.merge(newMembershipEnter);
84786 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
84787 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
84789 var addRow = selection.selectAll('.add-row').data([0]); // enter
84791 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
84792 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
84793 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
84794 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
84795 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
84797 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
84800 addRow = addRow.merge(addRowEnter);
84801 addRow.select('.add-relation').on('click', function () {
84803 section.reRender();
84804 list.selectAll('.member-entity-input').node().focus();
84807 function acceptEntity(d) {
84811 } // remove hover-higlighting
84814 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
84815 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
84816 addMembership(d, role);
84819 function cancelEntity() {
84820 var input = newMembership.selectAll('.member-entity-input');
84821 input.property('value', ''); // remove hover-higlighting
84823 context.surface().selectAll('.highlighted').classed('highlighted', false);
84826 function bindTypeahead(d) {
84827 var row = select(this);
84828 var role = row.selectAll('input.member-role');
84829 var origValue = role.property('value');
84831 function sort(value, data) {
84832 var sameletter = [];
84835 for (var i = 0; i < data.length; i++) {
84836 if (data[i].value.substring(0, value.length) === value) {
84837 sameletter.push(data[i]);
84839 other.push(data[i]);
84843 return sameletter.concat(other);
84846 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
84847 var rtype = d.relation.tags.type;
84850 rtype: rtype || '',
84851 geometry: context.graph().geometry(_entityIDs[0]),
84853 }, function (err, data) {
84854 if (!err) callback(sort(role, data));
84856 }).on('cancel', function () {
84857 role.property('value', origValue);
84861 function unbind() {
84862 var row = select(this);
84863 row.selectAll('input.member-role').call(uiCombobox.off, context);
84867 section.entityIDs = function (val) {
84868 if (!arguments.length) return _entityIDs;
84870 _showBlank = false;
84877 function uiSectionSelectionList(context) {
84878 var _selectedIDs = [];
84879 var section = uiSection('selected-features', context).shouldDisplay(function () {
84880 return _selectedIDs.length > 1;
84881 }).label(function () {
84882 return _t('inspector.title_count', {
84883 title: _t.html('inspector.features'),
84884 count: _selectedIDs.length
84886 }).disclosureContent(renderDisclosureContent);
84887 context.history().on('change.selectionList', function (difference) {
84889 section.reRender();
84893 section.entityIDs = function (val) {
84894 if (!arguments.length) return _selectedIDs;
84895 _selectedIDs = val;
84899 function selectEntity(d3_event, entity) {
84900 context.enter(modeSelect(context, [entity.id]));
84903 function deselectEntity(d3_event, entity) {
84904 var selectedIDs = _selectedIDs.slice();
84906 var index = selectedIDs.indexOf(entity.id);
84909 selectedIDs.splice(index, 1);
84910 context.enter(modeSelect(context, selectedIDs));
84914 function renderDisclosureContent(selection) {
84915 var list = selection.selectAll('.feature-list').data([0]);
84916 list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
84918 var entities = _selectedIDs.map(function (id) {
84919 return context.hasEntity(id);
84920 }).filter(Boolean);
84922 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
84923 items.exit().remove(); // Enter
84925 var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
84926 select(this).on('mouseover', function () {
84927 utilHighlightEntities([d.id], true, context);
84928 }).on('mouseout', function () {
84929 utilHighlightEntities([d.id], false, context);
84932 var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
84933 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
84934 label.append('span').attr('class', 'entity-type');
84935 label.append('span').attr('class', 'entity-name');
84936 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
84938 items = items.merge(enter);
84939 items.selectAll('.entity-geom-icon use').attr('href', function () {
84940 var entity = this.parentNode.parentNode.__data__;
84941 return '#iD-icon-' + entity.geometry(context.graph());
84943 items.selectAll('.entity-type').html(function (entity) {
84944 return _mainPresetIndex.match(entity, context.graph()).name();
84946 items.selectAll('.entity-name').html(function (d) {
84947 // fetch latest entity
84948 var entity = context.entity(d.id);
84949 return utilDisplayName(entity);
84956 function uiEntityEditor(context) {
84957 var dispatch = dispatch$8('choose');
84958 var _state = 'select';
84959 var _coalesceChanges = false;
84960 var _modified = false;
84966 var _activePresets = [];
84972 function entityEditor(selection) {
84973 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
84975 var header = selection.selectAll('.header').data([0]); // Enter
84977 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
84978 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
84979 headerEnter.append('button').attr('class', 'close').on('click', function () {
84980 context.enter(modeBrowse(context));
84981 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
84982 headerEnter.append('h3'); // Update
84984 header = header.merge(headerEnter);
84985 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
84986 header.selectAll('.preset-reset').on('click', function () {
84987 dispatch.call('choose', this, _activePresets);
84990 var body = selection.selectAll('.inspector-body').data([0]); // Enter
84992 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
84994 body = body.merge(bodyEnter);
84997 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
84998 dispatch.call('choose', this, presets);
84999 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
85002 _sections.forEach(function (section) {
85003 if (section.entityIDs) {
85004 section.entityIDs(_entityIDs);
85007 if (section.presets) {
85008 section.presets(_activePresets);
85011 if (section.tags) {
85012 section.tags(combinedTags);
85015 if (section.state) {
85016 section.state(_state);
85019 body.call(section.render);
85022 context.history().on('change.entity-editor', historyChanged);
85024 function historyChanged(difference) {
85025 if (selection.selectAll('.entity-editor').empty()) return;
85026 if (_state === 'hide') return;
85027 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
85028 if (!significant) return;
85029 _entityIDs = _entityIDs.filter(context.hasEntity);
85030 if (!_entityIDs.length) return;
85031 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
85032 loadActivePresets();
85033 var graph = context.graph();
85034 entityEditor.modified(_base !== graph);
85035 entityEditor(selection);
85037 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
85038 // flash the button to indicate the preset changed
85039 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
85042 } // Tag changes that fire on input can all get coalesced into a single
85043 // history operation when the user leaves the field. #2342
85044 // Use explicit entityIDs in case the selection changes before the event is fired.
85047 function changeTags(entityIDs, changed, onInput) {
85050 for (var i in entityIDs) {
85051 var entityID = entityIDs[i];
85052 var entity = context.entity(entityID);
85053 var tags = Object.assign({}, entity.tags); // shallow copy
85055 for (var k in changed) {
85057 var v = changed[k];
85059 if (v !== undefined || tags.hasOwnProperty(k)) {
85065 tags = utilCleanTags(tags);
85068 if (!fastDeepEqual(entity.tags, tags)) {
85069 actions.push(actionChangeTags(entityID, tags));
85073 if (actions.length) {
85074 var combinedAction = function combinedAction(graph) {
85075 actions.forEach(function (action) {
85076 graph = action(graph);
85081 var annotation = _t('operations.change_tags.annotation');
85083 if (_coalesceChanges) {
85084 context.overwrite(combinedAction, annotation);
85086 context.perform(combinedAction, annotation);
85087 _coalesceChanges = !!onInput;
85089 } // if leaving field (blur event), rerun validation
85093 context.validator().validate();
85097 function revertTags(keys) {
85100 for (var i in _entityIDs) {
85101 var entityID = _entityIDs[i];
85102 var original = context.graph().base().entities[entityID];
85105 for (var j in keys) {
85107 changed[key] = original ? original.tags[key] : undefined;
85110 var entity = context.entity(entityID);
85111 var tags = Object.assign({}, entity.tags); // shallow copy
85113 for (var k in changed) {
85115 var v = changed[k];
85117 if (v !== undefined || tags.hasOwnProperty(k)) {
85122 tags = utilCleanTags(tags);
85124 if (!fastDeepEqual(entity.tags, tags)) {
85125 actions.push(actionChangeTags(entityID, tags));
85129 if (actions.length) {
85130 var combinedAction = function combinedAction(graph) {
85131 actions.forEach(function (action) {
85132 graph = action(graph);
85137 var annotation = _t('operations.change_tags.annotation');
85139 if (_coalesceChanges) {
85140 context.overwrite(combinedAction, annotation);
85142 context.perform(combinedAction, annotation);
85143 _coalesceChanges = false;
85147 context.validator().validate();
85150 entityEditor.modified = function (val) {
85151 if (!arguments.length) return _modified;
85153 return entityEditor;
85156 entityEditor.state = function (val) {
85157 if (!arguments.length) return _state;
85159 return entityEditor;
85162 entityEditor.entityIDs = function (val) {
85163 if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
85164 // could be reselecting after something like dragging a node
85166 _base = context.graph();
85167 _coalesceChanges = false;
85168 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
85171 loadActivePresets(true);
85172 return entityEditor.modified(false);
85175 entityEditor.newFeature = function (val) {
85176 if (!arguments.length) return _newFeature;
85178 return entityEditor;
85181 function loadActivePresets(isForNewSelection) {
85182 var graph = context.graph();
85185 for (var i in _entityIDs) {
85186 var entity = graph.hasEntity(_entityIDs[i]);
85187 if (!entity) return;
85188 var match = _mainPresetIndex.match(entity, graph);
85189 if (!counts[match.id]) counts[match.id] = 0;
85190 counts[match.id] += 1;
85193 var matches = Object.keys(counts).sort(function (p1, p2) {
85194 return counts[p2] - counts[p1];
85195 }).map(function (pID) {
85196 return _mainPresetIndex.item(pID);
85199 if (!isForNewSelection) {
85200 // A "weak" preset doesn't set any tags. (e.g. "Address")
85201 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")
85203 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
85206 entityEditor.presets(matches);
85209 entityEditor.presets = function (val) {
85210 if (!arguments.length) return _activePresets; // don't reload the same preset
85212 if (!utilArrayIdentical(val, _activePresets)) {
85213 _activePresets = val;
85216 return entityEditor;
85219 return utilRebind(entityEditor, dispatch, 'on');
85222 function uiPresetList(context) {
85223 var dispatch = dispatch$8('cancel', 'choose');
85229 var _currentPresets;
85231 var _autofocus = false;
85233 function presetList(selection) {
85234 if (!_entityIDs) return;
85235 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
85236 selection.html('');
85237 var messagewrap = selection.append('div').attr('class', 'header fillL');
85238 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
85239 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
85240 dispatch.call('cancel', this);
85241 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
85243 function initialKeydown(d3_event) {
85244 // hack to let delete shortcut work when search is autofocused
85245 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
85246 d3_event.preventDefault();
85247 d3_event.stopPropagation();
85248 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
85249 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
85250 d3_event.preventDefault();
85251 d3_event.stopPropagation();
85253 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
85254 // don't check for delete/undo hack on future keydown events
85255 select(this).on('keydown', keydown);
85256 keydown.call(this, d3_event);
85260 function keydown(d3_event) {
85262 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
85263 search.node().selectionStart === search.property('value').length) {
85264 d3_event.preventDefault();
85265 d3_event.stopPropagation(); // move focus to the first item in the preset list
85267 var buttons = list.selectAll('.preset-list-button');
85268 if (!buttons.empty()) buttons.nodes()[0].focus();
85272 function keypress(d3_event) {
85274 var value = search.property('value');
85276 if (d3_event.keyCode === 13 && // ↩ Return
85278 list.selectAll('.preset-list-item:first-child').each(function (d) {
85279 d.choose.call(this);
85284 function inputevent() {
85285 var value = search.property('value');
85286 list.classed('filtered', value.length);
85287 var results, messageText;
85289 if (value.length) {
85290 results = presets.search(value, entityGeometries()[0], _currLoc);
85291 messageText = _t('inspector.results', {
85292 n: results.collection.length,
85296 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc);
85297 messageText = _t('inspector.choose');
85300 list.call(drawList, results);
85301 message.html(messageText);
85304 var searchWrap = selection.append('div').attr('class', 'search-header');
85305 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
85306 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);
85309 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
85310 // so try again on the next pass
85312 setTimeout(function () {
85313 search.node().focus();
85317 var listWrap = selection.append('div').attr('class', 'inspector-body');
85318 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc));
85319 context.features().on('change.preset-list', updateForFeatureHiddenState);
85322 function drawList(list, presets) {
85323 presets = presets.matchAllGeometry(entityGeometries());
85324 var collection = presets.collection.reduce(function (collection, preset) {
85325 if (!preset) return collection;
85327 if (preset.members) {
85328 if (preset.members.collection.filter(function (preset) {
85329 return preset.addable();
85331 collection.push(CategoryItem(preset));
85333 } else if (preset.addable()) {
85334 collection.push(PresetItem(preset));
85339 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
85340 return d.preset.id;
85343 items.exit().remove();
85344 items.enter().append('div').attr('class', function (item) {
85345 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
85346 }).classed('current', function (item) {
85347 return _currentPresets.indexOf(item.preset) !== -1;
85348 }).each(function (item) {
85349 select(this).call(item);
85350 }).style('opacity', 0).transition().style('opacity', 1);
85351 updateForFeatureHiddenState();
85354 function itemKeydown(d3_event) {
85355 // the actively focused item
85356 var item = select(this.closest('.preset-list-item'));
85357 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
85359 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
85360 d3_event.preventDefault();
85361 d3_event.stopPropagation(); // the next item in the list at the same level
85363 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
85365 if (nextItem.empty()) {
85366 // if there is a parent item
85367 if (!parentItem.empty()) {
85368 // the item is the last item of a sublist,
85369 // select the next item at the parent level
85370 nextItem = select(parentItem.node().nextElementSibling);
85371 } // if the focused item is expanded
85373 } else if (select(this).classed('expanded')) {
85374 // select the first subitem instead
85375 nextItem = item.select('.subgrid .preset-list-item:first-child');
85378 if (!nextItem.empty()) {
85379 // focus on the next item
85380 nextItem.select('.preset-list-button').node().focus();
85381 } // arrow up, move focus to the previous, higher item
85383 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
85384 d3_event.preventDefault();
85385 d3_event.stopPropagation(); // the previous item in the list at the same level
85387 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
85389 if (previousItem.empty()) {
85390 // if there is a parent item
85391 if (!parentItem.empty()) {
85392 // the item is the first subitem of a sublist select the parent item
85393 previousItem = parentItem;
85394 } // if the previous item is expanded
85396 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
85397 // select the last subitem of the sublist of the previous item
85398 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
85401 if (!previousItem.empty()) {
85402 // focus on the previous item
85403 previousItem.select('.preset-list-button').node().focus();
85405 // the focus is at the top of the list, move focus back to the search field
85406 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
85407 search.node().focus();
85408 } // arrow left, move focus to the parent item if there is one
85410 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
85411 d3_event.preventDefault();
85412 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
85414 if (!parentItem.empty()) {
85415 parentItem.select('.preset-list-button').node().focus();
85416 } // arrow right, choose this item
85418 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
85419 d3_event.preventDefault();
85420 d3_event.stopPropagation();
85421 item.datum().choose.call(select(this).node());
85425 function CategoryItem(preset) {
85430 function item(selection) {
85431 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
85434 var isExpanded = select(this).classed('expanded');
85435 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
85436 select(this).classed('expanded', !isExpanded);
85437 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
85441 var geometries = entityGeometries();
85442 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) {
85443 // right arrow, expand the focused item
85444 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
85445 d3_event.preventDefault();
85446 d3_event.stopPropagation(); // if the item isn't expanded
85448 if (!select(this).classed('expanded')) {
85449 // toggle expansion (expand the item)
85450 click.call(this, d3_event);
85451 } // left arrow, collapse the focused item
85453 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
85454 d3_event.preventDefault();
85455 d3_event.stopPropagation(); // if the item is expanded
85457 if (select(this).classed('expanded')) {
85458 // toggle expansion (collapse the item)
85459 click.call(this, d3_event);
85462 itemKeydown.call(this, d3_event);
85465 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
85466 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
85467 return preset.nameLabel() + '…';
85469 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
85470 box.append('div').attr('class', 'arrow');
85471 sublist = box.append('div').attr('class', 'preset-list fillL3');
85474 item.choose = function () {
85475 if (!box || !sublist) return;
85479 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
85482 var members = preset.members.matchAllGeometry(entityGeometries());
85483 sublist.call(drawList, members);
85484 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
85488 item.preset = preset;
85492 function PresetItem(preset) {
85493 function item(selection) {
85494 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
85495 var geometries = entityGeometries();
85496 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);
85497 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
85498 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
85499 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
85502 wrap.call(item.reference.button);
85503 selection.call(item.reference.body);
85506 item.choose = function () {
85507 if (select(this).classed('disabled')) return;
85509 if (!context.inIntro()) {
85510 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
85513 context.perform(function (graph) {
85514 for (var i in _entityIDs) {
85515 var entityID = _entityIDs[i];
85516 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
85517 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
85521 }, _t('operations.change_tags.annotation'));
85522 context.validator().validate(); // rerun validation
85524 dispatch.call('choose', this, preset);
85527 item.help = function (d3_event) {
85528 d3_event.stopPropagation();
85529 item.reference.toggle();
85532 item.preset = preset;
85533 item.reference = uiTagReference(preset.reference());
85537 function updateForFeatureHiddenState() {
85538 if (!_entityIDs.every(context.hasEntity)) return;
85539 var geometries = entityGeometries();
85540 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
85542 button.call(uiTooltip().destroyAny);
85543 button.each(function (item, index) {
85544 var hiddenPresetFeaturesId;
85546 for (var i in geometries) {
85547 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
85548 if (hiddenPresetFeaturesId) break;
85551 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
85552 select(this).classed('disabled', isHiddenPreset);
85554 if (isHiddenPreset) {
85555 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
85556 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
85557 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
85558 })).placement(index < 2 ? 'bottom' : 'top'));
85563 presetList.autofocus = function (val) {
85564 if (!arguments.length) return _autofocus;
85569 presetList.entityIDs = function (val) {
85570 if (!arguments.length) return _entityIDs;
85574 if (_entityIDs && _entityIDs.length) {
85575 // calculate current location
85576 var extent = _entityIDs.reduce(function (extent, entityID) {
85577 var entity = context.graph().entity(entityID);
85578 return extent.extend(entity.extent(context.graph()));
85581 _currLoc = extent.center(); // match presets
85583 var presets = _entityIDs.map(function (entityID) {
85584 return _mainPresetIndex.match(context.entity(entityID), context.graph());
85587 presetList.presets(presets);
85593 presetList.presets = function (val) {
85594 if (!arguments.length) return _currentPresets;
85595 _currentPresets = val;
85599 function entityGeometries() {
85602 for (var i in _entityIDs) {
85603 var entityID = _entityIDs[i];
85604 var entity = context.entity(entityID);
85605 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
85607 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
85608 geometry = 'point';
85611 if (!counts[geometry]) counts[geometry] = 0;
85612 counts[geometry] += 1;
85615 return Object.keys(counts).sort(function (geom1, geom2) {
85616 return counts[geom2] - counts[geom1];
85620 return utilRebind(presetList, dispatch, 'on');
85623 function uiViewOnOSM(context) {
85624 var _what; // an osmEntity or osmNote
85627 function viewOnOSM(selection) {
85630 if (_what instanceof osmEntity) {
85631 url = context.connection().entityURL(_what);
85632 } else if (_what instanceof osmNote) {
85633 url = context.connection().noteURL(_what);
85636 var data = !_what || _what.isNew() ? [] : [_what];
85637 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
85641 link.exit().remove(); // enter
85643 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
85644 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
85647 viewOnOSM.what = function (_) {
85648 if (!arguments.length) return _what;
85656 function uiInspector(context) {
85657 var presetList = uiPresetList(context);
85658 var entityEditor = uiEntityEditor(context);
85659 var wrap = select(null),
85660 presetPane = select(null),
85661 editorPane = select(null);
85662 var _state = 'select';
85666 var _newFeature = false;
85668 function inspector(selection) {
85669 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
85670 inspector.setPreset();
85672 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
85673 wrap = selection.selectAll('.panewrap').data([0]);
85674 var enter = wrap.enter().append('div').attr('class', 'panewrap');
85675 enter.append('div').attr('class', 'preset-list-pane pane');
85676 enter.append('div').attr('class', 'entity-editor-pane pane');
85677 wrap = wrap.merge(enter);
85678 presetPane = wrap.selectAll('.preset-list-pane');
85679 editorPane = wrap.selectAll('.entity-editor-pane');
85681 function shouldDefaultToPresetList() {
85682 // always show the inspector on hover
85683 if (_state !== 'select') return false; // can only change preset on single selection
85685 if (_entityIDs.length !== 1) return false;
85686 var entityID = _entityIDs[0];
85687 var entity = context.hasEntity(entityID);
85688 if (!entity) return false; // default to inspector if there are already tags
85690 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
85692 if (_newFeature) return true; // all existing features except vertices should default to inspector
85694 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
85696 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
85698 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
85700 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
85705 if (shouldDefaultToPresetList()) {
85706 wrap.style('right', '-100%');
85707 editorPane.classed('hide', true);
85708 presetPane.classed('hide', false).call(presetList);
85710 wrap.style('right', '0%');
85711 presetPane.classed('hide', true);
85712 editorPane.classed('hide', false).call(entityEditor);
85715 var footer = selection.selectAll('.footer').data([0]);
85716 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
85717 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
85720 inspector.showList = function (presets) {
85721 presetPane.classed('hide', false);
85722 wrap.transition().styleTween('right', function () {
85723 return interpolate$1('0%', '-100%');
85724 }).on('end', function () {
85725 editorPane.classed('hide', true);
85729 presetList.presets(presets);
85732 presetPane.call(presetList.autofocus(true));
85735 inspector.setPreset = function (preset) {
85736 // upon setting multipolygon, go to the area preset list instead of the editor
85737 if (preset && preset.id === 'type/multipolygon') {
85738 presetPane.call(presetList.autofocus(true));
85740 editorPane.classed('hide', false);
85741 wrap.transition().styleTween('right', function () {
85742 return interpolate$1('-100%', '0%');
85743 }).on('end', function () {
85744 presetPane.classed('hide', true);
85748 entityEditor.presets([preset]);
85751 editorPane.call(entityEditor);
85755 inspector.state = function (val) {
85756 if (!arguments.length) return _state;
85758 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
85760 context.container().selectAll('.field-help-body').remove();
85764 inspector.entityIDs = function (val) {
85765 if (!arguments.length) return _entityIDs;
85770 inspector.newFeature = function (val) {
85771 if (!arguments.length) return _newFeature;
85779 function uiImproveOsmComments() {
85782 function issueComments(selection) {
85783 // make the div immediately so it appears above the buttons
85784 var comments = selection.selectAll('.comments-container').data([0]);
85785 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
85787 services.improveOSM.getComments(_qaItem).then(function (d) {
85788 if (!d.comments) return; // nothing to do here
85790 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
85791 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
85792 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
85793 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
85794 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
85795 var osm = services.osm;
85796 var selection = select(this);
85798 if (osm && d.username) {
85799 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
85802 selection.html(function (d) {
85806 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
85807 return _t.html('note.status.commented', {
85808 when: localeDateString(d.timestamp)
85811 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
85814 })["catch"](function (err) {
85815 console.log(err); // eslint-disable-line no-console
85819 function localeDateString(s) {
85820 if (!s) return null;
85826 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
85828 if (isNaN(d.getTime())) return null;
85829 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
85832 issueComments.issue = function (val) {
85833 if (!arguments.length) return _qaItem;
85835 return issueComments;
85838 return issueComments;
85841 function uiImproveOsmDetails(context) {
85844 function issueDetail(d) {
85845 if (d.desc) return d.desc;
85846 var issueKey = d.issueKey;
85847 d.replacements = d.replacements || {};
85848 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
85850 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
85853 function improveOsmDetails(selection) {
85854 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
85855 return "".concat(d.id, "-").concat(d.status || 0);
85857 details.exit().remove();
85858 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
85860 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
85861 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
85862 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
85864 var relatedEntities = [];
85865 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
85866 var link = select(this);
85867 var isObjectLink = link.classed('error_object_link');
85868 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
85869 var entity = context.hasEntity(entityID);
85870 relatedEntities.push(entityID); // Add click handler
85872 link.on('mouseenter', function () {
85873 utilHighlightEntities([entityID], true, context);
85874 }).on('mouseleave', function () {
85875 utilHighlightEntities([entityID], false, context);
85876 }).on('click', function (d3_event) {
85877 d3_event.preventDefault();
85878 utilHighlightEntities([entityID], false, context);
85879 var osmlayer = context.layers().layer('osm');
85881 if (!osmlayer.enabled()) {
85882 osmlayer.enabled(true);
85885 context.map().centerZoom(_qaItem.loc, 20);
85888 context.enter(modeSelect(context, [entityID]));
85890 context.loadEntity(entityID, function (err, result) {
85892 var entity = result.data.find(function (e) {
85893 return e.id === entityID;
85895 if (entity) context.enter(modeSelect(context, [entityID]));
85898 }); // Replace with friendly name if possible
85899 // (The entity may not yet be loaded into the graph)
85902 var name = utilDisplayName(entity); // try to use common name
85904 if (!name && !isObjectLink) {
85905 var preset = _mainPresetIndex.match(entity, context.graph());
85906 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
85910 this.innerText = name;
85913 }); // Don't hide entities related to this error - #5880
85915 context.features().forceVisible(relatedEntities);
85916 context.map().pan([0, 0]); // trigger a redraw
85919 improveOsmDetails.issue = function (val) {
85920 if (!arguments.length) return _qaItem;
85922 return improveOsmDetails;
85925 return improveOsmDetails;
85928 function uiImproveOsmHeader() {
85931 function issueTitle(d) {
85932 var issueKey = d.issueKey;
85933 d.replacements = d.replacements || {};
85934 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
85936 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
85939 function improveOsmHeader(selection) {
85940 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
85941 return "".concat(d.id, "-").concat(d.status || 0);
85943 header.exit().remove();
85944 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
85945 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
85947 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
85948 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
85950 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');
85951 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
85952 var picon = d.icon;
85957 var isMaki = /^maki-/.test(picon);
85958 return "#".concat(picon).concat(isMaki ? '-11' : '');
85961 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
85964 improveOsmHeader.issue = function (val) {
85965 if (!arguments.length) return _qaItem;
85967 return improveOsmHeader;
85970 return improveOsmHeader;
85973 function uiImproveOsmEditor(context) {
85974 var dispatch = dispatch$8('change');
85975 var qaDetails = uiImproveOsmDetails(context);
85976 var qaComments = uiImproveOsmComments();
85977 var qaHeader = uiImproveOsmHeader();
85981 function improveOsmEditor(selection) {
85982 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
85983 headerEnter.append('button').attr('class', 'close').on('click', function () {
85984 return context.enter(modeBrowse(context));
85985 }).call(svgIcon('#iD-icon-close'));
85986 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
85987 var body = selection.selectAll('.body').data([0]);
85988 body = body.enter().append('div').attr('class', 'body').merge(body);
85989 var editor = body.selectAll('.qa-editor').data([0]);
85990 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);
85993 function improveOsmSaveSection(selection) {
85994 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
85996 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
85997 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
85998 return "".concat(d.id, "-").concat(d.status || 0);
86001 saveSection.exit().remove(); // enter
86003 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
86004 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
86005 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
86006 return d.newComment;
86007 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
86009 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
86011 function changeInput() {
86012 var input = select(this);
86013 var val = input.property('value').trim();
86017 } // store the unsaved comment with the issue itself
86020 _qaItem = _qaItem.update({
86023 var qaService = services.improveOSM;
86026 qaService.replaceItem(_qaItem);
86029 saveSection.call(qaSaveButtons);
86033 function qaSaveButtons(selection) {
86034 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
86036 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
86037 return d.status + d.id;
86040 buttonSection.exit().remove(); // enter
86042 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
86043 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
86044 buttonEnter.append('button').attr('class', 'button close-button action');
86045 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
86047 buttonSection = buttonSection.merge(buttonEnter);
86048 buttonSection.select('.comment-button').attr('disabled', function (d) {
86049 return d.newComment ? null : true;
86050 }).on('click.comment', function (d3_event, d) {
86051 this.blur(); // avoid keeping focus on the button - #4641
86053 var qaService = services.improveOSM;
86056 qaService.postUpdate(d, function (err, item) {
86057 return dispatch.call('change', item);
86061 buttonSection.select('.close-button').html(function (d) {
86062 var andComment = d.newComment ? '_comment' : '';
86063 return _t.html("QA.keepRight.close".concat(andComment));
86064 }).on('click.close', function (d3_event, d) {
86065 this.blur(); // avoid keeping focus on the button - #4641
86067 var qaService = services.improveOSM;
86070 d.newStatus = 'SOLVED';
86071 qaService.postUpdate(d, function (err, item) {
86072 return dispatch.call('change', item);
86076 buttonSection.select('.ignore-button').html(function (d) {
86077 var andComment = d.newComment ? '_comment' : '';
86078 return _t.html("QA.keepRight.ignore".concat(andComment));
86079 }).on('click.ignore', function (d3_event, d) {
86080 this.blur(); // avoid keeping focus on the button - #4641
86082 var qaService = services.improveOSM;
86085 d.newStatus = 'INVALID';
86086 qaService.postUpdate(d, function (err, item) {
86087 return dispatch.call('change', item);
86091 } // NOTE: Don't change method name until UI v3 is merged
86094 improveOsmEditor.error = function (val) {
86095 if (!arguments.length) return _qaItem;
86097 return improveOsmEditor;
86100 return utilRebind(improveOsmEditor, dispatch, 'on');
86103 function uiKeepRightDetails(context) {
86106 function issueDetail(d) {
86107 var itemType = d.itemType,
86108 parentIssueType = d.parentIssueType;
86109 var unknown = _t.html('inspector.unknown');
86110 var replacements = d.replacements || {};
86111 replacements["default"] = unknown; // special key `default` works as a fallback string
86113 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
86115 if (detail === unknown) {
86116 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
86122 function keepRightDetails(selection) {
86123 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
86124 return "".concat(d.id, "-").concat(d.status || 0);
86126 details.exit().remove();
86127 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
86129 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
86130 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
86131 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
86133 var relatedEntities = [];
86134 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
86135 var link = select(this);
86136 var isObjectLink = link.classed('error_object_link');
86137 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
86138 var entity = context.hasEntity(entityID);
86139 relatedEntities.push(entityID); // Add click handler
86141 link.on('mouseenter', function () {
86142 utilHighlightEntities([entityID], true, context);
86143 }).on('mouseleave', function () {
86144 utilHighlightEntities([entityID], false, context);
86145 }).on('click', function (d3_event) {
86146 d3_event.preventDefault();
86147 utilHighlightEntities([entityID], false, context);
86148 var osmlayer = context.layers().layer('osm');
86150 if (!osmlayer.enabled()) {
86151 osmlayer.enabled(true);
86154 context.map().centerZoomEase(_qaItem.loc, 20);
86157 context.enter(modeSelect(context, [entityID]));
86159 context.loadEntity(entityID, function (err, result) {
86161 var entity = result.data.find(function (e) {
86162 return e.id === entityID;
86164 if (entity) context.enter(modeSelect(context, [entityID]));
86167 }); // Replace with friendly name if possible
86168 // (The entity may not yet be loaded into the graph)
86171 var name = utilDisplayName(entity); // try to use common name
86173 if (!name && !isObjectLink) {
86174 var preset = _mainPresetIndex.match(entity, context.graph());
86175 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
86179 this.innerText = name;
86182 }); // Don't hide entities related to this issue - #5880
86184 context.features().forceVisible(relatedEntities);
86185 context.map().pan([0, 0]); // trigger a redraw
86188 keepRightDetails.issue = function (val) {
86189 if (!arguments.length) return _qaItem;
86191 return keepRightDetails;
86194 return keepRightDetails;
86197 function uiKeepRightHeader() {
86200 function issueTitle(d) {
86201 var itemType = d.itemType,
86202 parentIssueType = d.parentIssueType;
86203 var unknown = _t.html('inspector.unknown');
86204 var replacements = d.replacements || {};
86205 replacements["default"] = unknown; // special key `default` works as a fallback string
86207 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
86209 if (title === unknown) {
86210 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
86216 function keepRightHeader(selection) {
86217 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
86218 return "".concat(d.id, "-").concat(d.status || 0);
86220 header.exit().remove();
86221 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
86222 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
86225 iconEnter.append('div').attr('class', function (d) {
86226 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
86227 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
86228 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
86231 keepRightHeader.issue = function (val) {
86232 if (!arguments.length) return _qaItem;
86234 return keepRightHeader;
86237 return keepRightHeader;
86240 function uiViewOnKeepRight() {
86243 function viewOnKeepRight(selection) {
86246 if (services.keepRight && _qaItem instanceof QAItem) {
86247 url = services.keepRight.issueURL(_qaItem);
86250 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
86252 link.exit().remove(); // enter
86254 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
86255 .attr('href', function (d) {
86257 }).call(svgIcon('#iD-icon-out-link', 'inline'));
86258 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
86261 viewOnKeepRight.what = function (val) {
86262 if (!arguments.length) return _qaItem;
86264 return viewOnKeepRight;
86267 return viewOnKeepRight;
86270 function uiKeepRightEditor(context) {
86271 var dispatch = dispatch$8('change');
86272 var qaDetails = uiKeepRightDetails(context);
86273 var qaHeader = uiKeepRightHeader();
86277 function keepRightEditor(selection) {
86278 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
86279 headerEnter.append('button').attr('class', 'close').on('click', function () {
86280 return context.enter(modeBrowse(context));
86281 }).call(svgIcon('#iD-icon-close'));
86282 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
86283 var body = selection.selectAll('.body').data([0]);
86284 body = body.enter().append('div').attr('class', 'body').merge(body);
86285 var editor = body.selectAll('.qa-editor').data([0]);
86286 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
86287 var footer = selection.selectAll('.footer').data([0]);
86288 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
86291 function keepRightSaveSection(selection) {
86292 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
86294 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
86295 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
86296 return "".concat(d.id, "-").concat(d.status || 0);
86299 saveSection.exit().remove(); // enter
86301 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
86302 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
86303 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
86304 return d.newComment || d.comment;
86305 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
86307 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
86309 function changeInput() {
86310 var input = select(this);
86311 var val = input.property('value').trim();
86313 if (val === _qaItem.comment) {
86315 } // store the unsaved comment with the issue itself
86318 _qaItem = _qaItem.update({
86321 var qaService = services.keepRight;
86324 qaService.replaceItem(_qaItem); // update keepright cache
86327 saveSection.call(qaSaveButtons);
86331 function qaSaveButtons(selection) {
86332 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
86334 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
86335 return d.status + d.id;
86338 buttonSection.exit().remove(); // enter
86340 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
86341 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
86342 buttonEnter.append('button').attr('class', 'button close-button action');
86343 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
86345 buttonSection = buttonSection.merge(buttonEnter);
86346 buttonSection.select('.comment-button') // select and propagate data
86347 .attr('disabled', function (d) {
86348 return d.newComment ? null : true;
86349 }).on('click.comment', function (d3_event, d) {
86350 this.blur(); // avoid keeping focus on the button - #4641
86352 var qaService = services.keepRight;
86355 qaService.postUpdate(d, function (err, item) {
86356 return dispatch.call('change', item);
86360 buttonSection.select('.close-button') // select and propagate data
86361 .html(function (d) {
86362 var andComment = d.newComment ? '_comment' : '';
86363 return _t.html("QA.keepRight.close".concat(andComment));
86364 }).on('click.close', function (d3_event, d) {
86365 this.blur(); // avoid keeping focus on the button - #4641
86367 var qaService = services.keepRight;
86370 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
86372 qaService.postUpdate(d, function (err, item) {
86373 return dispatch.call('change', item);
86377 buttonSection.select('.ignore-button') // select and propagate data
86378 .html(function (d) {
86379 var andComment = d.newComment ? '_comment' : '';
86380 return _t.html("QA.keepRight.ignore".concat(andComment));
86381 }).on('click.ignore', function (d3_event, d) {
86382 this.blur(); // avoid keeping focus on the button - #4641
86384 var qaService = services.keepRight;
86387 d.newStatus = 'ignore'; // ignore permanently (false positive)
86389 qaService.postUpdate(d, function (err, item) {
86390 return dispatch.call('change', item);
86394 } // NOTE: Don't change method name until UI v3 is merged
86397 keepRightEditor.error = function (val) {
86398 if (!arguments.length) return _qaItem;
86400 return keepRightEditor;
86403 return utilRebind(keepRightEditor, dispatch, 'on');
86406 function uiOsmoseDetails(context) {
86409 function issueString(d, type) {
86410 if (!d) return ''; // Issue strings are cached from Osmose API
86412 var s = services.osmose.getStrings(d.itemType);
86413 return type in s ? s[type] : '';
86416 function osmoseDetails(selection) {
86417 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
86418 return "".concat(d.id, "-").concat(d.status || 0);
86420 details.exit().remove();
86421 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
86423 if (issueString(_qaItem, 'detail')) {
86424 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
86425 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
86426 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
86427 return issueString(d, 'detail');
86428 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
86429 } // Elements (populated later as data is requested)
86432 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
86433 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
86435 if (issueString(_qaItem, 'fix')) {
86436 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
86438 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
86440 _div.append('p').html(function (d) {
86441 return issueString(d, 'fix');
86442 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
86443 } // Common Pitfalls (mustn't exist for every issue type)
86446 if (issueString(_qaItem, 'trap')) {
86447 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
86449 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
86451 _div2.append('p').html(function (d) {
86452 return issueString(d, 'trap');
86453 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
86454 } // Save current item to check if UI changed by time request resolves
86457 var thisItem = _qaItem;
86458 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
86459 // No details to add if there are no associated issue elements
86460 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
86462 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
86465 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
86466 detailsDiv.append('p').html(function (d) {
86468 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
86469 } // Create list of linked issue elements
86472 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
86473 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
86475 }).each(function () {
86476 var link = select(this);
86477 var entityID = this.textContent;
86478 var entity = context.hasEntity(entityID); // Add click handler
86480 link.on('mouseenter', function () {
86481 utilHighlightEntities([entityID], true, context);
86482 }).on('mouseleave', function () {
86483 utilHighlightEntities([entityID], false, context);
86484 }).on('click', function (d3_event) {
86485 d3_event.preventDefault();
86486 utilHighlightEntities([entityID], false, context);
86487 var osmlayer = context.layers().layer('osm');
86489 if (!osmlayer.enabled()) {
86490 osmlayer.enabled(true);
86493 context.map().centerZoom(d.loc, 20);
86496 context.enter(modeSelect(context, [entityID]));
86498 context.loadEntity(entityID, function (err, result) {
86500 var entity = result.data.find(function (e) {
86501 return e.id === entityID;
86503 if (entity) context.enter(modeSelect(context, [entityID]));
86506 }); // Replace with friendly name if possible
86507 // (The entity may not yet be loaded into the graph)
86510 var name = utilDisplayName(entity); // try to use common name
86513 var preset = _mainPresetIndex.match(entity, context.graph());
86514 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
86518 this.innerText = name;
86521 }); // Don't hide entities related to this issue - #5880
86523 context.features().forceVisible(d.elems);
86524 context.map().pan([0, 0]); // trigger a redraw
86525 })["catch"](function (err) {
86526 console.log(err); // eslint-disable-line no-console
86530 osmoseDetails.issue = function (val) {
86531 if (!arguments.length) return _qaItem;
86533 return osmoseDetails;
86536 return osmoseDetails;
86539 function uiOsmoseHeader() {
86542 function issueTitle(d) {
86543 var unknown = _t('inspector.unknown');
86544 if (!d) return unknown; // Issue titles supplied by Osmose
86546 var s = services.osmose.getStrings(d.itemType);
86547 return 'title' in s ? s.title : unknown;
86550 function osmoseHeader(selection) {
86551 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
86552 return "".concat(d.id, "-").concat(d.status || 0);
86554 header.exit().remove();
86555 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
86556 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
86558 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
86559 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
86561 svgEnter.append('polygon').attr('fill', function (d) {
86562 return services.osmose.getColor(d.item);
86563 }).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');
86564 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
86565 var picon = d.icon;
86570 var isMaki = /^maki-/.test(picon);
86571 return "#".concat(picon).concat(isMaki ? '-11' : '');
86574 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
86577 osmoseHeader.issue = function (val) {
86578 if (!arguments.length) return _qaItem;
86580 return osmoseHeader;
86583 return osmoseHeader;
86586 function uiViewOnOsmose() {
86589 function viewOnOsmose(selection) {
86592 if (services.osmose && _qaItem instanceof QAItem) {
86593 url = services.osmose.itemURL(_qaItem);
86596 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
86598 link.exit().remove(); // enter
86600 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
86601 .attr('href', function (d) {
86603 }).call(svgIcon('#iD-icon-out-link', 'inline'));
86604 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
86607 viewOnOsmose.what = function (val) {
86608 if (!arguments.length) return _qaItem;
86610 return viewOnOsmose;
86613 return viewOnOsmose;
86616 function uiOsmoseEditor(context) {
86617 var dispatch = dispatch$8('change');
86618 var qaDetails = uiOsmoseDetails(context);
86619 var qaHeader = uiOsmoseHeader();
86623 function osmoseEditor(selection) {
86624 var header = selection.selectAll('.header').data([0]);
86625 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
86626 headerEnter.append('button').attr('class', 'close').on('click', function () {
86627 return context.enter(modeBrowse(context));
86628 }).call(svgIcon('#iD-icon-close'));
86629 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
86630 var body = selection.selectAll('.body').data([0]);
86631 body = body.enter().append('div').attr('class', 'body').merge(body);
86632 var editor = body.selectAll('.qa-editor').data([0]);
86633 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
86634 var footer = selection.selectAll('.footer').data([0]);
86635 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
86638 function osmoseSaveSection(selection) {
86639 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
86641 var isShown = _qaItem && isSelected;
86642 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
86643 return "".concat(d.id, "-").concat(d.status || 0);
86646 saveSection.exit().remove(); // enter
86648 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
86650 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
86653 function qaSaveButtons(selection) {
86654 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
86656 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
86657 return d.status + d.id;
86660 buttonSection.exit().remove(); // enter
86662 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
86663 buttonEnter.append('button').attr('class', 'button close-button action');
86664 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
86666 buttonSection = buttonSection.merge(buttonEnter);
86667 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
86668 this.blur(); // avoid keeping focus on the button - #4641
86670 var qaService = services.osmose;
86673 d.newStatus = 'done';
86674 qaService.postUpdate(d, function (err, item) {
86675 return dispatch.call('change', item);
86679 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
86680 this.blur(); // avoid keeping focus on the button - #4641
86682 var qaService = services.osmose;
86685 d.newStatus = 'false';
86686 qaService.postUpdate(d, function (err, item) {
86687 return dispatch.call('change', item);
86691 } // NOTE: Don't change method name until UI v3 is merged
86694 osmoseEditor.error = function (val) {
86695 if (!arguments.length) return _qaItem;
86697 return osmoseEditor;
86700 return utilRebind(osmoseEditor, dispatch, 'on');
86703 function uiNoteComments() {
86706 function noteComments(selection) {
86707 if (_note.isNew()) return; // don't draw .comments-container
86709 var comments = selection.selectAll('.comments-container').data([0]);
86710 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
86711 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
86712 commentEnter.append('div').attr('class', function (d) {
86713 return 'comment-avatar user-' + d.uid;
86714 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
86715 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
86716 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
86717 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
86718 var selection = select(this);
86719 var osm = services.osm;
86721 if (osm && d.user) {
86722 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
86725 selection.html(function (d) {
86726 return d.user || _t.html('note.anonymous');
86729 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
86730 return _t('note.status.' + d.action, {
86731 when: localeDateString(d.date)
86734 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
86736 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
86737 comments.call(replaceAvatars);
86740 function replaceAvatars(selection) {
86741 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
86742 var osm = services.osm;
86743 if (showThirdPartyIcons !== 'true' || !osm) return;
86744 var uids = {}; // gather uids in the comment thread
86746 _note.comments.forEach(function (d) {
86747 if (d.uid) uids[d.uid] = true;
86750 Object.keys(uids).forEach(function (uid) {
86751 osm.loadUser(uid, function (err, user) {
86752 if (!user || !user.image_url) return;
86753 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);
86758 function localeDateString(s) {
86759 if (!s) return null;
86765 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
86767 var d = new Date(s);
86768 if (isNaN(d.getTime())) return null;
86769 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
86772 noteComments.note = function (val) {
86773 if (!arguments.length) return _note;
86775 return noteComments;
86778 return noteComments;
86781 function uiNoteHeader() {
86784 function noteHeader(selection) {
86785 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
86786 return d.status + d.id;
86788 header.exit().remove();
86789 var headerEnter = header.enter().append('div').attr('class', 'note-header');
86790 var iconEnter = headerEnter.append('div').attr('class', function (d) {
86791 return 'note-header-icon ' + d.status;
86792 }).classed('new', function (d) {
86795 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
86796 iconEnter.each(function (d) {
86800 statusIcon = '#iD-icon-plus';
86801 } else if (d.status === 'open') {
86802 statusIcon = '#iD-icon-close';
86804 statusIcon = '#iD-icon-apply';
86807 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
86809 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
86810 if (_note.isNew()) {
86811 return _t('note.new');
86814 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
86818 noteHeader.note = function (val) {
86819 if (!arguments.length) return _note;
86827 function uiNoteReport() {
86830 function noteReport(selection) {
86833 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
86834 url = services.osm.noteReportURL(_note);
86837 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
86839 link.exit().remove(); // enter
86841 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
86843 }).call(svgIcon('#iD-icon-out-link', 'inline'));
86844 linkEnter.append('span').html(_t.html('note.report'));
86847 noteReport.note = function (val) {
86848 if (!arguments.length) return _note;
86856 function uiNoteEditor(context) {
86857 var dispatch = dispatch$8('change');
86858 var noteComments = uiNoteComments();
86859 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
86863 var _newNote; // var _fieldsArr;
86866 function noteEditor(selection) {
86867 var header = selection.selectAll('.header').data([0]);
86868 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
86869 headerEnter.append('button').attr('class', 'close').on('click', function () {
86870 context.enter(modeBrowse(context));
86871 }).call(svgIcon('#iD-icon-close'));
86872 headerEnter.append('h3').html(_t.html('note.title'));
86873 var body = selection.selectAll('.body').data([0]);
86874 body = body.enter().append('div').attr('class', 'body').merge(body);
86875 var editor = body.selectAll('.note-editor').data([0]);
86876 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
86877 var footer = selection.selectAll('.footer').data([0]);
86878 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
86880 var osm = services.osm;
86883 osm.on('change.note-save', function () {
86884 selection.call(noteEditor);
86889 function noteSaveSection(selection) {
86890 var isSelected = _note && _note.id === context.selectedNoteID();
86892 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
86893 return d.status + d.id;
86896 noteSave.exit().remove(); // enter
86898 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
86899 // if (_note.isNew()) {
86900 // var presets = presetManager;
86901 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
86903 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
86905 // _fieldsArr.forEach(function(field) {
86907 // .on('change', changeCategory);
86911 // .attr('class', 'note-category')
86912 // .call(formFields.fieldsArr(_fieldsArr));
86914 // function changeCategory() {
86915 // // NOTE: perhaps there is a better way to get value
86916 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
86917 // // store the unsaved category with the note itself
86918 // _note = _note.update({ newCategory: val });
86919 // var osm = services.osm;
86921 // osm.replaceNote(_note); // update note cache
86924 // .call(noteSaveButtons);
86927 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
86928 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
86930 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
86931 return d.newComment;
86932 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
86934 if (!commentTextarea.empty() && _newNote) {
86935 // autofocus the comment field for new notes
86936 commentTextarea.node().focus();
86940 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
86942 function keydown(d3_event) {
86943 if (!(d3_event.keyCode === 13 && // ↩ Return
86944 d3_event.metaKey)) return;
86945 var osm = services.osm;
86947 var hasAuth = osm.authenticated();
86948 if (!hasAuth) return;
86949 if (!_note.newComment) return;
86950 d3_event.preventDefault();
86951 select(this).on('keydown.note-input', null); // focus on button and submit
86953 window.setTimeout(function () {
86954 if (_note.isNew()) {
86955 noteSave.selectAll('.save-button').node().focus();
86958 noteSave.selectAll('.comment-button').node().focus();
86964 function changeInput() {
86965 var input = select(this);
86966 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
86968 _note = _note.update({
86971 var osm = services.osm;
86974 osm.replaceNote(_note); // update note cache
86977 noteSave.call(noteSaveButtons);
86981 function userDetails(selection) {
86982 var detailSection = selection.selectAll('.detail-section').data([0]);
86983 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
86984 var osm = services.osm;
86985 if (!osm) return; // Add warning if user is not logged in
86987 var hasAuth = osm.authenticated();
86988 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
86989 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
86990 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
86991 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
86992 authEnter.append('span').html(_t.html('note.login'));
86993 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) {
86994 d3_event.preventDefault();
86995 osm.authenticate();
86997 authEnter.transition().duration(200).style('opacity', 1);
86998 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
86999 prose.exit().remove();
87000 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
87001 osm.userDetails(function (err, user) {
87003 var userLink = select(document.createElement('div'));
87005 if (user.image_url) {
87006 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
87009 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
87010 prose.html(_t.html('note.upload_explanation_with_user', {
87011 user: userLink.html()
87016 function noteSaveButtons(selection) {
87017 var osm = services.osm;
87018 var hasAuth = osm && osm.authenticated();
87020 var isSelected = _note && _note.id === context.selectedNoteID();
87022 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
87023 return d.status + d.id;
87026 buttonSection.exit().remove(); // enter
87028 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
87030 if (_note.isNew()) {
87031 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
87032 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
87034 buttonEnter.append('button').attr('class', 'button status-button action');
87035 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
87039 buttonSection = buttonSection.merge(buttonEnter);
87040 buttonSection.select('.cancel-button') // select and propagate data
87041 .on('click.cancel', clickCancel);
87042 buttonSection.select('.save-button') // select and propagate data
87043 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
87044 buttonSection.select('.status-button') // select and propagate data
87045 .attr('disabled', hasAuth ? null : true).html(function (d) {
87046 var action = d.status === 'open' ? 'close' : 'open';
87047 var andComment = d.newComment ? '_comment' : '';
87048 return _t('note.' + action + andComment);
87049 }).on('click.status', clickStatus);
87050 buttonSection.select('.comment-button') // select and propagate data
87051 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
87053 function isSaveDisabled(d) {
87054 return hasAuth && d.status === 'open' && d.newComment ? null : true;
87058 function clickCancel(d3_event, d) {
87059 this.blur(); // avoid keeping focus on the button - #4641
87061 var osm = services.osm;
87067 context.enter(modeBrowse(context));
87068 dispatch.call('change');
87071 function clickSave(d3_event, d) {
87072 this.blur(); // avoid keeping focus on the button - #4641
87074 var osm = services.osm;
87077 osm.postNoteCreate(d, function (err, note) {
87078 dispatch.call('change', note);
87083 function clickStatus(d3_event, d) {
87084 this.blur(); // avoid keeping focus on the button - #4641
87086 var osm = services.osm;
87089 var setStatus = d.status === 'open' ? 'closed' : 'open';
87090 osm.postNoteUpdate(d, setStatus, function (err, note) {
87091 dispatch.call('change', note);
87096 function clickComment(d3_event, d) {
87097 this.blur(); // avoid keeping focus on the button - #4641
87099 var osm = services.osm;
87102 osm.postNoteUpdate(d, d.status, function (err, note) {
87103 dispatch.call('change', note);
87108 noteEditor.note = function (val) {
87109 if (!arguments.length) return _note;
87114 noteEditor.newNote = function (val) {
87115 if (!arguments.length) return _newNote;
87120 return utilRebind(noteEditor, dispatch, 'on');
87123 function uiSidebar(context) {
87124 var inspector = uiInspector(context);
87125 var dataEditor = uiDataEditor(context);
87126 var noteEditor = uiNoteEditor(context);
87127 var improveOsmEditor = uiImproveOsmEditor(context);
87128 var keepRightEditor = uiKeepRightEditor(context);
87129 var osmoseEditor = uiOsmoseEditor(context);
87133 var _wasData = false;
87134 var _wasNote = false;
87135 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
87137 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
87139 function sidebar(selection) {
87140 var container = context.container();
87141 var minWidth = 240;
87143 var containerWidth;
87144 var dragOffset; // Set the initial width constraints
87146 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
87147 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
87148 var downPointerId, lastClientX, containerLocGetter;
87150 function pointerdown(d3_event) {
87151 if (downPointerId) return;
87152 if ('button' in d3_event && d3_event.button !== 0) return;
87153 downPointerId = d3_event.pointerId || 'mouse';
87154 lastClientX = d3_event.clientX;
87155 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
87157 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
87158 sidebarWidth = selection.node().getBoundingClientRect().width;
87159 containerWidth = container.node().getBoundingClientRect().width;
87160 var widthPct = sidebarWidth / containerWidth * 100;
87161 selection.style('width', widthPct + '%') // lock in current width
87162 .style('max-width', '85%'); // but allow larger widths
87164 resizer.classed('dragging', true);
87165 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
87166 // disable page scrolling while resizing on touch input
87167 d3_event.preventDefault();
87170 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
87173 function pointermove(d3_event) {
87174 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
87175 d3_event.preventDefault();
87176 var dx = d3_event.clientX - lastClientX;
87177 lastClientX = d3_event.clientX;
87178 var isRTL = _mainLocalizer.textDirection() === 'rtl';
87179 var scaleX = isRTL ? 0 : 1;
87180 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
87181 var x = containerLocGetter(d3_event)[0] - dragOffset;
87182 sidebarWidth = isRTL ? containerWidth - x : x;
87183 var isCollapsed = selection.classed('collapsed');
87184 var shouldCollapse = sidebarWidth < minWidth;
87185 selection.classed('collapsed', shouldCollapse);
87187 if (shouldCollapse) {
87188 if (!isCollapsed) {
87189 selection.style(xMarginProperty, '-400px').style('width', '400px');
87190 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
87193 var widthPct = sidebarWidth / containerWidth * 100;
87194 selection.style(xMarginProperty, null).style('width', widthPct + '%');
87197 context.ui().onResize([-sidebarWidth * scaleX, 0]);
87199 context.ui().onResize([-dx * scaleX, 0]);
87204 function pointerup(d3_event) {
87205 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
87206 downPointerId = null;
87207 resizer.classed('dragging', false);
87208 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
87211 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
87212 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
87214 var hoverModeSelect = function hoverModeSelect(targets) {
87215 context.container().selectAll('.feature-list-item button').classed('hover', false);
87217 if (context.selectedIDs().length > 1 && targets && targets.length) {
87218 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
87219 return targets.indexOf(node) !== -1;
87222 if (!elements.empty()) {
87223 elements.classed('hover', true);
87228 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
87230 function hover(targets) {
87231 var datum = targets && targets.length && targets[0];
87233 if (datum && datum.__featurehash__) {
87234 // hovering on data
87236 sidebar.show(dataEditor.datum(datum));
87237 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
87238 } else if (datum instanceof osmNote) {
87239 if (context.mode().id === 'drag-note') return;
87241 var osm = services.osm;
87244 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
87247 sidebar.show(noteEditor.note(datum));
87248 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
87249 } else if (datum instanceof QAItem) {
87251 var errService = services[datum.service];
87254 // marker may contain stale data - get latest
87255 datum = errService.getError(datum.id);
87256 } // Currently only three possible services
87261 if (datum.service === 'keepRight') {
87262 errEditor = keepRightEditor;
87263 } else if (datum.service === 'osmose') {
87264 errEditor = osmoseEditor;
87266 errEditor = improveOsmEditor;
87269 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
87270 return d.id === datum.id;
87272 sidebar.show(errEditor.error(datum));
87273 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
87274 } else if (!_current && datum instanceof osmEntity) {
87275 featureListWrap.classed('inspector-hidden', true);
87276 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
87278 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
87279 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
87280 inspectorWrap.call(inspector);
87282 } else if (!_current) {
87283 featureListWrap.classed('inspector-hidden', false);
87284 inspectorWrap.classed('inspector-hidden', true);
87285 inspector.state('hide');
87286 } else if (_wasData || _wasNote || _wasQaItem) {
87289 _wasQaItem = false;
87290 context.container().selectAll('.note').classed('hover', false);
87291 context.container().selectAll('.qaItem').classed('hover', false);
87296 sidebar.hover = throttle(hover, 200);
87298 sidebar.intersects = function (extent) {
87299 var rect = selection.node().getBoundingClientRect();
87300 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
87303 sidebar.select = function (ids, newFeature) {
87306 if (ids && ids.length) {
87307 var entity = ids.length === 1 && context.entity(ids[0]);
87309 if (entity && newFeature && selection.classed('collapsed')) {
87310 // uncollapse the sidebar
87311 var extent = entity.extent(context.graph());
87312 sidebar.expand(sidebar.intersects(extent));
87315 featureListWrap.classed('inspector-hidden', true);
87316 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
87317 // themselves may have changed
87319 inspector.state('select').entityIDs(ids).newFeature(newFeature);
87320 inspectorWrap.call(inspector);
87322 inspector.state('hide');
87326 sidebar.showPresetList = function () {
87327 inspector.showList();
87330 sidebar.show = function (component, element) {
87331 featureListWrap.classed('inspector-hidden', true);
87332 inspectorWrap.classed('inspector-hidden', true);
87333 if (_current) _current.remove();
87334 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
87337 sidebar.hide = function () {
87338 featureListWrap.classed('inspector-hidden', false);
87339 inspectorWrap.classed('inspector-hidden', true);
87340 if (_current) _current.remove();
87344 sidebar.expand = function (moveMap) {
87345 if (selection.classed('collapsed')) {
87346 sidebar.toggle(moveMap);
87350 sidebar.collapse = function (moveMap) {
87351 if (!selection.classed('collapsed')) {
87352 sidebar.toggle(moveMap);
87356 sidebar.toggle = function (moveMap) {
87357 // Don't allow sidebar to toggle when the user is in the walkthrough.
87358 if (context.inIntro()) return;
87359 var isCollapsed = selection.classed('collapsed');
87360 var isCollapsing = !isCollapsed;
87361 var isRTL = _mainLocalizer.textDirection() === 'rtl';
87362 var scaleX = isRTL ? 0 : 1;
87363 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
87364 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
87366 selection.style('width', sidebarWidth + 'px');
87367 var startMargin, endMargin, lastMargin;
87369 if (isCollapsing) {
87370 startMargin = lastMargin = 0;
87371 endMargin = -sidebarWidth;
87373 startMargin = lastMargin = -sidebarWidth;
87377 if (!isCollapsing) {
87378 // unhide the sidebar's content before it transitions onscreen
87379 selection.classed('collapsed', isCollapsing);
87382 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
87383 var i = d3_interpolateNumber(startMargin, endMargin);
87384 return function (t) {
87385 var dx = lastMargin - Math.round(i(t));
87386 lastMargin = lastMargin - dx;
87387 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
87389 }).on('end', function () {
87390 if (isCollapsing) {
87391 // hide the sidebar's content after it transitions offscreen
87392 selection.classed('collapsed', isCollapsing);
87393 } // switch back from px to %
87396 if (!isCollapsing) {
87397 var containerWidth = container.node().getBoundingClientRect().width;
87398 var widthPct = sidebarWidth / containerWidth * 100;
87399 selection.style(xMarginProperty, null).style('width', widthPct + '%');
87402 }; // toggle the sidebar collapse when double-clicking the resizer
87405 resizer.on('dblclick', function (d3_event) {
87406 d3_event.preventDefault();
87408 if (d3_event.sourceEvent) {
87409 d3_event.sourceEvent.preventDefault();
87413 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
87415 context.map().on('crossEditableZoom.sidebar', function (within) {
87416 if (!within && !selection.select('.inspector-hover').empty()) {
87422 sidebar.showPresetList = function () {};
87424 sidebar.hover = function () {};
87426 sidebar.hover.cancel = function () {};
87428 sidebar.intersects = function () {};
87430 sidebar.select = function () {};
87432 sidebar.show = function () {};
87434 sidebar.hide = function () {};
87436 sidebar.expand = function () {};
87438 sidebar.collapse = function () {};
87440 sidebar.toggle = function () {};
87445 function uiSourceSwitch(context) {
87448 function click(d3_event) {
87449 d3_event.preventDefault();
87450 var osm = context.connection();
87452 if (context.inIntro()) return;
87453 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
87454 var isLive = select(this).classed('live');
87456 context.enter(modeBrowse(context));
87457 context.history().clearSaved(); // remove saved history
87459 context.flush(); // remove stored data
87461 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
87462 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
87465 var sourceSwitch = function sourceSwitch(selection) {
87466 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
87469 sourceSwitch.keys = function (_) {
87470 if (!arguments.length) return keys;
87472 return sourceSwitch;
87475 return sourceSwitch;
87478 function uiSpinner(context) {
87479 var osm = context.connection();
87480 return function (selection) {
87481 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
87484 osm.on('loading.spinner', function () {
87485 img.transition().style('opacity', 1);
87486 }).on('loaded.spinner', function () {
87487 img.transition().style('opacity', 0);
87493 function uiSplash(context) {
87494 return function (selection) {
87495 // Exception - if there are restorable changes, skip this splash screen.
87496 // This is because we currently only support one `uiModal` at a time
87497 // and we need to show them `uiRestore`` instead of this one.
87498 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
87500 var updateMessage = '';
87501 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
87502 var showSplash = !corePreferences('sawSplash');
87504 if (sawPrivacyVersion !== context.privacyVersion) {
87505 updateMessage = _t('splash.privacy_update');
87509 if (!showSplash) return;
87510 corePreferences('sawSplash', true);
87511 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
87513 _mainFileFetcher.get('intro_graph');
87514 var modalSelection = uiModal(selection);
87515 modalSelection.select('.modal').attr('class', 'modal-splash modal');
87516 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
87517 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
87518 var modalSection = introModal.append('div').attr('class', 'modal-section');
87519 modalSection.append('p').html(_t.html('splash.text', {
87520 version: context.version,
87521 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
87522 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
87524 modalSection.append('p').html(_t.html('splash.privacy', {
87525 updateMessage: updateMessage,
87526 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
87528 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
87529 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
87530 context.container().call(uiIntro(context));
87531 modalSelection.close();
87533 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
87534 walkthrough.append('div').html(_t.html('splash.walkthrough'));
87535 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
87536 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
87537 startEditing.append('div').html(_t.html('splash.start'));
87538 modalSelection.select('button.close').attr('class', 'hide');
87542 function uiStatus(context) {
87543 var osm = context.connection();
87544 return function (selection) {
87547 function update(err, apiStatus) {
87548 selection.html('');
87551 if (apiStatus === 'connectionSwitched') {
87552 // if the connection was just switched, we can't rely on
87553 // the status (we're getting the status of the previous api)
87555 } else if (apiStatus === 'rateLimited') {
87556 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) {
87557 d3_event.preventDefault();
87558 osm.authenticate();
87561 // don't allow retrying too rapidly
87562 var throttledRetry = throttle(function () {
87563 // try loading the visible tiles
87564 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
87566 osm.reloadApiStatus();
87567 }, 2000); // eslint-disable-next-line no-warning-comments
87568 // TODO: nice messages for different error types
87571 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
87572 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
87573 d3_event.preventDefault();
87577 } else if (apiStatus === 'readonly') {
87578 selection.html(_t.html('osm_api_status.message.readonly'));
87579 } else if (apiStatus === 'offline') {
87580 selection.html(_t.html('osm_api_status.message.offline'));
87583 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
87586 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
87588 window.setInterval(function () {
87589 osm.reloadApiStatus();
87590 }, 90000); // load the initial status in case no OSM data was loaded yet
87592 osm.reloadApiStatus();
87596 function modeDrawArea(context, wayID, startGraph, button) {
87601 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
87602 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
87604 mode.wayID = wayID;
87606 mode.enter = function () {
87607 context.install(behavior);
87610 mode.exit = function () {
87611 context.uninstall(behavior);
87614 mode.selectedIDs = function () {
87618 mode.activeID = function () {
87619 return behavior && behavior.activeID() || [];
87625 function modeAddArea(context, mode) {
87626 mode.id = 'add-area';
87627 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
87628 var defaultTags = {
87631 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
87633 function actionClose(wayId) {
87634 return function (graph) {
87635 return graph.replace(graph.entity(wayId).close());
87639 function start(loc) {
87640 var startGraph = context.graph();
87641 var node = osmNode({
87647 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
87648 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
87651 function startFromWay(loc, edge) {
87652 var startGraph = context.graph();
87653 var node = osmNode({
87659 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
87663 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
87666 function startFromNode(node) {
87667 var startGraph = context.graph();
87671 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
87672 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
87675 mode.enter = function () {
87676 context.install(behavior);
87679 mode.exit = function () {
87680 context.uninstall(behavior);
87686 function modeAddLine(context, mode) {
87687 mode.id = 'add-line';
87688 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
87689 var defaultTags = {};
87690 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
87692 function start(loc) {
87693 var startGraph = context.graph();
87694 var node = osmNode({
87700 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
87701 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
87704 function startFromWay(loc, edge) {
87705 var startGraph = context.graph();
87706 var node = osmNode({
87712 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
87716 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
87719 function startFromNode(node) {
87720 var startGraph = context.graph();
87724 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
87725 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
87728 mode.enter = function () {
87729 context.install(behavior);
87732 mode.exit = function () {
87733 context.uninstall(behavior);
87739 function modeAddPoint(context, mode) {
87740 mode.id = 'add-point';
87741 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
87742 var defaultTags = {};
87743 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
87745 function add(loc) {
87746 var node = osmNode({
87750 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
87751 enterSelectMode(node);
87754 function addWay(loc, edge) {
87755 var node = osmNode({
87758 context.perform(actionAddMidpoint({
87761 }, node), _t('operations.add.annotation.vertex'));
87762 enterSelectMode(node);
87765 function enterSelectMode(node) {
87766 context.enter(modeSelect(context, [node.id]).newFeature(true));
87769 function addNode(node) {
87770 if (Object.keys(defaultTags).length === 0) {
87771 enterSelectMode(node);
87775 var tags = Object.assign({}, node.tags); // shallow copy
87777 for (var key in defaultTags) {
87778 tags[key] = defaultTags[key];
87781 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
87782 enterSelectMode(node);
87785 function cancel() {
87786 context.enter(modeBrowse(context));
87789 mode.enter = function () {
87790 context.install(behavior);
87793 mode.exit = function () {
87794 context.uninstall(behavior);
87800 function modeSelectNote(context, selectedNoteID) {
87806 var _keybinding = utilKeybinding('select-note');
87808 var _noteEditor = uiNoteEditor(context).on('change', function () {
87809 context.map().pan([0, 0]); // trigger a redraw
87811 var note = checkSelectedID();
87813 context.ui().sidebar.show(_noteEditor.note(note));
87816 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
87817 var _newFeature = false;
87819 function checkSelectedID() {
87820 if (!services.osm) return;
87821 var note = services.osm.getNote(selectedNoteID);
87824 context.enter(modeBrowse(context));
87828 } // class the note as selected, or return to browse mode if the note is gone
87831 function selectNote(d3_event, drawn) {
87832 if (!checkSelectedID()) return;
87833 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
87835 if (selection.empty()) {
87836 // Return to browse mode if selected DOM elements have
87837 // disappeared because the user moved them out of view..
87838 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
87840 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
87841 context.enter(modeBrowse(context));
87844 selection.classed('selected', true);
87845 context.selectedNoteID(selectedNoteID);
87850 if (context.container().select('.combobox').size()) return;
87851 context.enter(modeBrowse(context));
87854 mode.zoomToSelected = function () {
87855 if (!services.osm) return;
87856 var note = services.osm.getNote(selectedNoteID);
87859 context.map().centerZoomEase(note.loc, 20);
87863 mode.newFeature = function (val) {
87864 if (!arguments.length) return _newFeature;
87869 mode.enter = function () {
87870 var note = checkSelectedID();
87873 _behaviors.forEach(context.install);
87875 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
87877 select(document).call(_keybinding);
87879 var sidebar = context.ui().sidebar;
87880 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
87882 sidebar.expand(sidebar.intersects(note.extent()));
87883 context.map().on('drawn.select', selectNote);
87886 mode.exit = function () {
87887 _behaviors.forEach(context.uninstall);
87889 select(document).call(_keybinding.unbind);
87890 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
87891 context.map().on('drawn.select', null);
87892 context.ui().sidebar.hide();
87893 context.selectedNoteID(null);
87899 function modeAddNote(context) {
87903 description: _t.html('modes.add_note.description'),
87904 key: _t('modes.add_note.key')
87906 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
87908 function add(loc) {
87909 var osm = services.osm;
87911 var note = osmNote({
87916 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
87918 context.map().pan([0, 0]);
87919 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
87922 function cancel() {
87923 context.enter(modeBrowse(context));
87926 mode.enter = function () {
87927 context.install(behavior);
87930 mode.exit = function () {
87931 context.uninstall(behavior);
87937 var JXON = new function () {
87938 var sValueProp = 'keyValue',
87939 sAttributesProp = 'keyAttributes',
87942 /* you can customize these values */
87945 rIsBool = /^(?:true|false)$/i;
87947 function parseText(sValue) {
87948 if (rIsNull.test(sValue)) {
87952 if (rIsBool.test(sValue)) {
87953 return sValue.toLowerCase() === 'true';
87956 if (isFinite(sValue)) {
87957 return parseFloat(sValue);
87960 if (isFinite(Date.parse(sValue))) {
87961 return new Date(sValue);
87967 function EmptyTree() {}
87969 EmptyTree.prototype.toString = function () {
87973 EmptyTree.prototype.valueOf = function () {
87977 function objectify(vValue) {
87978 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
87981 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
87982 var nLevelStart = aCache.length,
87983 bChildren = oParentNode.hasChildNodes(),
87984 bAttributes = oParentNode.hasAttributes(),
87985 bHighVerb = Boolean(nVerb & 2);
87989 sCollectedTxt = '',
87990 vResult = bHighVerb ? {} :
87991 /* put here the default value for empty nodes: */
87995 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
87996 oNode = oParentNode.childNodes.item(nItem);
87998 if (oNode.nodeType === 4) {
87999 /* nodeType is 'CDATASection' (4) */
88000 sCollectedTxt += oNode.nodeValue;
88001 } else if (oNode.nodeType === 3) {
88002 /* nodeType is 'Text' (3) */
88003 sCollectedTxt += oNode.nodeValue.trim();
88004 } else if (oNode.nodeType === 1 && !oNode.prefix) {
88005 /* nodeType is 'Element' (1) */
88006 aCache.push(oNode);
88011 var nLevelEnd = aCache.length,
88012 vBuiltVal = parseText(sCollectedTxt);
88014 if (!bHighVerb && (bChildren || bAttributes)) {
88015 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
88018 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
88019 sProp = aCache[nElId].nodeName.toLowerCase();
88020 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
88022 if (vResult.hasOwnProperty(sProp)) {
88023 if (vResult[sProp].constructor !== Array) {
88024 vResult[sProp] = [vResult[sProp]];
88027 vResult[sProp].push(vContent);
88029 vResult[sProp] = vContent;
88035 var nAttrLen = oParentNode.attributes.length,
88036 sAPrefix = bNesteAttr ? '' : sAttrPref,
88037 oAttrParent = bNesteAttr ? {} : vResult;
88039 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
88040 oAttrib = oParentNode.attributes.item(nAttrib);
88041 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
88046 Object.freeze(oAttrParent);
88049 vResult[sAttributesProp] = oAttrParent;
88050 nLength -= nAttrLen - 1;
88054 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
88055 vResult[sValueProp] = vBuiltVal;
88056 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
88057 vResult = vBuiltVal;
88060 if (bFreeze && (bHighVerb || nLength > 0)) {
88061 Object.freeze(vResult);
88064 aCache.length = nLevelStart;
88068 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
88069 var vValue, oChild;
88071 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
88072 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
88073 /* verbosity level is 0 */
88074 } else if (oParentObj.constructor === Date) {
88075 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
88078 for (var sName in oParentObj) {
88079 vValue = oParentObj[sName];
88081 if (isFinite(sName) || vValue instanceof Function) {
88084 /* verbosity level is 0 */
88087 if (sName === sValueProp) {
88088 if (vValue !== null && vValue !== true) {
88089 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
88091 } else if (sName === sAttributesProp) {
88092 /* verbosity level is 3 */
88093 for (var sAttrib in vValue) {
88094 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
88096 } else if (sName.charAt(0) === sAttrPref) {
88097 oParentEl.setAttribute(sName.slice(1), vValue);
88098 } else if (vValue.constructor === Array) {
88099 for (var nItem = 0; nItem < vValue.length; nItem++) {
88100 oChild = oXMLDoc.createElement(sName);
88101 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
88102 oParentEl.appendChild(oChild);
88105 oChild = oXMLDoc.createElement(sName);
88107 if (vValue instanceof Object) {
88108 loadObjTree(oXMLDoc, oChild, vValue);
88109 } else if (vValue !== null && vValue !== true) {
88110 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
88113 oParentEl.appendChild(oChild);
88118 this.build = function (oXMLParent, nVerbosity
88125 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
88126 /* put here the default verbosity level: */
88129 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
88132 this.unbuild = function (oObjTree) {
88133 var oNewDoc = document.implementation.createDocument('', '', null);
88134 loadObjTree(oNewDoc, oNewDoc, oObjTree);
88138 this.stringify = function (oObjTree) {
88139 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
88141 }(); // var myObject = JXON.build(doc);
88142 // we got our javascript object! try: alert(JSON.stringify(myObject));
88143 // var newDoc = JXON.unbuild(myObject);
88144 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
88146 function uiConflicts(context) {
88147 var dispatch = dispatch$8('cancel', 'save');
88148 var keybinding = utilKeybinding('conflicts');
88154 var _shownConflictIndex;
88156 function keybindingOn() {
88157 select(document).call(keybinding.on('⎋', cancel, true));
88160 function keybindingOff() {
88161 select(document).call(keybinding.unbind);
88164 function tryAgain() {
88166 dispatch.call('save');
88169 function cancel() {
88171 dispatch.call('cancel');
88174 function conflicts(selection) {
88176 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
88177 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
88178 headerEnter.append('h3').html(_t.html('save.conflict.header'));
88179 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
88180 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
88182 var detected = utilDetect();
88183 var changeset = new osmChangeset();
88184 delete changeset.id; // Export without changeset_id
88186 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
88187 var blob = new Blob([data], {
88188 type: 'text/xml;charset=utf-8;'
88190 var fileName = 'changes.osc';
88191 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
88193 if (detected.download) {
88194 // All except IE11 and Edge
88195 linkEnter // download the data as a file
88196 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
88199 linkEnter // open data uri in a new tab
88200 .attr('target', '_blank').on('click.download', function () {
88201 navigator.msSaveBlob(blob, fileName);
88205 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
88206 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
88207 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
88208 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
88209 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
88210 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
88213 function showConflict(selection, index) {
88214 index = utilWrap(index, _conflictList.length);
88215 _shownConflictIndex = index;
88216 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
88218 if (index === _conflictList.length - 1) {
88219 window.setTimeout(function () {
88220 parent.select('.conflicts-button').attr('disabled', null);
88221 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
88225 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
88226 conflict.exit().remove();
88227 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
88228 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
88230 total: _conflictList.length
88232 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
88234 }).on('click', function (d3_event, d) {
88235 d3_event.preventDefault();
88236 zoomToEntity(d.id);
88238 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
88239 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
88240 return d.details || [];
88241 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
88244 details.append('div').attr('class', 'conflict-choices').call(addChoices);
88245 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
88246 return _t.html('save.conflict.' + d);
88247 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
88248 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
88249 }).on('click', function (d3_event, d) {
88250 d3_event.preventDefault();
88251 var container = parent.selectAll('.conflict-container');
88252 var sign = d === 'previous' ? -1 : 1;
88253 container.selectAll('.conflict').remove();
88254 container.call(showConflict, index + sign);
88258 function addChoices(selection) {
88259 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
88260 return d.choices || [];
88263 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
88264 var labelEnter = choicesEnter.append('label');
88265 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
88267 }).on('change', function (d3_event, d) {
88268 var ul = this.parentNode.parentNode.parentNode;
88269 ul.__data__.chosen = d.id;
88270 choose(d3_event, ul, d);
88272 labelEnter.append('span').html(function (d) {
88276 choicesEnter.merge(choices).each(function (d) {
88277 var ul = this.parentNode;
88279 if (ul.__data__.chosen === d.id) {
88280 choose(null, ul, d);
88285 function choose(d3_event, ul, datum) {
88286 if (d3_event) d3_event.preventDefault();
88287 select(ul).selectAll('li').classed('active', function (d) {
88288 return d === datum;
88289 }).selectAll('input').property('checked', function (d) {
88290 return d === datum;
88292 var extent = geoExtent();
88294 entity = context.graph().hasEntity(datum.id);
88295 if (entity) extent._extend(entity.extent(context.graph()));
88297 entity = context.graph().hasEntity(datum.id);
88298 if (entity) extent._extend(entity.extent(context.graph()));
88299 zoomToEntity(datum.id, extent);
88302 function zoomToEntity(id, extent) {
88303 context.surface().selectAll('.hover').classed('hover', false);
88304 var entity = context.graph().hasEntity(id);
88308 context.map().trimmedExtent(extent);
88310 context.map().zoomToEase(entity);
88313 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
88315 } // The conflict list should be an array of objects like:
88318 // name: entityName(local),
88319 // details: merge.conflicts(),
88322 // choice(id, keepMine, forceLocal),
88323 // choice(id, keepTheirs, forceRemote)
88328 conflicts.conflictList = function (_) {
88329 if (!arguments.length) return _conflictList;
88334 conflicts.origChanges = function (_) {
88335 if (!arguments.length) return _origChanges;
88340 conflicts.shownEntityIds = function () {
88341 if (_conflictList && typeof _shownConflictIndex === 'number') {
88342 return [_conflictList[_shownConflictIndex].id];
88348 return utilRebind(conflicts, dispatch, 'on');
88351 function uiConfirm(selection) {
88352 var modalSelection = uiModal(selection);
88353 modalSelection.select('.modal').classed('modal-alert', true);
88354 var section = modalSelection.select('.content');
88355 section.append('div').attr('class', 'modal-section header');
88356 section.append('div').attr('class', 'modal-section message-text');
88357 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
88359 modalSelection.okButton = function () {
88360 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
88361 modalSelection.remove();
88362 }).html(_t.html('confirm.okay')).node().focus();
88363 return modalSelection;
88366 return modalSelection;
88369 function uiChangesetEditor(context) {
88370 var dispatch = dispatch$8('change');
88371 var formFields = uiFormFields(context);
88372 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
88380 function changesetEditor(selection) {
88384 function render(selection) {
88385 var initial = false;
88389 var presets = _mainPresetIndex;
88390 _fieldsArr = [uiField(context, presets.field('comment'), null, {
88393 }), uiField(context, presets.field('source'), null, {
88396 }), uiField(context, presets.field('hashtags'), null, {
88401 _fieldsArr.forEach(function (field) {
88402 field.on('change', function (t, onInput) {
88403 dispatch.call('change', field, undefined, t, onInput);
88408 _fieldsArr.forEach(function (field) {
88412 selection.call(formFields.fieldsArr(_fieldsArr));
88415 var commentField = selection.select('.form-field-comment textarea');
88416 var commentNode = commentField.node();
88419 commentNode.focus();
88420 commentNode.select();
88421 } // trigger a 'blur' event so that comment field can be cleaned
88422 // and checked for hashtags, even if retrieved from localstorage
88425 utilTriggerEvent(commentField, 'blur');
88426 var osm = context.connection();
88429 osm.userChangesets(function (err, changesets) {
88431 var comments = changesets.map(function (changeset) {
88432 var comment = changeset.tags.comment;
88437 }).filter(Boolean);
88438 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
88441 } // Add warning if comment mentions Google
88444 var hasGoogle = _tags.comment.match(/google/i);
88446 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
88447 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
88448 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
88449 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'));
88450 commentEnter.transition().duration(200).style('opacity', 1);
88453 changesetEditor.tags = function (_) {
88454 if (!arguments.length) return _tags;
88455 _tags = _; // Don't reset _fieldsArr here.
88457 return changesetEditor;
88460 changesetEditor.changesetID = function (_) {
88461 if (!arguments.length) return _changesetID;
88462 if (_changesetID === _) return changesetEditor;
88465 return changesetEditor;
88468 return utilRebind(changesetEditor, dispatch, 'on');
88471 function uiSectionChanges(context) {
88472 var detected = utilDetect();
88473 var _discardTags = {};
88474 _mainFileFetcher.get('discarded').then(function (d) {
88476 })["catch"](function () {
88479 var section = uiSection('changes-list', context).label(function () {
88480 var history = context.history();
88481 var summary = history.difference().summary();
88482 return _t('inspector.title_count', {
88483 title: _t.html('commit.changes'),
88484 count: summary.length
88486 }).disclosureContent(renderDisclosureContent);
88488 function renderDisclosureContent(selection) {
88489 var history = context.history();
88490 var summary = history.difference().summary();
88491 var container = selection.selectAll('.commit-section').data([0]);
88492 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
88493 containerEnter.append('ul').attr('class', 'changeset-list');
88494 container = containerEnter.merge(container);
88495 var items = container.select('ul').selectAll('li').data(summary);
88496 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
88497 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
88498 buttons.each(function (d) {
88499 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
88501 buttons.append('span').attr('class', 'change-type').html(function (d) {
88502 return _t.html('commit.' + d.changeType) + ' ';
88504 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
88505 var matched = _mainPresetIndex.match(d.entity, d.graph);
88506 return matched && matched.name() || utilDisplayType(d.entity.id);
88508 buttons.append('span').attr('class', 'entity-name').html(function (d) {
88509 var name = utilDisplayName(d.entity) || '',
88516 return string += ' ' + name;
88518 items = itemsEnter.merge(items); // Download changeset link
88520 var changeset = new osmChangeset().update({
88523 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
88524 delete changeset.id; // Export without chnageset_id
88526 var data = JXON.stringify(changeset.osmChangeJXON(changes));
88527 var blob = new Blob([data], {
88528 type: 'text/xml;charset=utf-8;'
88530 var fileName = 'changes.osc';
88531 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
88533 if (detected.download) {
88534 // All except IE11 and Edge
88535 linkEnter // download the data as a file
88536 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
88539 linkEnter // open data uri in a new tab
88540 .attr('target', '_blank').on('click.download', function () {
88541 navigator.msSaveBlob(blob, fileName);
88545 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
88547 function mouseover(d) {
88549 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
88553 function mouseout() {
88554 context.surface().selectAll('.hover').classed('hover', false);
88557 function click(d3_event, change) {
88558 if (change.changeType !== 'deleted') {
88559 var entity = change.entity;
88560 context.map().zoomToEase(entity);
88561 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
88569 function uiCommitWarnings(context) {
88570 function commitWarnings(selection) {
88571 var issuesBySeverity = context.validator().getIssuesBySeverity({
88574 includeDisabledRules: true
88577 for (var severity in issuesBySeverity) {
88578 var issues = issuesBySeverity[severity];
88579 var section = severity + '-section';
88580 var issueItem = severity + '-item';
88581 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
88582 container.exit().remove();
88583 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
88584 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
88585 containerEnter.append('ul').attr('class', 'changeset-list');
88586 container = containerEnter.merge(container);
88587 var items = container.select('ul').selectAll('li').data(issues, function (d) {
88590 items.exit().remove();
88591 var itemsEnter = items.enter().append('li').attr('class', issueItem);
88592 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
88594 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
88596 }).on('mouseout', function () {
88597 context.surface().selectAll('.hover').classed('hover', false);
88598 }).on('click', function (d3_event, d) {
88599 context.validator().focusIssue(d);
88601 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
88602 buttons.append('strong').attr('class', 'issue-message');
88603 buttons.filter(function (d) {
88605 }).call(uiTooltip().title(function (d) {
88607 }).placement('top'));
88608 items = itemsEnter.merge(items);
88609 items.selectAll('.issue-message').html(function (d) {
88610 return d.message(context);
88615 return commitWarnings;
88618 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
88619 // from https://stackoverflow.com/a/25575009
88621 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
88622 function uiCommit(context) {
88623 var dispatch = dispatch$8('cancel');
88629 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
88630 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
88631 var commitChanges = uiSectionChanges(context);
88632 var commitWarnings = uiCommitWarnings(context);
88634 function commit(selection) {
88635 _selection = selection; // Initialize changeset if one does not exist yet.
88637 if (!context.changeset) initChangeset();
88638 loadDerivedChangesetTags();
88639 selection.call(render);
88642 function initChangeset() {
88643 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
88644 var commentDate = +corePreferences('commentDate') || 0;
88645 var currDate = Date.now();
88646 var cutoff = 2 * 86400 * 1000; // 2 days
88648 if (commentDate > currDate || currDate - commentDate > cutoff) {
88649 corePreferences('comment', null);
88650 corePreferences('hashtags', null);
88651 corePreferences('source', null);
88652 } // load in explicitly-set values, if any
88655 if (context.defaultChangesetComment()) {
88656 corePreferences('comment', context.defaultChangesetComment());
88657 corePreferences('commentDate', Date.now());
88660 if (context.defaultChangesetSource()) {
88661 corePreferences('source', context.defaultChangesetSource());
88662 corePreferences('commentDate', Date.now());
88665 if (context.defaultChangesetHashtags()) {
88666 corePreferences('hashtags', context.defaultChangesetHashtags());
88667 corePreferences('commentDate', Date.now());
88670 var detected = utilDetect();
88672 comment: corePreferences('comment') || '',
88673 created_by: context.cleanTagValue('iD ' + context.version),
88674 host: context.cleanTagValue(detected.host),
88675 locale: context.cleanTagValue(_mainLocalizer.localeCode())
88676 }; // call findHashtags initially - this will remove stored
88677 // hashtags if any hashtags are found in the comment - #4304
88679 findHashtags(tags, true);
88680 var hashtags = corePreferences('hashtags');
88683 tags.hashtags = hashtags;
88686 var source = corePreferences('source');
88689 tags.source = source;
88692 var photoOverlaysUsed = context.history().photoOverlaysUsed();
88694 if (photoOverlaysUsed.length) {
88695 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
88697 if (sources.indexOf('streetlevel imagery') === -1) {
88698 sources.push('streetlevel imagery');
88699 } // add the photo overlays used during editing as sources
88702 photoOverlaysUsed.forEach(function (photoOverlay) {
88703 if (sources.indexOf(photoOverlay) === -1) {
88704 sources.push(photoOverlay);
88707 tags.source = context.cleanTagValue(sources.join(';'));
88710 context.changeset = new osmChangeset({
88713 } // Calculates read-only metadata tags based on the user's editing session and applies
88714 // them to the changeset.
88717 function loadDerivedChangesetTags() {
88718 var osm = context.connection();
88720 var tags = Object.assign({}, context.changeset.tags); // shallow copy
88721 // assign tags for imagery used
88723 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
88724 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
88726 var osmClosed = osm.getClosedIDs();
88729 if (osmClosed.length) {
88730 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
88733 if (services.keepRight) {
88734 var krClosed = services.keepRight.getClosedIDs();
88736 if (krClosed.length) {
88737 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
88741 if (services.improveOSM) {
88742 var iOsmClosed = services.improveOSM.getClosedCounts();
88744 for (itemType in iOsmClosed) {
88745 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
88749 if (services.osmose) {
88750 var osmoseClosed = services.osmose.getClosedCounts();
88752 for (itemType in osmoseClosed) {
88753 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
88755 } // remove existing issue counts
88758 for (var key in tags) {
88759 if (key.match(/(^warnings:)|(^resolved:)/)) {
88764 function addIssueCounts(issues, prefix) {
88765 var issuesByType = utilArrayGroupBy(issues, 'type');
88767 for (var issueType in issuesByType) {
88768 var issuesOfType = issuesByType[issueType];
88770 if (issuesOfType[0].subtype) {
88771 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
88773 for (var issueSubtype in issuesBySubtype) {
88774 var issuesOfSubtype = issuesBySubtype[issueSubtype];
88775 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
88778 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
88781 } // add counts of warnings generated by the user's edits
88784 var warnings = context.validator().getIssuesBySeverity({
88787 includeIgnored: true,
88788 includeDisabledRules: true
88790 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
88792 var resolvedIssues = context.validator().getResolvedIssues();
88793 addIssueCounts(resolvedIssues, 'resolved');
88794 context.changeset = context.changeset.update({
88799 function render(selection) {
88800 var osm = context.connection();
88802 var header = selection.selectAll('.header').data([0]);
88803 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
88804 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
88805 headerTitle.append('button').attr('class', 'close').on('click', function () {
88806 dispatch.call('cancel', this);
88807 }).call(svgIcon('#iD-icon-close'));
88808 var body = selection.selectAll('.body').data([0]);
88809 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
88811 var changesetSection = body.selectAll('.changeset-editor').data([0]);
88812 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
88813 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
88815 body.call(commitWarnings); // Upload Explanation
88817 var saveSection = body.selectAll('.save-section').data([0]);
88818 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
88819 var prose = saveSection.selectAll('.commit-info').data([0]);
88821 if (prose.enter().size()) {
88822 // first time, make sure to update user details in prose
88823 _userDetails = null;
88826 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()
88827 // if needed, because it can trigger a style recalculation
88829 osm.userDetails(function (err, user) {
88831 if (_userDetails === user) return; // no change
88833 _userDetails = user;
88834 var userLink = select(document.createElement('div'));
88836 if (user.image_url) {
88837 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
88840 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
88841 prose.html(_t.html('commit.upload_explanation_with_user', {
88842 user: userLink.html()
88844 }); // Request Review
88846 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
88848 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
88849 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
88850 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
88852 if (!labelEnter.empty()) {
88853 labelEnter.call(uiTooltip().title(_t.html('commit.request_review_info')).placement('top'));
88856 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
88857 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
88859 requestReview = requestReview.merge(requestReviewEnter);
88860 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
88862 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
88864 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
88865 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
88866 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
88867 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
88868 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
88870 buttonSection = buttonSection.merge(buttonEnter);
88871 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
88872 dispatch.call('cancel', this);
88874 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
88875 if (!select(this).classed('disabled')) {
88876 this.blur(); // avoid keeping focus on the button - #4641
88878 for (var key in context.changeset.tags) {
88879 // remove any empty keys before upload
88880 if (!key) delete context.changeset.tags[key];
88883 context.uploader().save(context.changeset);
88885 }); // remove any existing tooltip
88887 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
88889 if (uploadBlockerTooltipText) {
88890 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
88891 } // Raw Tag Editor
88894 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
88895 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
88896 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
88898 var changesSection = body.selectAll('.commit-changes-section').data([0]);
88899 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
88901 changesSection.call(commitChanges.render);
88903 function toggleRequestReview() {
88904 var rr = requestReviewInput.property('checked');
88906 review_requested: rr ? 'yes' : undefined
88908 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
88913 function getUploadBlockerMessage() {
88914 var errors = context.validator().getIssuesBySeverity({
88919 if (errors.length) {
88920 return _t('commit.outstanding_errors_message', {
88921 count: errors.length
88924 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
88926 if (!hasChangesetComment) {
88927 return _t('commit.comment_needed_message');
88934 function changeTags(_, changed, onInput) {
88935 if (changed.hasOwnProperty('comment')) {
88936 if (changed.comment === undefined) {
88937 changed.comment = '';
88941 corePreferences('comment', changed.comment);
88942 corePreferences('commentDate', Date.now());
88946 if (changed.hasOwnProperty('source')) {
88947 if (changed.source === undefined) {
88948 corePreferences('source', null);
88949 } else if (!onInput) {
88950 corePreferences('source', changed.source);
88951 corePreferences('commentDate', Date.now());
88953 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
88956 updateChangeset(changed, onInput);
88959 _selection.call(render);
88963 function findHashtags(tags, commentOnly) {
88964 var detectedHashtags = commentHashtags();
88966 if (detectedHashtags.length) {
88967 // always remove stored hashtags if there are hashtags in the comment - #4304
88968 corePreferences('hashtags', null);
88971 if (!detectedHashtags.length || !commentOnly) {
88972 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
88975 var allLowerCase = new Set();
88976 return detectedHashtags.filter(function (hashtag) {
88977 // Compare tags as lowercase strings, but keep original case tags
88978 var lowerCase = hashtag.toLowerCase();
88980 if (!allLowerCase.has(lowerCase)) {
88981 allLowerCase.add(lowerCase);
88986 }); // Extract hashtags from `comment`
88988 function commentHashtags() {
88989 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
88990 .match(hashtagRegex);
88991 return matches || [];
88992 } // Extract and clean hashtags from `hashtags`
88995 function hashtagHashtags() {
88996 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
88997 if (s[0] !== '#') {
89002 var matched = s.match(hashtagRegex);
89003 return matched && matched[0];
89004 }).filter(Boolean); // exclude falsy
89006 return matches || [];
89010 function isReviewRequested(tags) {
89011 var rr = tags.review_requested;
89012 if (rr === undefined) return false;
89013 rr = rr.trim().toLowerCase();
89014 return !(rr === '' || rr === 'no');
89017 function updateChangeset(changed, onInput) {
89018 var tags = Object.assign({}, context.changeset.tags); // shallow copy
89020 Object.keys(changed).forEach(function (k) {
89021 var v = changed[k];
89022 k = context.cleanTagKey(k);
89023 if (readOnlyTags.indexOf(k) !== -1) return;
89025 if (v === undefined) {
89027 } else if (onInput) {
89030 tags[k] = context.cleanTagValue(v);
89035 // when changing the comment, override hashtags with any found in comment.
89036 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
89037 var arr = findHashtags(tags, commentOnly);
89040 tags.hashtags = context.cleanTagValue(arr.join(';'));
89041 corePreferences('hashtags', tags.hashtags);
89043 delete tags.hashtags;
89044 corePreferences('hashtags', null);
89046 } // always update userdetails, just in case user reauthenticates as someone else
89049 if (_userDetails && _userDetails.changesets_count !== undefined) {
89050 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
89052 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
89054 if (changesetsCount <= 100) {
89056 s = corePreferences('walkthrough_completed');
89059 tags['ideditor:walkthrough_completed'] = s;
89062 s = corePreferences('walkthrough_progress');
89065 tags['ideditor:walkthrough_progress'] = s;
89068 s = corePreferences('walkthrough_started');
89071 tags['ideditor:walkthrough_started'] = s;
89075 delete tags.changesets_count;
89078 if (!fastDeepEqual(context.changeset.tags, tags)) {
89079 context.changeset = context.changeset.update({
89085 commit.reset = function () {
89086 context.changeset = null;
89089 return utilRebind(commit, dispatch, 'on');
89092 // for punction see https://stackoverflow.com/a/21224179
89094 function simplify(str) {
89095 if (typeof str !== 'string') return '';
89096 return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i').replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>«»~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u200b-\u200f\u2016\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18\u2e19\u2e1b\u2e1e\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
89099 // `resolveStrings`
89100 // Resolves the text strings for a given community index item
89103 // `item`: Object containing the community index item
89104 // `defaults`: Object containing the community index default strings
89105 // `localizerFn?`: optional function we will call to do the localization.
89106 // This function should be like the iD `t()` function that
89107 // accepts a `stringID` and returns a localized string
89110 // An Object containing all the resolved strings:
89112 // name: 'talk-ru Mailing List',
89113 // url: 'https://lists.openstreetmap.org/listinfo/talk-ru',
89114 // signupUrl: 'https://example.url/signup',
89115 // description: 'A one line description',
89116 // extendedDescription: 'Extended description',
89117 // nameHTML: '<a href="the url">the name</a>',
89118 // urlHTML: '<a href="the url">the url</a>',
89119 // signupUrlHTML: '<a href="the signupUrl">the signupUrl</a>',
89120 // descriptionHTML: the description, with urls and signupUrls linkified,
89121 // extendedDescriptionHTML: the extendedDescription with urls and signupUrls linkified
89125 function resolveStrings(item, defaults, localizerFn) {
89126 var itemStrings = Object.assign({}, item.strings); // shallow clone
89128 var defaultStrings = Object.assign({}, defaults[item.type]); // shallow clone
89130 var anyToken = new RegExp(/(\{\w+\})/, 'gi'); // Pre-localize the item and default strings
89133 if (itemStrings.community) {
89134 var communityID = simplify(itemStrings.community);
89135 itemStrings.community = localizerFn("_communities.".concat(communityID));
89138 ['name', 'description', 'extendedDescription'].forEach(function (prop) {
89139 if (defaultStrings[prop]) defaultStrings[prop] = localizerFn("_defaults.".concat(item.type, ".").concat(prop));
89140 if (itemStrings[prop]) itemStrings[prop] = localizerFn("".concat(item.id, ".").concat(prop));
89144 var replacements = {
89145 account: item.account,
89146 community: itemStrings.community,
89147 signupUrl: itemStrings.signupUrl,
89148 url: itemStrings.url
89149 }; // Resolve URLs first (which may refer to {account})
89151 if (!replacements.signupUrl) {
89152 replacements.signupUrl = resolve(itemStrings.signupUrl || defaultStrings.signupUrl);
89155 if (!replacements.url) {
89156 replacements.url = resolve(itemStrings.url || defaultStrings.url);
89160 name: resolve(itemStrings.name || defaultStrings.name),
89161 url: resolve(itemStrings.url || defaultStrings.url),
89162 signupUrl: resolve(itemStrings.signupUrl || defaultStrings.signupUrl),
89163 description: resolve(itemStrings.description || defaultStrings.description),
89164 extendedDescription: resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription)
89165 }; // Generate linkified strings
89167 resolved.nameHTML = linkify(resolved.url, resolved.name);
89168 resolved.urlHTML = linkify(resolved.url);
89169 resolved.signupUrlHTML = linkify(resolved.signupUrl);
89170 resolved.descriptionHTML = resolve(itemStrings.description || defaultStrings.description, true);
89171 resolved.extendedDescriptionHTML = resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription, true);
89174 function resolve(s, addLinks) {
89175 if (!s) return undefined;
89178 for (var key in replacements) {
89179 var token = "{".concat(key, "}");
89180 var regex = new RegExp(token, 'g');
89182 if (regex.test(result)) {
89183 var replacement = replacements[key];
89185 if (!replacement) {
89186 throw new Error("Cannot resolve token: ".concat(token));
89188 if (addLinks && (key === 'signupUrl' || key === 'url')) {
89189 replacement = linkify(replacement);
89192 result = result.replace(regex, replacement);
89195 } // There shouldn't be any leftover tokens in a resolved string
89198 var leftovers = result.match(anyToken);
89201 throw new Error("Cannot resolve tokens: ".concat(leftovers));
89202 } // Linkify subreddits like `/r/openstreetmap`
89203 // https://github.com/osmlab/osm-community-index/issues/82
89204 // https://github.com/openstreetmap/iD/issues/4997
89207 if (addLinks && item.type === 'reddit') {
89208 result = result.replace(/(\/r\/\w+\/*)/i, function (match) {
89209 return linkify(resolved.url, match);
89216 function linkify(url, text) {
89217 if (!url) return undefined;
89218 text = text || url;
89219 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
89224 function uiSuccess(context) {
89226 var dispatch = dispatch$8('cancel');
89232 ensureOSMCommunityIndex(); // start fetching the data
89234 function ensureOSMCommunityIndex() {
89235 var data = _mainFileFetcher;
89236 return Promise.all([data.get('oci_features'), data.get('oci_resources'), data.get('oci_defaults')]).then(function (vals) {
89237 if (_oci) return _oci; // Merge Custom Features
89239 if (vals[0] && Array.isArray(vals[0].features)) {
89240 _mainLocations.mergeCustomGeoJSON(vals[0]);
89243 var ociResources = Object.values(vals[1].resources);
89245 if (ociResources.length) {
89246 // Resolve all locationSet features.
89247 return _mainLocations.mergeLocationSets(ociResources).then(function () {
89249 resources: ociResources,
89250 defaults: vals[2].defaults
89258 defaults: vals[2].defaults
89263 } // string-to-date parsing in JavaScript is weird
89266 function parseEventDate(when) {
89268 var raw = when.trim();
89271 if (!/Z$/.test(raw)) {
89272 // if no trailing 'Z', add one
89273 raw += 'Z'; // this forces date to be parsed as a UTC date
89276 var parsed = new Date(raw);
89277 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
89280 function success(selection) {
89281 var header = selection.append('div').attr('class', 'header fillL');
89282 header.append('h3').html(_t.html('success.just_edited'));
89283 header.append('button').attr('class', 'close').on('click', function () {
89284 return dispatch.call('cancel');
89285 }).call(svgIcon('#iD-icon-close'));
89286 var body = selection.append('div').attr('class', 'body save-success fillL');
89287 var summary = body.append('div').attr('class', 'save-summary');
89288 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
89291 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'));
89292 var osm = context.connection();
89294 var changesetURL = osm.changesetURL(_changeset.id);
89295 var table = summary.append('table').attr('class', 'summary-table');
89296 var row = table.append('tr').attr('class', 'summary-row');
89297 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');
89298 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
89299 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
89300 summaryDetail.append('div').html(_t.html('success.changeset_id', {
89301 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
89302 })); // Get OSM community index features intersecting the map..
89304 ensureOSMCommunityIndex().then(function (oci) {
89305 var loc = context.map().center();
89306 var validLocations = _mainLocations.locationsAt(loc); // Gather the communities
89308 var communities = [];
89309 oci.resources.forEach(function (resource) {
89310 var area = validLocations[resource.locationSetID];
89311 if (!area) return; // Resolve strings
89313 var localizer = function localizer(stringID) {
89314 return _t.html("community.".concat(stringID));
89317 resource.resolved = resolveStrings(resource, oci.defaults, localizer);
89320 order: resource.order || 0,
89323 }); // sort communities by feature area ascending, community order descending
89325 communities.sort(function (a, b) {
89326 return a.area - b.area || b.order - a.order;
89328 body.call(showCommunityLinks, communities.map(function (c) {
89334 function showCommunityLinks(selection, resources) {
89335 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
89336 communityLinks.append('h3').html(_t.html('success.like_osm'));
89337 var table = communityLinks.append('table').attr('class', 'community-table');
89338 var row = table.selectAll('.community-row').data(resources);
89339 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
89340 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
89341 return d.resolved.url;
89342 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
89343 return "#community-".concat(d.type);
89345 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
89346 communityDetail.each(showCommunityDetails);
89347 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'));
89350 function showCommunityDetails(d) {
89351 var selection = select(this);
89352 var communityID = d.id;
89353 selection.append('div').attr('class', 'community-name').html(d.resolved.nameHTML);
89354 selection.append('div').attr('class', 'community-description').html(d.resolved.descriptionHTML); // Create an expanding section if any of these are present..
89356 if (d.resolved.extendedDescriptionHTML || d.languageCodes && d.languageCodes.length) {
89357 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
89360 var nextEvents = (d.events || []).map(function (event) {
89361 event.date = parseEventDate(event.when);
89363 }).filter(function (event) {
89364 // date is valid and future (or today)
89365 var t = event.date.getTime();
89366 var now = new Date().setHours(0, 0, 0, 0);
89367 return !isNaN(t) && t >= now;
89368 }).sort(function (a, b) {
89369 // sort by date ascending
89370 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
89371 }).slice(0, MAXEVENTS); // limit number of events shown
89373 if (nextEvents.length) {
89374 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);
89377 function showMore(selection) {
89378 var more = selection.selectAll('.community-more').data([0]);
89379 var moreEnter = more.enter().append('div').attr('class', 'community-more');
89381 if (d.resolved.extendedDescriptionHTML) {
89382 moreEnter.append('div').attr('class', 'community-extended-description').html(d.resolved.extendedDescriptionHTML);
89385 if (d.languageCodes && d.languageCodes.length) {
89386 var languageList = d.languageCodes.map(function (code) {
89387 return _mainLocalizer.languageName(code);
89389 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
89390 languages: languageList
89395 function showNextEvents(selection) {
89396 var events = selection.append('div').attr('class', 'community-events');
89397 var item = events.selectAll('.community-event').data(nextEvents);
89398 var itemEnter = item.enter().append('div').attr('class', 'community-event');
89399 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
89401 }).html(function (d) {
89404 if (d.i18n && d.id) {
89405 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
89412 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
89420 if (d.date.getHours() || d.date.getMinutes()) {
89421 // include time if it has one
89422 options.hour = 'numeric';
89423 options.minute = 'numeric';
89426 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
89428 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
89429 var where = d.where;
89431 if (d.i18n && d.id) {
89432 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
89439 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
89440 var description = d.description;
89442 if (d.i18n && d.id) {
89443 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
89444 "default": description
89448 return description;
89453 success.changeset = function (val) {
89454 if (!arguments.length) return _changeset;
89459 success.location = function (val) {
89460 if (!arguments.length) return _location;
89465 return utilRebind(success, dispatch, 'on');
89468 function modeSave(context) {
89472 var keybinding = utilKeybinding('modeSave');
89473 var commit = uiCommit(context).on('cancel', cancel);
89475 var _conflictsUi; // uiConflicts
89482 var uploader = context.uploader().on('saveStarted.modeSave', function () {
89484 }) // fire off some async work that we want to be ready later
89485 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
89487 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
89489 function cancel() {
89490 context.enter(modeBrowse(context));
89493 function showProgress(num, total) {
89494 var modal = context.container().select('.loading-modal .modal-section');
89495 var progress = modal.selectAll('.progress').data([0]); // enter/update
89497 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
89503 function showConflicts(changeset, conflicts, origChanges) {
89504 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
89505 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
89506 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
89507 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
89508 selection.remove();
89510 uploader.cancelConflictResolution();
89511 }).on('save', function () {
89512 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
89513 selection.remove();
89514 uploader.processResolvedConflicts(changeset);
89516 selection.call(_conflictsUi);
89519 function showErrors(errors) {
89521 var selection = uiConfirm(context.container());
89522 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
89523 addErrors(selection, errors);
89524 selection.okButton();
89527 function addErrors(selection, data) {
89528 var message = selection.select('.modal-section.message-text');
89529 var items = message.selectAll('.error-container').data(data);
89530 var enter = items.enter().append('div').attr('class', 'error-container');
89531 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
89532 return d.msg || _t('save.unknown_error_details');
89533 }).on('click', function (d3_event) {
89534 d3_event.preventDefault();
89535 var error = select(this);
89536 var detail = select(this.nextElementSibling);
89537 var exp = error.classed('expanded');
89538 detail.style('display', exp ? 'none' : 'block');
89539 error.classed('expanded', !exp);
89541 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
89542 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
89543 return d.details || [];
89544 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
89547 items.exit().remove();
89550 function showSuccess(changeset) {
89553 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
89554 context.ui().sidebar.hide();
89557 context.enter(modeBrowse(context).sidebar(ui));
89560 function keybindingOn() {
89561 select(document).call(keybinding.on('⎋', cancel, true));
89564 function keybindingOff() {
89565 select(document).call(keybinding.unbind);
89566 } // Reverse geocode current map location so we can display a message on
89567 // the success screen like "Thank you for editing around place, region."
89570 function prepareForSuccess() {
89571 _success = uiSuccess(context);
89573 if (!services.geocoder) return;
89574 services.geocoder.reverse(context.map().center(), function (err, result) {
89575 if (err || !result || !result.address) return;
89576 var addr = result.address;
89577 var place = addr && (addr.town || addr.city || addr.county) || '';
89578 var region = addr && (addr.state || addr.country) || '';
89579 var separator = place && region ? _t('success.thank_you_where.separator') : '';
89580 _location = _t('success.thank_you_where.format', {
89582 separator: separator,
89588 mode.selectedIDs = function () {
89589 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
89592 mode.enter = function () {
89594 context.ui().sidebar.expand();
89597 context.ui().sidebar.show(commit);
89601 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
89602 var osm = context.connection();
89609 if (osm.authenticated()) {
89612 osm.authenticate(function (err) {
89622 mode.exit = function () {
89624 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
89625 context.ui().sidebar.hide();
89631 function modeSelectError(context, selectedErrorID, selectedErrorService) {
89633 id: 'select-error',
89636 var keybinding = utilKeybinding('select-error');
89637 var errorService = services[selectedErrorService];
89640 switch (selectedErrorService) {
89642 errorEditor = uiImproveOsmEditor(context).on('change', function () {
89643 context.map().pan([0, 0]); // trigger a redraw
89645 var error = checkSelectedID();
89646 if (!error) return;
89647 context.ui().sidebar.show(errorEditor.error(error));
89652 errorEditor = uiKeepRightEditor(context).on('change', function () {
89653 context.map().pan([0, 0]); // trigger a redraw
89655 var error = checkSelectedID();
89656 if (!error) return;
89657 context.ui().sidebar.show(errorEditor.error(error));
89662 errorEditor = uiOsmoseEditor(context).on('change', function () {
89663 context.map().pan([0, 0]); // trigger a redraw
89665 var error = checkSelectedID();
89666 if (!error) return;
89667 context.ui().sidebar.show(errorEditor.error(error));
89672 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
89674 function checkSelectedID() {
89675 if (!errorService) return;
89676 var error = errorService.getError(selectedErrorID);
89679 context.enter(modeBrowse(context));
89685 mode.zoomToSelected = function () {
89686 if (!errorService) return;
89687 var error = errorService.getError(selectedErrorID);
89690 context.map().centerZoomEase(error.loc, 20);
89694 mode.enter = function () {
89695 var error = checkSelectedID();
89696 if (!error) return;
89697 behaviors.forEach(context.install);
89698 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
89699 select(document).call(keybinding);
89701 var sidebar = context.ui().sidebar;
89702 sidebar.show(errorEditor.error(error));
89703 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
89705 function selectError(d3_event, drawn) {
89706 if (!checkSelectedID()) return;
89707 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
89709 if (selection.empty()) {
89710 // Return to browse mode if selected DOM elements have
89711 // disappeared because the user moved them out of view..
89712 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
89714 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
89715 context.enter(modeBrowse(context));
89718 selection.classed('selected', true);
89719 context.selectedErrorID(selectedErrorID);
89724 if (context.container().select('.combobox').size()) return;
89725 context.enter(modeBrowse(context));
89729 mode.exit = function () {
89730 behaviors.forEach(context.uninstall);
89731 select(document).call(keybinding.unbind);
89732 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
89733 context.map().on('drawn.select-error', null);
89734 context.ui().sidebar.hide();
89735 context.selectedErrorID(null);
89736 context.features().forceVisible([]);
89742 function uiToolOldDrawModes(context) {
89745 label: _t.html('toolbar.add_feature')
89747 var modes = [modeAddPoint(context, {
89748 title: _t.html('modes.add_point.title'),
89750 description: _t.html('modes.add_point.description'),
89751 preset: _mainPresetIndex.item('point'),
89753 }), modeAddLine(context, {
89754 title: _t.html('modes.add_line.title'),
89756 description: _t.html('modes.add_line.description'),
89757 preset: _mainPresetIndex.item('line'),
89759 }), modeAddArea(context, {
89760 title: _t.html('modes.add_area.title'),
89762 description: _t.html('modes.add_area.description'),
89763 preset: _mainPresetIndex.item('area'),
89767 function enabled() {
89768 return osmEditable();
89771 function osmEditable() {
89772 return context.editable();
89775 modes.forEach(function (mode) {
89776 context.keybinding().on(mode.key, function () {
89777 if (!enabled()) return;
89779 if (mode.id === context.mode().id) {
89780 context.enter(modeBrowse(context));
89782 context.enter(mode);
89787 tool.render = function (selection) {
89788 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
89790 var debouncedUpdate = debounce(update, 500, {
89795 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
89796 context.on('enter.modes', update);
89799 function update() {
89800 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
89804 buttons.exit().remove(); // enter
89806 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
89807 return d.id + ' add-button bar-button';
89808 }).on('click.mode-buttons', function (d3_event, d) {
89809 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
89811 var currMode = context.mode().id;
89812 if (/^draw/.test(currMode)) return;
89814 if (d.id === currMode) {
89815 context.enter(modeBrowse(context));
89819 }).call(uiTooltip().placement('bottom').title(function (d) {
89820 return d.description;
89821 }).keys(function (d) {
89823 }).scrollContainer(context.container().select('.top-toolbar')));
89824 buttonsEnter.each(function (d) {
89825 select(this).call(svgIcon('#iD-icon-' + d.button));
89827 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
89829 }); // if we are adding/removing the buttons, check if toolbar has overflowed
89831 if (buttons.enter().size() || buttons.exit().size()) {
89832 context.ui().checkOverflow('.top-toolbar', true);
89836 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
89838 }).classed('active', function (d) {
89839 return context.mode() && context.mode().button === d.button;
89847 function uiToolNotes(context) {
89850 label: _t.html('modes.add_note.label')
89852 var mode = modeAddNote(context);
89854 function enabled() {
89855 return notesEnabled() && notesEditable();
89858 function notesEnabled() {
89859 var noteLayer = context.layers().layer('notes');
89860 return noteLayer && noteLayer.enabled();
89863 function notesEditable() {
89864 var mode = context.mode();
89865 return context.map().notesEditable() && mode && mode.id !== 'save';
89868 context.keybinding().on(mode.key, function () {
89869 if (!enabled()) return;
89871 if (mode.id === context.mode().id) {
89872 context.enter(modeBrowse(context));
89874 context.enter(mode);
89878 tool.render = function (selection) {
89879 var debouncedUpdate = debounce(update, 500, {
89884 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
89885 context.on('enter.notes', update);
89888 function update() {
89889 var showNotes = notesEnabled();
89890 var data = showNotes ? [mode] : [];
89891 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
89895 buttons.exit().remove(); // enter
89897 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
89898 return d.id + ' add-button bar-button';
89899 }).on('click.notes', function (d3_event, d) {
89900 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
89902 var currMode = context.mode().id;
89903 if (/^draw/.test(currMode)) return;
89905 if (d.id === currMode) {
89906 context.enter(modeBrowse(context));
89910 }).call(uiTooltip().placement('bottom').title(function (d) {
89911 return d.description;
89912 }).keys(function (d) {
89914 }).scrollContainer(context.container().select('.top-toolbar')));
89915 buttonsEnter.each(function (d) {
89916 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
89917 }); // if we are adding/removing the buttons, check if toolbar has overflowed
89919 if (buttons.enter().size() || buttons.exit().size()) {
89920 context.ui().checkOverflow('.top-toolbar', true);
89924 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
89926 }).classed('active', function (d) {
89927 return context.mode() && context.mode().button === d.button;
89932 tool.uninstall = function () {
89933 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
89934 context.map().on('move.notes', null).on('drawn.notes', null);
89940 function uiToolSave(context) {
89943 label: _t.html('save.title')
89946 var tooltipBehavior = null;
89947 var history = context.history();
89948 var key = uiCmd('⌘S');
89949 var _numChanges = 0;
89951 function isSaving() {
89952 var mode = context.mode();
89953 return mode && mode.id === 'save';
89956 function isDisabled() {
89957 return _numChanges === 0 || isSaving();
89960 function save(d3_event) {
89961 d3_event.preventDefault();
89963 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
89964 context.enter(modeSave(context));
89968 function bgColor() {
89971 if (_numChanges === 0) {
89973 } else if (_numChanges <= 50) {
89974 step = _numChanges / 50;
89975 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
89977 step = Math.min((_numChanges - 50) / 50, 1.0);
89978 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
89982 function updateCount() {
89983 var val = history.difference().summary().length;
89984 if (val === _numChanges) return;
89987 if (tooltipBehavior) {
89988 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
89992 button.classed('disabled', isDisabled()).style('background', bgColor());
89993 button.select('span.count').html(_numChanges);
89997 tool.render = function (selection) {
89998 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
89999 var lastPointerUpType;
90000 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
90001 lastPointerUpType = d3_event.pointerType;
90002 }).on('click', function (d3_event) {
90005 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
90006 // there are no tooltips for touch interactions so flash feedback instead
90007 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
90010 lastPointerUpType = null;
90011 }).call(tooltipBehavior);
90012 button.call(svgIcon('#iD-icon-save'));
90013 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
90015 context.keybinding().on(key, save, true);
90016 context.history().on('change.save', updateCount);
90017 context.on('enter.save', function () {
90019 button.classed('disabled', isDisabled());
90022 button.call(tooltipBehavior.hide);
90028 tool.uninstall = function () {
90029 context.keybinding().off(key, true);
90030 context.history().on('change.save', null);
90031 context.on('enter.save', null);
90033 tooltipBehavior = null;
90039 function uiToolSidebarToggle(context) {
90041 id: 'sidebar_toggle',
90042 label: _t.html('toolbar.inspect')
90045 tool.render = function (selection) {
90046 selection.append('button').attr('class', 'bar-button').on('click', function () {
90047 context.ui().sidebar.toggle();
90048 }).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')));
90054 function uiToolUndoRedo(context) {
90057 label: _t.html('toolbar.undo_redo')
90062 action: function action() {
90065 annotation: function annotation() {
90066 return context.history().undoAnnotation();
90068 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
90072 action: function action() {
90075 annotation: function annotation() {
90076 return context.history().redoAnnotation();
90078 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
90081 function editable() {
90082 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
90083 /* ignore min zoom */
90087 tool.render = function (selection) {
90088 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
90089 return d.annotation() ? _t.html(d.id + '.tooltip', {
90090 action: d.annotation()
90091 }) : _t.html(d.id + '.nothing');
90092 }).keys(function (d) {
90094 }).scrollContainer(context.container().select('.top-toolbar'));
90095 var lastPointerUpType;
90096 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
90097 return 'disabled ' + d.id + '-button bar-button';
90098 }).on('pointerup', function (d3_event) {
90099 // `pointerup` is always called before `click`
90100 lastPointerUpType = d3_event.pointerType;
90101 }).on('click', function (d3_event, d) {
90102 d3_event.preventDefault();
90103 var annotation = d.annotation();
90105 if (editable() && annotation) {
90109 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
90110 // there are no tooltips for touch interactions so flash feedback instead
90111 var text = annotation ? _t(d.id + '.tooltip', {
90113 }) : _t(d.id + '.nothing');
90114 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
90117 lastPointerUpType = null;
90118 }).call(tooltipBehavior);
90119 buttons.each(function (d) {
90120 select(this).call(svgIcon('#' + d.icon));
90122 context.keybinding().on(commands[0].cmd, function (d3_event) {
90123 d3_event.preventDefault();
90124 if (editable()) commands[0].action();
90125 }).on(commands[1].cmd, function (d3_event) {
90126 d3_event.preventDefault();
90127 if (editable()) commands[1].action();
90130 var debouncedUpdate = debounce(update, 500, {
90135 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
90136 context.history().on('change.undo_redo', function (difference) {
90137 if (difference) update();
90139 context.on('enter.undo_redo', update);
90141 function update() {
90142 buttons.classed('disabled', function (d) {
90143 return !editable() || !d.annotation();
90144 }).each(function () {
90145 var selection = select(this);
90147 if (!selection.select('.tooltip.in').empty()) {
90148 selection.call(tooltipBehavior.updateContent);
90154 tool.uninstall = function () {
90155 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
90156 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
90157 context.history().on('change.undo_redo', null);
90158 context.on('enter.undo_redo', null);
90164 function uiTopToolbar(context) {
90165 var sidebarToggle = uiToolSidebarToggle(context),
90166 modes = uiToolOldDrawModes(context),
90167 notes = uiToolNotes(context),
90168 undoRedo = uiToolUndoRedo(context),
90169 save = uiToolSave(context);
90171 function notesEnabled() {
90172 var noteLayer = context.layers().layer('notes');
90173 return noteLayer && noteLayer.enabled();
90176 function topToolbar(bar) {
90177 bar.on('wheel.topToolbar', function (d3_event) {
90178 if (!d3_event.deltaX) {
90179 // translate vertical scrolling into horizontal scrolling in case
90180 // the user doesn't have an input device that can scroll horizontally
90181 bar.node().scrollLeft += d3_event.deltaY;
90185 var debouncedUpdate = debounce(update, 500, {
90190 context.layers().on('change.topToolbar', debouncedUpdate);
90193 function update() {
90194 var tools = [sidebarToggle, 'spacer', modes];
90195 tools.push('spacer');
90197 if (notesEnabled()) {
90198 tools = tools.concat([notes, 'spacer']);
90201 tools = tools.concat([undoRedo, save]);
90202 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
90205 toolbarItems.exit().each(function (d) {
90210 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
90211 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
90212 if (d.klass) classes += ' ' + d.klass;
90215 var actionableItems = itemsEnter.filter(function (d) {
90216 return d !== 'spacer';
90218 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
90219 select(this).call(d.render, bar);
90221 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
90230 var sawVersion = null;
90231 var isNewVersion = false;
90232 var isNewUser = false;
90233 function uiVersion(context) {
90234 var currVersion = context.version;
90235 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
90237 if (sawVersion === null && matchedVersion !== null) {
90238 if (corePreferences('sawVersion')) {
90240 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
90243 isNewVersion = true;
90246 corePreferences('sawVersion', currVersion);
90247 sawVersion = currVersion;
90250 return function (selection) {
90251 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
90253 if (isNewVersion && !isNewUser) {
90254 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', {
90255 version: currVersion
90256 })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
90261 function uiZoom(context) {
90264 icon: 'iD-icon-plus',
90265 title: _t.html('zoom.in'),
90267 disabled: function disabled() {
90268 return !context.map().canZoomIn();
90270 disabledTitle: _t.html('zoom.disabled.in'),
90274 icon: 'iD-icon-minus',
90275 title: _t.html('zoom.out'),
90277 disabled: function disabled() {
90278 return !context.map().canZoomOut();
90280 disabledTitle: _t.html('zoom.disabled.out'),
90284 function zoomIn(d3_event) {
90285 if (d3_event.shiftKey) return;
90286 d3_event.preventDefault();
90287 context.map().zoomIn();
90290 function zoomOut(d3_event) {
90291 if (d3_event.shiftKey) return;
90292 d3_event.preventDefault();
90293 context.map().zoomOut();
90296 function zoomInFurther(d3_event) {
90297 if (d3_event.shiftKey) return;
90298 d3_event.preventDefault();
90299 context.map().zoomInFurther();
90302 function zoomOutFurther(d3_event) {
90303 if (d3_event.shiftKey) return;
90304 d3_event.preventDefault();
90305 context.map().zoomOutFurther();
90308 return function (selection) {
90309 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
90310 if (d.disabled()) {
90311 return d.disabledTitle;
90315 }).keys(function (d) {
90318 var lastPointerUpType;
90319 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
90321 }).on('pointerup.editor', function (d3_event) {
90322 lastPointerUpType = d3_event.pointerType;
90323 }).on('click.editor', function (d3_event, d) {
90324 if (!d.disabled()) {
90325 d.action(d3_event);
90326 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
90327 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
90330 lastPointerUpType = null;
90331 }).call(tooltipBehavior);
90332 buttons.each(function (d) {
90333 select(this).call(svgIcon('#' + d.icon, 'light'));
90335 utilKeybinding.plusKeys.forEach(function (key) {
90336 context.keybinding().on([key], zoomIn);
90337 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
90339 utilKeybinding.minusKeys.forEach(function (key) {
90340 context.keybinding().on([key], zoomOut);
90341 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
90344 function updateButtonStates() {
90345 buttons.classed('disabled', function (d) {
90346 return d.disabled();
90347 }).each(function () {
90348 var selection = select(this);
90350 if (!selection.select('.tooltip.in').empty()) {
90351 selection.call(tooltipBehavior.updateContent);
90356 updateButtonStates();
90357 context.map().on('move.uiZoom', updateButtonStates);
90361 function uiZoomToSelection(context) {
90362 function isDisabled() {
90363 var mode = context.mode();
90364 return !mode || !mode.zoomToSelected;
90367 var _lastPointerUpType;
90369 function pointerup(d3_event) {
90370 _lastPointerUpType = d3_event.pointerType;
90373 function click(d3_event) {
90374 d3_event.preventDefault();
90376 if (isDisabled()) {
90377 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
90378 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
90381 var mode = context.mode();
90383 if (mode && mode.zoomToSelected) {
90384 mode.zoomToSelected();
90388 _lastPointerUpType = null;
90391 return function (selection) {
90392 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
90393 if (isDisabled()) {
90394 return _t.html('inspector.zoom_to.no_selection');
90397 return _t.html('inspector.zoom_to.title');
90398 }).keys([_t('inspector.zoom_to.key')]);
90399 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
90401 function setEnabledState() {
90402 button.classed('disabled', isDisabled());
90404 if (!button.select('.tooltip.in').empty()) {
90405 button.call(tooltipBehavior.updateContent);
90409 context.on('enter.uiZoomToSelection', setEnabledState);
90414 function uiPane(id, context) {
90418 var _description = '';
90419 var _iconName = '';
90421 var _sections; // array of uiSection objects
90424 var _paneSelection = select(null);
90432 pane.label = function (val) {
90433 if (!arguments.length) return _label;
90438 pane.key = function (val) {
90439 if (!arguments.length) return _key;
90444 pane.description = function (val) {
90445 if (!arguments.length) return _description;
90446 _description = val;
90450 pane.iconName = function (val) {
90451 if (!arguments.length) return _iconName;
90456 pane.sections = function (val) {
90457 if (!arguments.length) return _sections;
90462 pane.selection = function () {
90463 return _paneSelection;
90466 function hidePane() {
90467 context.ui().togglePanes();
90470 pane.togglePane = function (d3_event) {
90471 if (d3_event) d3_event.preventDefault();
90473 _paneTooltip.hide();
90475 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
90478 pane.renderToggleButton = function (selection) {
90479 if (!_paneTooltip) {
90480 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
90483 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
90486 pane.renderContent = function (selection) {
90487 // override to fully customize content
90489 _sections.forEach(function (section) {
90490 selection.call(section.render);
90495 pane.renderPane = function (selection) {
90496 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
90498 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
90500 heading.append('h2').html(_label);
90501 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
90503 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
90506 context.keybinding().on(_key, pane.togglePane);
90513 function uiSectionBackgroundDisplayOptions(context) {
90514 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
90516 var _detected = utilDetect();
90518 var _storedOpacity = corePreferences('background-opacity');
90522 var _maxVal = _detected.cssfilters ? 3 : 1;
90524 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
90527 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
90533 function clamp(x, min, max) {
90534 return Math.max(min, Math.min(x, max));
90537 function updateValue(d, val) {
90538 val = clamp(val, _minVal, _maxVal);
90540 context.background()[d](val);
90542 if (d === 'brightness') {
90543 corePreferences('background-opacity', val);
90546 section.reRender();
90549 function renderDisclosureContent(selection) {
90550 var container = selection.selectAll('.display-options-container').data([0]);
90551 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
90553 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
90554 return 'display-control display-control-' + d;
90556 slidersEnter.append('h5').html(function (d) {
90557 return _t.html('background.' + d);
90558 }).append('span').attr('class', function (d) {
90559 return 'display-option-value display-option-value-' + d;
90561 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
90562 sildersControlEnter.append('input').attr('class', function (d) {
90563 return 'display-option-input display-option-input-' + d;
90564 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
90565 var val = select(this).property('value');
90567 if (!val && d3_event && d3_event.target) {
90568 val = d3_event.target.value;
90571 updateValue(d, val);
90573 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
90574 return 'display-option-reset display-option-reset-' + d;
90575 }).on('click', function (d3_event, d) {
90576 if (d3_event.button !== 0) return;
90578 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
90580 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
90581 d3_event.preventDefault();
90583 for (var i = 0; i < _sliders.length; i++) {
90584 updateValue(_sliders[i], 1);
90588 container = containerEnter.merge(container);
90589 container.selectAll('.display-option-input').property('value', function (d) {
90590 return _options[d];
90592 container.selectAll('.display-option-value').html(function (d) {
90593 return Math.floor(_options[d] * 100) + '%';
90595 container.selectAll('.display-option-reset').classed('disabled', function (d) {
90596 return _options[d] === 1;
90597 }); // first time only, set brightness if needed
90599 if (containerEnter.size() && _options.brightness !== 1) {
90600 context.background().brightness(_options.brightness);
90607 function uiSettingsCustomBackground() {
90608 var dispatch = dispatch$8('change');
90610 function render(selection) {
90611 // keep separate copies of original and current settings
90612 var _origSettings = {
90613 template: corePreferences('background-custom-template')
90615 var _currSettings = {
90616 template: corePreferences('background-custom-template')
90618 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
90619 var modal = uiConfirm(selection).okButton();
90620 modal.classed('settings-modal settings-custom-background', true);
90621 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
90622 var textSection = modal.select('.modal-section.message-text');
90623 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, "`");
90624 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
90625 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
90627 var buttonSection = modal.select('.modal-section.buttons');
90628 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
90629 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
90630 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
90632 function isSaveDisabled() {
90634 } // restore the original template
90637 function clickCancel() {
90638 textSection.select('.field-template').property('value', _origSettings.template);
90639 corePreferences('background-custom-template', _origSettings.template);
90642 } // accept the current template
90645 function clickSave() {
90646 _currSettings.template = textSection.select('.field-template').property('value');
90647 corePreferences('background-custom-template', _currSettings.template);
90650 dispatch.call('change', this, _currSettings);
90654 return utilRebind(render, dispatch, 'on');
90657 function uiSectionBackgroundList(context) {
90658 var _backgroundList = select(null);
90660 var _customSource = context.background().findSource('custom');
90662 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
90664 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
90666 function previousBackgroundID() {
90667 return corePreferences('background-last-used-toggle');
90670 function renderDisclosureContent(selection) {
90671 // the background list
90672 var container = selection.selectAll('.layer-background-list').data([0]);
90673 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
90675 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
90676 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'));
90677 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
90678 d3_event.preventDefault();
90679 uiMapInMap.toggle();
90681 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
90682 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'));
90683 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
90684 d3_event.preventDefault();
90685 context.ui().info.toggle('background');
90687 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
90688 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'));
90689 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
90690 d3_event.preventDefault();
90691 context.ui().info.toggle('location');
90693 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
90695 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'));
90697 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
90698 chooseBackground(d);
90700 return !d.isHidden() && !d.overlay;
90704 function setTooltips(selection) {
90705 selection.each(function (d, i, nodes) {
90706 var item = select(this).select('label');
90707 var span = item.select('span');
90708 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
90709 var description = d.description();
90710 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
90711 item.call(uiTooltip().destroyAny);
90713 if (d.id === previousBackgroundID()) {
90714 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
90715 } else if (description || isOverflowing) {
90716 item.call(uiTooltip().placement(placement).title(description || d.label()));
90721 function drawListItems(layerList, type, change, filter) {
90722 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
90723 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
90725 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
90726 // arrow key navigation of radio values likes to work in the order
90727 // they were added, not the display document order.
90728 .data(sources, function (d, i) {
90729 return d.id + '---' + i;
90731 layerLinks.exit().remove();
90732 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
90733 return d.id === 'custom';
90734 }).classed('best', function (d) {
90737 var label = enter.append('label');
90738 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
90740 }).on('change', change);
90741 label.append('span').html(function (d) {
90744 enter.filter(function (d) {
90745 return d.id === 'custom';
90746 }).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) {
90747 d3_event.preventDefault();
90749 }).call(svgIcon('#iD-icon-more'));
90750 enter.filter(function (d) {
90752 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
90753 layerList.call(updateLayerSelections);
90756 function updateLayerSelections(selection) {
90757 function active(d) {
90758 return context.background().showsLayer(d);
90761 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
90762 return d.id === previousBackgroundID();
90763 }).call(setTooltips).selectAll('input').property('checked', active);
90766 function chooseBackground(d) {
90767 if (d.id === 'custom' && !d.template()) {
90768 return editCustom();
90771 var previousBackground = context.background().baseLayerSource();
90772 corePreferences('background-last-used-toggle', previousBackground.id);
90773 corePreferences('background-last-used', d.id);
90774 context.background().baseLayerSource(d);
90777 function customChanged(d) {
90778 if (d && d.template) {
90779 _customSource.template(d.template);
90781 chooseBackground(_customSource);
90783 _customSource.template('');
90785 chooseBackground(context.background().findSource('none'));
90789 function editCustom() {
90790 context.container().call(_settingsCustomBackground);
90793 context.background().on('change.background_list', function () {
90794 _backgroundList.call(updateLayerSelections);
90796 context.map().on('move.background_list', debounce(function () {
90797 // layers in-view may have changed due to map move
90798 window.requestIdleCallback(section.reRender);
90803 function uiSectionBackgroundOffset(context) {
90804 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
90806 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90808 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
90810 function updateValue() {
90811 var meters = geoOffsetToMeters(context.background().offset());
90812 var x = +meters[0].toFixed(2);
90813 var y = +meters[1].toFixed(2);
90814 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
90815 context.container().selectAll('.nudge-reset').classed('disabled', function () {
90816 return x === 0 && y === 0;
90820 function resetOffset() {
90821 context.background().offset([0, 0]);
90825 function nudge(d) {
90826 context.background().nudge(d, context.map().zoom());
90830 function inputOffset() {
90831 var input = select(this);
90832 var d = input.node().value;
90833 if (d === '') return resetOffset();
90834 d = d.replace(/;/g, ',').split(',').map(function (n) {
90835 // if n is NaN, it will always get mapped to false.
90836 return !isNaN(n) && n;
90839 if (d.length !== 2 || !d[0] || !d[1]) {
90840 input.classed('error', true);
90844 context.background().offset(geoMetersToOffset(d));
90848 function dragOffset(d3_event) {
90849 if (d3_event.button !== 0) return;
90850 var origin = [d3_event.clientX, d3_event.clientY];
90851 var pointerId = d3_event.pointerId || 'mouse';
90852 context.container().append('div').attr('class', 'nudge-surface');
90853 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
90855 if (_pointerPrefix === 'pointer') {
90856 select(window).on('pointercancel.drag-bg-offset', pointerup);
90859 function pointermove(d3_event) {
90860 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
90861 var latest = [d3_event.clientX, d3_event.clientY];
90862 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
90867 function pointerup(d3_event) {
90868 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
90869 if (d3_event.button !== 0) return;
90870 context.container().selectAll('.nudge-surface').remove();
90871 select(window).on('.drag-bg-offset', null);
90875 function renderDisclosureContent(selection) {
90876 var container = selection.selectAll('.nudge-container').data([0]);
90877 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
90878 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
90879 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
90880 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
90881 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
90882 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
90883 return d[0] + ' nudge';
90884 }).on('click', function (d3_event, d) {
90887 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
90888 d3_event.preventDefault();
90890 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
90894 context.background().on('change.backgroundOffset-update', updateValue);
90898 function uiSectionOverlayList(context) {
90899 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
90901 var _overlayList = select(null);
90903 function setTooltips(selection) {
90904 selection.each(function (d, i, nodes) {
90905 var item = select(this).select('label');
90906 var span = item.select('span');
90907 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
90908 var description = d.description();
90909 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
90910 item.call(uiTooltip().destroyAny);
90912 if (description || isOverflowing) {
90913 item.call(uiTooltip().placement(placement).title(description || d.name()));
90918 function updateLayerSelections(selection) {
90919 function active(d) {
90920 return context.background().showsLayer(d);
90923 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
90926 function chooseOverlay(d3_event, d) {
90927 d3_event.preventDefault();
90928 context.background().toggleOverlayLayer(d);
90930 _overlayList.call(updateLayerSelections);
90932 document.activeElement.blur();
90935 function drawListItems(layerList, type, change, filter) {
90936 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
90937 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
90940 layerLinks.exit().remove();
90941 var enter = layerLinks.enter().append('li');
90942 var label = enter.append('label');
90943 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
90944 label.append('span').html(function (d) {
90947 layerList.selectAll('li').sort(sortSources);
90948 layerList.call(updateLayerSelections);
90950 function sortSources(a, b) {
90951 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
90955 function renderDisclosureContent(selection) {
90956 var container = selection.selectAll('.layer-overlay-list').data([0]);
90957 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
90959 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
90960 return !d.isHidden() && d.overlay;
90964 context.map().on('move.overlay_list', debounce(function () {
90965 // layers in-view may have changed due to map move
90966 window.requestIdleCallback(section.reRender);
90971 function uiPaneBackground(context) {
90972 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)]);
90973 return backgroundPane;
90976 function uiPaneHelp(context) {
90977 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']]];
90979 'help.help.open_data_h': 3,
90980 'help.help.before_start_h': 3,
90981 'help.help.open_source_h': 3,
90982 'help.overview.navigation_h': 3,
90983 'help.overview.features_h': 3,
90984 'help.editing.select_h': 3,
90985 'help.editing.multiselect_h': 3,
90986 'help.editing.undo_redo_h': 3,
90987 'help.editing.save_h': 3,
90988 'help.editing.upload_h': 3,
90989 'help.editing.backups_h': 3,
90990 'help.editing.keyboard_h': 3,
90991 'help.feature_editor.type_h': 3,
90992 'help.feature_editor.fields_h': 3,
90993 'help.feature_editor.tags_h': 3,
90994 'help.points.add_point_h': 3,
90995 'help.points.move_point_h': 3,
90996 'help.points.delete_point_h': 3,
90997 'help.lines.add_line_h': 3,
90998 'help.lines.modify_line_h': 3,
90999 'help.lines.connect_line_h': 3,
91000 'help.lines.disconnect_line_h': 3,
91001 'help.lines.move_line_h': 3,
91002 'help.lines.delete_line_h': 3,
91003 'help.areas.point_or_area_h': 3,
91004 'help.areas.add_area_h': 3,
91005 'help.areas.square_area_h': 3,
91006 'help.areas.modify_area_h': 3,
91007 'help.areas.delete_area_h': 3,
91008 'help.relations.edit_relation_h': 3,
91009 'help.relations.maintain_relation_h': 3,
91010 'help.relations.relation_types_h': 2,
91011 'help.relations.multipolygon_h': 3,
91012 'help.relations.turn_restriction_h': 3,
91013 'help.relations.route_h': 3,
91014 'help.relations.boundary_h': 3,
91015 'help.notes.add_note_h': 3,
91016 'help.notes.update_note_h': 3,
91017 'help.notes.save_note_h': 3,
91018 'help.imagery.sources_h': 3,
91019 'help.imagery.offsets_h': 3,
91020 'help.streetlevel.using_h': 3,
91021 'help.gps.using_h': 3,
91022 'help.qa.tools_h': 3,
91023 'help.qa.issues_h': 3
91024 }; // For each section, squash all the texts into a single markdown document
91026 var docs = docKeys.map(function (key) {
91027 var helpkey = 'help.' + key[0];
91028 var helpPaneReplacements = {
91029 version: context.version
91031 var text = key[1].reduce(function (all, part) {
91032 var subkey = helpkey + '.' + part;
91033 var depth = headings[subkey]; // is this subkey a heading?
91035 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
91037 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
91040 title: _t.html(helpkey + '.title'),
91041 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
91042 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
91045 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
91047 helpPane.renderContent = function (content) {
91048 function clickHelp(d, i) {
91049 var rtl = _mainLocalizer.textDirection() === 'rtl';
91050 content.property('scrollTop', 0);
91051 helpPane.selection().select('.pane-heading h2').html(d.title);
91052 body.html(d.content);
91053 body.selectAll('a').attr('target', '_blank');
91054 menuItems.classed('selected', function (m) {
91055 return m.title === d.title;
91060 nav.call(drawNext).call(drawPrevious);
91062 nav.call(drawPrevious).call(drawNext);
91065 function drawNext(selection) {
91066 if (i < docs.length - 1) {
91067 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
91068 d3_event.preventDefault();
91069 clickHelp(docs[i + 1], i + 1);
91071 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
91075 function drawPrevious(selection) {
91077 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
91078 d3_event.preventDefault();
91079 clickHelp(docs[i - 1], i - 1);
91081 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
91086 function clickWalkthrough(d3_event) {
91087 d3_event.preventDefault();
91088 if (context.inIntro()) return;
91089 context.container().call(uiIntro(context));
91090 context.ui().togglePanes();
91093 function clickShortcuts(d3_event) {
91094 d3_event.preventDefault();
91095 context.container().call(context.ui().shortcuts, true);
91098 var toc = content.append('ul').attr('class', 'toc');
91099 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
91101 }).on('click', function (d3_event, d) {
91102 d3_event.preventDefault();
91103 clickHelp(d, docs.indexOf(d));
91105 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);
91106 shortcuts.append('div').html(_t.html('shortcuts.title'));
91107 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
91108 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
91109 walkthrough.append('div').html(_t.html('splash.walkthrough'));
91110 var helpContent = content.append('div').attr('class', 'left-content');
91111 var body = helpContent.append('div').attr('class', 'body');
91112 var nav = helpContent.append('div').attr('class', 'nav');
91113 clickHelp(docs[0], 0);
91119 function uiSectionValidationIssues(id, severity, context) {
91121 var section = uiSection(id, context).label(function () {
91122 if (!_issues) return '';
91123 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
91124 return _t('inspector.title_count', {
91125 title: _t.html('issues.' + severity + 's.list_title'),
91126 count: issueCountText
91128 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
91129 return _issues && _issues.length;
91132 function getOptions() {
91134 what: corePreferences('validate-what') || 'edited',
91135 where: corePreferences('validate-where') || 'all'
91137 } // get and cache the issues to display, unordered
91140 function reloadIssues() {
91141 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
91144 function renderDisclosureContent(selection) {
91145 var center = context.map().center();
91146 var graph = context.graph(); // sort issues by distance away from the center of the map
91148 var issues = _issues.map(function withDistance(issue) {
91149 var extent = issue.extent(graph);
91150 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
91151 return Object.assign(issue, {
91154 }).sort(function byDistance(a, b) {
91155 return a.dist - b.dist;
91156 }); // cut off at 1000
91159 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
91161 selection.call(drawIssuesList, issues);
91164 function drawIssuesList(selection, issues) {
91165 var list = selection.selectAll('.issues-list').data([0]);
91166 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
91167 var items = list.selectAll('li').data(issues, function (d) {
91171 items.exit().remove(); // Enter
91173 var itemsEnter = items.enter().append('li').attr('class', function (d) {
91174 return 'issue severity-' + d.severity;
91176 var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
91177 context.validator().focusIssue(d);
91178 }).on('mouseover', function (d3_event, d) {
91179 utilHighlightEntities(d.entityIds, true, context);
91180 }).on('mouseout', function (d3_event, d) {
91181 utilHighlightEntities(d.entityIds, false, context);
91183 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
91184 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
91185 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
91186 select(this).call(svgIcon(iconName));
91188 textEnter.append('span').attr('class', 'issue-message');
91192 .attr('class', 'issue-autofix')
91193 .each(function(d) {
91194 if (!d.autoFix) return;
91197 .attr('title', t('issues.fix_one.title'))
91198 .datum(d.autoFix) // set button datum to the autofix
91199 .attr('class', 'autofix action')
91200 .on('click', function(d3_event, d) {
91201 d3_event.preventDefault();
91202 d3_event.stopPropagation();
91203 var issuesEntityIDs = d.issue.entityIds;
91204 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
91205 context.perform.apply(context, d.autoArgs);
91206 context.validator().validate();
91208 .call(svgIcon('#iD-icon-wrench'));
91213 items = items.merge(itemsEnter).order();
91214 items.selectAll('.issue-message').html(function (d) {
91215 return d.message(context);
91219 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
91220 var autoFixAll = selection.selectAll('.autofix-all')
91221 .data(canAutoFix.length ? [0] : []);
91226 var autoFixAllEnter = autoFixAll.enter()
91227 .insert('div', '.issues-list')
91228 .attr('class', 'autofix-all');
91229 var linkEnter = autoFixAllEnter
91231 .attr('class', 'autofix-all-link')
91232 .attr('href', '#');
91235 .attr('class', 'autofix-all-link-text')
91236 .html(t.html('issues.fix_all.title'));
91239 .attr('class', 'autofix-all-link-icon')
91240 .call(svgIcon('#iD-icon-wrench'));
91241 if (severity === 'warning') {
91242 renderIgnoredIssuesReset(selection);
91245 autoFixAll = autoFixAll
91246 .merge(autoFixAllEnter);
91247 autoFixAll.selectAll('.autofix-all-link')
91248 .on('click', function() {
91249 context.pauseChangeDispatch();
91250 context.perform(actionNoop());
91251 canAutoFix.forEach(function(issue) {
91252 var args = issue.autoFix.autoArgs.slice(); // copy
91253 if (typeof args[args.length - 1] !== 'function') {
91256 args.push(t('issues.fix_all.annotation'));
91257 context.replace.apply(context, args);
91259 context.resumeChangeDispatch();
91260 context.validator().validate();
91265 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
91266 window.requestIdleCallback(function () {
91268 section.reRender();
91271 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
91272 window.requestIdleCallback(function () {
91273 if (getOptions().where === 'visible') {
91274 // must refetch issues if they are viewport-dependent
91276 } // always reload list to re-sort-by-distance
91279 section.reRender();
91285 function uiSectionValidationOptions(context) {
91286 var section = uiSection('issues-options', context).content(renderContent);
91288 function renderContent(selection) {
91289 var container = selection.selectAll('.issues-options-container').data([0]);
91290 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
91293 values: ['edited', 'all']
91296 values: ['visible', 'all']
91298 var options = container.selectAll('.issues-option').data(data, function (d) {
91301 var optionsEnter = options.enter().append('div').attr('class', function (d) {
91302 return 'issues-option issues-option-' + d.key;
91304 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
91305 return _t.html('issues.options.' + d.key + '.title');
91307 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
91308 return d.values.map(function (val) {
91314 }).enter().append('label');
91315 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
91316 return 'issues-option-' + d.key;
91317 }).attr('value', function (d) {
91319 }).property('checked', function (d) {
91320 return getOptions()[d.key] === d.value;
91321 }).on('change', function (d3_event, d) {
91322 updateOptionValue(d3_event, d.key, d.value);
91324 valuesEnter.append('span').html(function (d) {
91325 return _t.html('issues.options.' + d.key + '.' + d.value);
91329 function getOptions() {
91331 what: corePreferences('validate-what') || 'edited',
91333 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
91338 function updateOptionValue(d3_event, d, val) {
91339 if (!val && d3_event && d3_event.target) {
91340 val = d3_event.target.value;
91343 corePreferences('validate-' + d, val);
91344 context.validator().validate();
91350 function uiSectionValidationRules(context) {
91352 var MAXSQUARE = 20;
91353 var DEFAULTSQUARE = 5; // see also unsquare_way.js
91355 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
91357 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
91358 return key !== 'maprules';
91359 }).sort(function (key1, key2) {
91360 // alphabetize by localized title
91361 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
91364 function renderDisclosureContent(selection) {
91365 var container = selection.selectAll('.issues-rulelist-container').data([0]);
91366 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
91367 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
91368 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
91369 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
91370 d3_event.preventDefault();
91371 context.validator().disableRules(_ruleKeys);
91373 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
91374 d3_event.preventDefault();
91375 context.validator().disableRules([]);
91378 container = container.merge(containerEnter);
91379 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
91382 function drawListItems(selection, data, type, name, change, active) {
91383 var items = selection.selectAll('li').data(data); // Exit
91385 items.exit().remove(); // Enter
91387 var enter = items.enter().append('li');
91389 if (name === 'rule') {
91390 enter.call(uiTooltip().title(function (d) {
91391 return _t.html('issues.' + d + '.tip');
91392 }).placement('top'));
91395 var label = enter.append('label');
91396 label.append('input').attr('type', type).attr('name', name).on('change', change);
91397 label.append('span').html(function (d) {
91400 if (d === 'unsquare_way') {
91401 params.val = '<span class="square-degrees"></span>';
91404 return _t.html('issues.' + d + '.title', params);
91407 items = items.merge(enter);
91408 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
91410 var degStr = corePreferences('validate-square-degrees');
91412 if (degStr === null) {
91413 degStr = DEFAULTSQUARE.toString();
91416 var span = items.selectAll('.square-degrees');
91417 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
91419 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) {
91420 d3_event.preventDefault();
91421 d3_event.stopPropagation();
91423 }).on('keyup', function (d3_event) {
91424 if (d3_event.keyCode === 13) {
91429 }).on('blur', changeSquare).merge(input).property('value', degStr);
91432 function changeSquare() {
91433 var input = select(this);
91434 var degStr = utilGetSetValue(input).trim();
91435 var degNum = parseFloat(degStr, 10);
91437 if (!isFinite(degNum)) {
91438 degNum = DEFAULTSQUARE;
91439 } else if (degNum > MAXSQUARE) {
91440 degNum = MAXSQUARE;
91441 } else if (degNum < MINSQUARE) {
91442 degNum = MINSQUARE;
91445 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
91447 degStr = degNum.toString();
91448 input.property('value', degStr);
91449 corePreferences('validate-square-degrees', degStr);
91450 context.validator().revalidateUnsquare();
91453 function isRuleEnabled(d) {
91454 return context.validator().isRuleEnabled(d);
91457 function toggleRule(d3_event, d) {
91458 context.validator().toggleRule(d);
91461 context.validator().on('validated.uiSectionValidationRules', function () {
91462 window.requestIdleCallback(section.reRender);
91467 function uiSectionValidationStatus(context) {
91468 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
91469 var issues = context.validator().getIssues(getOptions());
91470 return issues.length === 0;
91473 function getOptions() {
91475 what: corePreferences('validate-what') || 'edited',
91476 where: corePreferences('validate-where') || 'all'
91480 function renderContent(selection) {
91481 var box = selection.selectAll('.box').data([0]);
91482 var boxEnter = box.enter().append('div').attr('class', 'box');
91483 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
91484 var noIssuesMessage = boxEnter.append('span');
91485 noIssuesMessage.append('strong').attr('class', 'message');
91486 noIssuesMessage.append('br');
91487 noIssuesMessage.append('span').attr('class', 'details');
91488 renderIgnoredIssuesReset(selection);
91489 setNoIssuesText(selection);
91492 function renderIgnoredIssuesReset(selection) {
91493 var ignoredIssues = context.validator().getIssues({
91496 includeDisabledRules: true,
91497 includeIgnored: 'only'
91499 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
91501 resetIgnored.exit().remove(); // enter
91503 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
91504 resetIgnoredEnter.append('a').attr('href', '#'); // update
91506 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
91507 resetIgnored.select('a').html(_t('inspector.title_count', {
91508 title: _t.html('issues.reset_ignored'),
91509 count: ignoredIssues.length
91511 resetIgnored.on('click', function (d3_event) {
91512 d3_event.preventDefault();
91513 context.validator().resetIgnoredIssues();
91517 function setNoIssuesText(selection) {
91518 var opts = getOptions();
91520 function checkForHiddenIssues(cases) {
91521 for (var type in cases) {
91522 var hiddenOpts = cases[type];
91523 var hiddenIssues = context.validator().getIssues(hiddenOpts);
91525 if (hiddenIssues.length) {
91526 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
91527 count: hiddenIssues.length.toString()
91533 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
91538 if (opts.what === 'edited' && opts.where === 'visible') {
91539 messageType = 'edits_in_view';
91540 checkForHiddenIssues({
91552 includeDisabledRules: 'only'
91554 everything_else_elsewhere: {
91558 disabled_rules_elsewhere: {
91561 includeDisabledRules: 'only'
91566 includeIgnored: 'only'
91568 ignored_issues_elsewhere: {
91571 includeIgnored: 'only'
91574 } else if (opts.what === 'edited' && opts.where === 'all') {
91575 messageType = 'edits';
91576 checkForHiddenIssues({
91584 includeDisabledRules: 'only'
91589 includeIgnored: 'only'
91592 } else if (opts.what === 'all' && opts.where === 'visible') {
91593 messageType = 'everything_in_view';
91594 checkForHiddenIssues({
91602 includeDisabledRules: 'only'
91604 disabled_rules_elsewhere: {
91607 includeDisabledRules: 'only'
91612 includeIgnored: 'only'
91614 ignored_issues_elsewhere: {
91617 includeIgnored: 'only'
91620 } else if (opts.what === 'all' && opts.where === 'all') {
91621 messageType = 'everything';
91622 checkForHiddenIssues({
91626 includeDisabledRules: 'only'
91631 includeIgnored: 'only'
91636 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
91637 messageType = 'no_edits';
91640 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
91643 context.validator().on('validated.uiSectionValidationStatus', function () {
91644 window.requestIdleCallback(section.reRender);
91646 context.map().on('move.uiSectionValidationStatus', debounce(function () {
91647 window.requestIdleCallback(section.reRender);
91652 function uiPaneIssues(context) {
91653 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)]);
91657 function uiSettingsCustomData(context) {
91658 var dispatch = dispatch$8('change');
91660 function render(selection) {
91661 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
91663 var _origSettings = {
91664 fileList: dataLayer && dataLayer.fileList() || null,
91665 url: corePreferences('settings-custom-data-url')
91667 var _currSettings = {
91668 fileList: dataLayer && dataLayer.fileList() || null,
91669 url: corePreferences('settings-custom-data-url')
91670 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
91672 var modal = uiConfirm(selection).okButton();
91673 modal.classed('settings-modal settings-custom-data', true);
91674 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
91675 var textSection = modal.select('.modal-section.message-text');
91676 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
91677 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
91678 .on('change', function (d3_event) {
91679 var files = d3_event.target.files;
91681 if (files && files.length) {
91682 _currSettings.url = '';
91683 textSection.select('.field-url').property('value', '');
91684 _currSettings.fileList = files;
91686 _currSettings.fileList = null;
91689 textSection.append('h4').html(_t.html('settings.custom_data.or'));
91690 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
91691 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
91693 var buttonSection = modal.select('.modal-section.buttons');
91694 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
91695 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
91696 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
91698 function isSaveDisabled() {
91700 } // restore the original url
91703 function clickCancel() {
91704 textSection.select('.field-url').property('value', _origSettings.url);
91705 corePreferences('settings-custom-data-url', _origSettings.url);
91708 } // accept the current url
91711 function clickSave() {
91712 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
91714 if (_currSettings.url) {
91715 _currSettings.fileList = null;
91718 if (_currSettings.fileList) {
91719 _currSettings.url = '';
91722 corePreferences('settings-custom-data-url', _currSettings.url);
91725 dispatch.call('change', this, _currSettings);
91729 return utilRebind(render, dispatch, 'on');
91732 function uiSectionDataLayers(context) {
91733 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
91734 var layers = context.layers();
91735 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
91737 function renderDisclosureContent(selection) {
91738 var container = selection.selectAll('.data-layer-container').data([0]);
91739 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
91740 .call(drawPanelItems);
91743 function showsLayer(which) {
91744 var layer = layers.layer(which);
91747 return layer.enabled();
91753 function setLayer(which, enabled) {
91754 // Don't allow layer changes while drawing - #6584
91755 var mode = context.mode();
91756 if (mode && /^draw/.test(mode.id)) return;
91757 var layer = layers.layer(which);
91760 layer.enabled(enabled);
91762 if (!enabled && (which === 'osm' || which === 'notes')) {
91763 context.enter(modeBrowse(context));
91768 function toggleLayer(which) {
91769 setLayer(which, !showsLayer(which));
91772 function drawOsmItems(selection) {
91773 var osmKeys = ['osm', 'notes'];
91774 var osmLayers = layers.all().filter(function (obj) {
91775 return osmKeys.indexOf(obj.id) !== -1;
91777 var ul = selection.selectAll('.layer-list-osm').data([0]);
91778 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
91779 var li = ul.selectAll('.list-item').data(osmLayers);
91780 li.exit().remove();
91781 var liEnter = li.enter().append('li').attr('class', function (d) {
91782 return 'list-item list-item-' + d.id;
91784 var labelEnter = liEnter.append('label').each(function (d) {
91785 if (d.id === 'osm') {
91786 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
91788 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
91791 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
91794 labelEnter.append('span').html(function (d) {
91795 return _t.html('map_data.layers.' + d.id + '.title');
91798 li.merge(liEnter).classed('active', function (d) {
91799 return d.layer.enabled();
91800 }).selectAll('input').property('checked', function (d) {
91801 return d.layer.enabled();
91805 function drawQAItems(selection) {
91806 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
91807 var qaLayers = layers.all().filter(function (obj) {
91808 return qaKeys.indexOf(obj.id) !== -1;
91810 var ul = selection.selectAll('.layer-list-qa').data([0]);
91811 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
91812 var li = ul.selectAll('.list-item').data(qaLayers);
91813 li.exit().remove();
91814 var liEnter = li.enter().append('li').attr('class', function (d) {
91815 return 'list-item list-item-' + d.id;
91817 var labelEnter = liEnter.append('label').each(function (d) {
91818 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
91820 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
91823 labelEnter.append('span').html(function (d) {
91824 return _t.html('map_data.layers.' + d.id + '.title');
91827 li.merge(liEnter).classed('active', function (d) {
91828 return d.layer.enabled();
91829 }).selectAll('input').property('checked', function (d) {
91830 return d.layer.enabled();
91832 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
91833 // https://github.com/osmus/detroit-mapping-challenge
91836 function drawVectorItems(selection) {
91837 var dataLayer = layers.layer('data');
91839 name: 'Detroit Neighborhoods/Parks',
91840 src: 'neighborhoods-parks',
91841 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
91842 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'
91844 name: 'Detroit Composite POIs',
91845 src: 'composite-poi',
91846 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
91847 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'
91849 name: 'Detroit All-The-Places POIs',
91850 src: 'alltheplaces-poi',
91851 tooltip: 'Public domain business location data created by web scrapers.',
91852 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'
91853 }]; // Only show this if the map is around Detroit..
91855 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
91856 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
91857 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
91858 container.exit().remove();
91859 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
91860 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
91861 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
91862 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');
91863 container = container.merge(containerEnter);
91864 var ul = container.selectAll('.layer-list-vectortile');
91865 var li = ul.selectAll('.list-item').data(vtData);
91866 li.exit().remove();
91867 var liEnter = li.enter().append('li').attr('class', function (d) {
91868 return 'list-item list-item-' + d.src;
91870 var labelEnter = liEnter.append('label').each(function (d) {
91871 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
91873 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
91874 labelEnter.append('span').html(function (d) {
91878 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
91880 function isVTLayerSelected(d) {
91881 return dataLayer && dataLayer.template() === d.template;
91884 function selectVTLayer(d3_event, d) {
91885 corePreferences('settings-custom-data-url', d.template);
91888 dataLayer.template(d.template, d.src);
91889 dataLayer.enabled(true);
91894 function drawCustomDataItems(selection) {
91895 var dataLayer = layers.layer('data');
91896 var hasData = dataLayer && dataLayer.hasData();
91897 var showsData = hasData && dataLayer.enabled();
91898 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
91900 ul.exit().remove(); // Enter
91902 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
91903 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
91904 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
91905 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
91906 toggleLayer('data');
91908 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
91909 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) {
91910 d3_event.preventDefault();
91912 }).call(svgIcon('#iD-icon-more'));
91913 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) {
91914 if (select(this).classed('disabled')) return;
91915 d3_event.preventDefault();
91916 d3_event.stopPropagation();
91917 dataLayer.fitZoom();
91918 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
91920 ul = ul.merge(ulEnter);
91921 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
91922 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
91925 function editCustom() {
91926 context.container().call(settingsCustomData);
91929 function customChanged(d) {
91930 var dataLayer = layers.layer('data');
91933 dataLayer.url(d.url);
91934 } else if (d && d.fileList) {
91935 dataLayer.fileList(d.fileList);
91939 function drawPanelItems(selection) {
91940 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
91941 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'));
91942 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
91943 d3_event.preventDefault();
91944 context.ui().info.toggle('history');
91946 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
91947 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'));
91948 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
91949 d3_event.preventDefault();
91950 context.ui().info.toggle('measurement');
91952 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
91955 context.layers().on('change.uiSectionDataLayers', section.reRender);
91956 context.map().on('move.uiSectionDataLayers', debounce(function () {
91957 // Detroit layers may have moved in or out of view
91958 window.requestIdleCallback(section.reRender);
91963 function uiSectionMapFeatures(context) {
91964 var _features = context.features().keys();
91966 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
91968 function renderDisclosureContent(selection) {
91969 var container = selection.selectAll('.layer-feature-list-container').data([0]);
91970 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
91971 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
91972 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
91973 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
91974 d3_event.preventDefault();
91975 context.features().disableAll();
91977 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
91978 d3_event.preventDefault();
91979 context.features().enableAll();
91982 container = container.merge(containerEnter);
91983 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
91986 function drawListItems(selection, data, type, name, change, active) {
91987 var items = selection.selectAll('li').data(data); // Exit
91989 items.exit().remove(); // Enter
91991 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
91992 var tip = _t.html(name + '.' + d + '.tooltip');
91994 if (autoHiddenFeature(d)) {
91995 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
91996 tip += '<div>' + msg + '</div>';
92000 }).placement('top'));
92001 var label = enter.append('label');
92002 label.append('input').attr('type', type).attr('name', name).on('change', change);
92003 label.append('span').html(function (d) {
92004 return _t.html(name + '.' + d + '.description');
92007 items = items.merge(enter);
92008 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
92011 function autoHiddenFeature(d) {
92012 return context.features().autoHidden(d);
92015 function showsFeature(d) {
92016 return context.features().enabled(d);
92019 function clickFeature(d3_event, d) {
92020 context.features().toggle(d);
92023 function showsLayer(id) {
92024 var layer = context.layers().layer(id);
92025 return layer && layer.enabled();
92029 context.features().on('change.map_features', section.reRender);
92033 function uiSectionMapStyleOptions(context) {
92034 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
92036 function renderDisclosureContent(selection) {
92037 var container = selection.selectAll('.layer-fill-list').data([0]);
92038 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
92039 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
92040 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
92041 return context.surface().classed('highlight-edited');
92045 function drawListItems(selection, data, type, name, change, active) {
92046 var items = selection.selectAll('li').data(data); // Exit
92048 items.exit().remove(); // Enter
92050 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
92051 return _t.html(name + '.' + d + '.tooltip');
92052 }).keys(function (d) {
92053 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
92054 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
92055 return key ? [key] : null;
92056 }).placement('top'));
92057 var label = enter.append('label');
92058 label.append('input').attr('type', type).attr('name', name).on('change', change);
92059 label.append('span').html(function (d) {
92060 return _t.html(name + '.' + d + '.description');
92063 items = items.merge(enter);
92064 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
92067 function isActiveFill(d) {
92068 return context.map().activeAreaFill() === d;
92071 function toggleHighlightEdited(d3_event) {
92072 d3_event.preventDefault();
92073 context.map().toggleHighlightEdited();
92076 function setFill(d3_event, d) {
92077 context.map().activeAreaFill(d);
92080 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
92084 function uiSectionPhotoOverlays(context) {
92085 var layers = context.layers();
92086 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
92088 function renderDisclosureContent(selection) {
92089 var container = selection.selectAll('.photo-overlay-container').data([0]);
92090 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
92093 function drawPhotoItems(selection) {
92094 var photoKeys = context.photos().overlayLayerIDs();
92095 var photoLayers = layers.all().filter(function (obj) {
92096 return photoKeys.indexOf(obj.id) !== -1;
92098 var data = photoLayers.filter(function (obj) {
92099 return obj.layer.supported();
92102 function layerSupported(d) {
92103 return d.layer && d.layer.supported();
92106 function layerEnabled(d) {
92107 return layerSupported(d) && d.layer.enabled();
92110 var ul = selection.selectAll('.layer-list-photos').data([0]);
92111 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
92112 var li = ul.selectAll('.list-item-photos').data(data);
92113 li.exit().remove();
92114 var liEnter = li.enter().append('li').attr('class', function (d) {
92115 var classes = 'list-item-photos list-item-' + d.id;
92117 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
92118 classes += ' indented';
92123 var labelEnter = liEnter.append('label').each(function (d) {
92125 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';
92126 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
92128 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
92131 labelEnter.append('span').html(function (d) {
92133 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
92134 return _t.html(id.replace(/-/g, '_') + '.title');
92137 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
92140 function drawPhotoTypeItems(selection) {
92141 var data = context.photos().allPhotoTypes();
92143 function typeEnabled(d) {
92144 return context.photos().showsPhotoType(d);
92147 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
92148 ul.exit().remove();
92149 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
92150 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
92151 li.exit().remove();
92152 var liEnter = li.enter().append('li').attr('class', function (d) {
92153 return 'list-item-photo-types list-item-' + d;
92155 var labelEnter = liEnter.append('label').each(function (d) {
92156 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
92158 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
92159 context.photos().togglePhotoType(d);
92161 labelEnter.append('span').html(function (d) {
92162 return _t.html('photo_overlays.photo_type.' + d + '.title');
92165 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
92168 function drawDateFilter(selection) {
92169 var data = context.photos().dateFilters();
92171 function filterEnabled(d) {
92172 return context.photos().dateFilterValue(d);
92175 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
92176 ul.exit().remove();
92177 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
92178 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
92179 li.exit().remove();
92180 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
92181 var labelEnter = liEnter.append('label').each(function (d) {
92182 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
92184 labelEnter.append('span').html(function (d) {
92185 return _t.html('photo_overlays.date_filter.' + d + '.title');
92187 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
92188 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
92189 }).on('change', function (d3_event, d) {
92190 var value = utilGetSetValue(select(this)).trim();
92191 context.photos().setDateFilter(d, value, true); // reload the displayed dates
92193 li.selectAll('input').each(function (d) {
92194 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
92197 li = li.merge(liEnter).classed('active', filterEnabled);
92200 function drawUsernameFilter(selection) {
92201 function filterEnabled() {
92202 return context.photos().usernames();
92205 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
92206 ul.exit().remove();
92207 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
92208 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
92209 li.exit().remove();
92210 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
92211 var labelEnter = liEnter.append('label').each(function () {
92212 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
92214 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
92215 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
92216 var value = select(this).property('value');
92217 context.photos().setUsernameFilter(value, true);
92218 select(this).property('value', usernameValue);
92220 li.merge(liEnter).classed('active', filterEnabled);
92222 function usernameValue() {
92223 var usernames = context.photos().usernames();
92224 if (usernames) return usernames.join('; ');
92229 function toggleLayer(which) {
92230 setLayer(which, !showsLayer(which));
92233 function showsLayer(which) {
92234 var layer = layers.layer(which);
92237 return layer.enabled();
92243 function setLayer(which, enabled) {
92244 var layer = layers.layer(which);
92247 layer.enabled(enabled);
92251 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
92252 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
92256 function uiPaneMapData(context) {
92257 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)]);
92258 return mapDataPane;
92261 function uiSectionPrivacy(context) {
92262 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
92264 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
92266 function renderDisclosureContent(selection) {
92268 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
92269 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'));
92270 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
92271 d3_event.preventDefault();
92272 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
92273 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
92276 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
92278 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'));
92281 function update() {
92282 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
92289 function uiPanePreferences(context) {
92290 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)]);
92291 return preferencesPane;
92294 function uiInit(context) {
92295 var _initCounter = 0;
92296 var _needWidth = {};
92298 var _lastPointerType;
92300 function render(container) {
92301 container.on('click.ui', function (d3_event) {
92302 // we're only concerned with the primary mouse button
92303 if (d3_event.button !== 0) return;
92304 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
92306 var isOkayTarget = d3_event.composedPath().some(function (node) {
92307 // we only care about element nodes
92308 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
92309 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
92310 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
92311 node.nodeName === 'A');
92313 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
92315 d3_event.preventDefault();
92317 var detected = utilDetect(); // only WebKit supports gesture events
92319 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
92320 // but we only need to do this on desktop Safari anyway. – #7694
92321 !detected.isMobileWebKit) {
92322 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
92323 // CSS property, but on desktop Safari we need to manually cancel the
92324 // default gesture events.
92325 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
92326 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
92327 d3_event.preventDefault();
92331 if ('PointerEvent' in window) {
92332 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
92333 var pointerType = d3_event.pointerType || 'mouse';
92335 if (_lastPointerType !== pointerType) {
92336 _lastPointerType = pointerType;
92337 container.attr('pointer', pointerType);
92341 _lastPointerType = 'mouse';
92342 container.attr('pointer', 'mouse');
92345 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
92347 container.call(uiFullScreen(context));
92348 var map = context.map();
92349 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
92351 map.on('hitMinZoom.ui', function () {
92352 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
92354 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
92355 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
92356 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
92358 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
92359 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
92360 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
92361 // pressing, even if it's not targeted. This conflicts with long-pressing
92362 // to show the edit menu. We add a selectable offscreen element as the first
92363 // child to trick Safari into not showing the selection UI.
92365 overMap.append('div').attr('class', 'select-trap').text('t');
92366 overMap.call(uiMapInMap(context)).call(uiNotice(context));
92367 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
92369 var controls = overMap.append('div').attr('class', 'map-controls');
92370 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
92371 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
92372 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
92373 // This should happen after map is initialized, as some require surface()
92375 var panes = overMap.append('div').attr('class', 'map-panes');
92376 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
92377 uiPanes.forEach(function (pane) {
92378 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
92379 panes.call(pane.renderPane);
92381 ui.info = uiInfo(context);
92382 overMap.call(ui.info);
92383 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
92384 .classed('hide', true).call(ui.photoviewer);
92385 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
92387 var about = content.append('div').attr('class', 'map-footer');
92388 about.append('div').attr('class', 'api-status').call(uiStatus(context));
92389 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
92390 footer.append('div').attr('class', 'flash-wrap footer-hide');
92391 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
92392 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
92393 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
92394 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
92395 var apiConnections = context.apiConnections();
92397 if (apiConnections && apiConnections.length > 1) {
92398 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
92401 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
92402 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
92403 var issueLinks = aboutList.append('li');
92404 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'));
92405 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'));
92406 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
92408 if (!context.embed()) {
92409 aboutList.call(uiAccount(context));
92410 } // Setup map dimensions and move map to initial center/zoom.
92411 // This should happen after .main-content and toolbars exist.
92415 map.redrawEnable(true);
92416 ui.hash = behaviorHash(context);
92419 if (!ui.hash.hadHash) {
92420 map.centerZoom([0, 0], 2);
92424 window.onbeforeunload = function () {
92425 return context.save();
92428 window.onunload = function () {
92429 context.history().unlock();
92432 select(window).on('resize.editor', function () {
92435 var panPixels = 80;
92436 context.keybinding().on('⌫', function (d3_event) {
92437 d3_event.preventDefault();
92438 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
92439 .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) {
92441 d3_event.stopImmediatePropagation();
92442 d3_event.preventDefault();
92445 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
92447 if (previousBackground) {
92448 var currentBackground = context.background().baseLayerSource();
92449 corePreferences('background-last-used-toggle', currentBackground.id);
92450 corePreferences('background-last-used', previousBackground.id);
92451 context.background().baseLayerSource(previousBackground);
92453 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
92454 d3_event.preventDefault();
92455 d3_event.stopPropagation();
92456 context.map().toggleWireframe();
92457 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
92458 d3_event.preventDefault();
92459 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
92461 var mode = context.mode();
92462 if (mode && /^draw/.test(mode.id)) return;
92463 var layer = context.layers().layer('osm');
92466 layer.enabled(!layer.enabled());
92468 if (!layer.enabled()) {
92469 context.enter(modeBrowse(context));
92472 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
92473 d3_event.preventDefault();
92474 context.map().toggleHighlightEdited();
92476 context.on('enter.editor', function (entered) {
92477 container.classed('mode-' + entered.id, true);
92478 }).on('exit.editor', function (exited) {
92479 container.classed('mode-' + exited.id, false);
92481 context.enter(modeBrowse(context));
92483 if (!_initCounter++) {
92484 if (!ui.hash.startWalkthrough) {
92485 context.container().call(uiSplash(context)).call(uiRestore(context));
92488 context.container().call(ui.shortcuts);
92491 var osm = context.connection();
92492 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
92495 osm.on('authLoading.ui', function () {
92496 context.container().call(auth);
92497 }).on('authDone.ui', function () {
92504 if (ui.hash.startWalkthrough) {
92505 ui.hash.startWalkthrough = false;
92506 context.container().call(uiIntro(context));
92510 return function (d3_event) {
92511 if (d3_event.shiftKey) return;
92512 if (context.container().select('.combobox').size()) return;
92513 d3_event.preventDefault();
92514 context.map().pan(d, 100);
92521 var _loadPromise; // renders the iD interface into the container node
92524 ui.ensureLoaded = function () {
92525 if (_loadPromise) return _loadPromise;
92526 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
92527 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
92528 if (!context.container().empty()) render(context.container());
92529 })["catch"](function (err) {
92530 return console.error(err);
92531 }); // eslint-disable-line
92532 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
92533 // for example to switch the locale while iD is running.
92536 ui.restart = function () {
92537 context.keybinding().clear();
92538 _loadPromise = null;
92539 context.container().selectAll('*').remove();
92543 ui.lastPointerType = function () {
92544 return _lastPointerType;
92547 ui.svgDefs = svgDefs(context);
92548 ui.flash = uiFlash(context);
92549 ui.sidebar = uiSidebar(context);
92550 ui.photoviewer = uiPhotoviewer(context);
92551 ui.shortcuts = uiShortcuts(context);
92553 ui.onResize = function (withPan) {
92554 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
92555 // This will call `getBoundingClientRect` and trigger reflow,
92556 // but the values will be cached for later use.
92558 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
92559 utilGetDimensions(context.container().select('.sidebar'), true);
92561 if (withPan !== undefined) {
92562 map.redrawEnable(false);
92564 map.redrawEnable(true);
92567 map.dimensions(mapDimensions);
92568 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
92570 ui.checkOverflow('.top-toolbar');
92571 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
92573 var resizeWindowEvent = document.createEvent('Event');
92574 resizeWindowEvent.initEvent('resizeWindow', true, true);
92575 document.dispatchEvent(resizeWindowEvent);
92576 }; // Call checkOverflow when resizing or whenever the contents change.
92579 ui.checkOverflow = function (selector, reset) {
92581 delete _needWidth[selector];
92584 var selection = context.container().select(selector);
92585 if (selection.empty()) return;
92586 var scrollWidth = selection.property('scrollWidth');
92587 var clientWidth = selection.property('clientWidth');
92588 var needed = _needWidth[selector] || scrollWidth;
92590 if (scrollWidth > clientWidth) {
92591 // overflow happening
92592 selection.classed('narrow', true);
92594 if (!_needWidth[selector]) {
92595 _needWidth[selector] = scrollWidth;
92597 } else if (scrollWidth >= needed) {
92598 selection.classed('narrow', false);
92602 ui.togglePanes = function (showPane) {
92603 var hidePanes = context.container().selectAll('.map-pane.shown');
92604 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
92605 hidePanes.classed('shown', false).classed('hide', true);
92606 context.container().selectAll('.map-pane-control button').classed('active', false);
92609 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
92610 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
92611 showPane.classed('shown', true).classed('hide', false);
92613 if (hidePanes.empty()) {
92614 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
92616 showPane.style(side, '0px');
92619 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
92620 select(this).classed('shown', false).classed('hide', true);
92625 var _editMenu = uiEditMenu(context);
92627 ui.editMenu = function () {
92631 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
92632 // remove any displayed menu
92633 ui.closeEditMenu();
92634 if (!operations && context.mode().operations) operations = context.mode().operations();
92635 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
92637 if (!context.map().editableDataEnabled()) return;
92638 var surfaceNode = context.surface().node();
92640 if (surfaceNode.focus) {
92641 // FF doesn't support it
92642 // focus the surface or else clicking off the menu may not trigger modeBrowse
92643 surfaceNode.focus();
92646 operations.forEach(function (operation) {
92647 if (operation.point) operation.point(anchorPoint);
92650 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
92653 context.map().supersurface.call(_editMenu);
92656 ui.closeEditMenu = function () {
92657 // remove any existing menu no matter how it was added
92658 context.map().supersurface.select('.edit-menu').remove();
92661 var _saveLoading = select(null);
92663 context.uploader().on('saveStarted.ui', function () {
92664 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
92665 context.container().call(_saveLoading); // block input during upload
92666 }).on('saveEnded.ui', function () {
92667 _saveLoading.close();
92669 _saveLoading = select(null);
92674 function coreContext() {
92677 var dispatch = dispatch$8('enter', 'exit', 'change');
92678 var context = utilRebind({}, dispatch, 'on');
92680 var _deferred = new Set();
92682 context.version = '2.20.0';
92683 context.privacyVersion = '20201202'; // iD will alter the hash so cache the parameters intended to setup the session
92685 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
92686 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
92688 // An osmChangeset object. Not loaded until needed.
92690 context.changeset = null;
92691 var _defaultChangesetComment = context.initialHashParams.comment;
92692 var _defaultChangesetSource = context.initialHashParams.source;
92693 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
92695 context.defaultChangesetComment = function (val) {
92696 if (!arguments.length) return _defaultChangesetComment;
92697 _defaultChangesetComment = val;
92701 context.defaultChangesetSource = function (val) {
92702 if (!arguments.length) return _defaultChangesetSource;
92703 _defaultChangesetSource = val;
92707 context.defaultChangesetHashtags = function (val) {
92708 if (!arguments.length) return _defaultChangesetHashtags;
92709 _defaultChangesetHashtags = val;
92712 /* Document title */
92714 /* (typically shown as the label for the browser window/tab) */
92715 // If true, iD will update the title based on what the user is doing
92718 var _setsDocumentTitle = true;
92720 context.setsDocumentTitle = function (val) {
92721 if (!arguments.length) return _setsDocumentTitle;
92722 _setsDocumentTitle = val;
92724 }; // The part of the title that is always the same
92727 var _documentTitleBase = document.title;
92729 context.documentTitleBase = function (val) {
92730 if (!arguments.length) return _documentTitleBase;
92731 _documentTitleBase = val;
92734 /* User interface and keybinding */
92739 context.ui = function () {
92743 context.lastPointerType = function () {
92744 return _ui.lastPointerType();
92747 var _keybinding = utilKeybinding('context');
92749 context.keybinding = function () {
92750 return _keybinding;
92753 select(document).call(_keybinding);
92754 /* Straight accessors. Avoid using these if you can. */
92755 // Instantiate the connection here because it doesn't require passing in
92756 // `context` and it's needed for pre-init calls like `preauth`
92758 var _connection = services.osm;
92766 context.connection = function () {
92767 return _connection;
92770 context.history = function () {
92774 context.validator = function () {
92778 context.uploader = function () {
92784 context.preauth = function (options) {
92786 _connection["switch"](options);
92791 /* connection options for source switcher (optional) */
92794 var _apiConnections;
92796 context.apiConnections = function (val) {
92797 if (!arguments.length) return _apiConnections;
92798 _apiConnections = val;
92800 }; // A string or array or locale codes to prefer over the browser's settings
92803 context.locale = function (locale) {
92804 if (!arguments.length) return _mainLocalizer.localeCode();
92805 _mainLocalizer.preferredLocaleCodes(locale);
92809 function afterLoad(cid, callback) {
92810 return function (err, result) {
92812 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
92813 if (err.status === 400 || err.status === 401 || err.status === 403) {
92815 _connection.logout();
92819 if (typeof callback === 'function') {
92824 } else if (_connection && _connection.getConnectionId() !== cid) {
92825 if (typeof callback === 'function') {
92827 message: 'Connection Switched',
92834 _history.merge(result.data, result.extent);
92836 if (typeof callback === 'function') {
92837 callback(err, result);
92845 context.loadTiles = function (projection, callback) {
92846 var handle = window.requestIdleCallback(function () {
92847 _deferred["delete"](handle);
92849 if (_connection && context.editableDataEnabled()) {
92850 var cid = _connection.getConnectionId();
92852 _connection.loadTiles(projection, afterLoad(cid, callback));
92856 _deferred.add(handle);
92859 context.loadTileAtLoc = function (loc, callback) {
92860 var handle = window.requestIdleCallback(function () {
92861 _deferred["delete"](handle);
92863 if (_connection && context.editableDataEnabled()) {
92864 var cid = _connection.getConnectionId();
92866 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
92870 _deferred.add(handle);
92871 }; // Download the full entity and its parent relations. The callback may be called multiple times.
92874 context.loadEntity = function (entityID, callback) {
92876 var cid = _connection.getConnectionId();
92878 _connection.loadEntity(entityID, afterLoad(cid, callback)); // We need to fetch the parent relations separately.
92881 _connection.loadEntityRelations(entityID, afterLoad(cid, callback));
92885 context.zoomToEntity = function (entityID, zoomTo) {
92886 // be sure to load the entity even if we're not going to zoom to it
92887 context.loadEntity(entityID, function (err, result) {
92890 if (zoomTo !== false) {
92891 var entity = result.data.find(function (e) {
92892 return e.id === entityID;
92896 _map.zoomTo(entity);
92901 _map.on('drawn.zoomToEntity', function () {
92902 if (!context.hasEntity(entityID)) return;
92904 _map.on('drawn.zoomToEntity', null);
92906 context.on('enter.zoomToEntity', null);
92907 context.enter(modeSelect(context, [entityID]));
92910 context.on('enter.zoomToEntity', function () {
92911 if (_mode.id !== 'browse') {
92912 _map.on('drawn.zoomToEntity', null);
92914 context.on('enter.zoomToEntity', null);
92919 var _minEditableZoom = 16;
92921 context.minEditableZoom = function (val) {
92922 if (!arguments.length) return _minEditableZoom;
92923 _minEditableZoom = val;
92926 _connection.tileZoom(val);
92930 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
92933 context.maxCharsForTagKey = function () {
92937 context.maxCharsForTagValue = function () {
92941 context.maxCharsForRelationRole = function () {
92945 function cleanOsmString(val, maxChars) {
92946 // be lenient with input
92947 if (val === undefined || val === null) {
92950 val = val.toString();
92951 } // remove whitespace
92954 val = val.trim(); // use the canonical form of the string
92956 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
92958 return utilUnicodeCharsTruncated(val, maxChars);
92961 context.cleanTagKey = function (val) {
92962 return cleanOsmString(val, context.maxCharsForTagKey());
92965 context.cleanTagValue = function (val) {
92966 return cleanOsmString(val, context.maxCharsForTagValue());
92969 context.cleanRelationRole = function (val) {
92970 return cleanOsmString(val, context.maxCharsForRelationRole());
92975 var _inIntro = false;
92977 context.inIntro = function (val) {
92978 if (!arguments.length) return _inIntro;
92981 }; // Immediately save the user's history to localstorage, if possible
92982 // This is called someteimes, but also on the `window.onbeforeunload` handler
92985 context.save = function () {
92986 // no history save, no message onbeforeunload
92987 if (_inIntro || context.container().select('.modal').size()) return;
92990 if (_mode && _mode.id === 'save') {
92991 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
92993 if (services.osm && services.osm.isChangesetInflight()) {
92994 _history.clearSaved();
92999 canSave = context.selectedIDs().every(function (id) {
93000 var entity = context.hasEntity(id);
93001 return entity && !entity.isDegenerate();
93009 if (_history.hasChanges()) {
93010 return _t('save.unsaved_changes');
93012 }; // Debounce save, since it's a synchronous localStorage write,
93013 // and history changes can happen frequently (e.g. when dragging).
93016 context.debouncedSave = debounce(context.save, 350);
93018 function withDebouncedSave(fn) {
93019 return function () {
93020 var result = fn.apply(_history, arguments);
93021 context.debouncedSave();
93028 context.hasEntity = function (id) {
93029 return _history.graph().hasEntity(id);
93032 context.entity = function (id) {
93033 return _history.graph().entity(id);
93040 context.mode = function () {
93044 context.enter = function (newMode) {
93048 dispatch.call('exit', _this, _mode);
93055 dispatch.call('enter', _this, _mode);
93058 context.selectedIDs = function () {
93059 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
93062 context.activeID = function () {
93063 return _mode && _mode.activeID && _mode.activeID();
93066 var _selectedNoteID;
93068 context.selectedNoteID = function (noteID) {
93069 if (!arguments.length) return _selectedNoteID;
93070 _selectedNoteID = noteID;
93072 }; // NOTE: Don't change the name of this until UI v3 is merged
93075 var _selectedErrorID;
93077 context.selectedErrorID = function (errorID) {
93078 if (!arguments.length) return _selectedErrorID;
93079 _selectedErrorID = errorID;
93085 context.install = function (behavior) {
93086 return context.surface().call(behavior);
93089 context.uninstall = function (behavior) {
93090 return context.surface().call(behavior.off);
93097 context.copyGraph = function () {
93103 context.copyIDs = function (val) {
93104 if (!arguments.length) return _copyIDs;
93106 _copyGraph = _history.graph();
93112 context.copyLonLat = function (val) {
93113 if (!arguments.length) return _copyLonLat;
93122 context.background = function () {
93123 return _background;
93130 context.features = function () {
93134 context.hasHiddenConnections = function (id) {
93135 var graph = _history.graph();
93137 var entity = graph.entity(id);
93138 return _features.hasHiddenConnections(entity, graph);
93145 context.photos = function () {
93153 context.map = function () {
93157 context.layers = function () {
93158 return _map.layers();
93161 context.surface = function () {
93162 return _map.surface;
93165 context.editableDataEnabled = function () {
93166 return _map.editableDataEnabled();
93169 context.surfaceRect = function () {
93170 return _map.surface.node().getBoundingClientRect();
93173 context.editable = function () {
93174 // don't allow editing during save
93175 var mode = context.mode();
93176 if (!mode || mode.id === 'save') return false;
93177 return _map.editableDataEnabled();
93182 var _debugFlags = {
93186 // label collision bounding boxes
93188 // imagery bounding polygons
93191 downloaded: false // downloaded data from osm
93195 context.debugFlags = function () {
93196 return _debugFlags;
93199 context.getDebug = function (flag) {
93200 return flag && _debugFlags[flag];
93203 context.setDebug = function (flag, val) {
93204 if (arguments.length === 1) val = true;
93205 _debugFlags[flag] = val;
93206 dispatch.call('change');
93212 var _container = select(null);
93214 context.container = function (val) {
93215 if (!arguments.length) return _container;
93218 _container.classed('ideditor', true);
93223 context.containerNode = function (val) {
93224 if (!arguments.length) return context.container().node();
93225 context.container(select(val));
93231 context.embed = function (val) {
93232 if (!arguments.length) return _embed;
93239 var _assetPath = '';
93241 context.assetPath = function (val) {
93242 if (!arguments.length) return _assetPath;
93244 _mainFileFetcher.assetPath(val);
93248 var _assetMap = {};
93250 context.assetMap = function (val) {
93251 if (!arguments.length) return _assetMap;
93253 _mainFileFetcher.assetMap(val);
93257 context.asset = function (val) {
93258 if (/^http(s)?:\/\//i.test(val)) return val;
93259 var filename = _assetPath + val;
93260 return _assetMap[filename] || filename;
93263 context.imagePath = function (val) {
93264 return context.asset("img/".concat(val));
93266 /* reset (aka flush) */
93269 context.reset = context.flush = function () {
93270 context.debouncedSave.cancel();
93271 Array.from(_deferred).forEach(function (handle) {
93272 window.cancelIdleCallback(handle);
93274 _deferred["delete"](handle);
93276 Object.values(services).forEach(function (service) {
93277 if (service && typeof service.reset === 'function') {
93278 service.reset(context);
93281 context.changeset = null;
93283 _validator.reset();
93289 _uploader.reset(); // don't leave stale state in the inspector
93292 context.container().select('.inspector-wrap *').remove();
93298 context.projection = geoRawMercator();
93299 context.curtainProjection = geoRawMercator();
93302 context.init = function () {
93303 instantiateInternal();
93304 initializeDependents();
93305 return context; // Load variables and properties. No property of `context` should be accessed
93306 // until this is complete since load statuses are indeterminate. The order
93307 // of instantiation shouldn't matter.
93309 function instantiateInternal() {
93310 _history = coreHistory(context);
93311 context.graph = _history.graph;
93312 context.pauseChangeDispatch = _history.pauseChangeDispatch;
93313 context.resumeChangeDispatch = _history.resumeChangeDispatch;
93314 context.perform = withDebouncedSave(_history.perform);
93315 context.replace = withDebouncedSave(_history.replace);
93316 context.pop = withDebouncedSave(_history.pop);
93317 context.overwrite = withDebouncedSave(_history.overwrite);
93318 context.undo = withDebouncedSave(_history.undo);
93319 context.redo = withDebouncedSave(_history.redo);
93320 _validator = coreValidator(context);
93321 _uploader = coreUploader(context);
93322 _background = rendererBackground(context);
93323 _features = rendererFeatures(context);
93324 _map = rendererMap(context);
93325 _photos = rendererPhotos(context);
93326 _ui = uiInit(context);
93327 } // Set up objects that might need to access properties of `context`. The order
93328 // might matter if dependents make calls to each other. Be wary of async calls.
93331 function initializeDependents() {
93332 if (context.initialHashParams.presets) {
93333 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
93336 if (context.initialHashParams.locale) {
93337 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
93338 } // kick off some async work
93341 _mainLocalizer.ensureLoaded();
93343 _background.ensureLoaded();
93345 _mainPresetIndex.ensureLoaded();
93346 Object.values(services).forEach(function (service) {
93347 if (service && typeof service.init === 'function') {
93358 if (services.maprules && context.initialHashParams.maprules) {
93359 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
93360 services.maprules.init();
93361 mapcss.forEach(function (mapcssSelector) {
93362 return services.maprules.addRule(mapcssSelector);
93364 })["catch"](function () {
93367 } // if the container isn't available, e.g. when testing, don't load the UI
93370 if (!context.container().empty()) {
93371 _ui.ensureLoaded().then(function () {
93381 // NSI contains the most correct tagging for many commonly mapped features.
93382 // See https://github.com/osmlab/name-suggestion-index and https://nsi.guide
93385 var _nsiStatus = 'loading'; // 'loading', 'ok', 'failed'
93387 var _nsi = {}; // Sometimes we can upgrade a feature tagged like `building=yes` to a better tag.
93389 var buildingPreset = {
93390 'building/commercial': true,
93391 'building/government': true,
93392 'building/hotel': true,
93393 'building/retail': true,
93394 'building/office': true,
93395 'building/supermarket': true,
93396 'building/yes': true
93397 }; // Exceptions to the namelike regexes.
93398 // Usually a tag suffix contains a language code like `name:en`, `name:ru`
93399 // but we want to exclude things like `operator:type`, `name:etymology`, etc..
93401 var notNames = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // Exceptions to the branchlike regexes
93403 var notBranches = /(coop|express|wireless|factory|outlet)/i; // PRIVATE FUNCTIONS
93404 // `setNsiSources()`
93405 // Adds the sources to iD's filemap so we can start downloading data.
93408 function setNsiSources() {
93409 var nsiVersion = packageJSON.devDependencies['name-suggestion-index'];
93410 var v = vparse(nsiVersion);
93411 var vMinor = "".concat(v.major, ".").concat(v.minor);
93413 'nsi_data': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/nsi.min.json"),
93414 'nsi_dissolved': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/dissolved.min.json"),
93415 'nsi_features': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/featureCollection.min.json"),
93416 'nsi_generics': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/genericWords.min.json"),
93417 'nsi_presets': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/presets/nsi-id-presets.min.json"),
93418 'nsi_replacements': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/replacements.min.json"),
93419 'nsi_trees': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/trees.min.json")
93421 var fileMap = _mainFileFetcher.fileMap();
93423 for (var k in sources) {
93424 fileMap[k] = sources[k];
93426 } // `loadNsiPresets()`
93427 // Returns a Promise fulfilled when the presets have been downloaded and merged into iD.
93431 function loadNsiPresets() {
93432 return Promise.all([_mainFileFetcher.get('nsi_presets'), _mainFileFetcher.get('nsi_features')]).then(function (vals) {
93433 // Add `suggestion=true` to all the nsi presets
93434 // The preset json schema doesn't include it, but the iD code still uses it
93435 Object.values(vals[0].presets).forEach(function (preset) {
93436 return preset.suggestion = true;
93438 _mainPresetIndex.merge({
93439 presets: vals[0].presets,
93440 featureCollection: vals[1]
93443 } // `loadNsiData()`
93444 // Returns a Promise fulfilled when the other data have been downloaded and processed
93448 function loadNsiData() {
93449 return Promise.all([_mainFileFetcher.get('nsi_data'), _mainFileFetcher.get('nsi_dissolved'), _mainFileFetcher.get('nsi_replacements'), _mainFileFetcher.get('nsi_trees')]).then(function (vals) {
93452 // the raw name-suggestion-index data
93453 dissolved: vals[1].dissolved,
93454 // list of dissolved items
93455 replacements: vals[2].replacements,
93456 // trivial old->new qid replacements
93457 trees: vals[3].trees,
93458 // metadata about trees, main tags
93460 // Map (k -> Map (v -> t) )
93462 // Map (wd/wp tag values -> qids)
93463 ids: new Map() // Map (id -> NSI item)
93466 _nsi.matcher = new Matcher();
93468 _nsi.matcher.buildMatchIndex(_nsi.data);
93470 _nsi.matcher.buildLocationIndex(_nsi.data, _mainLocations.loco());
93472 Object.keys(_nsi.data).forEach(function (tkv) {
93473 var category = _nsi.data[tkv];
93474 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
93478 var v = parts[2]; // Build a reverse index of keys -> values -> trees present in the name-suggestion-index
93479 // Collect primary keys (e.g. "amenity", "craft", "shop", "man_made", "route", etc)
93481 // "restaurant": "brands"
93484 var vmap = _nsi.kvt.get(k);
93489 _nsi.kvt.set(k, vmap);
93493 var tree = _nsi.trees[t]; // e.g. "brands", "operators"
93495 var mainTag = tree.mainTag; // e.g. "brand:wikidata", "operator:wikidata", etc
93497 var items = category.items || [];
93498 items.forEach(function (item) {
93499 // Remember some useful things for later, cache NSI id -> item
93501 item.mainTag = mainTag;
93503 _nsi.ids.set(item.id, item); // Cache Wikidata/Wikipedia values -> qid, for #6416
93506 var wd = item.tags[mainTag];
93507 var wp = item.tags[mainTag.replace('wikidata', 'wikipedia')];
93508 if (wd) _nsi.qids.set(wd, wd);
93509 if (wp && wd) _nsi.qids.set(wp, wd);
93514 // Gather all the k/v pairs that we will run through the NSI matcher.
93515 // An OSM tags object can contain anything, but only a few tags will be interesting to NSI.
93517 // This function will return the interesting tag pairs like:
93518 // "amenity/restaurant", "man_made/flagpole"
93519 // and fallbacks like
93521 // excluding things like
93522 // "highway", "surface", "ref", etc.
93525 // `tags`: `Object` containing the feature's OSM tags
93527 // `Object` containing kv pairs to test:
93529 // 'primary': Set(),
93530 // 'alternate': Set()
93535 function gatherKVs(tags) {
93536 var primary = new Set();
93537 var alternate = new Set();
93538 Object.keys(tags).forEach(function (osmkey) {
93539 var osmvalue = tags[osmkey];
93540 if (!osmvalue) return;
93542 var vmap = _nsi.kvt.get(osmkey);
93546 if (osmvalue !== 'yes') {
93547 primary.add("".concat(osmkey, "/").concat(osmvalue));
93549 alternate.add("".concat(osmkey, "/").concat(osmvalue));
93551 }); // Can we try a generic building fallback match? - See #6122, #7197
93552 // Only try this if we do a preset match and find nothing else remarkable about that building.
93553 // For example, a way with `building=yes` + `name=Westfield` may be a Westfield department store.
93554 // But a way with `building=yes` + `name=Westfield` + `public_transport=station` is a train station for a town named "Westfield"
93556 var preset = _mainPresetIndex.matchTags(tags, 'area');
93558 if (buildingPreset[preset.id]) {
93559 alternate.add('building/yes');
93564 alternate: alternate
93566 } // `identifyTree()`
93567 // NSI has a concept of trees: "brands", "operators", "flags", "transit".
93568 // The tree determines things like which tags are namelike, and which tags hold important wikidata.
93569 // This takes an Object of tags and tries to identify what tree to use.
93572 // `tags`: `Object` containing the feature's OSM tags
93574 // `string` the name of the tree if known
93575 // or 'unknown' if it could match several trees (e.g. amenity/yes)
93576 // or null if no match
93580 function identifyTree(tags) {
93582 var t; // Check all tags
93584 Object.keys(tags).forEach(function (osmkey) {
93585 if (t) return; // found already
93587 var osmvalue = tags[osmkey];
93588 if (!osmvalue) return;
93590 var vmap = _nsi.kvt.get(osmkey);
93592 if (!vmap) return; // this key is not in nsi
93594 if (osmvalue === 'yes') {
93595 unknown = 'unknown';
93597 t = vmap.get(osmvalue);
93600 return t || unknown || null;
93601 } // `gatherNames()`
93602 // Gather all the namelike values that we will run through the NSI matcher.
93603 // It will gather values primarily from tags `name`, `name:ru`, `flag:name`
93604 // and fallback to alternate tags like `brand`, `brand:ru`, `alt_name`
93607 // `tags`: `Object` containing the feature's OSM tags
93609 // `Object` containing namelike values to test:
93611 // 'primary': Set(),
93612 // 'fallbacks': Set()
93617 function gatherNames(tags) {
93619 primary: new Set(),
93620 alternate: new Set()
93622 var primary = new Set();
93623 var alternate = new Set();
93624 var foundSemi = false;
93625 var testNameFragments = false;
93626 var patterns; // Patterns for matching OSM keys that might contain namelike values.
93627 // These roughly correspond to the "trees" concept in name-suggestion-index,
93629 var t = identifyTree(tags);
93630 if (!t) return empty;
93632 if (t === 'transit') {
93634 primary: /^network$/i,
93635 alternate: /^(operator|operator:\w+|network:\w+|\w+_name|\w+_name:\w+)$/i
93637 } else if (t === 'flags') {
93639 primary: /^(flag:name|flag:name:\w+)$/i,
93640 alternate: /^(flag|flag:\w+|subject|subject:\w+)$/i // note: no `country`, we special-case it below
93643 } else if (t === 'brands') {
93644 testNameFragments = true;
93646 primary: /^(name|name:\w+)$/i,
93647 alternate: /^(brand|brand:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
93649 } else if (t === 'operators') {
93650 testNameFragments = true;
93652 primary: /^(name|name:\w+|operator|operator:\w+)$/i,
93653 alternate: /^(brand|brand:\w+|\w+_name|\w+_name:\w+)/i
93656 // unknown/multiple
93657 testNameFragments = true;
93659 primary: /^(name|name:\w+)$/i,
93660 alternate: /^(brand|brand:\w+|network|network:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
93662 } // Test `name` fragments, longest to shortest, to fit them into a "Name Branch" pattern.
93663 // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
93666 if (tags.name && testNameFragments) {
93667 var nameParts = tags.name.split(/[\s\-\/,.]/);
93669 for (var split = nameParts.length; split > 0; split--) {
93670 var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
93674 } // Check all tags
93677 Object.keys(tags).forEach(function (osmkey) {
93678 var osmvalue = tags[osmkey];
93679 if (!osmvalue) return;
93681 if (isNamelike(osmkey, 'primary')) {
93682 if (/;/.test(osmvalue)) {
93685 primary.add(osmvalue);
93686 alternate["delete"](osmvalue);
93688 } else if (!primary.has(osmvalue) && isNamelike(osmkey, 'alternate')) {
93689 if (/;/.test(osmvalue)) {
93692 alternate.add(osmvalue);
93695 }); // For flags only, fallback to `country` tag only if no other namelike values were found.
93696 // See https://github.com/openstreetmap/iD/pull/8305#issuecomment-769174070
93698 if (tags.man_made === 'flagpole' && !primary.size && !alternate.size && !!tags.country) {
93699 var osmvalue = tags.country;
93701 if (/;/.test(osmvalue)) {
93704 alternate.add(osmvalue);
93706 } // If any namelike value contained a semicolon, return empty set and don't try matching anything.
93714 alternate: alternate
93718 function isNamelike(osmkey, which) {
93719 return patterns[which].test(osmkey) && !notNames.test(osmkey);
93721 } // `gatherTuples()`
93722 // Generate all combinations of [key,value,name] that we want to test.
93723 // This prioritizes them so that the primary name and k/v pairs go first
93726 // `tryKVs`: `Object` containing primary and alternate k/v pairs to test
93727 // `tryNames`: `Object` containing primary and alternate names to test
93729 // `Array`: tuple objects ordered by priority
93733 function gatherTuples(tryKVs, tryNames) {
93735 ['primary', 'alternate'].forEach(function (whichName) {
93736 // test names longest to shortest
93737 var arr = Array.from(tryNames[whichName]).sort(function (a, b) {
93738 return b.length - a.length;
93740 arr.forEach(function (n) {
93741 ['primary', 'alternate'].forEach(function (whichKV) {
93742 tryKVs[whichKV].forEach(function (kv) {
93743 var parts = kv.split('/', 2);
93756 } // `_upgradeTags()`
93757 // Try to match a feature to a canonical record in name-suggestion-index
93758 // and upgrade the tags to match.
93761 // `tags`: `Object` containing the feature's OSM tags
93762 // `loc`: Location where this feature exists, as a [lon, lat]
93764 // `Object`: The tags the the feature should have, or `null` if no changes needed
93768 function _upgradeTags(tags, loc) {
93769 var newTags = Object.assign({}, tags); // shallow copy
93771 var changed = false; // Before anything, perform trivial Wikipedia/Wikidata replacements
93773 Object.keys(newTags).forEach(function (osmkey) {
93774 var matchTag = osmkey.match(/^(\w+:)?wikidata$/);
93777 // Look at '*:wikidata' tags
93778 var prefix = matchTag[1] || '';
93779 var wd = newTags[osmkey];
93780 var replace = _nsi.replacements[wd]; // If it matches a QID in the replacement list...
93782 if (replace && replace.wikidata !== undefined) {
93783 // replace or delete `*:wikidata` tag
93786 if (replace.wikidata) {
93787 newTags[osmkey] = replace.wikidata;
93789 delete newTags[osmkey];
93793 if (replace && replace.wikipedia !== undefined) {
93794 // replace or delete `*:wikipedia` tag
93796 var wpkey = "".concat(prefix, "wikipedia");
93798 if (replace.wikipedia) {
93799 newTags[wpkey] = replace.wikipedia;
93801 delete newTags[wpkey];
93805 }); // Gather key/value tag pairs to try to match
93807 var tryKVs = gatherKVs(tags);
93808 if (!tryKVs.primary.size && !tryKVs.alternate.size) return changed ? newTags : null; // Gather namelike tag values to try to match
93810 var tryNames = gatherNames(tags); // Do `wikidata=*` or `wikipedia=*` tags identify this entity as a chain? - See #6416
93811 // If so, these tags can be swapped to e.g. `brand:wikidata`/`brand:wikipedia`.
93813 var foundQID = _nsi.qids.get(tags.wikidata) || _nsi.qids.get(tags.wikipedia);
93815 if (foundQID) tryNames.primary.add(foundQID); // matcher will recognize the Wikidata QID as name too
93817 if (!tryNames.primary.size && !tryNames.alternate.size) return changed ? newTags : null; // Order the [key,value,name] tuples - test primary before alternate
93819 var tuples = gatherTuples(tryKVs, tryNames);
93821 var _loop = function _loop(i) {
93822 var tuple = tuples[i];
93824 var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n, loc); // Attempt to match an item in NSI
93827 if (!hits || !hits.length) return "continue"; // no match, try next tuple
93829 if (hits[0].match !== 'primary' && hits[0].match !== 'alternate') return "break"; // a generic match, stop looking
93830 // A match may contain multiple results, the first one is likely the best one for this location
93831 // e.g. `['pfk-a54c14', 'kfc-1ff19c', 'kfc-658eea']`
93833 var itemID = void 0,
93836 for (var j = 0; j < hits.length; j++) {
93838 itemID = hit.itemID;
93839 if (_nsi.dissolved[itemID]) continue; // Don't upgrade to a dissolved item
93841 item = _nsi.ids.get(itemID);
93842 if (!item) continue;
93843 var mainTag = item.mainTag; // e.g. `brand:wikidata`
93845 var itemQID = item.tags[mainTag]; // e.g. `brand:wikidata` qid
93847 var notQID = newTags["not:".concat(mainTag)]; // e.g. `not:brand:wikidata` qid
93849 if ( // Exceptions, skip this hit
93850 !itemQID || itemQID === notQID || // No `*:wikidata` or matched a `not:*:wikidata`
93851 newTags.office && !item.tags.office // feature may be a corporate office for a brand? - #6416
93854 continue; // continue looking
93856 break; // use `item`
93858 } // Can't use any of these hits, try next tuple..
93861 if (!item) return "continue"; // At this point we have matched a canonical item and can suggest tag upgrades..
93863 var tkv = item.tkv;
93864 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
93868 var category = _nsi.data[tkv];
93869 var properties = category.properties || {}; // Preserve some tags that we specifically don't want NSI to overwrite. ('^name', sometimes)
93871 var preserveTags = item.preserveTags || properties.preserveTags || [];
93872 var regexes = preserveTags.map(function (s) {
93873 return new RegExp(s, 'i');
93875 regexes.push(/^building$/i, /^takeaway$/i);
93877 Object.keys(newTags).forEach(function (osmkey) {
93878 if (regexes.some(function (regex) {
93879 return regex.test(osmkey);
93881 keepTags[osmkey] = newTags[osmkey];
93883 }); // Remove any primary tags ("amenity", "craft", "shop", "man_made", "route", etc)
93884 // with a value like `amenity=yes` or `shop=yes`
93886 _nsi.kvt.forEach(function (vmap, k) {
93887 if (newTags[k] === 'yes') delete newTags[k];
93888 }); // Replace mistagged `wikidata`/`wikipedia` with e.g. `brand:wikidata`/`brand:wikipedia`
93892 delete newTags.wikipedia;
93893 delete newTags.wikidata;
93894 } // Do the tag upgrade
93897 Object.assign(newTags, item.tags, keepTags); // Special `branch` splitting rules - IF..
93898 // - NSI is suggesting to replace `name`, AND
93899 // - `branch` doesn't already contain something, AND
93900 // - original name has not moved to an alternate name (e.g. "Dunkin' Donuts" -> "Dunkin'"), AND
93901 // - original name is "some name" + "some stuff", THEN
93902 // consider splitting `name` into `name`/`branch`..
93904 var origName = tags.name;
93905 var newName = newTags.name;
93907 if (newName && origName && newName !== origName && !newTags.branch) {
93908 var newNames = gatherNames(newTags);
93909 var newSet = new Set([].concat(_toConsumableArray(newNames.primary), _toConsumableArray(newNames.alternate)));
93910 var isMoved = newSet.has(origName); // another tag holds the original name now
93913 // Test name fragments, longest to shortest, to fit them into a "Name Branch" pattern.
93914 // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
93915 var nameParts = origName.split(/[\s\-\/,.]/);
93917 for (var split = nameParts.length; split > 0; split--) {
93918 var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
93920 var branch = nameParts.slice(split).join(' '); // e.g. "Neuss Innenstadt"
93922 var nameHits = _nsi.matcher.match(k, v, name, loc);
93924 if (!nameHits || !nameHits.length) continue; // no match, try next name fragment
93926 if (nameHits.some(function (hit) {
93927 return hit.itemID === itemID;
93929 // matched the name fragment to the same itemID above
93931 if (notBranches.test(branch)) {
93932 // "branch" was detected but is noise ("factory outlet", etc)
93933 newTags.name = origName; // Leave `name` alone, this part of the name may be significant..
93935 var branchHits = _nsi.matcher.match(k, v, branch, loc);
93937 if (branchHits && branchHits.length) {
93938 // if "branch" matched something else in NSI..
93939 if (branchHits[0].match === 'primary' || branchHits[0].match === 'alternate') {
93940 // if another brand! (e.g. "KFC - Taco Bell"?)
93943 }; // bail out - can't suggest tags in this case
93944 } // else a generic (e.g. "gas", "cafe") - ignore
93947 // "branch" is not noise and not something in NSI
93948 newTags.branch = branch; // Stick it in the `branch` tag..
93964 for (var i = 0; i < tuples.length; i++) {
93965 var _ret = _loop(i);
93967 if (_ret === "continue") continue;
93968 if (_ret === "break") break;
93969 if (_typeof(_ret) === "object") return _ret.v;
93972 return changed ? newTags : null;
93973 } // `_isGenericName()`
93974 // Is the `name` tag generic?
93977 // `tags`: `Object` containing the feature's OSM tags
93979 // `true` if it is generic, `false` if not
93983 function _isGenericName(tags) {
93985 if (!n) return false; // tryNames just contains the `name` tag value and nothing else
93988 primary: new Set([n]),
93989 alternate: new Set()
93990 }; // Gather key/value tag pairs to try to match
93992 var tryKVs = gatherKVs(tags);
93993 if (!tryKVs.primary.size && !tryKVs.alternate.size) return false; // Order the [key,value,name] tuples - test primary before alternate
93995 var tuples = gatherTuples(tryKVs, tryNames);
93997 for (var i = 0; i < tuples.length; i++) {
93998 var tuple = tuples[i];
94000 var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n); // Attempt to match an item in NSI
94001 // If we get a `excludeGeneric` hit, this is a generic name.
94004 if (hits && hits.length && hits[0].match === 'excludeGeneric') return true;
94008 } // PUBLIC INTERFACE
94013 // On init, start preparing the name-suggestion-index
94015 init: function init() {
94016 // Note: service.init is called immediately after the presetManager has started loading its data.
94017 // We expect to chain onto an unfulfilled promise here.
94019 _mainPresetIndex.ensureLoaded().then(function () {
94020 return loadNsiPresets();
94021 }).then(function () {
94023 }) // wait briefly for locationSets to enter the locationManager queue
94024 .then(function () {
94025 return _mainLocations.mergeLocationSets([]);
94026 }) // wait for locationSets to resolve
94027 .then(function () {
94028 return loadNsiData();
94029 }).then(function () {
94030 return _nsiStatus = 'ok';
94031 })["catch"](function () {
94032 return _nsiStatus = 'failed';
94035 function delay(msec) {
94036 return new Promise(function (resolve) {
94037 window.setTimeout(resolve, msec);
94042 // Reset is called when user saves data to OSM (does nothing here)
94044 reset: function reset() {},
94046 // To let other code know how it's going...
94049 // `String`: 'loading', 'ok', 'failed'
94051 status: function status() {
94054 // `isGenericName()`
94055 // Is the `name` tag generic?
94058 // `tags`: `Object` containing the feature's OSM tags
94060 // `true` if it is generic, `false` if not
94062 isGenericName: function isGenericName(tags) {
94063 return _isGenericName(tags);
94066 // Suggest tag upgrades.
94067 // This function will not modify the input tags, it makes a copy.
94070 // `tags`: `Object` containing the feature's OSM tags
94071 // `loc`: Location where this feature exists, as a [lon, lat]
94073 // `Object`: The tags the the feature should have, or `null` if no change
94075 upgradeTags: function upgradeTags(tags, loc) {
94076 return _upgradeTags(tags, loc);
94079 // Direct access to the NSI cache, useful for testing or breaking things
94082 // `Object`: the internal NSI cache
94084 cache: function cache() {
94089 var apibase$1 = 'https://openstreetcam.org';
94090 var maxResults$1 = 1000;
94091 var tileZoom$1 = 14;
94092 var tiler$3 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
94093 var dispatch$3 = dispatch$8('loadedImages');
94094 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
94098 var _oscSelectedImage;
94100 var _loadViewerPromise$1;
94102 function abortRequest$3(controller) {
94103 controller.abort();
94106 function maxPageAtZoom(z) {
94107 if (z < 15) return 2;
94108 if (z === 15) return 5;
94109 if (z === 16) return 10;
94110 if (z === 17) return 20;
94111 if (z === 18) return 40;
94112 if (z > 18) return 80;
94115 function loadTiles$1(which, url, projection) {
94116 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
94117 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
94119 var cache = _oscCache[which];
94120 Object.keys(cache.inflight).forEach(function (k) {
94121 var wanted = tiles.find(function (tile) {
94122 return k.indexOf(tile.id + ',') === 0;
94126 abortRequest$3(cache.inflight[k]);
94127 delete cache.inflight[k];
94130 tiles.forEach(function (tile) {
94131 loadNextTilePage$1(which, currZoom, url, tile);
94135 function loadNextTilePage$1(which, currZoom, url, tile) {
94136 var cache = _oscCache[which];
94137 var bbox = tile.extent.bbox();
94138 var maxPages = maxPageAtZoom(currZoom);
94139 var nextPage = cache.nextPage[tile.id] || 1;
94140 var params = utilQsString({
94143 // client_id: clientId,
94144 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
94145 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
94147 if (nextPage > maxPages) return;
94148 var id = tile.id + ',' + String(nextPage);
94149 if (cache.loaded[id] || cache.inflight[id]) return;
94150 var controller = new AbortController();
94151 cache.inflight[id] = controller;
94154 signal: controller.signal,
94157 'Content-Type': 'application/x-www-form-urlencoded'
94160 d3_json(url, options).then(function (data) {
94161 cache.loaded[id] = true;
94162 delete cache.inflight[id];
94164 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
94165 throw new Error('No Data');
94168 var features = data.currentPageItems.map(function (item) {
94169 var loc = [+item.lng, +item.lat];
94172 if (which === 'images') {
94177 captured_at: item.shot_date || item.date_added,
94178 captured_by: item.username,
94179 imagePath: item.lth_name,
94180 sequence_id: item.sequence_id,
94181 sequence_index: +item.sequence_index
94182 }; // cache sequence info
94184 var seq = _oscCache.sequences[d.sequence_id];
94191 _oscCache.sequences[d.sequence_id] = seq;
94194 seq.images[d.sequence_index] = d;
94195 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
94206 cache.rtree.load(features);
94208 if (data.currentPageItems.length === maxResults$1) {
94209 // more pages to load
94210 cache.nextPage[tile.id] = nextPage + 1;
94211 loadNextTilePage$1(which, currZoom, url, tile);
94213 cache.nextPage[tile.id] = Infinity; // no more pages to load
94216 if (which === 'images') {
94217 dispatch$3.call('loadedImages');
94219 })["catch"](function () {
94220 cache.loaded[id] = true;
94221 delete cache.inflight[id];
94223 } // partition viewport into higher zoom tiles
94226 function partitionViewport$1(projection) {
94227 var z = geoScaleToZoom(projection.scale());
94228 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
94230 var tiler = utilTiler().zoomExtent([z2, z2]);
94231 return tiler.getTiles(projection).map(function (tile) {
94232 return tile.extent;
94234 } // no more than `limit` results per partition.
94237 function searchLimited$1(limit, projection, rtree) {
94238 limit = limit || 5;
94239 return partitionViewport$1(projection).reduce(function (result, extent) {
94240 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
94243 return found.length ? result.concat(found) : result;
94247 var serviceOpenstreetcam = {
94248 init: function init() {
94253 this.event = utilRebind(this, dispatch$3, 'on');
94255 reset: function reset() {
94257 Object.values(_oscCache.images.inflight).forEach(abortRequest$3);
94265 rtree: new RBush(),
94270 _oscSelectedImage = null;
94272 images: function images(projection) {
94274 return searchLimited$1(limit, projection, _oscCache.images.rtree);
94276 sequences: function sequences(projection) {
94277 var viewport = projection.clipExtent();
94278 var min = [viewport[0][0], viewport[1][1]];
94279 var max = [viewport[1][0], viewport[0][1]];
94280 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
94281 var sequenceKeys = {}; // all sequences for images in viewport
94283 _oscCache.images.rtree.search(bbox).forEach(function (d) {
94284 sequenceKeys[d.data.sequence_id] = true;
94285 }); // make linestrings from those sequences
94288 var lineStrings = [];
94289 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
94290 var seq = _oscCache.sequences[sequenceKey];
94291 var images = seq && seq.images;
94295 type: 'LineString',
94296 coordinates: images.map(function (d) {
94298 }).filter(Boolean),
94300 captured_at: images[0] ? images[0].captured_at : null,
94301 captured_by: images[0] ? images[0].captured_by : null,
94307 return lineStrings;
94309 cachedImage: function cachedImage(imageKey) {
94310 return _oscCache.images.forImageKey[imageKey];
94312 loadImages: function loadImages(projection) {
94313 var url = apibase$1 + '/1.0/list/nearby-photos/';
94314 loadTiles$1('images', url, projection);
94316 ensureViewerLoaded: function ensureViewerLoaded(context) {
94317 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
94319 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
94321 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
94322 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
94323 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
94324 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
94325 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
94326 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
94327 controlsEnter.append('button').on('click.forward', step(1)).html('►');
94328 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
94330 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
94331 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
94334 function zoomPan(d3_event) {
94335 var t = d3_event.transform;
94336 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
94339 function rotate(deg) {
94340 return function () {
94341 if (!_oscSelectedImage) return;
94342 var sequenceKey = _oscSelectedImage.sequence_id;
94343 var sequence = _oscCache.sequences[sequenceKey];
94344 if (!sequence) return;
94345 var r = sequence.rotation || 0;
94347 if (r > 180) r -= 360;
94348 if (r < -180) r += 360;
94349 sequence.rotation = r;
94350 var wrap = context.container().select('.photoviewer .osc-wrapper');
94351 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
94352 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
94356 function step(stepBy) {
94357 return function () {
94358 if (!_oscSelectedImage) return;
94359 var sequenceKey = _oscSelectedImage.sequence_id;
94360 var sequence = _oscCache.sequences[sequenceKey];
94361 if (!sequence) return;
94362 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
94363 var nextImage = sequence.images[nextIndex];
94364 if (!nextImage) return;
94365 context.map().centerEase(nextImage.loc);
94366 that.selectImage(context, nextImage.key);
94368 } // don't need any async loading so resolve immediately
94371 _loadViewerPromise$1 = Promise.resolve();
94372 return _loadViewerPromise$1;
94374 showViewer: function showViewer(context) {
94375 var viewer = context.container().select('.photoviewer').classed('hide', false);
94376 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
94379 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
94380 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
94385 hideViewer: function hideViewer(context) {
94386 _oscSelectedImage = null;
94387 this.updateUrlImage(null);
94388 var viewer = context.container().select('.photoviewer');
94389 if (!viewer.empty()) viewer.datum(null);
94390 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
94391 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
94392 return this.setStyles(context, null, true);
94394 selectImage: function selectImage(context, imageKey) {
94395 var d = this.cachedImage(imageKey);
94396 _oscSelectedImage = d;
94397 this.updateUrlImage(imageKey);
94398 var viewer = context.container().select('.photoviewer');
94399 if (!viewer.empty()) viewer.datum(d);
94400 this.setStyles(context, null, true);
94401 context.container().selectAll('.icon-sign').classed('currentView', false);
94402 if (!d) return this;
94403 var wrap = context.container().select('.photoviewer .osc-wrapper');
94404 var imageWrap = wrap.selectAll('.osc-image-wrap');
94405 var attribution = wrap.selectAll('.photo-attribution').html('');
94406 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
94407 imageWrap.selectAll('.osc-image').remove();
94410 var sequence = _oscCache.sequences[d.sequence_id];
94411 var r = sequence && sequence.rotation || 0;
94412 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$1 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
94414 if (d.captured_by) {
94415 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
94416 attribution.append('span').html('|');
94419 if (d.captured_at) {
94420 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
94421 attribution.append('span').html('|');
94424 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');
94429 function localeDateString(s) {
94430 if (!s) return null;
94436 var d = new Date(s);
94437 if (isNaN(d.getTime())) return null;
94438 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
94441 getSelectedImage: function getSelectedImage() {
94442 return _oscSelectedImage;
94444 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
94445 return d && d.sequence_id;
94447 // Updates the currently highlighted sequence and selected bubble.
94448 // Reset is only necessary when interacting with the viewport because
94449 // this implicitly changes the currently selected bubble/sequence
94450 setStyles: function setStyles(context, hovered, reset) {
94452 // reset all layers
94453 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
94454 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
94457 var hoveredImageKey = hovered && hovered.key;
94458 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
94459 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
94460 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
94463 var viewer = context.container().select('.photoviewer');
94464 var selected = viewer.empty() ? undefined : viewer.datum();
94465 var selectedImageKey = selected && selected.key;
94466 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
94467 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
94468 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
94470 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
94472 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
94473 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
94474 return highlightedImageKeys.indexOf(d.key) !== -1;
94475 }).classed('hovered', function (d) {
94476 return d.key === hoveredImageKey;
94477 }).classed('currentView', function (d) {
94478 return d.key === selectedImageKey;
94480 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
94481 return d.properties.key === hoveredSequenceKey;
94482 }).classed('currentView', function (d) {
94483 return d.properties.key === selectedSequenceKey;
94484 }); // update viewfields if needed
94486 context.container().selectAll('.layer-openstreetcam .viewfield-group .viewfield').attr('d', viewfieldPath);
94488 function viewfieldPath() {
94489 var d = this.parentNode.__data__;
94491 if (d.pano && d.key !== selectedImageKey) {
94492 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
94494 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
94500 updateUrlImage: function updateUrlImage(imageKey) {
94501 if (!window.mocha) {
94502 var hash = utilStringQs(window.location.hash);
94505 hash.photo = 'openstreetcam/' + imageKey;
94510 window.location.replace('#' + utilQsString(hash, true));
94513 cache: function cache() {
94518 var hashes = createCommonjsModule(function (module, exports) {
94522 function utf8Encode(str) {
94529 if (str && str.length) {
94532 while ((i += 1) < l) {
94533 /* Decode utf-16 surrogate pairs */
94534 x = str.charCodeAt(i);
94535 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
94537 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
94538 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
94541 /* Encode output as utf-8 */
94545 output += String.fromCharCode(x);
94546 } else if (x <= 0x7FF) {
94547 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
94548 } else if (x <= 0xFFFF) {
94549 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
94550 } else if (x <= 0x1FFFFF) {
94551 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
94559 function utf8Decode(str) {
94567 i = ac = c1 = c2 = c3 = 0;
94569 if (str && str.length) {
94574 c1 = str.charCodeAt(i);
94578 arr[ac] = String.fromCharCode(c1);
94580 } else if (c1 > 191 && c1 < 224) {
94581 c2 = str.charCodeAt(i + 1);
94582 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
94585 c2 = str.charCodeAt(i + 1);
94586 c3 = str.charCodeAt(i + 2);
94587 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
94593 return arr.join('');
94596 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
94597 * to work around bugs in some JS interpreters.
94601 function safe_add(x, y) {
94602 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
94603 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
94604 return msw << 16 | lsw & 0xFFFF;
94607 * Bitwise rotate a 32-bit number to the left.
94611 function bit_rol(num, cnt) {
94612 return num << cnt | num >>> 32 - cnt;
94615 * Convert a raw string to a hex string
94619 function rstr2hex(input, hexcase) {
94620 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
94626 for (; i < l; i += 1) {
94627 x = input.charCodeAt(i);
94628 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
94634 * Convert an array of big-endian words to a string
94638 function binb2rstr(input) {
94640 l = input.length * 32,
94643 for (i = 0; i < l; i += 8) {
94644 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
94650 * Convert an array of little-endian words to a string
94654 function binl2rstr(input) {
94656 l = input.length * 32,
94659 for (i = 0; i < l; i += 8) {
94660 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
94666 * Convert a raw string to an array of little-endian words
94667 * Characters >255 have their high-byte silently ignored.
94671 function rstr2binl(input) {
94673 l = input.length * 8,
94674 output = Array(input.length >> 2),
94675 lo = output.length;
94677 for (i = 0; i < lo; i += 1) {
94681 for (i = 0; i < l; i += 8) {
94682 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
94688 * Convert a raw string to an array of big-endian words
94689 * Characters >255 have their high-byte silently ignored.
94693 function rstr2binb(input) {
94695 l = input.length * 8,
94696 output = Array(input.length >> 2),
94697 lo = output.length;
94699 for (i = 0; i < lo; i += 1) {
94703 for (i = 0; i < l; i += 8) {
94704 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
94710 * Convert a raw string to an arbitrary string encoding
94714 function rstr2any(input, encoding) {
94715 var divisor = encoding.length,
94716 remainders = Array(),
94725 /* Convert to an array of 16-bit big-endian values, forming the dividend */
94727 dividend = Array(Math.ceil(input.length / 2));
94728 ld = dividend.length;
94730 for (i = 0; i < ld; i += 1) {
94731 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
94734 * Repeatedly perform a long division. The binary array forms the dividend,
94735 * the length of the encoding is the divisor. Once computed, the quotient
94736 * forms the dividend for the next step. We stop when the dividend is zerHashes.
94737 * All remainders are stored for later use.
94741 while (dividend.length > 0) {
94742 quotient = Array();
94745 for (i = 0; i < dividend.length; i += 1) {
94746 x = (x << 16) + dividend[i];
94747 q = Math.floor(x / divisor);
94750 if (quotient.length > 0 || q > 0) {
94751 quotient[quotient.length] = q;
94755 remainders[remainders.length] = x;
94756 dividend = quotient;
94758 /* Convert the remainders to the output string */
94763 for (i = remainders.length - 1; i >= 0; i--) {
94764 output += encoding.charAt(remainders[i]);
94766 /* Append leading zero equivalents */
94769 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
94771 for (i = output.length; i < full_length; i += 1) {
94772 output = encoding[0] + output;
94778 * Convert a raw string to a base-64 string
94782 function rstr2b64(input, b64pad) {
94783 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
94785 len = input.length,
94789 b64pad = b64pad || '=';
94791 for (i = 0; i < len; i += 3) {
94792 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
94794 for (j = 0; j < 4; j += 1) {
94795 if (i * 8 + j * 6 > input.length * 8) {
94798 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
94808 * @property {String} version
94818 Base64: function Base64() {
94819 // private properties
94820 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
94822 // URL encoding support @todo
94823 utf8 = true; // by default enable UTF-8 support encoding
94824 // public method for encoding
94826 this.encode = function (input) {
94831 len = input.length;
94833 input = utf8 ? utf8Encode(input) : input;
94835 for (i = 0; i < len; i += 3) {
94836 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
94838 for (j = 0; j < 4; j += 1) {
94839 if (i * 8 + j * 6 > len * 8) {
94842 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
94848 }; // public method for decoding
94851 this.decode = function (input) {
94852 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
94871 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
94875 // unpack four hexets into three octets using index points in b64
94876 h1 = tab.indexOf(input.charAt(i += 1));
94877 h2 = tab.indexOf(input.charAt(i += 1));
94878 h3 = tab.indexOf(input.charAt(i += 1));
94879 h4 = tab.indexOf(input.charAt(i += 1));
94880 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
94881 o1 = bits >> 16 & 0xff;
94882 o2 = bits >> 8 & 0xff;
94887 arr[ac] = String.fromCharCode(o1);
94888 } else if (h4 === 64) {
94889 arr[ac] = String.fromCharCode(o1, o2);
94891 arr[ac] = String.fromCharCode(o1, o2, o3);
94893 } while (i < input.length);
94895 dec = arr.join('');
94896 dec = utf8 ? utf8Decode(dec) : dec;
94898 }; // set custom pad string
94901 this.setPad = function (str) {
94904 }; // set custom tab string characters
94907 this.setTab = function (str) {
94912 this.setUTF8 = function (bool) {
94913 if (typeof bool === 'boolean') {
94922 * CRC-32 calculation
94926 * @param {String} str Input String
94929 CRC32: function CRC32(str) {
94936 str = utf8Encode(str);
94937 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('');
94940 for (i = 0, iTop = str.length; i < iTop; i += 1) {
94941 y = (crc ^ str.charCodeAt(i)) & 0xFF;
94942 x = '0x' + table.substr(y * 9, 8);
94943 crc = crc >>> 8 ^ x;
94944 } // always return a positive number (that's what >>> 0 does)
94947 return (crc ^ -1) >>> 0;
94954 * @param {Object} [config]
94956 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
94957 * Digest Algorithm, as defined in RFC 1321.
94958 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
94959 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
94960 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
94962 MD5: function MD5(options) {
94964 * Private config properties. You may need to tweak these to be compatible with
94965 * the server-side, but the defaults work in most cases.
94966 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
94968 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
94969 // hexadecimal output case format. false - lowercase; true - uppercase
94970 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
94971 // base-64 pad character. Defaults to '=' for strict RFC compliance
94972 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
94973 // privileged (public) methods
94975 this.hex = function (s) {
94976 return rstr2hex(rstr(s), hexcase);
94979 this.b64 = function (s) {
94980 return rstr2b64(rstr(s), b64pad);
94983 this.any = function (s, e) {
94984 return rstr2any(rstr(s), e);
94987 this.raw = function (s) {
94991 this.hex_hmac = function (k, d) {
94992 return rstr2hex(rstr_hmac(k, d), hexcase);
94995 this.b64_hmac = function (k, d) {
94996 return rstr2b64(rstr_hmac(k, d), b64pad);
94999 this.any_hmac = function (k, d, e) {
95000 return rstr2any(rstr_hmac(k, d), e);
95003 * Perform a simple self-test to see if the VM is working
95004 * @return {String} Hexadecimal hash sample
95008 this.vm_test = function () {
95009 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
95012 * Enable/disable uppercase hexadecimal returned string
95014 * @return {Object} this
95018 this.setUpperCase = function (a) {
95019 if (typeof a === 'boolean') {
95026 * Defines a base64 pad string
95027 * @param {String} Pad
95028 * @return {Object} this
95032 this.setPad = function (a) {
95033 b64pad = a || b64pad;
95037 * Defines a base64 pad string
95039 * @return {Object} [this]
95043 this.setUTF8 = function (a) {
95044 if (typeof a === 'boolean') {
95049 }; // private methods
95052 * Calculate the MD5 of a raw string
95057 s = utf8 ? utf8Encode(s) : s;
95058 return binl2rstr(binl(rstr2binl(s), s.length * 8));
95061 * Calculate the HMAC-MD5, of a key and some data (raw strings)
95065 function rstr_hmac(key, data) {
95066 var bkey, ipad, opad, hash, i;
95067 key = utf8 ? utf8Encode(key) : key;
95068 data = utf8 ? utf8Encode(data) : data;
95069 bkey = rstr2binl(key);
95071 if (bkey.length > 16) {
95072 bkey = binl(bkey, key.length * 8);
95075 ipad = Array(16), opad = Array(16);
95077 for (i = 0; i < 16; i += 1) {
95078 ipad[i] = bkey[i] ^ 0x36363636;
95079 opad[i] = bkey[i] ^ 0x5C5C5C5C;
95082 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
95083 return binl2rstr(binl(opad.concat(hash), 512 + 128));
95086 * Calculate the MD5 of an array of little-endian words, and a bit length.
95090 function binl(x, len) {
95100 /* append padding */
95102 x[len >> 5] |= 0x80 << len % 32;
95103 x[(len + 64 >>> 9 << 4) + 14] = len;
95105 for (i = 0; i < x.length; i += 16) {
95110 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
95111 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
95112 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
95113 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
95114 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
95115 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
95116 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
95117 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
95118 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
95119 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
95120 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
95121 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
95122 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
95123 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
95124 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
95125 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
95126 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
95127 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
95128 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
95129 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
95130 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
95131 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
95132 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
95133 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
95134 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
95135 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
95136 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
95137 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
95138 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
95139 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
95140 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
95141 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
95142 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
95143 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
95144 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
95145 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
95146 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
95147 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
95148 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
95149 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
95150 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
95151 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
95152 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
95153 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
95154 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
95155 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
95156 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
95157 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
95158 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
95159 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
95160 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
95161 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
95162 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
95163 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
95164 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
95165 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
95166 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
95167 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
95168 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
95169 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
95170 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
95171 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
95172 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
95173 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
95174 a = safe_add(a, olda);
95175 b = safe_add(b, oldb);
95176 c = safe_add(c, oldc);
95177 d = safe_add(d, oldd);
95180 return Array(a, b, c, d);
95183 * These functions implement the four basic operations the algorithm uses.
95187 function md5_cmn(q, a, b, x, s, t) {
95188 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
95191 function md5_ff(a, b, c, d, x, s, t) {
95192 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
95195 function md5_gg(a, b, c, d, x, s, t) {
95196 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
95199 function md5_hh(a, b, c, d, x, s, t) {
95200 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
95203 function md5_ii(a, b, c, d, x, s, t) {
95204 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
95210 * @class Hashes.SHA1
95211 * @param {Object} [config]
95214 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
95215 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
95216 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
95217 * See http://pajhome.org.uk/crypt/md5 for details.
95219 SHA1: function SHA1(options) {
95221 * Private config properties. You may need to tweak these to be compatible with
95222 * the server-side, but the defaults work in most cases.
95223 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
95225 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
95226 // hexadecimal output case format. false - lowercase; true - uppercase
95227 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
95228 // base-64 pad character. Defaults to '=' for strict RFC compliance
95229 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
95232 this.hex = function (s) {
95233 return rstr2hex(rstr(s), hexcase);
95236 this.b64 = function (s) {
95237 return rstr2b64(rstr(s), b64pad);
95240 this.any = function (s, e) {
95241 return rstr2any(rstr(s), e);
95244 this.raw = function (s) {
95248 this.hex_hmac = function (k, d) {
95249 return rstr2hex(rstr_hmac(k, d));
95252 this.b64_hmac = function (k, d) {
95253 return rstr2b64(rstr_hmac(k, d), b64pad);
95256 this.any_hmac = function (k, d, e) {
95257 return rstr2any(rstr_hmac(k, d), e);
95260 * Perform a simple self-test to see if the VM is working
95261 * @return {String} Hexadecimal hash sample
95266 this.vm_test = function () {
95267 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
95270 * @description Enable/disable uppercase hexadecimal returned string
95272 * @return {Object} this
95277 this.setUpperCase = function (a) {
95278 if (typeof a === 'boolean') {
95285 * @description Defines a base64 pad string
95286 * @param {string} Pad
95287 * @return {Object} this
95292 this.setPad = function (a) {
95293 b64pad = a || b64pad;
95297 * @description Defines a base64 pad string
95299 * @return {Object} this
95304 this.setUTF8 = function (a) {
95305 if (typeof a === 'boolean') {
95310 }; // private methods
95313 * Calculate the SHA-512 of a raw string
95318 s = utf8 ? utf8Encode(s) : s;
95319 return binb2rstr(binb(rstr2binb(s), s.length * 8));
95322 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
95326 function rstr_hmac(key, data) {
95327 var bkey, ipad, opad, i, hash;
95328 key = utf8 ? utf8Encode(key) : key;
95329 data = utf8 ? utf8Encode(data) : data;
95330 bkey = rstr2binb(key);
95332 if (bkey.length > 16) {
95333 bkey = binb(bkey, key.length * 8);
95336 ipad = Array(16), opad = Array(16);
95338 for (i = 0; i < 16; i += 1) {
95339 ipad[i] = bkey[i] ^ 0x36363636;
95340 opad[i] = bkey[i] ^ 0x5C5C5C5C;
95343 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
95344 return binb2rstr(binb(opad.concat(hash), 512 + 160));
95347 * Calculate the SHA-1 of an array of big-endian words, and a bit length
95351 function binb(x, len) {
95366 /* append padding */
95368 x[len >> 5] |= 0x80 << 24 - len % 32;
95369 x[(len + 64 >> 9 << 4) + 15] = len;
95371 for (i = 0; i < x.length; i += 16) {
95378 for (j = 0; j < 80; j += 1) {
95382 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
95385 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)));
95388 c = bit_rol(b, 30);
95393 a = safe_add(a, olda);
95394 b = safe_add(b, oldb);
95395 c = safe_add(c, oldc);
95396 d = safe_add(d, oldd);
95397 e = safe_add(e, olde);
95400 return Array(a, b, c, d, e);
95403 * Perform the appropriate triplet combination function for the current
95408 function sha1_ft(t, b, c, d) {
95410 return b & c | ~b & d;
95418 return b & c | b & d | c & d;
95424 * Determine the appropriate additive constant for the current iteration
95428 function sha1_kt(t) {
95429 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
95434 * @class Hashes.SHA256
95437 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
95438 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
95439 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
95440 * See http://pajhome.org.uk/crypt/md5 for details.
95441 * Also http://anmar.eu.org/projects/jssha2/
95443 SHA256: function SHA256(options) {
95445 * Private properties configuration variables. You may need to tweak these to be compatible with
95446 * the server-side, but the defaults work in most cases.
95447 * @see this.setUpperCase() method
95448 * @see this.setPad() method
95450 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
95451 var // hexadecimal output case format. false - lowercase; true - uppercase */
95452 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
95454 /* base-64 pad character. Default '=' for strict RFC compliance */
95455 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
95457 /* enable/disable utf8 encoding */
95459 /* privileged (public) methods */
95461 this.hex = function (s) {
95462 return rstr2hex(rstr(s, utf8));
95465 this.b64 = function (s) {
95466 return rstr2b64(rstr(s, utf8), b64pad);
95469 this.any = function (s, e) {
95470 return rstr2any(rstr(s, utf8), e);
95473 this.raw = function (s) {
95474 return rstr(s, utf8);
95477 this.hex_hmac = function (k, d) {
95478 return rstr2hex(rstr_hmac(k, d));
95481 this.b64_hmac = function (k, d) {
95482 return rstr2b64(rstr_hmac(k, d), b64pad);
95485 this.any_hmac = function (k, d, e) {
95486 return rstr2any(rstr_hmac(k, d), e);
95489 * Perform a simple self-test to see if the VM is working
95490 * @return {String} Hexadecimal hash sample
95495 this.vm_test = function () {
95496 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
95499 * Enable/disable uppercase hexadecimal returned string
95501 * @return {Object} this
95506 this.setUpperCase = function (a) {
95511 * @description Defines a base64 pad string
95512 * @param {string} Pad
95513 * @return {Object} this
95518 this.setPad = function (a) {
95519 b64pad = a || b64pad;
95523 * Defines a base64 pad string
95525 * @return {Object} this
95530 this.setUTF8 = function (a) {
95531 if (typeof a === 'boolean') {
95536 }; // private methods
95539 * Calculate the SHA-512 of a raw string
95543 function rstr(s, utf8) {
95544 s = utf8 ? utf8Encode(s) : s;
95545 return binb2rstr(binb(rstr2binb(s), s.length * 8));
95548 * Calculate the HMAC-sha256 of a key and some data (raw strings)
95552 function rstr_hmac(key, data) {
95553 key = utf8 ? utf8Encode(key) : key;
95554 data = utf8 ? utf8Encode(data) : data;
95557 bkey = rstr2binb(key),
95561 if (bkey.length > 16) {
95562 bkey = binb(bkey, key.length * 8);
95565 for (; i < 16; i += 1) {
95566 ipad[i] = bkey[i] ^ 0x36363636;
95567 opad[i] = bkey[i] ^ 0x5C5C5C5C;
95570 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
95571 return binb2rstr(binb(opad.concat(hash), 512 + 256));
95574 * Main sha256 function, with its support functions
95578 function sha256_S(X, n) {
95579 return X >>> n | X << 32 - n;
95582 function sha256_R(X, n) {
95586 function sha256_Ch(x, y, z) {
95587 return x & y ^ ~x & z;
95590 function sha256_Maj(x, y, z) {
95591 return x & y ^ x & z ^ y & z;
95594 function sha256_Sigma0256(x) {
95595 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
95598 function sha256_Sigma1256(x) {
95599 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
95602 function sha256_Gamma0256(x) {
95603 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
95606 function sha256_Gamma1256(x) {
95607 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
95610 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];
95612 function binb(m, l) {
95613 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
95614 var W = new Array(64);
95615 var a, b, c, d, e, f, g, h;
95617 /* append padding */
95619 m[l >> 5] |= 0x80 << 24 - l % 32;
95620 m[(l + 64 >> 9 << 4) + 15] = l;
95622 for (i = 0; i < m.length; i += 16) {
95632 for (j = 0; j < 64; j += 1) {
95636 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
95639 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
95640 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
95644 e = safe_add(d, T1);
95648 a = safe_add(T1, T2);
95651 HASH[0] = safe_add(a, HASH[0]);
95652 HASH[1] = safe_add(b, HASH[1]);
95653 HASH[2] = safe_add(c, HASH[2]);
95654 HASH[3] = safe_add(d, HASH[3]);
95655 HASH[4] = safe_add(e, HASH[4]);
95656 HASH[5] = safe_add(f, HASH[5]);
95657 HASH[6] = safe_add(g, HASH[6]);
95658 HASH[7] = safe_add(h, HASH[7]);
95666 * @class Hashes.SHA512
95669 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
95670 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
95671 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
95672 * See http://pajhome.org.uk/crypt/md5 for details.
95674 SHA512: function SHA512(options) {
95676 * Private properties configuration variables. You may need to tweak these to be compatible with
95677 * the server-side, but the defaults work in most cases.
95678 * @see this.setUpperCase() method
95679 * @see this.setPad() method
95681 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
95683 var /* hexadecimal output case format. false - lowercase; true - uppercase */
95684 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
95686 /* base-64 pad character. Default '=' for strict RFC compliance */
95687 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
95689 /* enable/disable utf8 encoding */
95691 /* privileged (public) methods */
95693 this.hex = function (s) {
95694 return rstr2hex(rstr(s));
95697 this.b64 = function (s) {
95698 return rstr2b64(rstr(s), b64pad);
95701 this.any = function (s, e) {
95702 return rstr2any(rstr(s), e);
95705 this.raw = function (s) {
95709 this.hex_hmac = function (k, d) {
95710 return rstr2hex(rstr_hmac(k, d));
95713 this.b64_hmac = function (k, d) {
95714 return rstr2b64(rstr_hmac(k, d), b64pad);
95717 this.any_hmac = function (k, d, e) {
95718 return rstr2any(rstr_hmac(k, d), e);
95721 * Perform a simple self-test to see if the VM is working
95722 * @return {String} Hexadecimal hash sample
95727 this.vm_test = function () {
95728 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
95731 * @description Enable/disable uppercase hexadecimal returned string
95733 * @return {Object} this
95738 this.setUpperCase = function (a) {
95743 * @description Defines a base64 pad string
95744 * @param {string} Pad
95745 * @return {Object} this
95750 this.setPad = function (a) {
95751 b64pad = a || b64pad;
95755 * @description Defines a base64 pad string
95757 * @return {Object} this
95762 this.setUTF8 = function (a) {
95763 if (typeof a === 'boolean') {
95769 /* private methods */
95772 * Calculate the SHA-512 of a raw string
95777 s = utf8 ? utf8Encode(s) : s;
95778 return binb2rstr(binb(rstr2binb(s), s.length * 8));
95781 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
95785 function rstr_hmac(key, data) {
95786 key = utf8 ? utf8Encode(key) : key;
95787 data = utf8 ? utf8Encode(data) : data;
95790 bkey = rstr2binb(key),
95794 if (bkey.length > 32) {
95795 bkey = binb(bkey, key.length * 8);
95798 for (; i < 32; i += 1) {
95799 ipad[i] = bkey[i] ^ 0x36363636;
95800 opad[i] = bkey[i] ^ 0x5C5C5C5C;
95803 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
95804 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
95807 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
95811 function binb(x, len) {
95816 hash = new Array(16),
95817 //Initial hash values
95818 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)],
95819 T1 = new int64(0, 0),
95820 T2 = new int64(0, 0),
95821 a = new int64(0, 0),
95822 b = new int64(0, 0),
95823 c = new int64(0, 0),
95824 d = new int64(0, 0),
95825 e = new int64(0, 0),
95826 f = new int64(0, 0),
95827 g = new int64(0, 0),
95828 h = new int64(0, 0),
95829 //Temporary variables not specified by the document
95830 s0 = new int64(0, 0),
95831 s1 = new int64(0, 0),
95832 Ch = new int64(0, 0),
95833 Maj = new int64(0, 0),
95834 r1 = new int64(0, 0),
95835 r2 = new int64(0, 0),
95836 r3 = new int64(0, 0);
95838 if (sha512_k === undefined) {
95840 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)];
95843 for (i = 0; i < 80; i += 1) {
95844 W[i] = new int64(0, 0);
95845 } // append padding to the source string. The format is described in the FIPS.
95848 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
95849 x[(len + 128 >> 10 << 5) + 31] = len;
95852 for (i = 0; i < l; i += 32) {
95853 //32 dwords is the block size
95854 int64copy(a, H[0]);
95855 int64copy(b, H[1]);
95856 int64copy(c, H[2]);
95857 int64copy(d, H[3]);
95858 int64copy(e, H[4]);
95859 int64copy(f, H[5]);
95860 int64copy(g, H[6]);
95861 int64copy(h, H[7]);
95863 for (j = 0; j < 16; j += 1) {
95864 W[j].h = x[i + 2 * j];
95865 W[j].l = x[i + 2 * j + 1];
95868 for (j = 16; j < 80; j += 1) {
95870 int64rrot(r1, W[j - 2], 19);
95871 int64revrrot(r2, W[j - 2], 29);
95872 int64shr(r3, W[j - 2], 6);
95873 s1.l = r1.l ^ r2.l ^ r3.l;
95874 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
95876 int64rrot(r1, W[j - 15], 1);
95877 int64rrot(r2, W[j - 15], 8);
95878 int64shr(r3, W[j - 15], 7);
95879 s0.l = r1.l ^ r2.l ^ r3.l;
95880 s0.h = r1.h ^ r2.h ^ r3.h;
95881 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
95884 for (j = 0; j < 80; j += 1) {
95886 Ch.l = e.l & f.l ^ ~e.l & g.l;
95887 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
95889 int64rrot(r1, e, 14);
95890 int64rrot(r2, e, 18);
95891 int64revrrot(r3, e, 9);
95892 s1.l = r1.l ^ r2.l ^ r3.l;
95893 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
95895 int64rrot(r1, a, 28);
95896 int64revrrot(r2, a, 2);
95897 int64revrrot(r3, a, 7);
95898 s0.l = r1.l ^ r2.l ^ r3.l;
95899 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
95901 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
95902 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
95903 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
95904 int64add(T2, s0, Maj);
95908 int64add(e, d, T1);
95912 int64add(a, T1, T2);
95915 int64add(H[0], H[0], a);
95916 int64add(H[1], H[1], b);
95917 int64add(H[2], H[2], c);
95918 int64add(H[3], H[3], d);
95919 int64add(H[4], H[4], e);
95920 int64add(H[5], H[5], f);
95921 int64add(H[6], H[6], g);
95922 int64add(H[7], H[7], h);
95923 } //represent the hash as an array of 32-bit dwords
95926 for (i = 0; i < 8; i += 1) {
95927 hash[2 * i] = H[i].h;
95928 hash[2 * i + 1] = H[i].l;
95932 } //A constructor for 64-bit numbers
95935 function int64(h, l) {
95937 this.l = l; //this.toString = int64toString;
95938 } //Copies src into dst, assuming both are 64-bit numbers
95941 function int64copy(dst, src) {
95944 } //Right-rotates a 64-bit number by shift
95945 //Won't handle cases of shift>=32
95946 //The function revrrot() is for that
95949 function int64rrot(dst, x, shift) {
95950 dst.l = x.l >>> shift | x.h << 32 - shift;
95951 dst.h = x.h >>> shift | x.l << 32 - shift;
95952 } //Reverses the dwords of the source and then rotates right by shift.
95953 //This is equivalent to rotation by 32+shift
95956 function int64revrrot(dst, x, shift) {
95957 dst.l = x.h >>> shift | x.l << 32 - shift;
95958 dst.h = x.l >>> shift | x.h << 32 - shift;
95959 } //Bitwise-shifts right a 64-bit number by shift
95960 //Won't handle shift>=32, but it's never needed in SHA512
95963 function int64shr(dst, x, shift) {
95964 dst.l = x.l >>> shift | x.h << 32 - shift;
95965 dst.h = x.h >>> shift;
95966 } //Adds two 64-bit numbers
95967 //Like the original implementation, does not rely on 32-bit operations
95970 function int64add(dst, x, y) {
95971 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
95972 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
95973 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
95974 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
95975 dst.l = w0 & 0xffff | w1 << 16;
95976 dst.h = w2 & 0xffff | w3 << 16;
95977 } //Same, except with 4 addends. Works faster than adding them one by one.
95980 function int64add4(dst, a, b, c, d) {
95981 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
95982 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
95983 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
95984 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
95985 dst.l = w0 & 0xffff | w1 << 16;
95986 dst.h = w2 & 0xffff | w3 << 16;
95987 } //Same, except with 5 addends
95990 function int64add5(dst, a, b, c, d, e) {
95991 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
95992 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
95993 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
95994 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
95995 dst.l = w0 & 0xffff | w1 << 16;
95996 dst.h = w2 & 0xffff | w3 << 16;
96001 * @class Hashes.RMD160
96003 * @param {Object} [config]
96005 * A JavaScript implementation of the RIPEMD-160 Algorithm
96006 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
96007 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
96008 * See http://pajhome.org.uk/crypt/md5 for details.
96009 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
96011 RMD160: function RMD160(options) {
96013 * Private properties configuration variables. You may need to tweak these to be compatible with
96014 * the server-side, but the defaults work in most cases.
96015 * @see this.setUpperCase() method
96016 * @see this.setPad() method
96018 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
96020 var /* hexadecimal output case format. false - lowercase; true - uppercase */
96021 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
96023 /* base-64 pad character. Default '=' for strict RFC compliance */
96024 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
96026 /* enable/disable utf8 encoding */
96027 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],
96028 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],
96029 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],
96030 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];
96031 /* privileged (public) methods */
96033 this.hex = function (s) {
96034 return rstr2hex(rstr(s));
96037 this.b64 = function (s) {
96038 return rstr2b64(rstr(s), b64pad);
96041 this.any = function (s, e) {
96042 return rstr2any(rstr(s), e);
96045 this.raw = function (s) {
96049 this.hex_hmac = function (k, d) {
96050 return rstr2hex(rstr_hmac(k, d));
96053 this.b64_hmac = function (k, d) {
96054 return rstr2b64(rstr_hmac(k, d), b64pad);
96057 this.any_hmac = function (k, d, e) {
96058 return rstr2any(rstr_hmac(k, d), e);
96061 * Perform a simple self-test to see if the VM is working
96062 * @return {String} Hexadecimal hash sample
96067 this.vm_test = function () {
96068 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
96071 * @description Enable/disable uppercase hexadecimal returned string
96073 * @return {Object} this
96078 this.setUpperCase = function (a) {
96083 * @description Defines a base64 pad string
96084 * @param {string} Pad
96085 * @return {Object} this
96090 this.setPad = function (a) {
96091 if (typeof a !== 'undefined') {
96098 * @description Defines a base64 pad string
96100 * @return {Object} this
96105 this.setUTF8 = function (a) {
96106 if (typeof a === 'boolean') {
96112 /* private methods */
96115 * Calculate the rmd160 of a raw string
96120 s = utf8 ? utf8Encode(s) : s;
96121 return binl2rstr(binl(rstr2binl(s), s.length * 8));
96124 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
96128 function rstr_hmac(key, data) {
96129 key = utf8 ? utf8Encode(key) : key;
96130 data = utf8 ? utf8Encode(data) : data;
96133 bkey = rstr2binl(key),
96137 if (bkey.length > 16) {
96138 bkey = binl(bkey, key.length * 8);
96141 for (i = 0; i < 16; i += 1) {
96142 ipad[i] = bkey[i] ^ 0x36363636;
96143 opad[i] = bkey[i] ^ 0x5C5C5C5C;
96146 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
96147 return binl2rstr(binl(opad.concat(hash), 512 + 160));
96150 * Convert an array of little-endian words to a string
96154 function binl2rstr(input) {
96157 l = input.length * 32;
96159 for (i = 0; i < l; i += 8) {
96160 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
96166 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
96170 function binl(x, len) {
96190 /* append padding */
96192 x[len >> 5] |= 0x80 << len % 32;
96193 x[(len + 64 >>> 9 << 4) + 14] = len;
96196 for (i = 0; i < l; i += 16) {
96203 for (j = 0; j <= 79; j += 1) {
96204 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
96205 T = safe_add(T, x[i + rmd160_r1[j]]);
96206 T = safe_add(T, rmd160_K1(j));
96207 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
96210 D1 = bit_rol(C1, 10);
96213 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
96214 T = safe_add(T, x[i + rmd160_r2[j]]);
96215 T = safe_add(T, rmd160_K2(j));
96216 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
96219 D2 = bit_rol(C2, 10);
96224 T = safe_add(h1, safe_add(C1, D2));
96225 h1 = safe_add(h2, safe_add(D1, E2));
96226 h2 = safe_add(h3, safe_add(E1, A2));
96227 h3 = safe_add(h4, safe_add(A1, B2));
96228 h4 = safe_add(h0, safe_add(B1, C2));
96232 return [h0, h1, h2, h3, h4];
96233 } // specific algorithm methods
96236 function rmd160_f(j, x, y, z) {
96237 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';
96240 function rmd160_K1(j) {
96241 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';
96244 function rmd160_K2(j) {
96245 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';
96248 }; // exposes Hashes
96250 (function (window, undefined$1) {
96251 var freeExports = false;
96254 freeExports = exports;
96256 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
96257 window = commonjsGlobal;
96261 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
96262 // define as an anonymous module, so, through path mapping, it can be aliased
96263 undefined$1(function () {
96266 } else if (freeExports) {
96267 // in Node.js or RingoJS v0.8.0+
96268 if (module && module.exports === freeExports) {
96269 module.exports = Hashes;
96270 } // in Narwhal or RingoJS v0.7.0-
96272 freeExports.Hashes = Hashes;
96275 // in a browser or Rhino
96276 window.Hashes = Hashes;
96283 var sha1 = new hashes.SHA1(); // # xtend
96285 var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
96290 for (var i = 0; i < arguments.length; i++) {
96291 var source = arguments[i];
96293 for (var key in source) {
96294 if (hasOwnProperty$1.call(source, key)) {
96295 target[key] = source[key];
96305 ohauth.qsString = function (obj) {
96306 return Object.keys(obj).sort().map(function (key) {
96307 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
96311 ohauth.stringQs = function (str) {
96312 return str.split('&').filter(function (pair) {
96313 return pair !== '';
96314 }).reduce(function (obj, pair) {
96315 var parts = pair.split('=');
96316 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
96321 ohauth.rawxhr = function (method, url, data, headers, callback) {
96322 var xhr = new XMLHttpRequest(),
96323 twoHundred = /^20\d$/;
96325 xhr.onreadystatechange = function () {
96326 if (4 === xhr.readyState && 0 !== xhr.status) {
96327 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
96331 xhr.onerror = function (e) {
96332 return callback(e, null);
96335 xhr.open(method, url, true);
96337 for (var h in headers) {
96338 xhr.setRequestHeader(h, headers[h]);
96345 ohauth.xhr = function (method, url, auth, data, options, callback) {
96346 var headers = options && options.header || {
96347 'Content-Type': 'application/x-www-form-urlencoded'
96349 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
96350 return ohauth.rawxhr(method, url, data, headers, callback);
96353 ohauth.nonce = function () {
96354 for (var o = ''; o.length < 6;) {
96355 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
96361 ohauth.authHeader = function (obj) {
96362 return Object.keys(obj).sort().map(function (key) {
96363 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
96367 ohauth.timestamp = function () {
96368 return ~~(+new Date() / 1000);
96371 ohauth.percentEncode = function (s) {
96372 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
96375 ohauth.baseString = function (method, url, params) {
96376 if (params.oauth_signature) delete params.oauth_signature;
96377 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
96380 ohauth.signature = function (oauth_secret, token_secret, baseString) {
96381 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
96384 * Takes an options object for configuration (consumer_key,
96385 * consumer_secret, version, signature_method, token, token_secret)
96386 * and returns a function that generates the Authorization header
96389 * The returned function takes these parameters:
96390 * - method: GET/POST/...
96391 * - uri: full URI with protocol, port, path and query string
96392 * - extra_params: any extra parameters (that are passed in the POST data),
96393 * can be an object or a from-urlencoded string.
96395 * Returned function returns full OAuth header with "OAuth" string in it.
96399 ohauth.headerGenerator = function (options) {
96400 options = options || {};
96401 var consumer_key = options.consumer_key || '',
96402 consumer_secret = options.consumer_secret || '',
96403 signature_method = options.signature_method || 'HMAC-SHA1',
96404 version = options.version || '1.0',
96405 token = options.token || '',
96406 token_secret = options.token_secret || '';
96407 return function (method, uri, extra_params) {
96408 method = method.toUpperCase();
96410 if (typeof extra_params === 'string' && extra_params.length > 0) {
96411 extra_params = ohauth.stringQs(extra_params);
96414 var uri_parts = uri.split('?', 2),
96415 base_uri = uri_parts[0];
96416 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
96417 var oauth_params = {
96418 oauth_consumer_key: consumer_key,
96419 oauth_signature_method: signature_method,
96420 oauth_version: version,
96421 oauth_timestamp: ohauth.timestamp(),
96422 oauth_nonce: ohauth.nonce()
96424 if (token) oauth_params.oauth_token = token;
96425 var all_params = xtend({}, oauth_params, query_params, extra_params),
96426 base_str = ohauth.baseString(method, base_uri, all_params);
96427 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
96428 return 'OAuth ' + ohauth.authHeader(oauth_params);
96432 var ohauth_1 = ohauth;
96434 var resolveUrl = createCommonjsModule(function (module, exports) {
96435 // Copyright 2014 Simon Lydell
96436 // X11 (“MIT”) Licensed. (See LICENSE.)
96437 void function (root, factory) {
96439 module.exports = factory();
96441 }(commonjsGlobal, function () {
96442 function resolveUrl()
96445 var numUrls = arguments.length;
96447 if (numUrls === 0) {
96448 throw new Error("resolveUrl requires at least one argument; got none.");
96451 var base = document.createElement("base");
96452 base.href = arguments[0];
96454 if (numUrls === 1) {
96458 var head = document.getElementsByTagName("head")[0];
96459 head.insertBefore(base, head.firstChild);
96460 var a = document.createElement("a");
96463 for (var index = 1; index < numUrls; index++) {
96464 a.href = arguments[index];
96466 base.href = resolved;
96469 head.removeChild(base);
96477 var assign = make_assign();
96478 var create$1 = make_create();
96479 var trim$1 = make_trim();
96480 var Global$5 = typeof window !== 'undefined' ? window : commonjsGlobal;
96491 isFunction: isFunction$1,
96492 isObject: isObject$1,
96496 function make_assign() {
96497 if (Object.assign) {
96498 return Object.assign;
96500 return function shimAssign(obj, props1, props2, etc) {
96501 for (var i = 1; i < arguments.length; i++) {
96502 each$7(Object(arguments[i]), function (val, key) {
96512 function make_create() {
96513 if (Object.create) {
96514 return function create(obj, assignProps1, assignProps2, etc) {
96515 var assignArgsList = slice$1(arguments, 1);
96516 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
96519 var F = function F() {}; // eslint-disable-line no-inner-declarations
96522 return function create(obj, assignProps1, assignProps2, etc) {
96523 var assignArgsList = slice$1(arguments, 1);
96525 return assign.apply(this, [new F()].concat(assignArgsList));
96530 function make_trim() {
96531 if (String.prototype.trim) {
96532 return function trim(str) {
96533 return String.prototype.trim.call(str);
96536 return function trim(str) {
96537 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
96542 function bind$1(obj, fn) {
96543 return function () {
96544 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
96548 function slice$1(arr, index) {
96549 return Array.prototype.slice.call(arr, index || 0);
96552 function each$7(obj, fn) {
96553 pluck$1(obj, function (val, key) {
96559 function map(obj, fn) {
96560 var res = isList$1(obj) ? [] : {};
96561 pluck$1(obj, function (v, k) {
96568 function pluck$1(obj, fn) {
96569 if (isList$1(obj)) {
96570 for (var i = 0; i < obj.length; i++) {
96571 if (fn(obj[i], i)) {
96576 for (var key in obj) {
96577 if (obj.hasOwnProperty(key)) {
96578 if (fn(obj[key], key)) {
96586 function isList$1(val) {
96587 return val != null && typeof val != 'function' && typeof val.length == 'number';
96590 function isFunction$1(val) {
96591 return val && {}.toString.call(val) === '[object Function]';
96594 function isObject$1(val) {
96595 return val && {}.toString.call(val) === '[object Object]';
96598 var slice = util.slice;
96599 var pluck = util.pluck;
96600 var each$6 = util.each;
96601 var bind = util.bind;
96602 var create = util.create;
96603 var isList = util.isList;
96604 var isFunction = util.isFunction;
96605 var isObject = util.isObject;
96606 var storeEngine = {
96607 createStore: _createStore
96612 // get returns the value of the given key. If that value
96613 // is undefined, it returns optionalDefaultValue instead.
96614 get: function get(key, optionalDefaultValue) {
96615 var data = this.storage.read(this._namespacePrefix + key);
96616 return this._deserialize(data, optionalDefaultValue);
96618 // set will store the given value at key and returns value.
96619 // Calling set with value === undefined is equivalent to calling remove.
96620 set: function set(key, value) {
96621 if (value === undefined) {
96622 return this.remove(key);
96625 this.storage.write(this._namespacePrefix + key, this._serialize(value));
96628 // remove deletes the key and value stored at the given key.
96629 remove: function remove(key) {
96630 this.storage.remove(this._namespacePrefix + key);
96632 // each will call the given callback once for each key-value pair
96634 each: function each(callback) {
96636 this.storage.each(function (val, namespacedKey) {
96637 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
96640 // clearAll will remove all the stored key-value pairs in this store.
96641 clearAll: function clearAll() {
96642 this.storage.clearAll();
96644 // additional functionality that can't live in plugins
96645 // ---------------------------------------------------
96646 // hasNamespace returns true if this store instance has the given namespace.
96647 hasNamespace: function hasNamespace(namespace) {
96648 return this._namespacePrefix == '__storejs_' + namespace + '_';
96650 // createStore creates a store.js instance with the first
96651 // functioning storage in the list of storage candidates,
96652 // and applies the the given mixins to the instance.
96653 createStore: function createStore() {
96654 return _createStore.apply(this, arguments);
96656 addPlugin: function addPlugin(plugin) {
96657 this._addPlugin(plugin);
96659 namespace: function namespace(_namespace) {
96660 return _createStore(this.storage, this.plugins, _namespace);
96665 var _console = typeof console == 'undefined' ? null : console;
96671 var fn = _console.warn ? _console.warn : _console.log;
96672 fn.apply(_console, arguments);
96675 function _createStore(storages, plugins, namespace) {
96680 if (storages && !isList(storages)) {
96681 storages = [storages];
96684 if (plugins && !isList(plugins)) {
96685 plugins = [plugins];
96688 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
96689 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
96690 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
96692 if (!legalNamespaces.test(namespace)) {
96693 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
96696 var _privateStoreProps = {
96697 _namespacePrefix: namespacePrefix,
96698 _namespaceRegexp: namespaceRegexp,
96699 _testStorage: function _testStorage(storage) {
96701 var testStr = '__storejs__test__';
96702 storage.write(testStr, testStr);
96703 var ok = storage.read(testStr) === testStr;
96704 storage.remove(testStr);
96710 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
96711 var oldFn = this[propName];
96713 this[propName] = function pluginFn() {
96714 var args = slice(arguments, 0);
96715 var self = this; // super_fn calls the old function which was overwritten by
96718 function super_fn() {
96723 each$6(arguments, function (arg, i) {
96726 return oldFn.apply(self, args);
96727 } // Give mixing function access to super_fn by prefixing all mixin function
96728 // arguments with super_fn.
96731 var newFnArgs = [super_fn].concat(args);
96732 return pluginFnProp.apply(self, newFnArgs);
96735 _serialize: function _serialize(obj) {
96736 return JSON.stringify(obj);
96738 _deserialize: function _deserialize(strVal, defaultVal) {
96741 } // It is possible that a raw string value has been previously stored
96742 // in a storage without using store.js, meaning it will be a raw
96743 // string value instead of a JSON serialized string. By defaulting
96744 // to the raw string value in case of a JSON parse error, we allow
96745 // for past stored values to be forwards-compatible with store.js
96751 val = JSON.parse(strVal);
96756 return val !== undefined ? val : defaultVal;
96758 _addStorage: function _addStorage(storage) {
96759 if (this.enabled) {
96763 if (this._testStorage(storage)) {
96764 this.storage = storage;
96765 this.enabled = true;
96768 _addPlugin: function _addPlugin(plugin) {
96769 var self = this; // If the plugin is an array, then add all plugins in the array.
96770 // This allows for a plugin to depend on other plugins.
96772 if (isList(plugin)) {
96773 each$6(plugin, function (plugin) {
96774 self._addPlugin(plugin);
96777 } // Keep track of all plugins we've seen so far, so that we
96778 // don't add any of them twice.
96781 var seenPlugin = pluck(this.plugins, function (seenPlugin) {
96782 return plugin === seenPlugin;
96789 this.plugins.push(plugin); // Check that the plugin is properly formed
96791 if (!isFunction(plugin)) {
96792 throw new Error('Plugins must be function values that return objects');
96795 var pluginProperties = plugin.call(this);
96797 if (!isObject(pluginProperties)) {
96798 throw new Error('Plugins must return an object of function properties');
96799 } // Add the plugin function properties to this store instance.
96802 each$6(pluginProperties, function (pluginFnProp, propName) {
96803 if (!isFunction(pluginFnProp)) {
96804 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
96807 self._assignPluginFnProp(pluginFnProp, propName);
96810 // Put deprecated properties in the private API, so as to not expose it to accidential
96811 // discovery through inspection of the store object.
96812 // Deprecated: addStorage
96813 addStorage: function addStorage(storage) {
96814 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
96816 this._addStorage(storage);
96819 var store = create(_privateStoreProps, storeAPI, {
96823 each$6(store, function (prop, propName) {
96824 if (isFunction(prop)) {
96825 store.raw[propName] = bind(store, prop);
96828 each$6(storages, function (storage) {
96829 store._addStorage(storage);
96831 each$6(plugins, function (plugin) {
96832 store._addPlugin(plugin);
96837 var Global$4 = util.Global;
96838 var localStorage_1 = {
96839 name: 'localStorage',
96844 clearAll: clearAll$5
96847 function localStorage$1() {
96848 return Global$4.localStorage;
96851 function read$5(key) {
96852 return localStorage$1().getItem(key);
96855 function write$5(key, data) {
96856 return localStorage$1().setItem(key, data);
96859 function each$5(fn) {
96860 for (var i = localStorage$1().length - 1; i >= 0; i--) {
96861 var key = localStorage$1().key(i);
96862 fn(read$5(key), key);
96866 function remove$5(key) {
96867 return localStorage$1().removeItem(key);
96870 function clearAll$5() {
96871 return localStorage$1().clear();
96874 // versions 6 and 7, where no localStorage, etc
96877 var Global$3 = util.Global;
96878 var oldFFGlobalStorage = {
96879 name: 'oldFF-globalStorage',
96884 clearAll: clearAll$4
96886 var globalStorage = Global$3.globalStorage;
96888 function read$4(key) {
96889 return globalStorage[key];
96892 function write$4(key, data) {
96893 globalStorage[key] = data;
96896 function each$4(fn) {
96897 for (var i = globalStorage.length - 1; i >= 0; i--) {
96898 var key = globalStorage.key(i);
96899 fn(globalStorage[key], key);
96903 function remove$4(key) {
96904 return globalStorage.removeItem(key);
96907 function clearAll$4() {
96908 each$4(function (key, _) {
96909 delete globalStorage[key];
96913 // versions 6 and 7, where no localStorage, sessionStorage, etc
96916 var Global$2 = util.Global;
96917 var oldIEUserDataStorage = {
96918 name: 'oldIE-userDataStorage',
96923 clearAll: clearAll$3
96925 var storageName = 'storejs';
96926 var doc$1 = Global$2.document;
96928 var _withStorageEl = _makeIEStorageElFunction();
96930 var disable = (Global$2.navigator ? Global$2.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
96932 function write$3(unfixedKey, data) {
96937 var fixedKey = fixKey(unfixedKey);
96939 _withStorageEl(function (storageEl) {
96940 storageEl.setAttribute(fixedKey, data);
96941 storageEl.save(storageName);
96945 function read$3(unfixedKey) {
96950 var fixedKey = fixKey(unfixedKey);
96953 _withStorageEl(function (storageEl) {
96954 res = storageEl.getAttribute(fixedKey);
96960 function each$3(callback) {
96961 _withStorageEl(function (storageEl) {
96962 var attributes = storageEl.XMLDocument.documentElement.attributes;
96964 for (var i = attributes.length - 1; i >= 0; i--) {
96965 var attr = attributes[i];
96966 callback(storageEl.getAttribute(attr.name), attr.name);
96971 function remove$3(unfixedKey) {
96972 var fixedKey = fixKey(unfixedKey);
96974 _withStorageEl(function (storageEl) {
96975 storageEl.removeAttribute(fixedKey);
96976 storageEl.save(storageName);
96980 function clearAll$3() {
96981 _withStorageEl(function (storageEl) {
96982 var attributes = storageEl.XMLDocument.documentElement.attributes;
96983 storageEl.load(storageName);
96985 for (var i = attributes.length - 1; i >= 0; i--) {
96986 storageEl.removeAttribute(attributes[i].name);
96989 storageEl.save(storageName);
96993 // In IE7, keys cannot start with a digit or contain certain chars.
96994 // See https://github.com/marcuswestin/store.js/issues/40
96995 // See https://github.com/marcuswestin/store.js/issues/83
96998 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
97000 function fixKey(key) {
97001 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
97004 function _makeIEStorageElFunction() {
97005 if (!doc$1 || !doc$1.documentElement || !doc$1.documentElement.addBehavior) {
97009 var scriptTag = 'script',
97012 storageEl; // Since #userData storage applies only to specific paths, we need to
97013 // somehow link our data to a specific path. We choose /favicon.ico
97014 // as a pretty safe option, since all browsers already make a request to
97015 // this URL anyway and being a 404 will not hurt us here. We wrap an
97016 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
97017 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
97018 // since the iframe access rules appear to allow direct access and
97019 // manipulation of the document element, even for a 404 page. This
97020 // document can be used instead of the current document (which would
97021 // have been limited to the current path) to perform #userData storage.
97024 /* global ActiveXObject */
97025 storageContainer = new ActiveXObject('htmlfile');
97026 storageContainer.open();
97027 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
97028 storageContainer.close();
97029 storageOwner = storageContainer.w.frames[0].document;
97030 storageEl = storageOwner.createElement('div');
97032 // somehow ActiveXObject instantiation failed (perhaps some special
97033 // security settings or otherwse), fall back to per-path storage
97034 storageEl = doc$1.createElement('div');
97035 storageOwner = doc$1.body;
97038 return function (storeFunction) {
97039 var args = [].slice.call(arguments, 0);
97040 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
97041 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
97043 storageOwner.appendChild(storageEl);
97044 storageEl.addBehavior('#default#userData');
97045 storageEl.load(storageName);
97046 storeFunction.apply(this, args);
97047 storageOwner.removeChild(storageEl);
97052 // doesn't work but cookies do. This implementation is adopted from
97053 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
97055 var Global$1 = util.Global;
97056 var trim = util.trim;
97057 var cookieStorage = {
97058 name: 'cookieStorage',
97063 clearAll: clearAll$2
97065 var doc = Global$1.document;
97067 function read$2(key) {
97068 if (!key || !_has(key)) {
97072 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
97073 return unescape(doc.cookie.replace(new RegExp(regexpStr), "$1"));
97076 function each$2(callback) {
97077 var cookies = doc.cookie.split(/; ?/g);
97079 for (var i = cookies.length - 1; i >= 0; i--) {
97080 if (!trim(cookies[i])) {
97084 var kvp = cookies[i].split('=');
97085 var key = unescape(kvp[0]);
97086 var val = unescape(kvp[1]);
97087 callback(val, key);
97091 function write$2(key, data) {
97096 doc.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
97099 function remove$2(key) {
97100 if (!key || !_has(key)) {
97104 doc.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
97107 function clearAll$2() {
97108 each$2(function (_, key) {
97113 function _has(key) {
97114 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc.cookie);
97117 var Global = util.Global;
97118 var sessionStorage_1 = {
97119 name: 'sessionStorage',
97124 clearAll: clearAll$1
97127 function sessionStorage() {
97128 return Global.sessionStorage;
97131 function read$1(key) {
97132 return sessionStorage().getItem(key);
97135 function write$1(key, data) {
97136 return sessionStorage().setItem(key, data);
97139 function each$1(fn) {
97140 for (var i = sessionStorage().length - 1; i >= 0; i--) {
97141 var key = sessionStorage().key(i);
97142 fn(read$1(key), key);
97146 function remove$1(key) {
97147 return sessionStorage().removeItem(key);
97150 function clearAll$1() {
97151 return sessionStorage().clear();
97154 // memoryStorage is a useful last fallback to ensure that the store
97155 // is functions (meaning store.get(), store.set(), etc will all function).
97156 // However, stored values will not persist when the browser navigates to
97157 // a new page or reloads the current page.
97158 var memoryStorage_1 = {
97159 name: 'memoryStorage',
97166 var memoryStorage = {};
97168 function read(key) {
97169 return memoryStorage[key];
97172 function write(key, data) {
97173 memoryStorage[key] = data;
97176 function each(callback) {
97177 for (var key in memoryStorage) {
97178 if (memoryStorage.hasOwnProperty(key)) {
97179 callback(memoryStorage[key], key);
97184 function remove(key) {
97185 delete memoryStorage[key];
97188 function clearAll(key) {
97189 memoryStorage = {};
97192 var all = [// Listed in order of usage preference
97193 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
97195 /* eslint-disable */
97199 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
97200 // See http://www.JSON.org/js.html
97201 // This code should be minified before deployment.
97202 // See http://javascript.crockford.com/jsmin.html
97203 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
97205 // This file creates a global JSON object containing two methods: stringify
97206 // and parse. This file provides the ES5 JSON capability to ES3 systems.
97207 // If a project might run on IE8 or earlier, then this file should be included.
97208 // This file does nothing on ES5 systems.
97209 // JSON.stringify(value, replacer, space)
97210 // value any JavaScript value, usually an object or array.
97211 // replacer an optional parameter that determines how object
97212 // values are stringified for objects. It can be a
97213 // function or an array of strings.
97214 // space an optional parameter that specifies the indentation
97215 // of nested structures. If it is omitted, the text will
97216 // be packed without extra whitespace. If it is a number,
97217 // it will specify the number of spaces to indent at each
97218 // level. If it is a string (such as "\t" or " "),
97219 // it contains the characters used to indent at each level.
97220 // This method produces a JSON text from a JavaScript value.
97221 // When an object value is found, if the object contains a toJSON
97222 // method, its toJSON method will be called and the result will be
97223 // stringified. A toJSON method does not serialize: it returns the
97224 // value represented by the name/value pair that should be serialized,
97225 // or undefined if nothing should be serialized. The toJSON method
97226 // will be passed the key associated with the value, and this will be
97227 // bound to the value.
97228 // For example, this would serialize Dates as ISO strings.
97229 // Date.prototype.toJSON = function (key) {
97231 // // Format integers to have at least two digits.
97236 // return this.getUTCFullYear() + "-" +
97237 // f(this.getUTCMonth() + 1) + "-" +
97238 // f(this.getUTCDate()) + "T" +
97239 // f(this.getUTCHours()) + ":" +
97240 // f(this.getUTCMinutes()) + ":" +
97241 // f(this.getUTCSeconds()) + "Z";
97243 // You can provide an optional replacer method. It will be passed the
97244 // key and value of each member, with this bound to the containing
97245 // object. The value that is returned from your method will be
97246 // serialized. If your method returns undefined, then the member will
97247 // be excluded from the serialization.
97248 // If the replacer parameter is an array of strings, then it will be
97249 // used to select the members to be serialized. It filters the results
97250 // such that only members with keys listed in the replacer array are
97252 // Values that do not have JSON representations, such as undefined or
97253 // functions, will not be serialized. Such values in objects will be
97254 // dropped; in arrays they will be replaced with null. You can use
97255 // a replacer function to replace those with JSON values.
97256 // JSON.stringify(undefined) returns undefined.
97257 // The optional space parameter produces a stringification of the
97258 // value that is filled with line breaks and indentation to make it
97260 // If the space parameter is a non-empty string, then that string will
97261 // be used for indentation. If the space parameter is a number, then
97262 // the indentation will be that many spaces.
97264 // text = JSON.stringify(["e", {pluribus: "unum"}]);
97265 // // text is '["e",{"pluribus":"unum"}]'
97266 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
97267 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
97268 // text = JSON.stringify([new Date()], function (key, value) {
97269 // return this[key] instanceof Date
97270 // ? "Date(" + this[key] + ")"
97273 // // text is '["Date(---current time---)"]'
97274 // JSON.parse(text, reviver)
97275 // This method parses a JSON text to produce an object or array.
97276 // It can throw a SyntaxError exception.
97277 // The optional reviver parameter is a function that can filter and
97278 // transform the results. It receives each of the keys and values,
97279 // and its return value is used instead of the original value.
97280 // If it returns what it received, then the structure is not modified.
97281 // If it returns undefined then the member is deleted.
97283 // // Parse the text. Values that look like ISO date strings will
97284 // // be converted to Date objects.
97285 // myData = JSON.parse(text, function (key, value) {
97287 // if (typeof value === "string") {
97289 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
97291 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
97297 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
97299 // if (typeof value === "string" &&
97300 // value.slice(0, 5) === "Date(" &&
97301 // value.slice(-1) === ")") {
97302 // d = new Date(value.slice(5, -1));
97309 // This is a reference implementation. You are free to copy, modify, or
97317 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
97318 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
97319 lastIndex, length, parse, prototype, push, replace, slice, stringify,
97320 test, toJSON, toString, valueOf
97322 // Create a JSON object only if one does not already exist. We create the
97323 // methods in a closure to avoid creating global variables.
97324 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
97330 var rx_one = /^[\],:{}\s]*$/;
97331 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
97332 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
97333 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
97334 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
97335 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
97338 // Format integers to have at least two digits.
97339 return n < 10 ? "0" + n : n;
97342 function this_value() {
97343 return this.valueOf();
97346 if (typeof Date.prototype.toJSON !== "function") {
97347 Date.prototype.toJSON = function () {
97348 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;
97351 Boolean.prototype.toJSON = this_value;
97352 Number.prototype.toJSON = this_value;
97353 String.prototype.toJSON = this_value;
97361 function quote(string) {
97362 // If the string contains no control characters, no quote characters, and no
97363 // backslash characters, then we can safely slap some quotes around it.
97364 // Otherwise we must also replace the offending characters with safe escape
97366 rx_escapable.lastIndex = 0;
97367 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
97369 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
97370 }) + "\"" : "\"" + string + "\"";
97373 function str(key, holder) {
97374 // Produce a string from holder[key].
97375 var i; // The loop counter.
97377 var k; // The member key.
97379 var v; // The member value.
97384 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
97386 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
97387 value = value.toJSON(key);
97388 } // If we were called with a replacer function, then call the replacer to
97389 // obtain a replacement value.
97392 if (typeof rep === "function") {
97393 value = rep.call(holder, key, value);
97394 } // What happens next depends on the value's type.
97397 switch (_typeof(value)) {
97399 return quote(value);
97402 // JSON numbers must be finite. Encode non-finite numbers as null.
97403 return isFinite(value) ? String(value) : "null";
97407 // If the value is a boolean or null, convert it to a string. Note:
97408 // typeof null does not produce "null". The case is included here in
97409 // the remote chance that this gets fixed someday.
97410 return String(value);
97411 // If the type is "object", we might be dealing with an object or an array or
97415 // Due to a specification blunder in ECMAScript, typeof null is "object",
97416 // so watch out for that case.
97419 } // Make an array to hold the partial results of stringifying this object value.
97423 partial = []; // Is the value an array?
97425 if (Object.prototype.toString.apply(value) === "[object Array]") {
97426 // The value is an array. Stringify every element. Use null as a placeholder
97427 // for non-JSON values.
97428 length = value.length;
97430 for (i = 0; i < length; i += 1) {
97431 partial[i] = str(i, value) || "null";
97432 } // Join all of the elements together, separated with commas, and wrap them in
97436 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
97439 } // If the replacer is an array, use it to select the members to be stringified.
97442 if (rep && _typeof(rep) === "object") {
97443 length = rep.length;
97445 for (i = 0; i < length; i += 1) {
97446 if (typeof rep[i] === "string") {
97451 partial.push(quote(k) + (gap ? ": " : ":") + v);
97456 // Otherwise, iterate through all of the keys in the object.
97458 if (Object.prototype.hasOwnProperty.call(value, k)) {
97462 partial.push(quote(k) + (gap ? ": " : ":") + v);
97466 } // Join all of the member texts together, separated with commas,
97467 // and wrap them in braces.
97470 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
97474 } // If the JSON object does not yet have a stringify method, give it one.
97477 if (typeof JSON.stringify !== "function") {
97479 // table of character substitutions
97489 JSON.stringify = function (value, replacer, space) {
97490 // The stringify method takes a value and an optional replacer, and an optional
97491 // space parameter, and returns a JSON text. The replacer can be a function
97492 // that can replace values, or an array of strings that will select the keys.
97493 // A default replacer method can be provided. Use of the space parameter can
97494 // produce text that is more easily readable.
97497 indent = ""; // If the space parameter is a number, make an indent string containing that
97500 if (typeof space === "number") {
97501 for (i = 0; i < space; i += 1) {
97503 } // If the space parameter is a string, it will be used as the indent string.
97505 } else if (typeof space === "string") {
97507 } // If there is a replacer, it must be a function or an array.
97508 // Otherwise, throw an error.
97513 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
97514 throw new Error("JSON.stringify");
97515 } // Make a fake root object containing our value under the key of "".
97516 // Return the result of stringifying the value.
97523 } // If the JSON object does not yet have a parse method, give it one.
97526 if (typeof JSON.parse !== "function") {
97527 JSON.parse = function (text, reviver) {
97528 // The parse method takes a text and an optional reviver function, and returns
97529 // a JavaScript value if the text is a valid JSON text.
97532 function walk(holder, key) {
97533 // The walk method is used to recursively walk the resulting structure so
97534 // that modifications can be made.
97537 var value = holder[key];
97539 if (value && _typeof(value) === "object") {
97541 if (Object.prototype.hasOwnProperty.call(value, k)) {
97542 v = walk(value, k);
97544 if (v !== undefined) {
97553 return reviver.call(holder, key, value);
97554 } // Parsing happens in four stages. In the first stage, we replace certain
97555 // Unicode characters with escape sequences. JavaScript handles many characters
97556 // incorrectly, either silently deleting them, or treating them as line endings.
97559 text = String(text);
97560 rx_dangerous.lastIndex = 0;
97562 if (rx_dangerous.test(text)) {
97563 text = text.replace(rx_dangerous, function (a) {
97564 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
97566 } // In the second stage, we run the text against regular expressions that look
97567 // for non-JSON patterns. We are especially concerned with "()" and "new"
97568 // because they can cause invocation, and "=" because it can cause mutation.
97569 // But just to be safe, we want to reject all unexpected forms.
97570 // We split the second stage into 4 regexp operations in order to work around
97571 // crippling inefficiencies in IE's and Safari's regexp engines. First we
97572 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
97573 // replace all simple value tokens with "]" characters. Third, we delete all
97574 // open brackets that follow a colon or comma or that begin the text. Finally,
97575 // we look to see that the remaining characters are only whitespace or "]" or
97576 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
97579 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
97580 // In the third stage we use the eval function to compile the text into a
97581 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
97582 // in JavaScript: it can begin a block or an object literal. We wrap the text
97583 // in parens to eliminate the ambiguity.
97584 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
97585 // each name/value pair to a reviver function for possible transformation.
97587 return typeof reviver === "function" ? walk({
97590 } // If the text is not JSON parseable, then a SyntaxError is thrown.
97593 throw new SyntaxError("JSON.parse");
97598 var json2 = json2Plugin;
97600 function json2Plugin() {
97604 var plugins = [json2];
97605 var store_legacy = storeEngine.createStore(all, plugins);
97607 var immutable = extend;
97608 var hasOwnProperty = Object.prototype.hasOwnProperty;
97610 function extend() {
97613 for (var i = 0; i < arguments.length; i++) {
97614 var source = arguments[i];
97616 for (var key in source) {
97617 if (hasOwnProperty.call(source, key)) {
97618 target[key] = source[key];
97627 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
97628 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
97629 // does not support custom headers, which this uses everywhere.
97632 var osmAuth = function osmAuth(o) {
97633 var oauth = {}; // authenticated users will also have a request token secret, but it's
97634 // not used in transactions with the server
97636 oauth.authenticated = function () {
97637 return !!(token('oauth_token') && token('oauth_token_secret'));
97640 oauth.logout = function () {
97641 token('oauth_token', '');
97642 token('oauth_token_secret', '');
97643 token('oauth_request_token_secret', '');
97645 }; // TODO: detect lack of click event
97648 oauth.authenticate = function (callback) {
97649 if (oauth.authenticated()) return callback();
97650 oauth.logout(); // ## Getting a request token
97652 var params = timenonce(getAuth(o)),
97653 url = o.url + '/oauth/request_token';
97654 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
97656 if (!o.singlepage) {
97657 // Create a 600x550 popup window in the center of the screen
97660 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
97661 return x.join('=');
97663 popup = window.open('about:blank', 'oauth_window', settings);
97664 oauth.popupWindow = popup;
97667 var error = new Error('Popup was blocked');
97668 error.status = 'popup-blocked';
97671 } // Request a request token. When this is complete, the popup
97672 // window is redirected to OSM's authorization page.
97675 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
97678 function reqTokenDone(err, xhr) {
97680 if (err) return callback(err);
97681 var resp = ohauth_1.stringQs(xhr.response);
97682 token('oauth_request_token_secret', resp.oauth_token_secret);
97683 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
97684 oauth_token: resp.oauth_token,
97685 oauth_callback: resolveUrl(o.landing)
97688 if (o.singlepage) {
97689 location.href = authorize_url;
97691 popup.location = authorize_url;
97693 } // Called by a function in a landing page, in the popup window. The
97694 // window closes itself.
97697 window.authComplete = function (token) {
97698 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
97699 get_access_token(oauth_token.oauth_token);
97700 delete window.authComplete;
97701 }; // ## Getting an request token
97703 // At this point we have an `oauth_token`, brought in from a function
97704 // call on a landing page popup.
97707 function get_access_token(oauth_token) {
97708 var url = o.url + '/oauth/access_token',
97709 params = timenonce(getAuth(o)),
97710 request_token_secret = token('oauth_request_token_secret');
97711 params.oauth_token = oauth_token;
97712 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
97714 // The final token required for authentication. At this point
97715 // we have a `request token secret`
97717 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
97721 function accessTokenDone(err, xhr) {
97723 if (err) return callback(err);
97724 var access_token = ohauth_1.stringQs(xhr.response);
97725 token('oauth_token', access_token.oauth_token);
97726 token('oauth_token_secret', access_token.oauth_token_secret);
97727 callback(null, oauth);
97731 oauth.bringPopupWindowToFront = function () {
97732 var brougtPopupToFront = false;
97735 // This may cause a cross-origin error:
97736 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
97737 if (oauth.popupWindow && !oauth.popupWindow.closed) {
97738 oauth.popupWindow.focus();
97739 brougtPopupToFront = true;
97741 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
97744 return brougtPopupToFront;
97747 oauth.bootstrapToken = function (oauth_token, callback) {
97748 // ## Getting an request token
97749 // At this point we have an `oauth_token`, brought in from a function
97750 // call on a landing page popup.
97751 function get_access_token(oauth_token) {
97752 var url = o.url + '/oauth/access_token',
97753 params = timenonce(getAuth(o)),
97754 request_token_secret = token('oauth_request_token_secret');
97755 params.oauth_token = oauth_token;
97756 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
97757 // The final token required for authentication. At this point
97758 // we have a `request token secret`
97760 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
97764 function accessTokenDone(err, xhr) {
97766 if (err) return callback(err);
97767 var access_token = ohauth_1.stringQs(xhr.response);
97768 token('oauth_token', access_token.oauth_token);
97769 token('oauth_token_secret', access_token.oauth_token_secret);
97770 callback(null, oauth);
97773 get_access_token(oauth_token);
97776 // A single XMLHttpRequest wrapper that does authenticated calls if the
97777 // user has logged in.
97780 oauth.xhr = function (options, callback) {
97781 if (!oauth.authenticated()) {
97783 return oauth.authenticate(run);
97785 callback('not authenticated', null);
97793 var params = timenonce(getAuth(o)),
97794 oauth_token_secret = token('oauth_token_secret'),
97795 url = options.prefix !== false ? o.url + options.path : options.path,
97796 url_parts = url.replace(/#.*$/, '').split('?', 2),
97797 base_url = url_parts[0],
97798 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
97800 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
97801 params = immutable(params, ohauth_1.stringQs(options.content));
97804 params.oauth_token = token('oauth_token');
97805 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))));
97806 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
97809 function done(err, xhr) {
97810 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
97812 }; // pre-authorize this object, if we can just get a token and token_secret
97816 oauth.preauth = function (c) {
97818 if (c.oauth_token) token('oauth_token', c.oauth_token);
97819 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
97823 oauth.options = function (_) {
97824 if (!arguments.length) return o;
97826 o.url = o.url || 'https://www.openstreetmap.org';
97827 o.landing = o.landing || 'land.html';
97828 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
97829 // by default, no-ops
97831 o.loading = o.loading || function () {};
97833 o.done = o.done || function () {};
97835 return oauth.preauth(o);
97836 }; // 'stamp' an authentication object from `getAuth()`
97837 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
97841 function timenonce(o) {
97842 o.oauth_timestamp = ohauth_1.timestamp();
97843 o.oauth_nonce = ohauth_1.nonce();
97845 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
97846 // can be used with multiple APIs and the keys in `localStorage`
97852 if (store_legacy.enabled) {
97853 token = function token(x, y) {
97854 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
97859 token = function token(x, y) {
97860 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
97862 } // Get an authentication object. If you just add and remove properties
97863 // from a single object, you'll need to use `delete` to make sure that
97864 // it doesn't contain undesired properties for authentication
97867 function getAuth(o) {
97869 oauth_consumer_key: o.oauth_consumer_key,
97870 oauth_signature_method: 'HMAC-SHA1'
97872 } // potentially pre-authorize
97879 var tiler$2 = utilTiler();
97880 var dispatch$2 = dispatch$8('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
97881 var urlroot = 'https://www.openstreetmap.org';
97882 var oauth = osmAuth({
97884 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
97885 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
97886 loading: authLoading,
97888 }); // hardcode default block of Google Maps
97890 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
97912 var _cachedApiStatus;
97914 var _changeset = {};
97916 var _deferred = new Set();
97918 var _connectionID = 1;
97919 var _tileZoom = 16;
97920 var _noteZoom = 12;
97922 var _rateLimitError;
97924 var _userChangesets;
97928 var _off; // set a default but also load this from the API status
97931 var _maxWayNodes = 2000;
97933 function authLoading() {
97934 dispatch$2.call('authLoading');
97937 function authDone() {
97938 dispatch$2.call('authDone');
97941 function abortRequest$2(controllerOrXHR) {
97942 if (controllerOrXHR) {
97943 controllerOrXHR.abort();
97947 function hasInflightRequests(cache) {
97948 return Object.keys(cache.inflight).length;
97951 function abortUnwantedRequests(cache, visibleTiles) {
97952 Object.keys(cache.inflight).forEach(function (k) {
97953 if (cache.toLoad[k]) return;
97954 if (visibleTiles.find(function (tile) {
97955 return k === tile.id;
97957 abortRequest$2(cache.inflight[k]);
97958 delete cache.inflight[k];
97962 function getLoc(attrs) {
97963 var lon = attrs.lon && attrs.lon.value;
97964 var lat = attrs.lat && attrs.lat.value;
97965 return [parseFloat(lon), parseFloat(lat)];
97968 function getNodes(obj) {
97969 var elems = obj.getElementsByTagName('nd');
97970 var nodes = new Array(elems.length);
97972 for (var i = 0, l = elems.length; i < l; i++) {
97973 nodes[i] = 'n' + elems[i].attributes.ref.value;
97979 function getNodesJSON(obj) {
97980 var elems = obj.nodes;
97981 var nodes = new Array(elems.length);
97983 for (var i = 0, l = elems.length; i < l; i++) {
97984 nodes[i] = 'n' + elems[i];
97990 function getTags(obj) {
97991 var elems = obj.getElementsByTagName('tag');
97994 for (var i = 0, l = elems.length; i < l; i++) {
97995 var attrs = elems[i].attributes;
97996 tags[attrs.k.value] = attrs.v.value;
98002 function getMembers(obj) {
98003 var elems = obj.getElementsByTagName('member');
98004 var members = new Array(elems.length);
98006 for (var i = 0, l = elems.length; i < l; i++) {
98007 var attrs = elems[i].attributes;
98009 id: attrs.type.value[0] + attrs.ref.value,
98010 type: attrs.type.value,
98011 role: attrs.role.value
98018 function getMembersJSON(obj) {
98019 var elems = obj.members;
98020 var members = new Array(elems.length);
98022 for (var i = 0, l = elems.length; i < l; i++) {
98023 var attrs = elems[i];
98025 id: attrs.type[0] + attrs.ref,
98034 function getVisible(attrs) {
98035 return !attrs.visible || attrs.visible.value !== 'false';
98038 function parseComments(comments) {
98039 var parsedComments = []; // for each comment
98041 for (var i = 0; i < comments.length; i++) {
98042 var comment = comments[i];
98044 if (comment.nodeName === 'comment') {
98045 var childNodes = comment.childNodes;
98046 var parsedComment = {};
98048 for (var j = 0; j < childNodes.length; j++) {
98049 var node = childNodes[j];
98050 var nodeName = node.nodeName;
98051 if (nodeName === '#text') continue;
98052 parsedComment[nodeName] = node.textContent;
98054 if (nodeName === 'uid') {
98055 var uid = node.textContent;
98057 if (uid && !_userCache.user[uid]) {
98058 _userCache.toLoad[uid] = true;
98063 if (parsedComment) {
98064 parsedComments.push(parsedComment);
98069 return parsedComments;
98072 function encodeNoteRtree(note) {
98082 var jsonparsers = {
98083 node: function nodeData(obj, uid) {
98084 return new osmNode({
98086 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
98087 version: obj.version && obj.version.toString(),
98088 changeset: obj.changeset && obj.changeset.toString(),
98089 timestamp: obj.timestamp,
98091 uid: obj.uid && obj.uid.toString(),
98092 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
98096 way: function wayData(obj, uid) {
98097 return new osmWay({
98099 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
98100 version: obj.version && obj.version.toString(),
98101 changeset: obj.changeset && obj.changeset.toString(),
98102 timestamp: obj.timestamp,
98104 uid: obj.uid && obj.uid.toString(),
98106 nodes: getNodesJSON(obj)
98109 relation: function relationData(obj, uid) {
98110 return new osmRelation({
98112 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
98113 version: obj.version && obj.version.toString(),
98114 changeset: obj.changeset && obj.changeset.toString(),
98115 timestamp: obj.timestamp,
98117 uid: obj.uid && obj.uid.toString(),
98119 members: getMembersJSON(obj)
98122 user: function parseUser(obj, uid) {
98125 display_name: obj.display_name,
98126 account_created: obj.account_created,
98127 image_url: obj.img && obj.img.href,
98128 changesets_count: obj.changesets && obj.changesets.count && obj.changesets.count.toString() || '0',
98129 active_blocks: obj.blocks && obj.blocks.received && obj.blocks.received.active && obj.blocks.received.active.toString() || '0'
98134 function parseJSON(payload, callback, options) {
98135 options = Object.assign({
98141 message: 'No JSON',
98146 var json = payload;
98147 if (_typeof(json) !== 'object') json = JSON.parse(payload);
98148 if (!json.elements) return callback({
98149 message: 'No JSON',
98152 var children = json.elements;
98153 var handle = window.requestIdleCallback(function () {
98154 _deferred["delete"](handle);
98159 for (var i = 0; i < children.length; i++) {
98160 result = parseChild(children[i]);
98161 if (result) results.push(result);
98164 callback(null, results);
98167 _deferred.add(handle);
98169 function parseChild(child) {
98170 var parser = jsonparsers[child.type];
98171 if (!parser) return null;
98173 uid = osmEntity.id.fromOSM(child.type, child.id);
98175 if (options.skipSeen) {
98176 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
98178 _tileCache.seen[uid] = true;
98181 return parser(child, uid);
98185 function parseUserJSON(payload, callback, options) {
98186 options = Object.assign({
98192 message: 'No JSON',
98197 var json = payload;
98198 if (_typeof(json) !== 'object') json = JSON.parse(payload);
98199 if (!json.users && !json.user) return callback({
98200 message: 'No JSON',
98203 var objs = json.users || [json];
98204 var handle = window.requestIdleCallback(function () {
98205 _deferred["delete"](handle);
98210 for (var i = 0; i < objs.length; i++) {
98211 result = parseObj(objs[i]);
98212 if (result) results.push(result);
98215 callback(null, results);
98218 _deferred.add(handle);
98220 function parseObj(obj) {
98221 var uid = obj.user.id && obj.user.id.toString();
98223 if (options.skipSeen && _userCache.user[uid]) {
98224 delete _userCache.toLoad[uid];
98228 var user = jsonparsers.user(obj.user, uid);
98229 _userCache.user[uid] = user;
98230 delete _userCache.toLoad[uid];
98236 node: function nodeData(obj, uid) {
98237 var attrs = obj.attributes;
98238 return new osmNode({
98240 visible: getVisible(attrs),
98241 version: attrs.version.value,
98242 changeset: attrs.changeset && attrs.changeset.value,
98243 timestamp: attrs.timestamp && attrs.timestamp.value,
98244 user: attrs.user && attrs.user.value,
98245 uid: attrs.uid && attrs.uid.value,
98246 loc: getLoc(attrs),
98250 way: function wayData(obj, uid) {
98251 var attrs = obj.attributes;
98252 return new osmWay({
98254 visible: getVisible(attrs),
98255 version: attrs.version.value,
98256 changeset: attrs.changeset && attrs.changeset.value,
98257 timestamp: attrs.timestamp && attrs.timestamp.value,
98258 user: attrs.user && attrs.user.value,
98259 uid: attrs.uid && attrs.uid.value,
98260 tags: getTags(obj),
98261 nodes: getNodes(obj)
98264 relation: function relationData(obj, uid) {
98265 var attrs = obj.attributes;
98266 return new osmRelation({
98268 visible: getVisible(attrs),
98269 version: attrs.version.value,
98270 changeset: attrs.changeset && attrs.changeset.value,
98271 timestamp: attrs.timestamp && attrs.timestamp.value,
98272 user: attrs.user && attrs.user.value,
98273 uid: attrs.uid && attrs.uid.value,
98274 tags: getTags(obj),
98275 members: getMembers(obj)
98278 note: function parseNote(obj, uid) {
98279 var attrs = obj.attributes;
98280 var childNodes = obj.childNodes;
98283 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
98285 var coincident = false;
98286 var epsilon = 0.00001;
98290 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
98293 var bbox = geoExtent(props.loc).bbox();
98294 coincident = _noteCache.rtree.search(bbox).length;
98295 } while (coincident); // parse note contents
98298 for (var i = 0; i < childNodes.length; i++) {
98299 var node = childNodes[i];
98300 var nodeName = node.nodeName;
98301 if (nodeName === '#text') continue; // if the element is comments, parse the comments
98303 if (nodeName === 'comments') {
98304 props[nodeName] = parseComments(node.childNodes);
98306 props[nodeName] = node.textContent;
98310 var note = new osmNote(props);
98311 var item = encodeNoteRtree(note);
98312 _noteCache.note[note.id] = note;
98314 _noteCache.rtree.insert(item);
98318 user: function parseUser(obj, uid) {
98319 var attrs = obj.attributes;
98322 display_name: attrs.display_name && attrs.display_name.value,
98323 account_created: attrs.account_created && attrs.account_created.value,
98324 changesets_count: '0',
98327 var img = obj.getElementsByTagName('img');
98329 if (img && img[0] && img[0].getAttribute('href')) {
98330 user.image_url = img[0].getAttribute('href');
98333 var changesets = obj.getElementsByTagName('changesets');
98335 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
98336 user.changesets_count = changesets[0].getAttribute('count');
98339 var blocks = obj.getElementsByTagName('blocks');
98341 if (blocks && blocks[0]) {
98342 var received = blocks[0].getElementsByTagName('received');
98344 if (received && received[0] && received[0].getAttribute('active')) {
98345 user.active_blocks = received[0].getAttribute('active');
98349 _userCache.user[uid] = user;
98350 delete _userCache.toLoad[uid];
98355 function parseXML(xml, callback, options) {
98356 options = Object.assign({
98360 if (!xml || !xml.childNodes) {
98367 var root = xml.childNodes[0];
98368 var children = root.childNodes;
98369 var handle = window.requestIdleCallback(function () {
98370 _deferred["delete"](handle);
98375 for (var i = 0; i < children.length; i++) {
98376 result = parseChild(children[i]);
98377 if (result) results.push(result);
98380 callback(null, results);
98383 _deferred.add(handle);
98385 function parseChild(child) {
98386 var parser = parsers[child.nodeName];
98387 if (!parser) return null;
98390 if (child.nodeName === 'user') {
98391 uid = child.attributes.id.value;
98393 if (options.skipSeen && _userCache.user[uid]) {
98394 delete _userCache.toLoad[uid];
98397 } else if (child.nodeName === 'note') {
98398 uid = child.getElementsByTagName('id')[0].textContent;
98400 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
98402 if (options.skipSeen) {
98403 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
98405 _tileCache.seen[uid] = true;
98409 return parser(child, uid);
98411 } // replace or remove note from rtree
98414 function updateRtree(item, replace) {
98415 _noteCache.rtree.remove(item, function isEql(a, b) {
98416 return a.data.id === b.data.id;
98420 _noteCache.rtree.insert(item);
98424 function wrapcb(thisArg, callback, cid) {
98425 return function (err, result) {
98427 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
98428 if (err.status === 400 || err.status === 401 || err.status === 403) {
98432 return callback.call(thisArg, err);
98433 } else if (thisArg.getConnectionId() !== cid) {
98434 return callback.call(thisArg, {
98435 message: 'Connection Switched',
98439 return callback.call(thisArg, err, result);
98445 init: function init() {
98446 utilRebind(this, dispatch$2, 'on');
98448 reset: function reset() {
98449 Array.from(_deferred).forEach(function (handle) {
98450 window.cancelIdleCallback(handle);
98452 _deferred["delete"](handle);
98455 _userChangesets = undefined;
98456 _userDetails = undefined;
98457 _rateLimitError = undefined;
98458 Object.values(_tileCache.inflight).forEach(abortRequest$2);
98459 Object.values(_noteCache.inflight).forEach(abortRequest$2);
98460 Object.values(_noteCache.inflightPost).forEach(abortRequest$2);
98461 if (_changeset.inflight) abortRequest$2(_changeset.inflight);
98482 _cachedApiStatus = undefined;
98486 getConnectionId: function getConnectionId() {
98487 return _connectionID;
98489 changesetURL: function changesetURL(changesetID) {
98490 return urlroot + '/changeset/' + changesetID;
98492 changesetsURL: function changesetsURL(center, zoom) {
98493 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
98494 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
98496 entityURL: function entityURL(entity) {
98497 return urlroot + '/' + entity.type + '/' + entity.osmId();
98499 historyURL: function historyURL(entity) {
98500 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
98502 userURL: function userURL(username) {
98503 return urlroot + '/user/' + username;
98505 noteURL: function noteURL(note) {
98506 return urlroot + '/note/' + note.id;
98508 noteReportURL: function noteReportURL(note) {
98509 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
98511 // Generic method to load data from the OSM API
98512 // Can handle either auth or unauth calls.
98513 loadFromAPI: function loadFromAPI(path, callback, options) {
98514 options = Object.assign({
98518 var cid = _connectionID;
98520 function done(err, payload) {
98521 if (that.getConnectionId() !== cid) {
98522 if (callback) callback({
98523 message: 'Connection Switched',
98529 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
98530 // Logout and retry the request..
98532 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
98534 that.loadFromAPI(path, callback, options); // else, no retry..
98536 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
98537 // Set the rateLimitError flag and trigger a warning..
98538 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
98539 _rateLimitError = err;
98540 dispatch$2.call('change');
98541 that.reloadApiStatus();
98542 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
98543 // If the response's error state doesn't match the status,
98544 // it's likely we lost or gained the connection so reload the status
98545 that.reloadApiStatus();
98550 return callback(err);
98552 if (path.indexOf('.json') !== -1) {
98553 return parseJSON(payload, callback, options);
98555 return parseXML(payload, callback, options);
98562 if (this.authenticated()) {
98568 var url = urlroot + path;
98569 var controller = new AbortController();
98572 if (path.indexOf('.json') !== -1) {
98579 signal: controller.signal
98580 }).then(function (data) {
98582 })["catch"](function (err) {
98583 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
98584 // but we can't access the response itself
98585 // https://github.com/d3/d3-fetch/issues/27
98587 var match = err.message.match(/^\d{3}/);
98592 statusText: err.message
98601 // Load a single entity by id (ways and relations use the `/full` call to include
98602 // nodes and members). Parent relations are not included, see `loadEntityRelations`.
98603 // GET /api/0.6/node/#id
98604 // GET /api/0.6/[way|relation]/#id/full
98605 loadEntity: function loadEntity(id, callback) {
98606 var type = osmEntity.id.type(id);
98607 var osmID = osmEntity.id.toOSM(id);
98611 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
98612 if (callback) callback(err, {
98617 // Load a single entity with a specific version
98618 // GET /api/0.6/[node|way|relation]/#id/#version
98619 loadEntityVersion: function loadEntityVersion(id, version, callback) {
98620 var type = osmEntity.id.type(id);
98621 var osmID = osmEntity.id.toOSM(id);
98625 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
98626 if (callback) callback(err, {
98631 // Load the relations of a single entity with the given.
98632 // GET /api/0.6/[node|way|relation]/#id/relations
98633 loadEntityRelations: function loadEntityRelations(id, callback) {
98634 var type = osmEntity.id.type(id);
98635 var osmID = osmEntity.id.toOSM(id);
98639 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/relations.json', function (err, entities) {
98640 if (callback) callback(err, {
98645 // Load multiple entities in chunks
98646 // (note: callback may be called multiple times)
98647 // Unlike `loadEntity`, child nodes and members are not fetched
98648 // GET /api/0.6/[nodes|ways|relations]?#parameters
98649 loadMultiple: function loadMultiple(ids, callback) {
98651 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
98652 Object.keys(groups).forEach(function (k) {
98653 var type = k + 's'; // nodes, ways, relations
98655 var osmIDs = groups[k].map(function (id) {
98656 return osmEntity.id.toOSM(id);
98661 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
98662 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
98663 if (callback) callback(err, {
98670 // Create, upload, and close a changeset
98671 // PUT /api/0.6/changeset/create
98672 // POST /api/0.6/changeset/#id/upload
98673 // PUT /api/0.6/changeset/#id/close
98674 putChangeset: function putChangeset(changeset, changes, callback) {
98675 var cid = _connectionID;
98677 if (_changeset.inflight) {
98679 message: 'Changeset already inflight',
98682 } else if (_changeset.open) {
98683 // reuse existing open changeset..
98684 return createdChangeset.call(this, null, _changeset.open);
98686 // Open a new changeset..
98689 path: '/api/0.6/changeset/create',
98692 'Content-Type': 'text/xml'
98695 content: JXON.stringify(changeset.asJXON())
98697 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
98700 function createdChangeset(err, changesetID) {
98701 _changeset.inflight = null;
98704 return callback(err, changeset);
98707 _changeset.open = changesetID;
98708 changeset = changeset.update({
98710 }); // Upload the changeset..
98714 path: '/api/0.6/changeset/' + changesetID + '/upload',
98717 'Content-Type': 'text/xml'
98720 content: JXON.stringify(changeset.osmChangeJXON(changes))
98722 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
98725 function uploadedChangeset(err) {
98726 _changeset.inflight = null;
98727 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
98728 // Add delay to allow for postgres replication #1646 #2678
98730 window.setTimeout(function () {
98731 callback(null, changeset);
98733 _changeset.open = null; // At this point, we don't really care if the connection was switched..
98734 // Only try to close the changeset if we're still talking to the same server.
98736 if (this.getConnectionId() === cid) {
98737 // Still attempt to close changeset, but ignore response because #2667
98740 path: '/api/0.6/changeset/' + changeset.id + '/close',
98743 'Content-Type': 'text/xml'
98752 // Load multiple users in chunks
98753 // (note: callback may be called multiple times)
98754 // GET /api/0.6/users?users=#id1,#id2,...,#idn
98755 loadUsers: function loadUsers(uids, callback) {
98758 utilArrayUniq(uids).forEach(function (uid) {
98759 if (_userCache.user[uid]) {
98760 delete _userCache.toLoad[uid];
98761 cached.push(_userCache.user[uid]);
98767 if (cached.length || !this.authenticated()) {
98768 callback(undefined, cached);
98769 if (!this.authenticated()) return; // require auth
98772 utilArrayChunk(toLoad, 150).forEach(function (arr) {
98775 path: '/api/0.6/users.json?users=' + arr.join()
98776 }, wrapcb(this, done, _connectionID));
98779 function done(err, payload) {
98780 if (err) return callback(err);
98784 return parseUserJSON(payload, function (err, results) {
98785 if (err) return callback(err);
98786 return callback(undefined, results);
98790 // Load a given user by id
98791 // GET /api/0.6/user/#id
98792 loadUser: function loadUser(uid, callback) {
98793 if (_userCache.user[uid] || !this.authenticated()) {
98795 delete _userCache.toLoad[uid];
98796 return callback(undefined, _userCache.user[uid]);
98801 path: '/api/0.6/user/' + uid + '.json'
98802 }, wrapcb(this, done, _connectionID));
98804 function done(err, payload) {
98805 if (err) return callback(err);
98809 return parseUserJSON(payload, function (err, results) {
98810 if (err) return callback(err);
98811 return callback(undefined, results[0]);
98815 // Load the details of the logged-in user
98816 // GET /api/0.6/user/details
98817 userDetails: function userDetails(callback) {
98818 if (_userDetails) {
98820 return callback(undefined, _userDetails);
98825 path: '/api/0.6/user/details.json'
98826 }, wrapcb(this, done, _connectionID));
98828 function done(err, payload) {
98829 if (err) return callback(err);
98833 return parseUserJSON(payload, function (err, results) {
98834 if (err) return callback(err);
98835 _userDetails = results[0];
98836 return callback(undefined, _userDetails);
98840 // Load previous changesets for the logged in user
98841 // GET /api/0.6/changesets?user=#id
98842 userChangesets: function userChangesets(callback) {
98843 if (_userChangesets) {
98845 return callback(undefined, _userChangesets);
98848 this.userDetails(wrapcb(this, gotDetails, _connectionID));
98850 function gotDetails(err, user) {
98852 return callback(err);
98857 path: '/api/0.6/changesets?user=' + user.id
98858 }, wrapcb(this, done, _connectionID));
98861 function done(err, xml) {
98863 return callback(err);
98866 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
98868 tags: getTags(changeset)
98870 }).filter(function (changeset) {
98871 var comment = changeset.tags.comment;
98872 return comment && comment !== '';
98874 return callback(undefined, _userChangesets);
98877 // Fetch the status of the OSM API
98878 // GET /api/capabilities
98879 status: function status(callback) {
98880 var url = urlroot + '/api/capabilities';
98881 var errback = wrapcb(this, done, _connectionID);
98882 d3_xml(url).then(function (data) {
98883 errback(null, data);
98884 })["catch"](function (err) {
98885 errback(err.message);
98888 function done(err, xml) {
98890 // the status is null if no response could be retrieved
98891 return callback(err, null);
98892 } // update blocklists
98895 var elements = xml.getElementsByTagName('blacklist');
98898 for (var i = 0; i < elements.length; i++) {
98899 var regexString = elements[i].getAttribute('regex'); // needs unencode?
98903 var regex = new RegExp(regexString);
98904 regexes.push(regex);
98911 if (regexes.length) {
98912 _imageryBlocklists = regexes;
98915 if (_rateLimitError) {
98916 return callback(_rateLimitError, 'rateLimited');
98918 var waynodes = xml.getElementsByTagName('waynodes');
98919 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
98920 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
98921 var apiStatus = xml.getElementsByTagName('status');
98922 var val = apiStatus[0].getAttribute('api');
98923 return callback(undefined, val);
98927 // Calls `status` and dispatches an `apiStatusChange` event if the returned
98928 // status differs from the cached status.
98929 reloadApiStatus: function reloadApiStatus() {
98930 // throttle to avoid unnecessary API calls
98931 if (!this.throttledReloadApiStatus) {
98933 this.throttledReloadApiStatus = throttle(function () {
98934 that.status(function (err, status) {
98935 if (status !== _cachedApiStatus) {
98936 _cachedApiStatus = status;
98937 dispatch$2.call('apiStatusChange', that, err, status);
98943 this.throttledReloadApiStatus();
98945 // Returns the maximum number of nodes a single way can have
98946 maxWayNodes: function maxWayNodes() {
98947 return _maxWayNodes;
98949 // Load data (entities) from the API in tiles
98950 // GET /api/0.6/map?bbox=
98951 loadTiles: function loadTiles(projection, callback) {
98952 if (_off) return; // determine the needed tiles to cover the view
98954 var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
98956 var hadRequests = hasInflightRequests(_tileCache);
98957 abortUnwantedRequests(_tileCache, tiles);
98959 if (hadRequests && !hasInflightRequests(_tileCache)) {
98960 dispatch$2.call('loaded'); // stop the spinner
98961 } // issue new requests..
98964 tiles.forEach(function (tile) {
98965 this.loadTile(tile, callback);
98968 // Load a single data tile
98969 // GET /api/0.6/map?bbox=
98970 loadTile: function loadTile(tile, callback) {
98972 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
98974 if (!hasInflightRequests(_tileCache)) {
98975 dispatch$2.call('loading'); // start the spinner
98978 var path = '/api/0.6/map.json?bbox=';
98982 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
98984 function tileCallback(err, parsed) {
98985 delete _tileCache.inflight[tile.id];
98988 delete _tileCache.toLoad[tile.id];
98989 _tileCache.loaded[tile.id] = true;
98990 var bbox = tile.extent.bbox();
98993 _tileCache.rtree.insert(bbox);
98997 callback(err, Object.assign({
99002 if (!hasInflightRequests(_tileCache)) {
99003 dispatch$2.call('loaded'); // stop the spinner
99007 isDataLoaded: function isDataLoaded(loc) {
99014 return _tileCache.rtree.collides(bbox);
99016 // load the tile that covers the given `loc`
99017 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
99018 // Back off if the toLoad queue is filling up.. re #6417
99019 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
99020 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
99021 if (Object.keys(_tileCache.toLoad).length > 50) return;
99022 var k = geoZoomToScale(_tileZoom + 1);
99023 var offset = geoRawMercator().scale(k)(loc);
99024 var projection = geoRawMercator().transform({
99029 var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection);
99030 tiles.forEach(function (tile) {
99031 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
99032 _tileCache.toLoad[tile.id] = true;
99033 this.loadTile(tile, callback);
99036 // Load notes from the API in tiles
99037 // GET /api/0.6/notes?bbox=
99038 loadNotes: function loadNotes(projection, noteOptions) {
99039 noteOptions = Object.assign({
99045 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
99047 var throttleLoadUsers = throttle(function () {
99048 var uids = Object.keys(_userCache.toLoad);
99049 if (!uids.length) return;
99050 that.loadUsers(uids, function () {}); // eagerly load user details
99051 }, 750); // determine the needed tiles to cover the view
99054 var tiles = tiler$2.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
99056 abortUnwantedRequests(_noteCache, tiles); // issue new requests..
99058 tiles.forEach(function (tile) {
99059 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
99063 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
99064 delete _noteCache.inflight[tile.id];
99067 _noteCache.loaded[tile.id] = true;
99070 throttleLoadUsers();
99071 dispatch$2.call('loadedNotes');
99076 // POST /api/0.6/notes?params
99077 postNoteCreate: function postNoteCreate(note, callback) {
99078 if (!this.authenticated()) {
99080 message: 'Not Authenticated',
99085 if (_noteCache.inflightPost[note.id]) {
99087 message: 'Note update already inflight',
99092 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
99094 var comment = note.newComment;
99096 if (note.newCategory && note.newCategory !== 'None') {
99097 comment += ' #' + note.newCategory;
99100 var path = '/api/0.6/notes?' + utilQsString({
99105 _noteCache.inflightPost[note.id] = oauth.xhr({
99108 }, wrapcb(this, done, _connectionID));
99110 function done(err, xml) {
99111 delete _noteCache.inflightPost[note.id];
99114 return callback(err);
99115 } // we get the updated note back, remove from caches and reparse..
99118 this.removeNote(note);
99122 return parseXML(xml, function (err, results) {
99124 return callback(err);
99126 return callback(undefined, results[0]);
99132 // POST /api/0.6/notes/#id/comment?text=comment
99133 // POST /api/0.6/notes/#id/close?text=comment
99134 // POST /api/0.6/notes/#id/reopen?text=comment
99135 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
99136 if (!this.authenticated()) {
99138 message: 'Not Authenticated',
99143 if (_noteCache.inflightPost[note.id]) {
99145 message: 'Note update already inflight',
99152 if (note.status !== 'closed' && newStatus === 'closed') {
99154 } else if (note.status !== 'open' && newStatus === 'open') {
99157 action = 'comment';
99158 if (!note.newComment) return; // when commenting, comment required
99161 var path = '/api/0.6/notes/' + note.id + '/' + action;
99163 if (note.newComment) {
99164 path += '?' + utilQsString({
99165 text: note.newComment
99169 _noteCache.inflightPost[note.id] = oauth.xhr({
99172 }, wrapcb(this, done, _connectionID));
99174 function done(err, xml) {
99175 delete _noteCache.inflightPost[note.id];
99178 return callback(err);
99179 } // we get the updated note back, remove from caches and reparse..
99182 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
99184 if (action === 'close') {
99185 _noteCache.closed[note.id] = true;
99186 } else if (action === 'reopen') {
99187 delete _noteCache.closed[note.id];
99193 return parseXML(xml, function (err, results) {
99195 return callback(err);
99197 return callback(undefined, results[0]);
99202 "switch": function _switch(options) {
99203 urlroot = options.urlroot;
99204 oauth.options(Object.assign({
99206 loading: authLoading,
99210 this.userChangesets(function () {}); // eagerly load user details/changesets
99212 dispatch$2.call('change');
99215 toggle: function toggle(val) {
99219 isChangesetInflight: function isChangesetInflight() {
99220 return !!_changeset.inflight;
99222 // get/set cached data
99223 // This is used to save/restore the state when entering/exiting the walkthrough
99224 // Also used for testing purposes.
99225 caches: function caches(obj) {
99226 function cloneCache(source) {
99228 Object.keys(source).forEach(function (k) {
99229 if (k === 'rtree') {
99230 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
99231 } else if (k === 'note') {
99233 Object.keys(source.note).forEach(function (id) {
99234 target.note[id] = osmNote(source.note[id]); // copy notes
99237 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
99243 if (!arguments.length) {
99245 tile: cloneCache(_tileCache),
99246 note: cloneCache(_noteCache),
99247 user: cloneCache(_userCache)
99249 } // access caches directly for testing (e.g., loading notes rtree)
99252 if (obj === 'get') {
99261 _tileCache = obj.tile;
99262 _tileCache.inflight = {};
99266 _noteCache = obj.note;
99267 _noteCache.inflight = {};
99268 _noteCache.inflightPost = {};
99272 _userCache = obj.user;
99277 logout: function logout() {
99278 _userChangesets = undefined;
99279 _userDetails = undefined;
99281 dispatch$2.call('change');
99284 authenticated: function authenticated() {
99285 return oauth.authenticated();
99287 authenticate: function authenticate(callback) {
99289 var cid = _connectionID;
99290 _userChangesets = undefined;
99291 _userDetails = undefined;
99293 function done(err, res) {
99295 if (callback) callback(err);
99299 if (that.getConnectionId() !== cid) {
99300 if (callback) callback({
99301 message: 'Connection Switched',
99307 _rateLimitError = undefined;
99308 dispatch$2.call('change');
99309 if (callback) callback(err, res);
99310 that.userChangesets(function () {}); // eagerly load user details/changesets
99313 return oauth.authenticate(done);
99315 imageryBlocklists: function imageryBlocklists() {
99316 return _imageryBlocklists;
99318 tileZoom: function tileZoom(val) {
99319 if (!arguments.length) return _tileZoom;
99323 // get all cached notes covering the viewport
99324 notes: function notes(projection) {
99325 var viewport = projection.clipExtent();
99326 var min = [viewport[0][0], viewport[1][1]];
99327 var max = [viewport[1][0], viewport[0][1]];
99328 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
99329 return _noteCache.rtree.search(bbox).map(function (d) {
99333 // get a single note from the cache
99334 getNote: function getNote(id) {
99335 return _noteCache.note[id];
99337 // remove a single note from the cache
99338 removeNote: function removeNote(note) {
99339 if (!(note instanceof osmNote) || !note.id) return;
99340 delete _noteCache.note[note.id];
99341 updateRtree(encodeNoteRtree(note), false); // false = remove
99343 // replace a single note in the cache
99344 replaceNote: function replaceNote(note) {
99345 if (!(note instanceof osmNote) || !note.id) return;
99346 _noteCache.note[note.id] = note;
99347 updateRtree(encodeNoteRtree(note), true); // true = replace
99351 // Get an array of note IDs closed during this session.
99352 // Used to populate `closed:note` changeset tag
99353 getClosedIDs: function getClosedIDs() {
99354 return Object.keys(_noteCache.closed).sort();
99358 var _apibase$1 = 'https://wiki.openstreetmap.org/w/api.php';
99359 var _inflight$1 = {};
99360 var _wikibaseCache = {};
99365 var debouncedRequest$1 = debounce(request$1, 500, {
99369 function request$1(url, callback) {
99370 if (_inflight$1[url]) return;
99371 var controller = new AbortController();
99372 _inflight$1[url] = controller;
99374 signal: controller.signal
99375 }).then(function (result) {
99376 delete _inflight$1[url];
99377 if (callback) callback(null, result);
99378 })["catch"](function (err) {
99379 delete _inflight$1[url];
99380 if (err.name === 'AbortError') return;
99381 if (callback) callback(err.message);
99385 var serviceOsmWikibase = {
99386 init: function init() {
99388 _wikibaseCache = {};
99391 reset: function reset() {
99392 Object.values(_inflight$1).forEach(function (controller) {
99393 controller.abort();
99399 * Get the best value for the property, or undefined if not found
99400 * @param entity object from wikibase
99401 * @param property string e.g. 'P4' for image
99402 * @param langCode string e.g. 'fr' for French
99404 claimToValue: function claimToValue(entity, property, langCode) {
99405 if (!entity.claims[property]) return undefined;
99406 var locale = _localeIDs[langCode];
99407 var preferredPick, localePick;
99408 entity.claims[property].forEach(function (stmt) {
99409 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
99410 // Or if not found, use the first value with the "preferred" rank
99411 if (!preferredPick && stmt.rank === 'preferred') {
99412 preferredPick = stmt;
99415 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
99419 var result = localePick || preferredPick;
99422 var datavalue = result.mainsnak.datavalue;
99423 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
99430 * Convert monolingual property into a key-value object (language -> value)
99431 * @param entity object from wikibase
99432 * @param property string e.g. 'P31' for monolingual wiki page title
99434 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
99435 if (!entity || !entity.claims[property]) return undefined;
99436 return entity.claims[property].reduce(function (acc, obj) {
99437 var value = obj.mainsnak.datavalue.value;
99438 acc[value.language] = value.text;
99442 toSitelink: function toSitelink(key, value) {
99443 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
99444 return result.replace(/_/g, ' ').trim();
99447 // Pass params object of the form:
99450 // value: 'string',
99451 // langCode: 'string'
99454 getEntity: function getEntity(params, callback) {
99455 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
99459 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
99460 var keySitelink = params.key ? this.toSitelink(params.key) : false;
99461 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
99462 var localeSitelink;
99464 if (params.langCodes) {
99465 params.langCodes.forEach(function (langCode) {
99466 if (_localeIDs[langCode] === undefined) {
99467 // If this is the first time we are asking about this locale,
99468 // fetch corresponding entity (if it exists), and cache it.
99469 // If there is no such entry, cache `false` value to avoid re-requesting it.
99470 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
99471 titles.push(localeSitelink);
99476 if (rtypeSitelink) {
99477 if (_wikibaseCache[rtypeSitelink]) {
99478 result.rtype = _wikibaseCache[rtypeSitelink];
99480 titles.push(rtypeSitelink);
99485 if (_wikibaseCache[keySitelink]) {
99486 result.key = _wikibaseCache[keySitelink];
99488 titles.push(keySitelink);
99493 if (_wikibaseCache[tagSitelink]) {
99494 result.tag = _wikibaseCache[tagSitelink];
99496 titles.push(tagSitelink);
99500 if (!titles.length) {
99501 // Nothing to do, we already had everything in the cache
99502 return callback(null, result);
99503 } // Requesting just the user language code
99504 // If backend recognizes the code, it will perform proper fallbacks,
99505 // and the result will contain the requested code. If not, all values are returned:
99506 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
99507 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
99511 action: 'wbgetentities',
99513 titles: titles.join('|'),
99514 languages: params.langCodes.join('|'),
99515 languagefallback: 1,
99517 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
99518 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
99519 // formatversion: 2,
99522 var url = _apibase$1 + '?' + utilQsString(obj);
99523 doRequest(url, function (err, d) {
99526 } else if (!d.success || d.error) {
99527 callback(d.error.messages.map(function (v) {
99528 return v.html['*'];
99531 var localeID = false;
99532 Object.values(d.entities).forEach(function (res) {
99533 if (res.missing !== '') {
99534 var title = res.sitelinks.wiki.title;
99536 if (title === rtypeSitelink) {
99537 _wikibaseCache[rtypeSitelink] = res;
99538 result.rtype = res;
99539 } else if (title === keySitelink) {
99540 _wikibaseCache[keySitelink] = res;
99542 } else if (title === tagSitelink) {
99543 _wikibaseCache[tagSitelink] = res;
99545 } else if (title === localeSitelink) {
99548 console.log('Unexpected title ' + title); // eslint-disable-line no-console
99553 if (localeSitelink) {
99554 // If locale ID is not found, store false to prevent repeated queries
99555 that.addLocale(params.langCodes[0], localeID);
99558 callback(null, result);
99563 // Pass params object of the form:
99565 // key: 'string', // required
99566 // value: 'string' // optional
99569 // Get an result object used to display tag documentation
99571 // title: 'string',
99572 // description: 'string',
99573 // editURL: 'string',
99574 // imageURL: 'string',
99575 // wiki: { title: 'string', text: 'string', url: 'string' }
99578 getDocs: function getDocs(params, callback) {
99580 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
99581 return code.toLowerCase();
99583 params.langCodes = langCodes;
99584 this.getEntity(params, function (err, data) {
99590 var entity = data.rtype || data.tag || data.key;
99593 callback('No entity');
99600 for (i in langCodes) {
99601 var _code = langCodes[i];
99603 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
99604 description = entity.descriptions[_code];
99609 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
99612 title: entity.title,
99613 description: description ? description.value : '',
99614 descriptionLocaleCode: description ? description.language : '',
99615 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
99618 if (entity.claims) {
99620 var image = that.claimToValue(entity, 'P4', langCodes[0]);
99623 imageroot = 'https://commons.wikimedia.org/w/index.php';
99625 image = that.claimToValue(entity, 'P28', langCodes[0]);
99628 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
99632 if (imageroot && image) {
99633 result.imageURL = imageroot + '?' + utilQsString({
99634 title: 'Special:Redirect/file/' + image,
99638 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
99639 // If neither tag nor key data item contain a wiki page in the needed language nor English,
99640 // get the first found wiki page from either the tag or the key item.
99643 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
99644 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
99645 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
99646 var wikis = [rtypeWiki, tagWiki, keyWiki];
99649 var wiki = wikis[i];
99651 for (var j in langCodes) {
99652 var code = langCodes[j];
99653 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
99654 var info = getWikiInfo(wiki, code, referenceId);
99657 result.wiki = info;
99662 if (result.wiki) break;
99665 callback(null, result); // Helper method to get wiki info if a given language exists
99667 function getWikiInfo(wiki, langCode, tKey) {
99668 if (wiki && wiki[langCode]) {
99670 title: wiki[langCode],
99672 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
99678 addLocale: function addLocale(langCode, qid) {
99679 // Makes it easier to unit test
99680 _localeIDs[langCode] = qid;
99682 apibase: function apibase(val) {
99683 if (!arguments.length) return _apibase$1;
99689 var jsonpCache = {};
99690 window.jsonpCache = jsonpCache;
99691 function jsonpRequest(url, callback) {
99693 abort: function abort() {}
99696 if (window.JSONP_FIX) {
99697 if (window.JSONP_DELAY === 0) {
99698 callback(window.JSONP_FIX);
99700 var t = window.setTimeout(function () {
99701 callback(window.JSONP_FIX);
99702 }, window.JSONP_DELAY || 0);
99704 request.abort = function () {
99705 window.clearTimeout(t);
99713 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
99718 c += chars.charAt(Math.floor(Math.random() * 52));
99724 function create(url) {
99725 var e = url.match(/callback=(\w+)/);
99726 var c = e ? e[1] : rand();
99728 jsonpCache[c] = function (data) {
99729 if (jsonpCache[c]) {
99736 function finalize() {
99737 delete jsonpCache[c];
99741 request.abort = finalize;
99742 return 'jsonpCache.' + c;
99745 var cb = create(url);
99746 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
99750 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
99751 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
99752 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
99753 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
99754 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
99755 var maxResults = 2000;
99756 var tileZoom = 16.5;
99757 var tiler$1 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
99758 var dispatch$1 = dispatch$8('loadedImages', 'viewerChanged');
99759 var minHfov = 10; // zoom in degrees: 20, 10, 5
99761 var maxHfov = 90; // zoom out degrees
99763 var defaultHfov = 45;
99764 var _hires = false;
99765 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
99767 var _currScene = 0;
99771 var _pannellumViewer;
99773 var _sceneOptions = {
99774 showFullscreenCtrl: false,
99785 var _loadViewerPromise;
99791 function abortRequest$1(i) {
99795 * localeTimeStamp().
99799 function localeTimestamp(s) {
99800 if (!s) return null;
99806 var d = new Date(s);
99807 if (isNaN(d.getTime())) return null;
99808 return d.toLocaleString(_mainLocalizer.localeCode(), options);
99811 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
99815 function loadTiles(which, url, projection, margin) {
99816 var tiles = tiler$1.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
99818 var cache = _ssCache[which];
99819 Object.keys(cache.inflight).forEach(function (k) {
99820 var wanted = tiles.find(function (tile) {
99821 return k.indexOf(tile.id + ',') === 0;
99825 abortRequest$1(cache.inflight[k]);
99826 delete cache.inflight[k];
99829 tiles.forEach(function (tile) {
99830 return loadNextTilePage(which, url, tile);
99834 * loadNextTilePage() load data for the next tile page in line.
99838 function loadNextTilePage(which, url, tile) {
99839 var cache = _ssCache[which];
99840 var nextPage = cache.nextPage[tile.id] || 0;
99841 var id = tile.id + ',' + String(nextPage);
99842 if (cache.loaded[id] || cache.inflight[id]) return;
99843 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
99844 cache.loaded[id] = true;
99845 delete cache.inflight[id];
99846 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
99849 var features = bubbles.map(function (bubble) {
99850 if (cache.points[bubble.id]) return null; // skip duplicates
99852 var loc = [bubble.lo, bubble.la];
99857 captured_at: bubble.cd,
99858 captured_by: 'microsoft',
99859 // nbn: bubble.nbn,
99860 // pbn: bubble.pbn,
99870 cache.points[bubble.id] = d; // a sequence starts here
99872 if (bubble.pr === undefined) {
99873 cache.leaders.push(bubble.id);
99883 }).filter(Boolean);
99884 cache.rtree.load(features);
99885 connectSequences();
99887 if (which === 'bubbles') {
99888 dispatch$1.call('loadedImages');
99891 } // call this sometimes to connect the bubbles into sequences
99894 function connectSequences() {
99895 var cache = _ssCache.bubbles;
99896 var keepLeaders = [];
99898 for (var i = 0; i < cache.leaders.length; i++) {
99899 var bubble = cache.points[cache.leaders[i]];
99900 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
99906 var complete = false;
99909 sequence.bubbles.push(bubble);
99910 seen[bubble.key] = true;
99912 if (bubble.ne === undefined) {
99915 bubble = cache.points[bubble.ne]; // advance to next
99917 } while (bubble && !seen[bubble.key] && !complete);
99920 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
99922 for (var j = 0; j < sequence.bubbles.length; j++) {
99923 sequence.bubbles[j].sequenceKey = sequence.key;
99924 } // create a GeoJSON LineString
99927 sequence.geojson = {
99928 type: 'LineString',
99930 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
99931 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
99934 coordinates: sequence.bubbles.map(function (d) {
99939 keepLeaders.push(cache.leaders[i]);
99941 } // couldn't complete these, save for later
99944 cache.leaders = keepLeaders;
99947 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
99951 function getBubbles(url, tile, callback) {
99952 var rect = tile.extent.rectangle();
99953 var urlForRequest = url + utilQsString({
99959 appkey: bubbleAppKey,
99960 jsCallback: '{callback}'
99962 return jsonpRequest(urlForRequest, function (data) {
99963 if (!data || data.error) {
99969 } // partition viewport into higher zoom tiles
99972 function partitionViewport(projection) {
99973 var z = geoScaleToZoom(projection.scale());
99974 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
99976 var tiler = utilTiler().zoomExtent([z2, z2]);
99977 return tiler.getTiles(projection).map(function (tile) {
99978 return tile.extent;
99980 } // no more than `limit` results per partition.
99983 function searchLimited(limit, projection, rtree) {
99984 limit = limit || 5;
99985 return partitionViewport(projection).reduce(function (result, extent) {
99986 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
99989 return found.length ? result.concat(found) : result;
99997 function loadImage(imgInfo) {
99998 return new Promise(function (resolve) {
99999 var img = new Image();
100001 img.onload = function () {
100002 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
100003 var ctx = canvas.getContext('2d');
100004 ctx.drawImage(img, imgInfo.x, imgInfo.y);
100011 img.onerror = function () {
100018 img.setAttribute('crossorigin', '');
100027 function loadCanvas(imageGroup) {
100028 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
100029 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
100038 var face = data[0].imgInfo.face;
100039 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
100041 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
100050 function loadFaces(faceGroup) {
100051 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
100053 status: 'loadFaces done'
100058 function setupCanvas(selection, reset) {
100060 selection.selectAll('#ideditor-stitcher-canvases').remove();
100061 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
100062 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
100065 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) {
100066 return 'ideditor-' + d;
100067 }).attr('width', _resolution).attr('height', _resolution);
100075 for (var i = qk.length; i > 0; i--) {
100077 x += +(key === '1' || key === '3') * scale;
100078 y += +(key === '2' || key === '3') * scale;
100085 function getQuadKeys() {
100086 var dim = _resolution / 256;
100090 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'];
100091 } else if (dim === 8) {
100092 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'];
100093 } else if (dim === 4) {
100094 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
100097 quadKeys = ['0', '1', '2', '3'];
100103 var serviceStreetside = {
100105 * init() initialize streetside.
100107 init: function init() {
100112 this.event = utilRebind(this, dispatch$1, 'on');
100116 * reset() reset the cache.
100118 reset: function reset() {
100120 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$1);
100139 bubbles: function bubbles(projection) {
100141 return searchLimited(limit, projection, _ssCache.bubbles.rtree);
100143 cachedImage: function cachedImage(imageKey) {
100144 return _ssCache.bubbles.points[imageKey];
100146 sequences: function sequences(projection) {
100147 var viewport = projection.clipExtent();
100148 var min = [viewport[0][0], viewport[1][1]];
100149 var max = [viewport[1][0], viewport[0][1]];
100150 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
100152 var results = []; // all sequences for bubbles in viewport
100154 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
100155 var key = d.data.sequenceKey;
100157 if (key && !seen[key]) {
100159 results.push(_ssCache.sequences[key].geojson);
100169 loadBubbles: function loadBubbles(projection, margin) {
100170 // by default: request 2 nearby tiles so we can connect sequences.
100171 if (margin === undefined) margin = 2;
100172 loadTiles('bubbles', bubbleApi, projection, margin);
100174 viewer: function viewer() {
100175 return _pannellumViewer;
100177 initViewer: function initViewer() {
100178 if (!window.pannellum) return;
100179 if (_pannellumViewer) return;
100182 var sceneID = _currScene.toString();
100190 options.scenes[sceneID] = _sceneOptions;
100191 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
100193 ensureViewerLoaded: function ensureViewerLoaded(context) {
100194 if (_loadViewerPromise) return _loadViewerPromise; // create ms-wrapper, a photo wrapper class
100196 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
100197 // (used by all to house each custom photo viewer)
100199 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
100201 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
100203 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
100204 select(window).on(pointerPrefix + 'move.streetside', function () {
100205 dispatch$1.call('viewerChanged');
100207 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
100208 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
100210 var t = timer(function (elapsed) {
100211 dispatch$1.call('viewerChanged');
100217 }).append('div').attr('class', 'photo-attribution fillD');
100218 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
100219 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
100220 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
100222 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
100224 context.ui().photoviewer.on('resize.streetside', function () {
100225 if (_pannellumViewer) {
100226 _pannellumViewer.resize();
100229 _loadViewerPromise = new Promise(function (resolve, reject) {
100233 loadedCount += 1; // wait until both files are loaded
100235 if (loadedCount === 2) resolve();
100238 var head = select('head'); // load streetside pannellum viewer css
100240 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 () {
100242 }); // load streetside pannellum viewer js
100244 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 () {
100247 })["catch"](function () {
100248 _loadViewerPromise = null;
100250 return _loadViewerPromise;
100252 function step(stepBy) {
100254 var viewer = context.container().select('.photoviewer');
100255 var selected = viewer.empty() ? undefined : viewer.datum();
100257 var nextID = stepBy === 1 ? selected.ne : selected.pr;
100259 var yaw = _pannellumViewer.getYaw();
100261 var ca = selected.ca + yaw;
100262 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
100265 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
100266 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
100267 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
100268 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
100269 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
100271 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
100272 poly = geoRotate(poly, -angle, origin);
100273 var extent = poly.reduce(function (extent, point) {
100274 return extent.extend(geoExtent(point));
100275 }, geoExtent()); // find nearest other bubble in the search polygon
100277 var minDist = Infinity;
100279 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
100280 if (d.data.key === selected.key) return;
100281 if (!geoPointInPolygon(d.data.loc, poly)) return;
100282 var dist = geoVecLength(d.data.loc, selected.loc);
100283 var theta = selected.ca - d.data.ca;
100284 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
100287 dist += 5; // penalize distance if camera angles don't match
100296 var nextBubble = nextID && that.cachedImage(nextID);
100297 if (!nextBubble) return;
100298 context.map().centerEase(nextBubble.loc);
100299 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
100303 yaw: function yaw(_yaw) {
100304 if (typeof _yaw !== 'number') return _yaw;
100305 _sceneOptions.yaw = _yaw;
100312 showViewer: function showViewer(context) {
100313 var wrap = context.container().select('.photoviewer').classed('hide', false);
100314 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
100317 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
100318 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
100327 hideViewer: function hideViewer(context) {
100328 var viewer = context.container().select('.photoviewer');
100329 if (!viewer.empty()) viewer.datum(null);
100330 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
100331 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
100332 this.updateUrlImage(null);
100333 return this.setStyles(context, null, true);
100339 selectImage: function selectImage(context, key) {
100341 var d = this.cachedImage(key);
100342 var viewer = context.container().select('.photoviewer');
100343 if (!viewer.empty()) viewer.datum(d);
100344 this.setStyles(context, null, true);
100345 var wrap = context.container().select('.photoviewer .ms-wrapper');
100346 var attribution = wrap.selectAll('.photo-attribution').html('');
100347 wrap.selectAll('.pnlm-load-box') // display "loading.."
100348 .style('display', 'block');
100350 this.updateUrlImage(key);
100351 _sceneOptions.northOffset = d.ca;
100352 var line1 = attribution.append('div').attr('class', 'attribution-row');
100353 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
100355 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
100356 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
100357 d3_event.stopPropagation();
100359 _resolution = _hires ? 1024 : 512;
100360 wrap.call(setupCanvas, true);
100362 yaw: _pannellumViewer.getYaw(),
100363 pitch: _pannellumViewer.getPitch(),
100364 hfov: _pannellumViewer.getHfov()
100366 _sceneOptions = Object.assign(_sceneOptions, viewstate);
100367 that.selectImage(context, d.key).showViewer(context);
100369 label.append('span').html(_t.html('streetside.hires'));
100370 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
100373 var yyyy = new Date().getFullYear();
100374 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
100375 captureInfo.append('span').html('|');
100379 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
100383 var line2 = attribution.append('div').attr('class', 'attribution-row');
100384 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'));
100385 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'));
100386 var bubbleIdQuadKey = d.key.toString(4);
100387 var paddingNeeded = 16 - bubbleIdQuadKey.length;
100389 for (var i = 0; i < paddingNeeded; i++) {
100390 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
100393 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
100394 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
100396 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
100398 var quadKeys = getQuadKeys();
100399 var faces = faceKeys.map(function (faceKey) {
100400 return quadKeys.map(function (quadKey) {
100401 var xy = qkToXY(quadKey);
100404 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
100410 loadFaces(faces).then(function () {
100411 if (!_pannellumViewer) {
100417 var sceneID = _currScene.toString();
100419 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
100423 sceneID = (_currScene - 1).toString();
100425 _pannellumViewer.removeScene(sceneID);
100431 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
100432 return d && d.sequenceKey;
100434 // Updates the currently highlighted sequence and selected bubble.
100435 // Reset is only necessary when interacting with the viewport because
100436 // this implicitly changes the currently selected bubble/sequence
100437 setStyles: function setStyles(context, hovered, reset) {
100440 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
100441 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
100444 var hoveredBubbleKey = hovered && hovered.key;
100445 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
100446 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
100447 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
100450 var viewer = context.container().select('.photoviewer');
100451 var selected = viewer.empty() ? undefined : viewer.datum();
100452 var selectedBubbleKey = selected && selected.key;
100453 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
100454 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
100455 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
100457 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
100459 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
100460 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
100461 return highlightedBubbleKeys.indexOf(d.key) !== -1;
100462 }).classed('hovered', function (d) {
100463 return d.key === hoveredBubbleKey;
100464 }).classed('currentView', function (d) {
100465 return d.key === selectedBubbleKey;
100467 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
100468 return d.properties.key === hoveredSequenceKey;
100469 }).classed('currentView', function (d) {
100470 return d.properties.key === selectedSequenceKey;
100471 }); // update viewfields if needed
100473 context.container().selectAll('.layer-streetside-images .viewfield-group .viewfield').attr('d', viewfieldPath);
100475 function viewfieldPath() {
100476 var d = this.parentNode.__data__;
100478 if (d.pano && d.key !== selectedBubbleKey) {
100479 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
100481 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
100487 updateUrlImage: function updateUrlImage(imageKey) {
100489 var hash = utilStringQs(window.location.hash);
100492 hash.photo = 'streetside/' + imageKey;
100497 window.location.replace('#' + utilQsString(hash, true));
100504 cache: function cache() {
100509 var _apibase = 'https://taginfo.openstreetmap.org/api/4/';
100512 var _taginfoCache = {};
100519 var tag_sort_members = {
100520 point: 'count_node_members',
100521 vertex: 'count_node_members',
100522 area: 'count_way_members',
100523 line: 'count_way_members',
100524 relation: 'count_relation_members'
100532 var tag_members_fractions = {
100533 point: 'count_node_members_fraction',
100534 vertex: 'count_node_members_fraction',
100535 area: 'count_way_members_fraction',
100536 line: 'count_way_members_fraction',
100537 relation: 'count_relation_members_fraction'
100540 function sets(params, n, o) {
100541 if (params.geometry && o[params.geometry]) {
100542 params[n] = o[params.geometry];
100548 function setFilter(params) {
100549 return sets(params, 'filter', tag_filters);
100552 function setSort(params) {
100553 return sets(params, 'sortname', tag_sorts);
100556 function setSortMembers(params) {
100557 return sets(params, 'sortname', tag_sort_members);
100560 function clean(params) {
100561 return utilObjectOmit(params, ['geometry', 'debounce']);
100564 function filterKeys(type) {
100565 var count_type = type ? 'count_' + type : 'count_all';
100567 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
100571 function filterMultikeys(prefix) {
100573 // d.key begins with prefix, and d.key contains no additional ':'s
100574 var re = new RegExp('^' + prefix + '(.*)$');
100575 var matches = d.key.match(re) || [];
100576 return matches.length === 2 && matches[1].indexOf(':') === -1;
100580 function filterValues(allowUpperCase) {
100582 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
100584 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
100586 return parseFloat(d.fraction) > 0.0;
100590 function filterRoles(geometry) {
100592 if (d.role === '') return false; // exclude empty role
100594 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
100596 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
100607 function valKeyDescription(d) {
100610 title: d.description || d.value
100625 } // sort keys with ':' lower than keys without ':'
100628 function sortKeys(a, b) {
100629 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
100632 var debouncedRequest = debounce(request, 300, {
100636 function request(url, params, exactMatch, callback, loaded) {
100637 if (_inflight[url]) return;
100638 if (checkCache(url, params, exactMatch, callback)) return;
100639 var controller = new AbortController();
100640 _inflight[url] = controller;
100642 signal: controller.signal
100643 }).then(function (result) {
100645 if (loaded) loaded(null, result);
100646 })["catch"](function (err) {
100648 if (err.name === 'AbortError') return;
100649 if (loaded) loaded(err.message);
100653 function checkCache(url, params, exactMatch, callback) {
100654 var rp = params.rp || 25;
100655 var testQuery = params.query || '';
100659 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
100661 if (hit && (url === testUrl || hit.length < rp)) {
100664 } // don't try to shorten the query
100667 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
100668 // that has returned fewer than max results (rp)
100670 testQuery = testQuery.slice(0, -1);
100671 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
100672 } while (testQuery.length >= 0);
100678 init: function init() {
100682 // manually exclude some keys – #5377, #7485
100693 }; // Fetch popular keys. We'll exclude these from `values`
100694 // lookups because they stress taginfo, and they aren't likely
100695 // to yield meaningful autocomplete results.. see #3955
100699 sortname: 'values_all',
100703 lang: _mainLocalizer.languageCode()
100705 this.keys(params, function (err, data) {
100707 data.forEach(function (d) {
100708 if (d.value === 'opening_hours') return; // exception
100710 _popularKeys[d.value] = true;
100714 reset: function reset() {
100715 Object.values(_inflight).forEach(function (controller) {
100720 keys: function keys(params, callback) {
100721 var doRequest = params.debounce ? debouncedRequest : request;
100722 params = clean(setSort(params));
100723 params = Object.assign({
100728 lang: _mainLocalizer.languageCode()
100730 var url = _apibase + 'keys/all?' + utilQsString(params);
100731 doRequest(url, params, false, callback, function (err, d) {
100735 var f = filterKeys(params.filter);
100736 var result = d.data.filter(f).sort(sortKeys).map(valKey);
100737 _taginfoCache[url] = result;
100738 callback(null, result);
100742 multikeys: function multikeys(params, callback) {
100743 var doRequest = params.debounce ? debouncedRequest : request;
100744 params = clean(setSort(params));
100745 params = Object.assign({
100750 lang: _mainLocalizer.languageCode()
100752 var prefix = params.query;
100753 var url = _apibase + 'keys/all?' + utilQsString(params);
100754 doRequest(url, params, true, callback, function (err, d) {
100758 var f = filterMultikeys(prefix);
100759 var result = d.data.filter(f).map(valKey);
100760 _taginfoCache[url] = result;
100761 callback(null, result);
100765 values: function values(params, callback) {
100766 // Exclude popular keys from values lookups.. see #3955
100769 if (key && _popularKeys[key]) {
100774 var doRequest = params.debounce ? debouncedRequest : request;
100775 params = clean(setSort(setFilter(params)));
100776 params = Object.assign({
100781 lang: _mainLocalizer.languageCode()
100783 var url = _apibase + 'key/values?' + utilQsString(params);
100784 doRequest(url, params, false, callback, function (err, d) {
100788 // In most cases we prefer taginfo value results with lowercase letters.
100789 // A few OSM keys expect values to contain uppercase values (see #3377).
100790 // This is not an exhaustive list (e.g. `name` also has uppercase values)
100791 // but these are the fields where taginfo value lookup is most useful.
100792 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
100793 var allowUpperCase = re.test(params.key);
100794 var f = filterValues(allowUpperCase);
100795 var result = d.data.filter(f).map(valKeyDescription);
100796 _taginfoCache[url] = result;
100797 callback(null, result);
100801 roles: function roles(params, callback) {
100802 var doRequest = params.debounce ? debouncedRequest : request;
100803 var geometry = params.geometry;
100804 params = clean(setSortMembers(params));
100805 params = Object.assign({
100807 sortname: 'count_all_members',
100810 lang: _mainLocalizer.languageCode()
100812 var url = _apibase + 'relation/roles?' + utilQsString(params);
100813 doRequest(url, params, true, callback, function (err, d) {
100817 var f = filterRoles(geometry);
100818 var result = d.data.filter(f).map(roleKey);
100819 _taginfoCache[url] = result;
100820 callback(null, result);
100824 docs: function docs(params, callback) {
100825 var doRequest = params.debounce ? debouncedRequest : request;
100826 params = clean(setSort(params));
100827 var path = 'key/wiki_pages?';
100830 path = 'tag/wiki_pages?';
100831 } else if (params.rtype) {
100832 path = 'relation/wiki_pages?';
100835 var url = _apibase + path + utilQsString(params);
100836 doRequest(url, params, true, callback, function (err, d) {
100840 _taginfoCache[url] = d.data;
100841 callback(null, d.data);
100845 apibase: function apibase(_) {
100846 if (!arguments.length) return _apibase;
100853 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
100856 * @param {Geometry} geometry input geometry
100857 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
100858 * @param {Object} [options={}] Optional Parameters
100859 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
100860 * @param {string|number} [options.id] Identifier associated with the Feature
100861 * @returns {Feature} a GeoJSON Feature
100865 * "coordinates": [110, 50]
100868 * var feature = turf.feature(geometry);
100873 function feature(geom, properties, options) {
100874 if (options === void 0) {
100882 if (options.id === 0 || options.id) {
100887 feat.bbox = options.bbox;
100890 feat.properties = properties || {};
100895 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
100898 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
100899 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
100900 * @param {Object} [options={}] Optional Parameters
100901 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
100902 * @param {string|number} [options.id] Identifier associated with the Feature
100903 * @returns {Feature<Polygon>} Polygon Feature
100905 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
100910 function polygon(coordinates, properties, options) {
100911 if (options === void 0) {
100915 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
100916 var ring = coordinates_1[_i];
100919 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
100922 for (var j = 0; j < ring[ring.length - 1].length; j++) {
100923 // Check if first point of Polygon contains two numbers
100924 if (ring[ring.length - 1][j] !== ring[0][j]) {
100925 throw new Error("First and last Position are not equivalent.");
100932 coordinates: coordinates
100934 return feature(geom, properties, options);
100937 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
100940 * @param {Array<Array<number>>} coordinates an array of Positions
100941 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
100942 * @param {Object} [options={}] Optional Parameters
100943 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
100944 * @param {string|number} [options.id] Identifier associated with the Feature
100945 * @returns {Feature<LineString>} LineString Feature
100947 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
100948 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
100954 function lineString(coordinates, properties, options) {
100955 if (options === void 0) {
100959 if (coordinates.length < 2) {
100960 throw new Error("coordinates must be an array of two or more positions");
100965 coordinates: coordinates
100967 return feature(geom, properties, options);
100970 * Creates a {@link Feature<MultiLineString>} based on a
100971 * coordinate array. Properties can be added optionally.
100973 * @name multiLineString
100974 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
100975 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
100976 * @param {Object} [options={}] Optional Parameters
100977 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
100978 * @param {string|number} [options.id] Identifier associated with the Feature
100979 * @returns {Feature<MultiLineString>} a MultiLineString feature
100980 * @throws {Error} if no coordinates are passed
100982 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
100987 function multiLineString(coordinates, properties, options) {
100988 if (options === void 0) {
100993 type: "MultiLineString",
100994 coordinates: coordinates
100996 return feature(geom, properties, options);
100999 * Creates a {@link Feature<MultiPolygon>} based on a
101000 * coordinate array. Properties can be added optionally.
101003 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
101004 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
101005 * @param {Object} [options={}] Optional Parameters
101006 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
101007 * @param {string|number} [options.id] Identifier associated with the Feature
101008 * @returns {Feature<MultiPolygon>} a multipolygon feature
101009 * @throws {Error} if no coordinates are passed
101011 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
101017 function multiPolygon(coordinates, properties, options) {
101018 if (options === void 0) {
101024 coordinates: coordinates
101026 return feature(geom, properties, options);
101030 * Get Geometry from Feature or Geometry Object
101032 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
101033 * @returns {Geometry|null} GeoJSON Geometry Object
101034 * @throws {Error} if geojson is not a Feature or Geometry Object
101041 * "coordinates": [110, 40]
101044 * var geom = turf.getGeom(point)
101045 * //={"type": "Point", "coordinates": [110, 40]}
101048 function getGeom(geojson) {
101049 if (geojson.type === "Feature") {
101050 return geojson.geometry;
101056 // Cohen-Sutherland line clipping algorithm, adapted to efficiently
101057 // handle polylines rather than just segments
101058 function lineclip(points, bbox, result) {
101059 var len = points.length,
101060 codeA = bitCode(points[0], bbox),
101067 if (!result) result = [];
101069 for (i = 1; i < len; i++) {
101072 codeB = lastCode = bitCode(b, bbox);
101075 if (!(codeA | codeB)) {
101079 if (codeB !== lastCode) {
101080 // segment went outside
101088 } else if (i === len - 1) {
101093 } else if (codeA & codeB) {
101097 // a outside, intersect with clip edge
101098 a = intersect(a, b, codeA, bbox);
101099 codeA = bitCode(a, bbox);
101102 b = intersect(a, b, codeB, bbox);
101103 codeB = bitCode(b, bbox);
101110 if (part.length) result.push(part);
101112 } // Sutherland-Hodgeman polygon clipping algorithm
101114 function polygonclip(points, bbox) {
101115 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
101117 for (edge = 1; edge <= 8; edge *= 2) {
101119 prev = points[points.length - 1];
101120 prevInside = !(bitCode(prev, bbox) & edge);
101122 for (i = 0; i < points.length; i++) {
101124 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
101126 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
101127 if (inside) result.push(p); // add a point if it's inside
101134 if (!points.length) break;
101138 } // intersect a segment against one of the 4 lines that make up the bbox
101140 function intersect(a, b, edge, bbox) {
101141 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] // top
101142 : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] // bottom
101143 : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] // right
101144 : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] // left
101146 } // bit code reflects the point position relative to the bbox:
101150 // bottom 0101 0100 0110
101153 function bitCode(p, bbox) {
101155 if (p[0] < bbox[0]) code |= 1; // left
101156 else if (p[0] > bbox[2]) code |= 2; // right
101158 if (p[1] < bbox[1]) code |= 4; // bottom
101159 else if (p[1] > bbox[3]) code |= 8; // top
101165 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
101166 * [lineclip](https://github.com/mapbox/lineclip).
101167 * May result in degenerate edges when clipping Polygons.
101170 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
101171 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
101172 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
101174 * var bbox = [0, 0, 10, 10];
101175 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
101177 * var clipped = turf.bboxClip(poly, bbox);
101180 * var addToMap = [bbox, poly, clipped]
101183 function bboxClip(feature, bbox) {
101184 var geom = getGeom(feature);
101186 var properties = feature.type === "Feature" ? feature.properties : {};
101187 var coords = geom.coordinates;
101191 case "MultiLineString":
101194 if (type === "LineString") {
101198 coords.forEach(function (line) {
101199 lineclip(line, bbox, lines_1);
101202 if (lines_1.length === 1) {
101203 return lineString(lines_1[0], properties);
101206 return multiLineString(lines_1, properties);
101209 return polygon(clipPolygon(coords, bbox), properties);
101212 return multiPolygon(coords.map(function (poly) {
101213 return clipPolygon(poly, bbox);
101217 throw new Error("geometry " + type + " not supported");
101221 function clipPolygon(rings, bbox) {
101224 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
101225 var ring = rings_1[_i];
101226 var clipped = polygonclip(ring, bbox);
101228 if (clipped.length > 0) {
101229 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
101230 clipped.push(clipped[0]);
101233 if (clipped.length >= 4) {
101234 outRings.push(clipped);
101242 var tiler = utilTiler().tileSize(512).margin(1);
101243 var dispatch = dispatch$8('loadedData');
101247 function abortRequest(controller) {
101251 function vtToGeoJSON(data, tile, mergeCache) {
101252 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
101253 var layers = Object.keys(vectorTile$1.layers);
101255 if (!Array.isArray(layers)) {
101260 layers.forEach(function (layerID) {
101261 var layer = vectorTile$1.layers[layerID];
101264 for (var i = 0; i < layer.length; i++) {
101265 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
101266 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
101268 if (geometry.type === 'Polygon') {
101269 geometry.type = 'MultiPolygon';
101270 geometry.coordinates = [geometry.coordinates];
101273 var isClipped = false; // Clip to tile bounds
101275 if (geometry.type === 'MultiPolygon') {
101276 var featureClip = bboxClip(feature, tile.extent.rectangle());
101278 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
101279 // feature = featureClip;
101283 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
101285 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
101286 } // Generate some unique IDs and add some metadata
101289 var featurehash = utilHashcode(fastJsonStableStringify(feature));
101290 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
101291 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
101292 feature.__featurehash__ = featurehash;
101293 feature.__propertyhash__ = propertyhash;
101294 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
101296 if (isClipped && geometry.type === 'MultiPolygon') {
101297 var merged = mergeCache[propertyhash];
101299 if (merged && merged.length) {
101301 var coords = index.union(feature.geometry.coordinates, other.geometry.coordinates);
101303 if (!coords || !coords.length) {
101304 continue; // something failed in polygon union
101309 for (var j = 0; j < merged.length; j++) {
101310 // all these features get...
101311 merged[j].geometry.coordinates = coords; // same coords
101313 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
101316 mergeCache[propertyhash] = [feature];
101325 function loadTile(source, tile) {
101326 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
101327 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
101328 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
101329 var subdomains = r.split(',');
101330 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
101332 var controller = new AbortController();
101333 source.inflight[tile.id] = controller;
101335 signal: controller.signal
101336 }).then(function (response) {
101338 throw new Error(response.status + ' ' + response.statusText);
101341 source.loaded[tile.id] = [];
101342 delete source.inflight[tile.id];
101343 return response.arrayBuffer();
101344 }).then(function (data) {
101346 throw new Error('No Data');
101351 if (!source.canMerge[z]) {
101352 source.canMerge[z] = {}; // initialize mergeCache
101355 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
101356 dispatch.call('loadedData');
101357 })["catch"](function () {
101358 source.loaded[tile.id] = [];
101359 delete source.inflight[tile.id];
101363 var serviceVectorTile = {
101364 init: function init() {
101369 this.event = utilRebind(this, dispatch, 'on');
101371 reset: function reset() {
101372 for (var sourceID in _vtCache) {
101373 var source = _vtCache[sourceID];
101375 if (source && source.inflight) {
101376 Object.values(source.inflight).forEach(abortRequest);
101382 addSource: function addSource(sourceID, template) {
101389 return _vtCache[sourceID];
101391 data: function data(sourceID, projection) {
101392 var source = _vtCache[sourceID];
101393 if (!source) return [];
101394 var tiles = tiler.getTiles(projection);
101398 for (var i = 0; i < tiles.length; i++) {
101399 var features = source.loaded[tiles[i].id];
101400 if (!features || !features.length) continue;
101402 for (var j = 0; j < features.length; j++) {
101403 var feature = features[j];
101404 var hash = feature.__featurehash__;
101405 if (seen[hash]) continue;
101406 seen[hash] = true; // return a shallow copy, because the hash may change
101407 // later if this feature gets merged with another
101409 results.push(Object.assign({}, feature)); // shallow copy
101415 loadTiles: function loadTiles(sourceID, template, projection) {
101416 var source = _vtCache[sourceID];
101419 source = this.addSource(sourceID, template);
101422 var tiles = tiler.getTiles(projection); // abort inflight requests that are no longer needed
101424 Object.keys(source.inflight).forEach(function (k) {
101425 var wanted = tiles.find(function (tile) {
101430 abortRequest(source.inflight[k]);
101431 delete source.inflight[k];
101434 tiles.forEach(function (tile) {
101435 loadTile(source, tile);
101438 cache: function cache() {
101443 var apibase = 'https://www.wikidata.org/w/api.php?';
101444 var _wikidataCache = {};
101445 var serviceWikidata = {
101446 init: function init() {},
101447 reset: function reset() {
101450 // Search for Wikidata items matching the query
101451 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
101453 if (callback) callback('No query', {});
101457 var lang = this.languagesToQuery()[0];
101458 var url = apibase + utilQsString({
101459 action: 'wbsearchentities',
101464 // the language to search
101466 // the language for the label and description in the result
101471 d3_json(url).then(function (result) {
101472 if (result && result.error) {
101473 throw new Error(result.error);
101476 if (callback) callback(null, result.search || {});
101477 })["catch"](function (err) {
101478 if (callback) callback(err.message, {});
101481 // Given a Wikipedia language and article title,
101482 // return an array of corresponding Wikidata entities.
101483 itemsByTitle: function itemsByTitle(lang, title, callback) {
101485 if (callback) callback('No title', {});
101490 var url = apibase + utilQsString({
101491 action: 'wbgetentities',
101494 sites: lang.replace(/-/g, '_') + 'wiki',
101497 // shrink response by filtering to one language
101500 d3_json(url).then(function (result) {
101501 if (result && result.error) {
101502 throw new Error(result.error);
101505 if (callback) callback(null, result.entities || {});
101506 })["catch"](function (err) {
101507 if (callback) callback(err.message, {});
101510 languagesToQuery: function languagesToQuery() {
101511 return _mainLocalizer.localeCodes().map(function (code) {
101512 return code.toLowerCase();
101513 }).filter(function (code) {
101514 // HACK: en-us isn't a wikidata language. We should really be filtering by
101515 // the languages known to be supported by wikidata.
101516 return code !== 'en-us';
101519 entityByQID: function entityByQID(qid, callback) {
101521 callback('No qid', {});
101525 if (_wikidataCache[qid]) {
101526 if (callback) callback(null, _wikidataCache[qid]);
101530 var langs = this.languagesToQuery();
101531 var url = apibase + utilQsString({
101532 action: 'wbgetentities',
101536 props: 'labels|descriptions|claims|sitelinks',
101537 sitefilter: langs.map(function (d) {
101540 languages: langs.join('|'),
101544 d3_json(url).then(function (result) {
101545 if (result && result.error) {
101546 throw new Error(result.error);
101549 if (callback) callback(null, result.entities[qid] || {});
101550 })["catch"](function (err) {
101551 if (callback) callback(err.message, {});
101554 // Pass `params` object of the form:
101556 // qid: 'string' // brand wikidata (e.g. 'Q37158')
101559 // Get an result object used to display tag documentation
101562 // description: 'string',
101565 // wiki: { title: 'string', text: 'string', url: 'string' }
101568 getDocs: function getDocs(params, callback) {
101569 var langs = this.languagesToQuery();
101570 this.entityByQID(params.qid, function (err, entity) {
101572 callback(err || 'No entity');
101582 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
101583 description = entity.descriptions[code];
101588 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
101592 description: description ? description.value : '',
101593 descriptionLocaleCode: description ? description.language : '',
101594 editURL: 'https://www.wikidata.org/wiki/' + entity.id
101598 var imageroot = 'https://commons.wikimedia.org/w/index.php';
101599 var props = ['P154', 'P18']; // logo image, image
101603 for (i = 0; i < props.length; i++) {
101604 prop = entity.claims[props[i]];
101606 if (prop && Object.keys(prop).length > 0) {
101607 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
101610 result.imageURL = imageroot + '?' + utilQsString({
101611 title: 'Special:Redirect/file/' + image,
101620 if (entity.sitelinks) {
101621 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
101623 for (i = 0; i < langs.length; i++) {
101624 // check each, in order of preference
101625 var w = langs[i] + 'wiki';
101627 if (entity.sitelinks[w]) {
101628 var title = entity.sitelinks[w].title;
101629 var tKey = 'inspector.wiki_reference';
101631 if (!englishLocale && langs[i] === 'en') {
101632 // user's locale isn't English but
101633 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
101639 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
101646 callback(null, result);
101651 var endpoint = 'https://en.wikipedia.org/w/api.php?';
101652 var serviceWikipedia = {
101653 init: function init() {},
101654 reset: function reset() {},
101655 search: function search(lang, query, callback) {
101657 if (callback) callback('No Query', []);
101662 var url = endpoint.replace('en', lang) + utilQsString({
101671 d3_json(url).then(function (result) {
101672 if (result && result.error) {
101673 throw new Error(result.error);
101674 } else if (!result || !result.query || !result.query.search) {
101675 throw new Error('No Results');
101679 var titles = result.query.search.map(function (d) {
101682 callback(null, titles);
101684 })["catch"](function (err) {
101685 if (callback) callback(err, []);
101688 suggestions: function suggestions(lang, query, callback) {
101690 if (callback) callback('', []);
101695 var url = endpoint.replace('en', lang) + utilQsString({
101703 d3_json(url).then(function (result) {
101704 if (result && result.error) {
101705 throw new Error(result.error);
101706 } else if (!result || result.length < 2) {
101707 throw new Error('No Results');
101710 if (callback) callback(null, result[1] || []);
101711 })["catch"](function (err) {
101712 if (callback) callback(err.message, []);
101715 translations: function translations(lang, title, callback) {
101717 if (callback) callback('No Title');
101721 var url = endpoint.replace('en', lang) + utilQsString({
101729 d3_json(url).then(function (result) {
101730 if (result && result.error) {
101731 throw new Error(result.error);
101732 } else if (!result || !result.query || !result.query.pages) {
101733 throw new Error('No Results');
101737 var list = result.query.pages[Object.keys(result.query.pages)[0]];
101740 if (list && list.langlinks) {
101741 list.langlinks.forEach(function (d) {
101742 translations[d.lang] = d['*'];
101746 callback(null, translations);
101748 })["catch"](function (err) {
101749 if (callback) callback(err.message);
101755 geocoder: serviceNominatim,
101756 keepRight: serviceKeepRight,
101757 improveOSM: serviceImproveOSM,
101759 mapillary: serviceMapillary,
101761 openstreetcam: serviceOpenstreetcam,
101763 osmWikibase: serviceOsmWikibase,
101764 maprules: serviceMapRules,
101765 streetside: serviceStreetside,
101766 taginfo: serviceTaginfo,
101767 vectorTile: serviceVectorTile,
101768 wikidata: serviceWikidata,
101769 wikipedia: serviceWikipedia
101772 function modeDragNote(context) {
101777 var edit = behaviorEdit(context);
101783 var _note; // most current note.. dragged note may have stale datum.
101786 function startNudge(d3_event, nudge) {
101787 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
101788 _nudgeInterval = window.setInterval(function () {
101789 context.map().pan(nudge);
101790 doMove(d3_event, nudge);
101796 window.clearInterval(_nudgeInterval);
101801 function origin(note) {
101802 return context.projection(note.loc);
101805 function start(d3_event, note) {
101807 var osm = services.osm;
101810 // Get latest note from cache.. The marker may have a stale datum bound to it
101811 // and dragging it around can sometimes delete the users note comment.
101812 _note = osm.getNote(_note.id);
101815 context.surface().selectAll('.note-' + _note.id).classed('active', true);
101816 context.perform(actionNoop());
101818 context.selectedNoteID(_note.id);
101821 function move(d3_event, entity, point) {
101822 d3_event.stopPropagation();
101823 _lastLoc = context.projection.invert(point);
101825 var nudge = geoViewportEdge(point, context.map().dimensions());
101828 startNudge(d3_event, nudge);
101834 function doMove(d3_event, nudge) {
101835 nudge = nudge || [0, 0];
101836 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
101837 var currMouse = geoVecSubtract(currPoint, nudge);
101838 var loc = context.projection.invert(currMouse);
101839 _note = _note.move(loc);
101840 var osm = services.osm;
101843 osm.replaceNote(_note); // update note cache
101846 context.replace(actionNoop()); // trigger redraw
101850 context.replace(actionNoop()); // trigger redraw
101852 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
101855 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);
101857 mode.enter = function () {
101861 mode.exit = function () {
101862 context.ui().sidebar.hover.cancel();
101863 context.uninstall(edit);
101864 context.surface().selectAll('.active').classed('active', false);
101872 function modeSelectData(context, selectedDatum) {
101877 var keybinding = utilKeybinding('select-data');
101878 var dataEditor = uiDataEditor(context);
101879 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
101881 function selectData(d3_event, drawn) {
101882 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
101884 if (selection.empty()) {
101885 // Return to browse mode if selected DOM elements have
101886 // disappeared because the user moved them out of view..
101887 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
101889 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
101890 context.enter(modeBrowse(context));
101893 selection.classed('selected', true);
101898 if (context.container().select('.combobox').size()) return;
101899 context.enter(modeBrowse(context));
101902 mode.zoomToSelected = function () {
101903 var extent = geoExtent(d3_geoBounds(selectedDatum));
101904 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
101907 mode.enter = function () {
101908 behaviors.forEach(context.install);
101909 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
101910 select(document).call(keybinding);
101912 var sidebar = context.ui().sidebar;
101913 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
101915 var extent = geoExtent(d3_geoBounds(selectedDatum));
101916 sidebar.expand(sidebar.intersects(extent));
101917 context.map().on('drawn.select-data', selectData);
101920 mode.exit = function () {
101921 behaviors.forEach(context.uninstall);
101922 select(document).call(keybinding.unbind);
101923 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
101924 context.map().on('drawn.select-data', null);
101925 context.ui().sidebar.hide();
101931 function behaviorSelect(context) {
101932 var _tolerancePx = 4; // see also behaviorDrag
101934 var _lastMouseEvent = null;
101936 var _downPointers = {};
101937 var _longPressTimeout = null;
101938 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
101940 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
101942 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
101944 function keydown(d3_event) {
101945 if (d3_event.keyCode === 32) {
101946 // don't react to spacebar events during text input
101947 var activeNode = document.activeElement;
101948 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
101951 if (d3_event.keyCode === 93 || // context menu key
101952 d3_event.keyCode === 32) {
101954 d3_event.preventDefault();
101957 if (d3_event.repeat) return; // ignore repeated events for held keys
101958 // if any key is pressed the user is probably doing something other than long-pressing
101962 if (d3_event.shiftKey) {
101963 context.surface().classed('behavior-multiselect', true);
101966 if (d3_event.keyCode === 32) {
101968 if (!_downPointers.spacebar && _lastMouseEvent) {
101970 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
101971 _downPointers.spacebar = {
101972 firstEvent: _lastMouseEvent,
101973 lastEvent: _lastMouseEvent
101979 function keyup(d3_event) {
101982 if (!d3_event.shiftKey) {
101983 context.surface().classed('behavior-multiselect', false);
101986 if (d3_event.keyCode === 93) {
101988 d3_event.preventDefault();
101989 _lastInteractionType = 'menukey';
101991 } else if (d3_event.keyCode === 32) {
101993 var pointer = _downPointers.spacebar;
101996 delete _downPointers.spacebar;
101997 if (pointer.done) return;
101998 d3_event.preventDefault();
101999 _lastInteractionType = 'spacebar';
102000 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
102005 function pointerdown(d3_event) {
102006 var id = (d3_event.pointerId || 'mouse').toString();
102008 if (d3_event.buttons && d3_event.buttons !== 1) return;
102009 context.ui().closeEditMenu();
102010 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
102017 function didLongPress(id, interactionType) {
102018 var pointer = _downPointers[id];
102021 for (var i in _downPointers) {
102022 // don't allow this or any currently down pointer to trigger another click
102023 _downPointers[i].done = true;
102024 } // treat long presses like right-clicks
102027 _longPressTimeout = null;
102028 _lastInteractionType = interactionType;
102030 click(pointer.firstEvent, pointer.lastEvent, id);
102033 function pointermove(d3_event) {
102034 var id = (d3_event.pointerId || 'mouse').toString();
102036 if (_downPointers[id]) {
102037 _downPointers[id].lastEvent = d3_event;
102040 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
102041 _lastMouseEvent = d3_event;
102043 if (_downPointers.spacebar) {
102044 _downPointers.spacebar.lastEvent = d3_event;
102049 function pointerup(d3_event) {
102050 var id = (d3_event.pointerId || 'mouse').toString();
102051 var pointer = _downPointers[id];
102053 delete _downPointers[id];
102055 if (_multiselectionPointerId === id) {
102056 _multiselectionPointerId = null;
102059 if (pointer.done) return;
102060 click(pointer.firstEvent, d3_event, id);
102063 function pointercancel(d3_event) {
102064 var id = (d3_event.pointerId || 'mouse').toString();
102065 if (!_downPointers[id]) return;
102066 delete _downPointers[id];
102068 if (_multiselectionPointerId === id) {
102069 _multiselectionPointerId = null;
102073 function contextmenu(d3_event) {
102074 d3_event.preventDefault();
102076 if (!+d3_event.clientX && !+d3_event.clientY) {
102078 d3_event.sourceEvent = _lastMouseEvent;
102083 _lastMouseEvent = d3_event;
102084 _lastInteractionType = 'rightclick';
102088 click(d3_event, d3_event);
102091 function click(firstEvent, lastEvent, pointerId) {
102093 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
102094 // are transformed when drag-panning.
102096 var pointGetter = utilFastMouse(mapNode);
102097 var p1 = pointGetter(firstEvent);
102098 var p2 = pointGetter(lastEvent);
102099 var dist = geoVecLength(p1, p2);
102101 if (dist > _tolerancePx || !mapContains(lastEvent)) {
102106 var targetDatum = lastEvent.target.__data__;
102107 var multiselectEntityId;
102109 if (!_multiselectionPointerId) {
102110 // If a different pointer than the one triggering this click is down on a
102111 // feature, treat this and all future clicks as multiselection until that
102113 var selectPointerInfo = pointerDownOnSelection(pointerId);
102115 if (selectPointerInfo) {
102116 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
102118 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
102119 _downPointers[selectPointerInfo.pointerId].done = true;
102121 } // support multiselect if data is already selected
102124 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
102125 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
102126 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
102127 _multiselectionPointerId && !multiselectEntityId);
102129 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
102131 function mapContains(event) {
102132 var rect = mapNode.getBoundingClientRect();
102133 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
102136 function pointerDownOnSelection(skipPointerId) {
102137 var mode = context.mode();
102138 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
102140 for (var pointerId in _downPointers) {
102141 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
102142 var pointerInfo = _downPointers[pointerId];
102143 var p1 = pointGetter(pointerInfo.firstEvent);
102144 var p2 = pointGetter(pointerInfo.lastEvent);
102145 if (geoVecLength(p1, p2) > _tolerancePx) continue;
102146 var datum = pointerInfo.firstEvent.target.__data__;
102147 var entity = datum && datum.properties && datum.properties.entity || datum;
102149 if (context.graph().hasEntity(entity.id)) {
102153 selected: selectedIDs.indexOf(entity.id) !== -1
102162 function processClick(datum, isMultiselect, point, alsoSelectId) {
102163 var mode = context.mode();
102164 var showMenu = _showMenu;
102165 var interactionType = _lastInteractionType;
102166 var entity = datum && datum.properties && datum.properties.entity;
102167 if (entity) datum = entity;
102169 if (datum && datum.type === 'midpoint') {
102170 // treat targeting midpoints as if targeting the parent way
102171 datum = datum.parents[0];
102176 if (datum instanceof osmEntity) {
102178 var selectedIDs = context.selectedIDs();
102179 context.selectedNoteID(null);
102180 context.selectedErrorID(null);
102183 // don't change the selection if we're toggling the menu atop a multiselection
102184 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
102185 if (alsoSelectId === datum.id) alsoSelectId = null;
102186 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
102187 // selected since listeners may expect `context.enter` events,
102188 // e.g. in the walkthrough
102190 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
102191 context.enter(newMode);
102194 if (selectedIDs.indexOf(datum.id) !== -1) {
102195 // clicked entity is already in the selectedIDs list..
102197 // deselect clicked entity, then reenter select mode or return to browse mode..
102198 selectedIDs = selectedIDs.filter(function (id) {
102199 return id !== datum.id;
102201 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
102202 context.enter(newMode);
102205 // clicked entity is not in the selected list, add it..
102206 selectedIDs = selectedIDs.concat([datum.id]);
102207 newMode = mode.selectedIDs(selectedIDs);
102208 context.enter(newMode);
102211 } else if (datum && datum.__featurehash__ && !isMultiselect) {
102212 // targeting custom data
102213 context.selectedNoteID(null).enter(modeSelectData(context, datum));
102214 } else if (datum instanceof osmNote && !isMultiselect) {
102216 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
102217 } else if (datum instanceof QAItem & !isMultiselect) {
102218 // targeting an external QA issue
102219 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
102222 context.selectedNoteID(null);
102223 context.selectedErrorID(null);
102225 if (!isMultiselect && mode.id !== 'browse') {
102226 context.enter(modeBrowse(context));
102230 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
102232 if (showMenu) context.ui().showEditMenu(point, interactionType);
102236 function cancelLongPress() {
102237 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
102238 _longPressTimeout = null;
102241 function resetProperties() {
102244 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
102247 function behavior(selection) {
102249 _lastMouseEvent = context.map().lastPointerEvent();
102250 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) {
102251 // Edge and IE really like to show the contextmenu on the
102252 // menubar when user presses a keyboard menu button
102253 // even after we've already preventdefaulted the key event.
102256 if (+e.clientX === 0 && +e.clientY === 0) {
102257 d3_event.preventDefault();
102260 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
102261 /*if (d3_event && d3_event.shiftKey) {
102263 .classed('behavior-multiselect', true);
102267 behavior.off = function (selection) {
102269 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);
102270 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
102271 context.surface().classed('behavior-multiselect', false);
102277 function operationContinue(context, selectedIDs) {
102278 var _entities = selectedIDs.map(function (id) {
102279 return context.graph().entity(id);
102282 var _geometries = Object.assign({
102285 }, utilArrayGroupBy(_entities, function (entity) {
102286 return entity.geometry(context.graph());
102289 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
102291 function candidateWays() {
102292 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
102293 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
102297 var _candidates = candidateWays();
102299 var operation = function operation() {
102300 var candidate = _candidates[0];
102301 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
102304 operation.relatedEntityIds = function () {
102305 return _candidates.length ? [_candidates[0].id] : [];
102308 operation.available = function () {
102309 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
102312 operation.disabled = function () {
102313 if (_candidates.length === 0) {
102315 } else if (_candidates.length > 1) {
102322 operation.tooltip = function () {
102323 var disable = operation.disabled();
102324 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
102327 operation.annotation = function () {
102328 return _t('operations.continue.annotation.line');
102331 operation.id = 'continue';
102332 operation.keys = [_t('operations.continue.key')];
102333 operation.title = _t('operations.continue.title');
102334 operation.behavior = behaviorOperation(context).which(operation);
102338 function operationCopy(context, selectedIDs) {
102339 function getFilteredIdsToCopy() {
102340 return selectedIDs.filter(function (selectedID) {
102341 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
102343 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
102347 var operation = function operation() {
102348 var graph = context.graph();
102349 var selected = groupEntities(getFilteredIdsToCopy(), graph);
102355 for (i = 0; i < selected.relation.length; i++) {
102356 entity = selected.relation[i];
102358 if (!skip[entity.id] && entity.isComplete(graph)) {
102359 canCopy.push(entity.id);
102360 skip = getDescendants(entity.id, graph, skip);
102364 for (i = 0; i < selected.way.length; i++) {
102365 entity = selected.way[i];
102367 if (!skip[entity.id]) {
102368 canCopy.push(entity.id);
102369 skip = getDescendants(entity.id, graph, skip);
102373 for (i = 0; i < selected.node.length; i++) {
102374 entity = selected.node[i];
102376 if (!skip[entity.id]) {
102377 canCopy.push(entity.id);
102381 context.copyIDs(canCopy);
102383 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
102384 // store the anchor coordinates if copying more than a single node
102385 context.copyLonLat(context.projection.invert(_point));
102387 context.copyLonLat(null);
102391 function groupEntities(ids, graph) {
102392 var entities = ids.map(function (id) {
102393 return graph.entity(id);
102399 }, utilArrayGroupBy(entities, 'type'));
102402 function getDescendants(id, graph, descendants) {
102403 var entity = graph.entity(id);
102405 descendants = descendants || {};
102407 if (entity.type === 'relation') {
102408 children = entity.members.map(function (m) {
102411 } else if (entity.type === 'way') {
102412 children = entity.nodes;
102417 for (var i = 0; i < children.length; i++) {
102418 if (!descendants[children[i]]) {
102419 descendants[children[i]] = true;
102420 descendants = getDescendants(children[i], graph, descendants);
102427 operation.available = function () {
102428 return getFilteredIdsToCopy().length > 0;
102431 operation.disabled = function () {
102432 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
102434 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
102441 operation.availableForKeypress = function () {
102442 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
102444 return !selection || !selection.toString();
102447 operation.tooltip = function () {
102448 var disable = operation.disabled();
102449 return disable ? _t('operations.copy.' + disable, {
102451 }) : _t('operations.copy.description', {
102456 operation.annotation = function () {
102457 return _t('operations.copy.annotation', {
102464 operation.point = function (val) {
102470 operation.keys = [uiCmd('⌘C')];
102471 operation.title = _t('operations.copy.title');
102472 operation.behavior = behaviorOperation(context).which(operation);
102476 function operationDisconnect(context, selectedIDs) {
102481 selectedIDs.forEach(function (id) {
102482 var entity = context.entity(id);
102484 if (entity.type === 'way') {
102486 } else if (entity.geometry(context.graph()) === 'vertex') {
102495 _annotationID = 'features';
102497 var _disconnectingVertexIds = [];
102498 var _disconnectingWayIds = [];
102500 if (_vertexIDs.length > 0) {
102501 // At the selected vertices, disconnect the selected ways, if any, else
102502 // disconnect all connected ways
102503 _disconnectingVertexIds = _vertexIDs;
102505 _vertexIDs.forEach(function (vertexID) {
102506 var action = actionDisconnect(vertexID);
102508 if (_wayIDs.length > 0) {
102509 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
102510 var way = context.entity(wayID);
102511 return way.nodes.indexOf(vertexID) !== -1;
102514 action.limitWays(waysIDsForVertex);
102519 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
102524 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
102525 return _wayIDs.indexOf(id) === -1;
102527 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
102529 if (_wayIDs.length === 1) {
102530 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
102532 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
102534 } else if (_wayIDs.length > 0) {
102535 // Disconnect the selected ways from each other, if they're connected,
102536 // else disconnect them from all connected ways
102537 var ways = _wayIDs.map(function (id) {
102538 return context.entity(id);
102541 var nodes = utilGetAllNodes(_wayIDs, context.graph());
102542 _coords = nodes.map(function (n) {
102544 }); // actions for connected nodes shared by at least two selected ways
102546 var sharedActions = [];
102547 var sharedNodes = []; // actions for connected nodes
102549 var unsharedActions = [];
102550 var unsharedNodes = [];
102551 nodes.forEach(function (node) {
102552 var action = actionDisconnect(node.id).limitWays(_wayIDs);
102554 if (action.disabled(context.graph()) !== 'not_connected') {
102560 if (way.nodes.indexOf(node.id) !== -1) {
102568 sharedActions.push(action);
102569 sharedNodes.push(node);
102571 unsharedActions.push(action);
102572 unsharedNodes.push(node);
102576 _descriptionID += 'no_points.';
102577 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
102579 if (sharedActions.length) {
102580 // if any nodes are shared, only disconnect the selected ways from each other
102581 _actions = sharedActions;
102582 _disconnectingVertexIds = sharedNodes.map(function (node) {
102585 _descriptionID += 'conjoined';
102586 _annotationID = 'from_each_other';
102588 // if no nodes are shared, disconnect the selected ways from all connected ways
102589 _actions = unsharedActions;
102590 _disconnectingVertexIds = unsharedNodes.map(function (node) {
102594 if (_wayIDs.length === 1) {
102595 _descriptionID += context.graph().geometry(_wayIDs[0]);
102597 _descriptionID += 'separate';
102602 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
102604 var operation = function operation() {
102605 context.perform(function (graph) {
102606 return _actions.reduce(function (graph, action) {
102609 }, operation.annotation());
102610 context.validator().validate();
102613 operation.relatedEntityIds = function () {
102614 if (_vertexIDs.length) {
102615 return _disconnectingWayIds;
102618 return _disconnectingVertexIds;
102621 operation.available = function () {
102622 if (_actions.length === 0) return false;
102623 if (_otherIDs.length !== 0) return false;
102624 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
102625 return _vertexIDs.some(function (vertexID) {
102626 var way = context.entity(wayID);
102627 return way.nodes.indexOf(vertexID) !== -1;
102633 operation.disabled = function () {
102636 for (var actionIndex in _actions) {
102637 reason = _actions[actionIndex].disabled(context.graph());
102638 if (reason) return reason;
102641 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
102642 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
102643 } else if (_coords && someMissing()) {
102644 return 'not_downloaded';
102645 } else if (selectedIDs.some(context.hasHiddenConnections)) {
102646 return 'connected_to_hidden';
102651 function someMissing() {
102652 if (context.inIntro()) return false;
102653 var osm = context.connection();
102656 var missing = _coords.filter(function (loc) {
102657 return !osm.isDataLoaded(loc);
102661 missing.forEach(function (loc) {
102662 context.loadTileAtLoc(loc);
102672 operation.tooltip = function () {
102673 var disable = operation.disabled();
102676 return _t('operations.disconnect.' + disable);
102679 return _t('operations.disconnect.description.' + _descriptionID);
102682 operation.annotation = function () {
102683 return _t('operations.disconnect.annotation.' + _annotationID);
102686 operation.id = 'disconnect';
102687 operation.keys = [_t('operations.disconnect.key')];
102688 operation.title = _t('operations.disconnect.title');
102689 operation.behavior = behaviorOperation(context).which(operation);
102693 function operationDowngrade(context, selectedIDs) {
102694 var _affectedFeatureCount = 0;
102696 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
102698 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
102700 function downgradeTypeForEntityIDs(entityIds) {
102702 _affectedFeatureCount = 0;
102704 for (var i in entityIds) {
102705 var entityID = entityIds[i];
102706 var type = downgradeTypeForEntityID(entityID);
102709 _affectedFeatureCount += 1;
102711 if (downgradeType && type !== downgradeType) {
102712 if (downgradeType !== 'generic' && type !== 'generic') {
102713 downgradeType = 'building_address';
102715 downgradeType = 'generic';
102726 function downgradeTypeForEntityID(entityID) {
102727 var graph = context.graph();
102728 var entity = graph.entity(entityID);
102729 var preset = _mainPresetIndex.match(entity, graph);
102730 if (!preset || preset.isFallback()) return null;
102732 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
102733 return key.match(/^addr:.{1,}/);
102738 var geometry = entity.geometry(graph);
102740 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
102744 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
102751 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
102752 var addressKeysToKeep = ['source'];
102754 var operation = function operation() {
102755 context.perform(function (graph) {
102756 for (var i in selectedIDs) {
102757 var entityID = selectedIDs[i];
102758 var type = downgradeTypeForEntityID(entityID);
102760 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
102762 for (var key in tags) {
102763 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
102765 if (type === 'building') {
102766 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
102769 if (type !== 'generic') {
102770 if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
102776 graph = actionChangeTags(entityID, tags)(graph);
102780 }, operation.annotation());
102781 context.validator().validate(); // refresh the select mode to enable the delete operation
102783 context.enter(modeSelect(context, selectedIDs));
102786 operation.available = function () {
102790 operation.disabled = function () {
102791 if (selectedIDs.some(hasWikidataTag)) {
102792 return 'has_wikidata_tag';
102797 function hasWikidataTag(id) {
102798 var entity = context.entity(id);
102799 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
102803 operation.tooltip = function () {
102804 var disable = operation.disabled();
102805 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
102808 operation.annotation = function () {
102811 if (_downgradeType === 'building_address') {
102814 suffix = _downgradeType;
102817 return _t('operations.downgrade.annotation.' + suffix, {
102818 n: _affectedFeatureCount
102822 operation.id = 'downgrade';
102823 operation.keys = [uiCmd('⌫')];
102824 operation.title = _t('operations.downgrade.title');
102825 operation.behavior = behaviorOperation(context).which(operation);
102829 function operationExtract(context, selectedIDs) {
102830 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
102832 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
102833 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
102836 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
102840 var _actions = selectedIDs.map(function (entityID) {
102841 var graph = context.graph();
102842 var entity = graph.hasEntity(entityID);
102843 if (!entity || !entity.hasInterestingTags()) return null;
102844 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
102846 if (entity.type !== 'node') {
102847 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
102849 if (preset.geometry.indexOf('point') === -1) return null;
102852 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
102853 return actionExtract(entityID, context.projection);
102856 var operation = function operation() {
102857 var combinedAction = function combinedAction(graph) {
102858 _actions.forEach(function (action) {
102865 context.perform(combinedAction, operation.annotation()); // do the extract
102867 var extractedNodeIDs = _actions.map(function (action) {
102868 return action.getExtractedNodeID();
102871 context.enter(modeSelect(context, extractedNodeIDs));
102874 operation.available = function () {
102875 return _actions.length && selectedIDs.length === _actions.length;
102878 operation.disabled = function () {
102879 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
102881 } else if (selectedIDs.some(function (entityID) {
102882 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
102884 return 'connected_to_hidden';
102890 operation.tooltip = function () {
102891 var disableReason = operation.disabled();
102894 return _t('operations.extract.' + disableReason + '.' + _amount);
102896 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
102900 operation.annotation = function () {
102901 return _t('operations.extract.annotation', {
102906 operation.id = 'extract';
102907 operation.keys = [_t('operations.extract.key')];
102908 operation.title = _t('operations.extract.title');
102909 operation.behavior = behaviorOperation(context).which(operation);
102913 function operationMerge(context, selectedIDs) {
102914 var _action = getAction();
102917 // prefer a non-disabled action first
102918 var join = actionJoin(selectedIDs);
102919 if (!join.disabled(context.graph())) return join;
102920 var merge = actionMerge(selectedIDs);
102921 if (!merge.disabled(context.graph())) return merge;
102922 var mergePolygon = actionMergePolygon(selectedIDs);
102923 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
102924 var mergeNodes = actionMergeNodes(selectedIDs);
102925 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
102927 if (join.disabled(context.graph()) !== 'not_eligible') return join;
102928 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
102929 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
102933 var operation = function operation() {
102934 if (operation.disabled()) return;
102935 context.perform(_action, operation.annotation());
102936 context.validator().validate();
102937 var resultIDs = selectedIDs.filter(context.hasEntity);
102939 if (resultIDs.length > 1) {
102940 var interestingIDs = resultIDs.filter(function (id) {
102941 return context.entity(id).hasInterestingTags();
102943 if (interestingIDs.length) resultIDs = interestingIDs;
102946 context.enter(modeSelect(context, resultIDs));
102949 operation.available = function () {
102950 return selectedIDs.length >= 2;
102953 operation.disabled = function () {
102954 var actionDisabled = _action.disabled(context.graph());
102956 if (actionDisabled) return actionDisabled;
102957 var osm = context.connection();
102959 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
102960 return 'too_many_vertices';
102966 operation.tooltip = function () {
102967 var disabled = operation.disabled();
102970 if (disabled === 'restriction') {
102971 return _t('operations.merge.restriction', {
102972 relation: _mainPresetIndex.item('type/restriction').name()
102976 return _t('operations.merge.' + disabled);
102979 return _t('operations.merge.description');
102982 operation.annotation = function () {
102983 return _t('operations.merge.annotation', {
102988 operation.id = 'merge';
102989 operation.keys = [_t('operations.merge.key')];
102990 operation.title = _t('operations.merge.title');
102991 operation.behavior = behaviorOperation(context).which(operation);
102995 function operationPaste(context) {
102998 var operation = function operation() {
102999 if (!_pastePoint) return;
103000 var oldIDs = context.copyIDs();
103001 if (!oldIDs.length) return;
103002 var projection = context.projection;
103003 var extent = geoExtent();
103004 var oldGraph = context.copyGraph();
103006 var action = actionCopyEntities(oldIDs, oldGraph);
103007 context.perform(action);
103008 var copies = action.copies();
103009 var originals = new Set();
103010 Object.values(copies).forEach(function (entity) {
103011 originals.add(entity.id);
103014 for (var id in copies) {
103015 var oldEntity = oldGraph.entity(id);
103016 var newEntity = copies[id];
103018 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
103021 var parents = context.graph().parentWays(newEntity);
103022 var parentCopied = parents.some(function (parent) {
103023 return originals.has(parent.id);
103027 newIDs.push(newEntity.id);
103029 } // Use the location of the copy operation to offset the paste location,
103030 // or else use the center of the pasted extent
103033 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
103034 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
103036 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
103037 context.enter(modeSelect(context, newIDs));
103040 operation.point = function (val) {
103045 operation.available = function () {
103046 return context.mode().id === 'browse';
103049 operation.disabled = function () {
103050 return !context.copyIDs().length;
103053 operation.tooltip = function () {
103054 var oldGraph = context.copyGraph();
103055 var ids = context.copyIDs();
103058 return _t('operations.paste.nothing_copied');
103061 return _t('operations.paste.description', {
103062 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
103067 operation.annotation = function () {
103068 var ids = context.copyIDs();
103069 return _t('operations.paste.annotation', {
103074 operation.id = 'paste';
103075 operation.keys = [uiCmd('⌘V')];
103076 operation.title = _t('operations.paste.title');
103080 function operationReverse(context, selectedIDs) {
103081 var operation = function operation() {
103082 context.perform(function combinedReverseAction(graph) {
103083 actions().forEach(function (action) {
103087 }, operation.annotation());
103088 context.validator().validate();
103091 function actions(situation) {
103092 return selectedIDs.map(function (entityID) {
103093 var entity = context.hasEntity(entityID);
103094 if (!entity) return null;
103096 if (situation === 'toolbar') {
103097 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
103100 var geometry = entity.geometry(context.graph());
103101 if (entity.type !== 'node' && geometry !== 'line') return null;
103102 var action = actionReverse(entityID);
103103 if (action.disabled(context.graph())) return null;
103108 function reverseTypeID() {
103110 var nodeActionCount = acts.filter(function (act) {
103111 var entity = context.hasEntity(act.entityID());
103112 return entity && entity.type === 'node';
103114 if (nodeActionCount === 0) return 'line';
103115 if (nodeActionCount === acts.length) return 'point';
103119 operation.available = function (situation) {
103120 return actions(situation).length > 0;
103123 operation.disabled = function () {
103127 operation.tooltip = function () {
103128 return _t('operations.reverse.description.' + reverseTypeID());
103131 operation.annotation = function () {
103133 return _t('operations.reverse.annotation.' + reverseTypeID(), {
103138 operation.id = 'reverse';
103139 operation.keys = [_t('operations.reverse.key')];
103140 operation.title = _t('operations.reverse.title');
103141 operation.behavior = behaviorOperation(context).which(operation);
103145 function operationSplit(context, selectedIDs) {
103146 var _vertexIds = selectedIDs.filter(function (id) {
103147 return context.graph().geometry(id) === 'vertex';
103150 var _selectedWayIds = selectedIDs.filter(function (id) {
103151 var entity = context.graph().hasEntity(id);
103152 return entity && entity.type === 'way';
103155 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
103157 var _action = actionSplit(_vertexIds);
103160 var _geometry = 'feature';
103161 var _waysAmount = 'single';
103163 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
103166 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
103167 _ways = _action.ways(context.graph());
103170 _ways.forEach(function (way) {
103171 geometries[way.geometry(context.graph())] = true;
103174 if (Object.keys(geometries).length === 1) {
103175 _geometry = Object.keys(geometries)[0];
103178 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
103181 var operation = function operation() {
103182 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
103184 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
103185 // filter out relations that may have had member additions
103186 return context.entity(id).type === 'way';
103189 context.enter(modeSelect(context, idsToSelect));
103192 operation.relatedEntityIds = function () {
103193 return _selectedWayIds.length ? [] : _ways.map(function (way) {
103198 operation.available = function () {
103202 operation.disabled = function () {
103203 var reason = _action.disabled(context.graph());
103207 } else if (selectedIDs.some(context.hasHiddenConnections)) {
103208 return 'connected_to_hidden';
103214 operation.tooltip = function () {
103215 var disable = operation.disabled();
103216 if (disable) return _t('operations.split.' + disable);
103217 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
103220 operation.annotation = function () {
103221 return _t('operations.split.annotation.' + _geometry, {
103226 operation.id = 'split';
103227 operation.keys = [_t('operations.split.key')];
103228 operation.title = _t('operations.split.title');
103229 operation.behavior = behaviorOperation(context).which(operation);
103233 function operationStraighten(context, selectedIDs) {
103234 var _wayIDs = selectedIDs.filter(function (id) {
103235 return id.charAt(0) === 'w';
103238 var _nodeIDs = selectedIDs.filter(function (id) {
103239 return id.charAt(0) === 'n';
103242 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
103244 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
103246 var _coords = _nodes.map(function (n) {
103250 var _extent = utilTotalExtent(selectedIDs, context.graph());
103252 var _action = chooseAction();
103256 function chooseAction() {
103257 // straighten selected nodes
103258 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
103260 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
103261 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
103265 for (var i = 0; i < selectedIDs.length; i++) {
103266 var entity = context.entity(selectedIDs[i]);
103268 if (entity.type === 'node') {
103270 } else if (entity.type !== 'way' || entity.isClosed()) {
103271 return null; // exit early, can't straighten these
103274 startNodeIDs.push(entity.first());
103275 endNodeIDs.push(entity.last());
103276 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
103279 startNodeIDs = startNodeIDs.filter(function (n) {
103280 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
103282 endNodeIDs = endNodeIDs.filter(function (n) {
103283 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
103284 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
103286 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
103288 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
103291 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
103293 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
103296 // If we're only straightenting between two points, we only need that extent visible
103297 _extent = utilTotalExtent(_nodeIDs, context.graph());
103301 return actionStraightenWay(selectedIDs, context.projection);
103309 context.perform(_action, operation.annotation());
103310 window.setTimeout(function () {
103311 context.validator().validate();
103312 }, 300); // after any transition
103315 operation.available = function () {
103316 return Boolean(_action);
103319 operation.disabled = function () {
103320 var reason = _action.disabled(context.graph());
103324 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
103326 } else if (someMissing()) {
103327 return 'not_downloaded';
103328 } else if (selectedIDs.some(context.hasHiddenConnections)) {
103329 return 'connected_to_hidden';
103334 function someMissing() {
103335 if (context.inIntro()) return false;
103336 var osm = context.connection();
103339 var missing = _coords.filter(function (loc) {
103340 return !osm.isDataLoaded(loc);
103344 missing.forEach(function (loc) {
103345 context.loadTileAtLoc(loc);
103355 operation.tooltip = function () {
103356 var disable = operation.disabled();
103357 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
103360 operation.annotation = function () {
103361 return _t('operations.straighten.annotation.' + _geometry, {
103362 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
103366 operation.id = 'straighten';
103367 operation.keys = [_t('operations.straighten.key')];
103368 operation.title = _t('operations.straighten.title');
103369 operation.behavior = behaviorOperation(context).which(operation);
103373 var Operations = /*#__PURE__*/Object.freeze({
103375 operationCircularize: operationCircularize,
103376 operationContinue: operationContinue,
103377 operationCopy: operationCopy,
103378 operationDelete: operationDelete,
103379 operationDisconnect: operationDisconnect,
103380 operationDowngrade: operationDowngrade,
103381 operationExtract: operationExtract,
103382 operationMerge: operationMerge,
103383 operationMove: operationMove,
103384 operationOrthogonalize: operationOrthogonalize,
103385 operationPaste: operationPaste,
103386 operationReflectShort: operationReflectShort,
103387 operationReflectLong: operationReflectLong,
103388 operationReverse: operationReverse,
103389 operationRotate: operationRotate,
103390 operationSplit: operationSplit,
103391 operationStraighten: operationStraighten
103394 function modeSelect(context, selectedIDs) {
103399 var keybinding = utilKeybinding('select');
103401 var _breatheBehavior = behaviorBreathe();
103403 var _modeDragNode = modeDragNode(context);
103409 var _newFeature = false;
103410 var _follow = false; // `_focusedParentWayId` is used when we visit a vertex with multiple
103411 // parents, and we want to remember which parent line we started on.
103413 var _focusedParentWayId;
103418 if (selectedIDs && selectedIDs.length === 1) {
103419 return context.hasEntity(selectedIDs[0]);
103423 function selectedEntities() {
103424 return selectedIDs.map(function (id) {
103425 return context.hasEntity(id);
103429 function checkSelectedIDs() {
103432 if (Array.isArray(selectedIDs)) {
103433 ids = selectedIDs.filter(function (id) {
103434 return context.hasEntity(id);
103439 context.enter(modeBrowse(context));
103441 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
103442 // switch between single- and multi-select UI
103443 context.enter(modeSelect(context, ids));
103449 } // find the parent ways for nextVertex, previousVertex, and selectParent
103452 function parentWaysIdsOfSelection(onlyCommonParents) {
103453 var graph = context.graph();
103456 for (var i = 0; i < selectedIDs.length; i++) {
103457 var entity = context.hasEntity(selectedIDs[i]);
103459 if (!entity || entity.geometry(graph) !== 'vertex') {
103460 return []; // selection includes some non-vertices
103463 var currParents = graph.parentWays(entity).map(function (w) {
103472 parents = (onlyCommonParents ? utilArrayIntersection : utilArrayUnion)(parents, currParents);
103480 } // find the child nodes for selected ways
103483 function childNodeIdsOfSelection(onlyCommon) {
103484 var graph = context.graph();
103487 for (var i = 0; i < selectedIDs.length; i++) {
103488 var entity = context.hasEntity(selectedIDs[i]);
103490 if (!entity || !['area', 'line'].includes(entity.geometry(graph))) {
103491 return []; // selection includes non-area/non-line
103494 var currChilds = graph.childNodes(entity).map(function (node) {
103503 childs = (onlyCommon ? utilArrayIntersection : utilArrayUnion)(childs, currChilds);
103513 function checkFocusedParent() {
103514 if (_focusedParentWayId) {
103515 var parents = parentWaysIdsOfSelection(true);
103516 if (parents.indexOf(_focusedParentWayId) === -1) _focusedParentWayId = null;
103520 function parentWayIdForVertexNavigation() {
103521 var parentIds = parentWaysIdsOfSelection(true);
103523 if (_focusedParentWayId && parentIds.indexOf(_focusedParentWayId) !== -1) {
103524 // prefer the previously seen parent
103525 return _focusedParentWayId;
103528 return parentIds.length ? parentIds[0] : null;
103531 mode.selectedIDs = function (val) {
103532 if (!arguments.length) return selectedIDs;
103537 mode.zoomToSelected = function () {
103538 context.map().zoomToEase(selectedEntities());
103541 mode.newFeature = function (val) {
103542 if (!arguments.length) return _newFeature;
103547 mode.selectBehavior = function (val) {
103548 if (!arguments.length) return _selectBehavior;
103553 mode.follow = function (val) {
103554 if (!arguments.length) return _follow;
103559 function loadOperations() {
103560 _operations.forEach(function (operation) {
103561 if (operation.behavior) {
103562 context.uninstall(operation.behavior);
103566 _operations = Object.values(Operations).map(function (o) {
103567 return o(context, selectedIDs);
103568 }).filter(function (o) {
103569 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
103570 }).concat([// group copy/downgrade/delete operation together at the end of the list
103571 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
103572 return operation.available();
103575 _operations.forEach(function (operation) {
103576 if (operation.behavior) {
103577 context.install(operation.behavior);
103579 }); // remove any displayed menu
103582 context.ui().closeEditMenu();
103585 mode.operations = function () {
103589 mode.enter = function () {
103590 if (!checkSelectedIDs()) return;
103591 context.features().forceVisible(selectedIDs);
103593 _modeDragNode.restoreSelectedIDs(selectedIDs);
103597 if (!_behaviors.length) {
103598 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
103599 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
103602 _behaviors.forEach(context.install);
103604 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) {
103605 return uiCmd('⇧' + key);
103606 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
103607 return uiCmd('⇧⌥' + key);
103608 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
103609 return uiCmd('⇧' + key);
103610 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
103611 return uiCmd('⇧⌥' + key);
103612 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], focusNextParent).on(uiCmd('⌘↑'), selectParent).on(uiCmd('⌘↓'), selectChild).on('⎋', esc, true);
103613 select(document).call(keybinding);
103614 context.ui().sidebar.select(selectedIDs, _newFeature);
103615 context.history().on('change.select', function () {
103616 loadOperations(); // reselect after change in case relation members were removed or added
103619 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
103620 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
103623 _breatheBehavior.restartIfNeeded(context.surface());
103625 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
103629 var extent = geoExtent();
103630 var graph = context.graph();
103631 selectedIDs.forEach(function (id) {
103632 var entity = context.entity(id);
103634 extent._extend(entity.extent(graph));
103636 var loc = extent.center();
103637 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
103642 function nudgeSelection(delta) {
103644 // prevent nudging during low zoom selection
103645 if (!context.map().withinEditableZoom()) return;
103646 var moveOp = operationMove(context, selectedIDs);
103648 if (moveOp.disabled()) {
103649 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
103651 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
103652 context.validator().validate();
103657 function scaleSelection(factor) {
103659 // prevent scaling during low zoom selection
103660 if (!context.map().withinEditableZoom()) return;
103661 var nodes = utilGetAllNodes(selectedIDs, context.graph());
103662 var isUp = factor > 1; // can only scale if multiple nodes are selected
103664 if (nodes.length <= 1) return;
103665 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
103666 // object, but we don't want an actual scale operation at this point.
103668 function scalingDisabled() {
103671 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
103673 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
103674 return 'not_downloaded';
103675 } else if (selectedIDs.some(context.hasHiddenConnections)) {
103676 return 'connected_to_hidden';
103682 if (isUp) return false;
103683 var dLon = Math.abs(extent[1][0] - extent[0][0]);
103684 var dLat = Math.abs(extent[1][1] - extent[0][1]);
103685 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
103688 function someMissing() {
103689 if (context.inIntro()) return false;
103690 var osm = context.connection();
103693 var missing = nodes.filter(function (n) {
103694 return !osm.isDataLoaded(n.loc);
103698 missing.forEach(function (loc) {
103699 context.loadTileAtLoc(loc);
103708 function incompleteRelation(id) {
103709 var entity = context.entity(id);
103710 return entity.type === 'relation' && !entity.isComplete(context.graph());
103714 var disabled = scalingDisabled();
103717 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
103718 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
103720 var pivot = context.projection(extent.center());
103721 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
103724 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
103725 context.validator().validate();
103730 function didDoubleUp(d3_event, loc) {
103731 if (!context.map().withinEditableZoom()) return;
103732 var target = select(d3_event.target);
103733 var datum = target.datum();
103734 var entity = datum && datum.properties && datum.properties.entity;
103737 if (entity instanceof osmWay && target.classed('target')) {
103738 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
103739 var prev = entity.nodes[choice.index - 1];
103740 var next = entity.nodes[choice.index];
103741 context.perform(actionAddMidpoint({
103744 }, osmNode()), _t('operations.add.annotation.vertex'));
103745 } else if (entity.type === 'midpoint') {
103746 context.perform(actionAddMidpoint({
103749 }, osmNode()), _t('operations.add.annotation.vertex'));
103753 function selectElements() {
103754 if (!checkSelectedIDs()) return;
103755 var surface = context.surface();
103756 surface.selectAll('.selected-member').classed('selected-member', false);
103757 surface.selectAll('.selected').classed('selected', false);
103758 surface.selectAll('.related').classed('related', false); // reload `_focusedParentWayId` based on the current selection
103762 if (_focusedParentWayId) {
103763 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
103766 if (context.map().withinEditableZoom()) {
103767 // Apply selection styling if not in wide selection
103768 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
103769 /* skipMultipolgonMembers */
103770 )).classed('selected-member', true);
103771 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
103776 if (context.container().select('.combobox').size()) return;
103777 context.enter(modeBrowse(context));
103780 function firstVertex(d3_event) {
103781 d3_event.preventDefault();
103782 var entity = singular();
103783 var parentId = parentWayIdForVertexNavigation();
103786 if (entity && entity.type === 'way') {
103789 way = context.entity(parentId);
103792 _focusedParentWayId = way && way.id;
103795 context.enter(mode.selectedIDs([way.first()]).follow(true));
103799 function lastVertex(d3_event) {
103800 d3_event.preventDefault();
103801 var entity = singular();
103802 var parentId = parentWayIdForVertexNavigation();
103805 if (entity && entity.type === 'way') {
103808 way = context.entity(parentId);
103811 _focusedParentWayId = way && way.id;
103814 context.enter(mode.selectedIDs([way.last()]).follow(true));
103818 function previousVertex(d3_event) {
103819 d3_event.preventDefault();
103820 var parentId = parentWayIdForVertexNavigation();
103821 _focusedParentWayId = parentId;
103823 var way = context.entity(parentId);
103824 var length = way.nodes.length;
103825 var curr = way.nodes.indexOf(selectedIDs[0]);
103830 } else if (way.isClosed()) {
103835 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
103839 function nextVertex(d3_event) {
103840 d3_event.preventDefault();
103841 var parentId = parentWayIdForVertexNavigation();
103842 _focusedParentWayId = parentId;
103844 var way = context.entity(parentId);
103845 var length = way.nodes.length;
103846 var curr = way.nodes.indexOf(selectedIDs[0]);
103849 if (curr < length - 1) {
103851 } else if (way.isClosed()) {
103856 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
103860 function focusNextParent(d3_event) {
103861 d3_event.preventDefault();
103862 var parents = parentWaysIdsOfSelection(true);
103863 if (!parents || parents.length < 2) return;
103864 var index = parents.indexOf(_focusedParentWayId);
103866 if (index < 0 || index > parents.length - 2) {
103867 _focusedParentWayId = parents[0];
103869 _focusedParentWayId = parents[index + 1];
103872 var surface = context.surface();
103873 surface.selectAll('.related').classed('related', false);
103875 if (_focusedParentWayId) {
103876 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
103880 function selectParent(d3_event) {
103881 d3_event.preventDefault();
103882 var currentSelectedIds = mode.selectedIDs();
103883 var parentIds = _focusedParentWayId ? [_focusedParentWayId] : parentWaysIdsOfSelection(false);
103884 if (!parentIds.length) return;
103885 context.enter(mode.selectedIDs(parentIds)); // set this after re-entering the selection since we normally want it cleared on exit
103887 _focusedVertexIds = currentSelectedIds;
103890 function selectChild(d3_event) {
103891 d3_event.preventDefault();
103892 var currentSelectedIds = mode.selectedIDs();
103893 var childIds = _focusedVertexIds ? _focusedVertexIds.filter(function (id) {
103894 return context.hasEntity(id);
103895 }) : childNodeIdsOfSelection(true);
103896 if (!childIds || !childIds.length) return;
103897 if (currentSelectedIds.length === 1) _focusedParentWayId = currentSelectedIds[0];
103898 context.enter(mode.selectedIDs(childIds));
103902 mode.exit = function () {
103903 // we could enter the mode multiple times but it's only new the first time
103905 _focusedVertexIds = null;
103907 _operations.forEach(function (operation) {
103908 if (operation.behavior) {
103909 context.uninstall(operation.behavior);
103915 _behaviors.forEach(context.uninstall);
103917 select(document).call(keybinding.unbind);
103918 context.ui().closeEditMenu();
103919 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
103920 var surface = context.surface();
103921 surface.selectAll('.selected-member').classed('selected-member', false);
103922 surface.selectAll('.selected').classed('selected', false);
103923 surface.selectAll('.highlighted').classed('highlighted', false);
103924 surface.selectAll('.related').classed('related', false);
103925 context.map().on('drawn.select', null);
103926 context.ui().sidebar.hide();
103927 context.features().forceVisible([]);
103928 var entity = singular();
103930 if (_newFeature && entity && entity.type === 'relation' && // no tags
103931 Object.keys(entity.tags).length === 0 && // no parent relations
103932 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
103933 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
103934 // the user added this relation but didn't edit it at all, so just delete it
103935 var deleteAction = actionDeleteRelation(entity.id, true
103936 /* don't delete untagged members */
103938 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
103945 function uiLasso(context) {
103947 lasso.coordinates = [];
103949 function lasso(selection) {
103950 context.container().classed('lasso', true);
103951 group = selection.append('g').attr('class', 'lasso hide');
103952 polygon = group.append('path').attr('class', 'lasso-path');
103953 group.call(uiToggle(true));
103958 polygon.data([lasso.coordinates]).attr('d', function (d) {
103959 return 'M' + d.join(' L') + ' Z';
103964 lasso.extent = function () {
103965 return lasso.coordinates.reduce(function (extent, point) {
103966 return extent.extend(geoExtent(point));
103970 lasso.p = function (_) {
103971 if (!arguments.length) return lasso;
103972 lasso.coordinates.push(_);
103977 lasso.close = function () {
103979 group.call(uiToggle(false, function () {
103984 context.container().classed('lasso', false);
103990 function behaviorLasso(context) {
103991 // use pointer events on supported platforms; fallback to mouse events
103992 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
103994 var behavior = function behavior(selection) {
103997 function pointerdown(d3_event) {
103998 var button = 0; // left
104000 if (d3_event.button === button && d3_event.shiftKey === true) {
104002 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
104003 d3_event.stopPropagation();
104007 function pointermove() {
104009 lasso = uiLasso(context);
104010 context.surface().call(lasso);
104013 lasso.p(context.map().mouse());
104016 function normalize(a, b) {
104017 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])]];
104022 var graph = context.graph();
104025 if (context.map().editableDataEnabled(true
104027 ) && context.map().isInWideSelection()) {
104028 // only select from the visible nodes
104029 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
104030 } else if (!context.map().editableDataEnabled()) {
104034 var bounds = lasso.extent().map(context.projection.invert);
104035 var extent = geoExtent(normalize(bounds[0], bounds[1]));
104036 var intersects = context.history().intersects(extent).filter(function (entity) {
104037 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
104038 }); // sort the lassoed nodes as best we can
104040 intersects.sort(function (node1, node2) {
104041 var parents1 = graph.parentWays(node1);
104042 var parents2 = graph.parentWays(node2);
104044 if (parents1.length && parents2.length) {
104045 // both nodes are vertices
104046 var sharedParents = utilArrayIntersection(parents1, parents2);
104048 if (sharedParents.length) {
104049 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
104051 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
104053 // vertices do not share a way; group them by their respective parent ways
104054 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
104056 } else if (parents1.length || parents2.length) {
104057 // only one node is a vertex; sort standalone points before vertices
104058 return parents1.length - parents2.length;
104059 } // both nodes are standalone points; sort left to right
104062 return node1.loc[0] - node2.loc[0];
104064 return intersects.map(function (entity) {
104070 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
104076 context.enter(modeSelect(context, ids));
104080 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
104083 behavior.off = function (selection) {
104084 selection.on(_pointerPrefix + 'down.lasso', null);
104090 function modeBrowse(context) {
104094 title: _t('modes.browse.title'),
104095 description: _t('modes.browse.description')
104103 mode.selectBehavior = function (val) {
104104 if (!arguments.length) return _selectBehavior;
104109 mode.enter = function () {
104110 if (!_behaviors.length) {
104111 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
104112 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
104115 _behaviors.forEach(context.install); // Get focus on the body.
104118 if (document.activeElement && document.activeElement.blur) {
104119 document.activeElement.blur();
104123 context.ui().sidebar.show(sidebar);
104125 context.ui().sidebar.select(null);
104129 mode.exit = function () {
104130 context.ui().sidebar.hover.cancel();
104132 _behaviors.forEach(context.uninstall);
104135 context.ui().sidebar.hide();
104139 mode.sidebar = function (_) {
104140 if (!arguments.length) return sidebar;
104145 mode.operations = function () {
104146 return [operationPaste(context)];
104152 function behaviorAddWay(context) {
104153 var dispatch = dispatch$8('start', 'startFromWay', 'startFromNode');
104154 var draw = behaviorDraw(context);
104156 function behavior(surface) {
104157 draw.on('click', function () {
104158 dispatch.apply('start', this, arguments);
104159 }).on('clickWay', function () {
104160 dispatch.apply('startFromWay', this, arguments);
104161 }).on('clickNode', function () {
104162 dispatch.apply('startFromNode', this, arguments);
104163 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
104164 context.map().dblclickZoomEnable(false);
104168 behavior.off = function (surface) {
104169 surface.call(draw.off);
104172 behavior.cancel = function () {
104173 window.setTimeout(function () {
104174 context.map().dblclickZoomEnable(true);
104176 context.enter(modeBrowse(context));
104179 return utilRebind(behavior, dispatch, 'on');
104182 function behaviorHash(context) {
104183 // cached window.location.hash
104184 var _cachedHash = null; // allowable latitude range
104186 var _latitudeLimit = 90 - 1e-8;
104188 function computedHashParameters() {
104189 var map = context.map();
104190 var center = map.center();
104192 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
104193 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
104196 var selected = context.selectedIDs().filter(function (id) {
104197 return context.hasEntity(id);
104201 newParams.id = selected.join(',');
104204 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
104205 return Object.assign(oldParams, newParams);
104208 function computedHash() {
104209 return '#' + utilQsString(computedHashParameters(), true);
104212 function computedTitle(includeChangeCount) {
104213 var baseTitle = context.documentTitleBase() || 'iD';
104217 var selected = context.selectedIDs().filter(function (id) {
104218 return context.hasEntity(id);
104222 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
104224 if (selected.length > 1) {
104225 contextual = _t('title.labeled_and_more', {
104227 count: selected.length - 1
104230 contextual = firstLabel;
104236 if (includeChangeCount) {
104237 changeCount = context.history().difference().summary().length;
104240 titleID = contextual ? 'changes_context' : 'changes';
104245 return _t('title.format.' + titleID, {
104255 function updateTitle(includeChangeCount) {
104256 if (!context.setsDocumentTitle()) return;
104257 var newTitle = computedTitle(includeChangeCount);
104259 if (document.title !== newTitle) {
104260 document.title = newTitle;
104264 function updateHashIfNeeded() {
104265 if (context.inIntro()) return;
104266 var latestHash = computedHash();
104268 if (_cachedHash !== latestHash) {
104269 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
104270 // though unavoidably creating a browser history entry
104272 window.history.replaceState(null, computedTitle(false
104273 /* includeChangeCount */
104274 ), latestHash); // set the title we want displayed for the browser tab/window
104277 /* includeChangeCount */
104282 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
104284 var _throttledUpdateTitle = throttle(function () {
104286 /* includeChangeCount */
104290 function hashchange() {
104291 // ignore spurious hashchange events
104292 if (window.location.hash === _cachedHash) return;
104293 _cachedHash = window.location.hash;
104294 var q = utilStringQs(_cachedHash);
104295 var mapArgs = (q.map || '').split('/').map(Number);
104297 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
104301 // don't update if the new hash already reflects the state of iD
104302 if (_cachedHash === computedHash()) return;
104303 var mode = context.mode();
104304 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
104307 var ids = q.id.split(',').filter(function (id) {
104308 return context.hasEntity(id);
104311 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
104312 context.enter(modeSelect(context, ids));
104317 var center = context.map().center();
104318 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
104319 var maxdist = 500; // Don't allow the hash location to change too much while drawing
104320 // This can happen if the user accidentally hit the back button. #3996
104322 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
104323 context.enter(modeBrowse(context));
104330 context.map().on('move.behaviorHash', _throttledUpdate);
104331 context.history().on('change.behaviorHash', _throttledUpdateTitle);
104332 context.on('enter.behaviorHash', _throttledUpdate);
104333 select(window).on('hashchange.behaviorHash', hashchange);
104335 if (window.location.hash) {
104336 var q = utilStringQs(window.location.hash);
104339 //if (!context.history().hasRestorableChanges()) {
104340 // targeting specific features: download, select, and zoom to them
104341 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
104344 if (q.walkthrough === 'true') {
104345 behavior.startWalkthrough = true;
104349 behavior.hadHash = true;
104357 behavior.off = function () {
104358 _throttledUpdate.cancel();
104360 _throttledUpdateTitle.cancel();
104362 context.map().on('move.behaviorHash', null);
104363 context.on('enter.behaviorHash', null);
104364 select(window).on('hashchange.behaviorHash', null);
104365 window.location.hash = '';
104371 // This is only done in testing because of the performance penalty.
104373 var debug = false; // Reexport just what our tests use, see #4379
104377 geoProjection: projection,
104378 polygonArea: d3_polygonArea,
104379 polygonCentroid: d3_polygonCentroid,
104385 var iD = /*#__PURE__*/Object.freeze({
104389 actionAddEntity: actionAddEntity,
104390 actionAddMember: actionAddMember,
104391 actionAddMidpoint: actionAddMidpoint,
104392 actionAddVertex: actionAddVertex,
104393 actionChangeMember: actionChangeMember,
104394 actionChangePreset: actionChangePreset,
104395 actionChangeTags: actionChangeTags,
104396 actionCircularize: actionCircularize,
104397 actionConnect: actionConnect,
104398 actionCopyEntities: actionCopyEntities,
104399 actionDeleteMember: actionDeleteMember,
104400 actionDeleteMultiple: actionDeleteMultiple,
104401 actionDeleteNode: actionDeleteNode,
104402 actionDeleteRelation: actionDeleteRelation,
104403 actionDeleteWay: actionDeleteWay,
104404 actionDiscardTags: actionDiscardTags,
104405 actionDisconnect: actionDisconnect,
104406 actionExtract: actionExtract,
104407 actionJoin: actionJoin,
104408 actionMerge: actionMerge,
104409 actionMergeNodes: actionMergeNodes,
104410 actionMergePolygon: actionMergePolygon,
104411 actionMergeRemoteChanges: actionMergeRemoteChanges,
104412 actionMove: actionMove,
104413 actionMoveMember: actionMoveMember,
104414 actionMoveNode: actionMoveNode,
104415 actionNoop: actionNoop,
104416 actionOrthogonalize: actionOrthogonalize,
104417 actionRestrictTurn: actionRestrictTurn,
104418 actionReverse: actionReverse,
104419 actionRevert: actionRevert,
104420 actionRotate: actionRotate,
104421 actionScale: actionScale,
104422 actionSplit: actionSplit,
104423 actionStraightenNodes: actionStraightenNodes,
104424 actionStraightenWay: actionStraightenWay,
104425 actionUnrestrictTurn: actionUnrestrictTurn,
104426 actionReflect: actionReflect,
104427 actionUpgradeTags: actionUpgradeTags,
104428 behaviorAddWay: behaviorAddWay,
104429 behaviorBreathe: behaviorBreathe,
104430 behaviorDrag: behaviorDrag,
104431 behaviorDrawWay: behaviorDrawWay,
104432 behaviorDraw: behaviorDraw,
104433 behaviorEdit: behaviorEdit,
104434 behaviorHash: behaviorHash,
104435 behaviorHover: behaviorHover,
104436 behaviorLasso: behaviorLasso,
104437 behaviorOperation: behaviorOperation,
104438 behaviorPaste: behaviorPaste,
104439 behaviorSelect: behaviorSelect,
104440 coreContext: coreContext,
104441 coreFileFetcher: coreFileFetcher,
104442 fileFetcher: _mainFileFetcher,
104443 coreDifference: coreDifference,
104445 coreHistory: coreHistory,
104446 coreLocalizer: coreLocalizer,
104448 localizer: _mainLocalizer,
104449 coreLocations: coreLocations,
104450 locationManager: _mainLocations,
104451 prefs: corePreferences,
104453 coreUploader: coreUploader,
104454 coreValidator: coreValidator,
104456 geoLatToMeters: geoLatToMeters,
104457 geoLonToMeters: geoLonToMeters,
104458 geoMetersToLat: geoMetersToLat,
104459 geoMetersToLon: geoMetersToLon,
104460 geoMetersToOffset: geoMetersToOffset,
104461 geoOffsetToMeters: geoOffsetToMeters,
104462 geoScaleToZoom: geoScaleToZoom,
104463 geoSphericalClosestNode: geoSphericalClosestNode,
104464 geoSphericalDistance: geoSphericalDistance,
104465 geoZoomToScale: geoZoomToScale,
104467 geoChooseEdge: geoChooseEdge,
104468 geoEdgeEqual: geoEdgeEqual,
104469 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
104470 geoHasLineIntersections: geoHasLineIntersections,
104471 geoHasSelfIntersections: geoHasSelfIntersections,
104473 geoLineIntersection: geoLineIntersection,
104474 geoPathHasIntersections: geoPathHasIntersections,
104475 geoPathIntersections: geoPathIntersections,
104476 geoPathLength: geoPathLength,
104477 geoPointInPolygon: geoPointInPolygon,
104478 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
104479 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
104480 geoViewportEdge: geoViewportEdge,
104481 geoRawMercator: geoRawMercator,
104483 geoVecAngle: geoVecAngle,
104484 geoVecCross: geoVecCross,
104486 geoVecEqual: geoVecEqual,
104487 geoVecFloor: geoVecFloor,
104488 geoVecInterp: geoVecInterp,
104489 geoVecLength: geoVecLength,
104490 geoVecLengthSquare: geoVecLengthSquare,
104491 geoVecNormalize: geoVecNormalize,
104492 geoVecNormalizedDot: geoVecNormalizedDot,
104493 geoVecProject: geoVecProject,
104494 geoVecSubtract: geoVecSubtract,
104495 geoVecScale: geoVecScale,
104496 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
104497 geoOrthoCalcScore: geoOrthoCalcScore,
104498 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
104499 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
104500 modeAddArea: modeAddArea,
104501 modeAddLine: modeAddLine,
104502 modeAddPoint: modeAddPoint,
104503 modeAddNote: modeAddNote,
104504 modeBrowse: modeBrowse,
104505 modeDragNode: modeDragNode,
104506 modeDragNote: modeDragNote,
104507 modeDrawArea: modeDrawArea,
104508 modeDrawLine: modeDrawLine,
104510 modeRotate: modeRotate,
104512 modeSelect: modeSelect,
104513 modeSelectData: modeSelectData,
104514 modeSelectError: modeSelectError,
104515 modeSelectNote: modeSelectNote,
104516 operationCircularize: operationCircularize,
104517 operationContinue: operationContinue,
104518 operationCopy: operationCopy,
104519 operationDelete: operationDelete,
104520 operationDisconnect: operationDisconnect,
104521 operationDowngrade: operationDowngrade,
104522 operationExtract: operationExtract,
104523 operationMerge: operationMerge,
104524 operationMove: operationMove,
104525 operationOrthogonalize: operationOrthogonalize,
104526 operationPaste: operationPaste,
104527 operationReflectShort: operationReflectShort,
104528 operationReflectLong: operationReflectLong,
104529 operationReverse: operationReverse,
104530 operationRotate: operationRotate,
104531 operationSplit: operationSplit,
104532 operationStraighten: operationStraighten,
104533 osmChangeset: osmChangeset,
104537 osmRelation: osmRelation,
104540 osmIntersection: osmIntersection,
104542 osmInferRestriction: osmInferRestriction,
104544 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
104545 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
104546 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
104547 osmJoinWays: osmJoinWays,
104548 get osmAreaKeys () { return osmAreaKeys; },
104549 osmSetAreaKeys: osmSetAreaKeys,
104550 osmTagSuggestingArea: osmTagSuggestingArea,
104551 get osmPointTags () { return osmPointTags; },
104552 osmSetPointTags: osmSetPointTags,
104553 get osmVertexTags () { return osmVertexTags; },
104554 osmSetVertexTags: osmSetVertexTags,
104555 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
104556 osmOneWayTags: osmOneWayTags,
104557 osmPavedTags: osmPavedTags,
104558 osmIsInterestingTag: osmIsInterestingTag,
104559 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
104560 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
104561 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
104562 presetCategory: presetCategory,
104563 presetCollection: presetCollection,
104564 presetField: presetField,
104565 presetPreset: presetPreset,
104566 presetManager: _mainPresetIndex,
104567 presetIndex: presetIndex,
104568 rendererBackgroundSource: rendererBackgroundSource,
104569 rendererBackground: rendererBackground,
104570 rendererFeatures: rendererFeatures,
104571 rendererMap: rendererMap,
104572 rendererPhotos: rendererPhotos,
104573 rendererTileLayer: rendererTileLayer,
104575 serviceKeepRight: serviceKeepRight,
104576 serviceImproveOSM: serviceImproveOSM,
104577 serviceOsmose: serviceOsmose,
104578 serviceMapillary: serviceMapillary,
104579 serviceMapRules: serviceMapRules,
104580 serviceNominatim: serviceNominatim,
104581 serviceNsi: serviceNsi,
104582 serviceOpenstreetcam: serviceOpenstreetcam,
104583 serviceOsm: serviceOsm,
104584 serviceOsmWikibase: serviceOsmWikibase,
104585 serviceStreetside: serviceStreetside,
104586 serviceTaginfo: serviceTaginfo,
104587 serviceVectorTile: serviceVectorTile,
104588 serviceWikidata: serviceWikidata,
104589 serviceWikipedia: serviceWikipedia,
104594 svgKeepRight: svgKeepRight,
104596 svgGeolocate: svgGeolocate,
104600 svgMapillaryImages: svgMapillaryImages,
104601 svgMapillarySigns: svgMapillarySigns,
104602 svgMidpoints: svgMidpoints,
104604 svgMarkerSegments: svgMarkerSegments,
104605 svgOpenstreetcamImages: svgOpenstreetcamImages,
104607 svgPassiveVertex: svgPassiveVertex,
104609 svgPointTransform: svgPointTransform,
104611 svgRelationMemberTags: svgRelationMemberTags,
104612 svgSegmentWay: svgSegmentWay,
104613 svgStreetside: svgStreetside,
104614 svgTagClasses: svgTagClasses,
104615 svgTagPattern: svgTagPattern,
104618 svgVertices: svgVertices,
104619 uiFieldDefaultCheck: uiFieldCheck,
104620 uiFieldOnewayCheck: uiFieldCheck,
104621 uiFieldCheck: uiFieldCheck,
104622 uiFieldManyCombo: uiFieldCombo,
104623 uiFieldMultiCombo: uiFieldCombo,
104624 uiFieldNetworkCombo: uiFieldCombo,
104625 uiFieldSemiCombo: uiFieldCombo,
104626 uiFieldTypeCombo: uiFieldCombo,
104627 uiFieldCombo: uiFieldCombo,
104628 uiFieldUrl: uiFieldText,
104629 uiFieldIdentifier: uiFieldText,
104630 uiFieldNumber: uiFieldText,
104631 uiFieldTel: uiFieldText,
104632 uiFieldEmail: uiFieldText,
104633 uiFieldText: uiFieldText,
104634 uiFieldAccess: uiFieldAccess,
104635 uiFieldAddress: uiFieldAddress,
104636 uiFieldCycleway: uiFieldCycleway,
104637 uiFieldLanes: uiFieldLanes,
104638 uiFieldLocalized: uiFieldLocalized,
104639 uiFieldRoadspeed: uiFieldRoadspeed,
104640 uiFieldStructureRadio: uiFieldRadio,
104641 uiFieldRadio: uiFieldRadio,
104642 uiFieldRestrictions: uiFieldRestrictions,
104643 uiFieldTextarea: uiFieldTextarea,
104644 uiFieldWikidata: uiFieldWikidata,
104645 uiFieldWikipedia: uiFieldWikipedia,
104648 uiPanelBackground: uiPanelBackground,
104649 uiPanelHistory: uiPanelHistory,
104650 uiPanelLocation: uiPanelLocation,
104651 uiPanelMeasurement: uiPanelMeasurement,
104652 uiInfoPanels: uiInfoPanels,
104653 uiPaneBackground: uiPaneBackground,
104654 uiPaneHelp: uiPaneHelp,
104655 uiPaneIssues: uiPaneIssues,
104656 uiPaneMapData: uiPaneMapData,
104657 uiPanePreferences: uiPanePreferences,
104658 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
104659 uiSectionBackgroundList: uiSectionBackgroundList,
104660 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
104661 uiSectionChanges: uiSectionChanges,
104662 uiSectionDataLayers: uiSectionDataLayers,
104663 uiSectionEntityIssues: uiSectionEntityIssues,
104664 uiSectionFeatureType: uiSectionFeatureType,
104665 uiSectionMapFeatures: uiSectionMapFeatures,
104666 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
104667 uiSectionOverlayList: uiSectionOverlayList,
104668 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
104669 uiSectionPresetFields: uiSectionPresetFields,
104670 uiSectionPrivacy: uiSectionPrivacy,
104671 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
104672 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
104673 uiSectionRawTagEditor: uiSectionRawTagEditor,
104674 uiSectionSelectionList: uiSectionSelectionList,
104675 uiSectionValidationIssues: uiSectionValidationIssues,
104676 uiSectionValidationOptions: uiSectionValidationOptions,
104677 uiSectionValidationRules: uiSectionValidationRules,
104678 uiSectionValidationStatus: uiSectionValidationStatus,
104679 uiSettingsCustomBackground: uiSettingsCustomBackground,
104680 uiSettingsCustomData: uiSettingsCustomData,
104683 uiAttribution: uiAttribution,
104684 uiChangesetEditor: uiChangesetEditor,
104686 uiCombobox: uiCombobox,
104688 uiCommitWarnings: uiCommitWarnings,
104690 uiConflicts: uiConflicts,
104691 uiContributors: uiContributors,
104693 uiDataEditor: uiDataEditor,
104694 uiDataHeader: uiDataHeader,
104695 uiDisclosure: uiDisclosure,
104696 uiEditMenu: uiEditMenu,
104697 uiEntityEditor: uiEntityEditor,
104698 uiFeatureInfo: uiFeatureInfo,
104699 uiFeatureList: uiFeatureList,
104701 uiFieldHelp: uiFieldHelp,
104703 uiFormFields: uiFormFields,
104704 uiFullScreen: uiFullScreen,
104705 uiGeolocate: uiGeolocate,
104706 uiImproveOsmComments: uiImproveOsmComments,
104707 uiImproveOsmDetails: uiImproveOsmDetails,
104708 uiImproveOsmEditor: uiImproveOsmEditor,
104709 uiImproveOsmHeader: uiImproveOsmHeader,
104711 uiInspector: uiInspector,
104712 uiIssuesInfo: uiIssuesInfo,
104713 uiKeepRightDetails: uiKeepRightDetails,
104714 uiKeepRightEditor: uiKeepRightEditor,
104715 uiKeepRightHeader: uiKeepRightHeader,
104718 uiMapInMap: uiMapInMap,
104721 uiNoteComments: uiNoteComments,
104722 uiNoteEditor: uiNoteEditor,
104723 uiNoteHeader: uiNoteHeader,
104724 uiNoteReport: uiNoteReport,
104726 uiPresetIcon: uiPresetIcon,
104727 uiPresetList: uiPresetList,
104731 uiSourceSwitch: uiSourceSwitch,
104736 uiTagReference: uiTagReference,
104740 uiViewOnOSM: uiViewOnOSM,
104741 uiViewOnKeepRight: uiViewOnKeepRight,
104743 utilAesEncrypt: utilAesEncrypt,
104744 utilAesDecrypt: utilAesDecrypt,
104745 utilArrayChunk: utilArrayChunk,
104746 utilArrayDifference: utilArrayDifference,
104747 utilArrayFlatten: utilArrayFlatten,
104748 utilArrayGroupBy: utilArrayGroupBy,
104749 utilArrayIdentical: utilArrayIdentical,
104750 utilArrayIntersection: utilArrayIntersection,
104751 utilArrayUnion: utilArrayUnion,
104752 utilArrayUniq: utilArrayUniq,
104753 utilArrayUniqBy: utilArrayUniqBy,
104754 utilAsyncMap: utilAsyncMap,
104755 utilCleanTags: utilCleanTags,
104756 utilCombinedTags: utilCombinedTags,
104757 utilDeepMemberSelector: utilDeepMemberSelector,
104758 utilDetect: utilDetect,
104759 utilDisplayName: utilDisplayName,
104760 utilDisplayNameForPath: utilDisplayNameForPath,
104761 utilDisplayType: utilDisplayType,
104762 utilDisplayLabel: utilDisplayLabel,
104763 utilEntityRoot: utilEntityRoot,
104764 utilEditDistance: utilEditDistance,
104765 utilEntitySelector: utilEntitySelector,
104766 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
104767 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
104768 utilFastMouse: utilFastMouse,
104769 utilFetchJson: utilFetchJson,
104770 utilFunctor: utilFunctor,
104771 utilGetAllNodes: utilGetAllNodes,
104772 utilGetSetValue: utilGetSetValue,
104773 utilHashcode: utilHashcode,
104774 utilHighlightEntities: utilHighlightEntities,
104775 utilKeybinding: utilKeybinding,
104776 utilNoAuto: utilNoAuto,
104777 utilObjectOmit: utilObjectOmit,
104778 utilPrefixCSSProperty: utilPrefixCSSProperty,
104779 utilPrefixDOMProperty: utilPrefixDOMProperty,
104780 utilQsString: utilQsString,
104781 utilRebind: utilRebind,
104782 utilSafeClassName: utilSafeClassName,
104783 utilSetTransform: utilSetTransform,
104784 utilSessionMutex: utilSessionMutex,
104785 utilStringQs: utilStringQs,
104786 utilTagDiff: utilTagDiff,
104787 utilTagText: utilTagText,
104789 utilTotalExtent: utilTotalExtent,
104790 utilTriggerEvent: utilTriggerEvent,
104791 utilUnicodeCharsCount: utilUnicodeCharsCount,
104792 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
104793 utilUniqueDomId: utilUniqueDomId,
104795 validationAlmostJunction: validationAlmostJunction,
104796 validationCloseNodes: validationCloseNodes,
104797 validationCrossingWays: validationCrossingWays,
104798 validationDisconnectedWay: validationDisconnectedWay,
104799 validationFormatting: validationFormatting,
104800 validationHelpRequest: validationHelpRequest,
104801 validationImpossibleOneway: validationImpossibleOneway,
104802 validationIncompatibleSource: validationIncompatibleSource,
104803 validationMaprules: validationMaprules,
104804 validationMismatchedGeometry: validationMismatchedGeometry,
104805 validationMissingRole: validationMissingRole,
104806 validationMissingTag: validationMissingTag,
104807 validationOutdatedTags: validationOutdatedTags,
104808 validationPrivateData: validationPrivateData,
104809 validationSuspiciousName: validationSuspiciousName,
104810 validationUnsquareWay: validationUnsquareWay
104813 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
104814 var start = Date.now();
104815 return window.requestAnimationFrame(function () {
104818 timeRemaining: function timeRemaining() {
104819 return Math.max(0, 50 - (Date.now() - start));
104825 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
104826 window.cancelAnimationFrame(id);